Hello World with Google App Engine (Java 8) + Spring Boot + Gradle

Overview

--Create a simple Hello World display web application with Google App Engine Java 8 standard environment + Spring Boot configuration

environment

--Google App Engine Java 8 standard environment

Source code

Source code list

├── build.gradle
├── settings.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── example
    │   │           └── helloworld
    │   │               ├── HelloworldApplication.java
    │   │               ├── HelloworldController.java
    │   │               ├── HelloworldErrorController.java
    │   │               └── HelloworldServletInitializer.java
    │   ├── resources
    │   │   ├── application.yml
    │   │   ├── static
    │   │   │   └── assets
    │   │   │       └── helloworld.png
    │   │   └── templates
    │   │       ├── error.html
    │   │       └── helloworld.html
    │   └── webapp
    │       └── WEB-INF
    │           └── appengine-web.xml
    └── test
        └── java
            └── com
                └── example
                    └── helloworld
                        └── HelloworldApplicationTests.java

build.gradle

A file that describes the process related to build in Gradle. Google App Engine Gradle plugin uses version 2 series.

build.gradle


buildscript {
  repositories {
    mavenCentral()
  }
  dependencies {
    //Use Spring Boot Gradle Plugin
    classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.2.0.RELEASE'
    //Use Google App Engine Gradle plugin
    classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.2.0'
  }
}

plugins {
  //Introduced Java plugin
  id 'java'
  //Introduced War plugin
  id 'war'
  // https://plugins.gradle.org/plugin/org.springframework.boot
  id 'org.springframework.boot' version '2.2.0.RELEASE'
  // https://plugins.gradle.org/plugin/io.spring.dependency-management
  id 'io.spring.dependency-management' version '1.0.8.RELEASE'
}

//Introduced App Engine plugin
apply plugin: 'com.google.cloud.tools.appengine'

repositories {
  mavenCentral()
}

dependencies {
  //Latest version of App Engine API
  implementation 'com.google.appengine:appengine-api-1.0-sdk:+'
  // Thymeleaf
  implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
  // Spring Web
  implementation 'org.springframework.boot:spring-boot-starter-web'
  //Embedded Tomcat is not used when deploying
  providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
  // Test
  testImplementation('org.springframework.boot:spring-boot-starter-test') {
    //Exclude support for JUnit 4
    exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
  }
}

test {
  //Enable JUnit 5 support
  useJUnitPlatform()

  testLogging {
    //Display standard output and standard error output during testing
    showStandardStreams true
    //Output event(TestLogEvent)
    events 'started', 'skipped', 'passed', 'failed'
  }
}

//Web application group ID and version
group   = "com.example.helloworld"
version = "0.0.1"

//Uses Java 8
sourceCompatibility = '1.8'
targetCompatibility = '1.8'

//Google App Engine task settings
appengine {
  //Deployment settings
  // GCLOUD_If you specify CONFIG
  //The project information set in gcloud config is set
  deploy {
    //Deploy to Google Cloud Project ID
    projectId = "GCLOUD_CONFIG"
    //Web app version reflected by deployment
    //If not specified, a new one will be generated
    version = "GCLOUD_CONFIG"
  }
}

//Run tests before deploy
appengineDeploy.dependsOn test
appengineStage.dependsOn test

reference:

settings.gradle

If you do not set setting.gradle, you will go to the parent directory to find setting.gradle, so put it in a single project as well.

settings.gradle


rootProject.name = 'helloworld'

HelloworldApplication.java

Application class. I'm writing only routine processes for using Spring Boot.

package com.example.helloworld;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class HelloworldApplication {

  public static void main(String[] args) {
    SpringApplication.run(HelloworldApplication.class, args);
  }
}

HelloworldController.java

Controller class.

package com.example.helloworld;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HelloworldController {

  /**
   * application.Message obtained from yml.
   */
  @Value("${application.message}")
  private String applicationYamlMessage;

  /**
   *Returns the response of the top page.
   *
   * @return page display information
   */
  @GetMapping("/")
  public ModelAndView index() {

    System.out.println("HelloworldController#index");

    //Obtained from system properties
    String systemPropertyMessage = System.getProperty("com.example.helloworld.message");

    //Set the data to be displayed
    ModelAndView mav = new ModelAndView();
    mav.addObject("systemPropertyMessage", systemPropertyMessage);
    mav.addObject("applicationYamlMessage", applicationYamlMessage);
    mav.setViewName("helloworld"); //View name. Specify Thymeleaf template file

    return mav;
  }

  /**
   *A test method that displays an error page.
   */
  @GetMapping("/exception/")
  public void exception() {
    System.out.println("HelloworldController#exception");
    throw new RuntimeException("This is a sample exception.");
  }
}

HelloworldErrorController.java

Error controller class for the entire web application. It handles Not Found etc. that cannot be captured by a general controller class. Only the minimum processing is described here, but customize it as needed.

package com.example.helloworld;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;

/**
 *Error controller for the entire web application.
 *Implementation class of the ErrorController interface.
 */
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}") //Mapping to error page
public class HelloworldErrorController implements ErrorController {

  /**
   *The path of the error page.
   */
  @Value("${server.error.path:${error.path:/error}}")
  private String errorPath;

  /**
   *Returns the path of the error page.
   *
   * @return Error page path
   */
  @Override
  public String getErrorPath() {
    return errorPath;
  }

  /**
   *Returns a ModelAndView object for the response.
   *
   * @param req request information
   * @param mav response information
   * @ModelAndView object for return HTML response
   */
  @RequestMapping
  public ModelAndView error(HttpServletRequest req, ModelAndView mav) {

    System.out.println("HelloWorldErrorController#error");

    //404 Not Found for any error
    //The stator code and output contents can be customized as needed.
    HttpStatus status = HttpStatus.NOT_FOUND;
    mav.setStatus(status);
    mav.setViewName("error"); // error.html
    return mav;
  }
}

HelloworldServletInitializer.java

WebApplicationInitializer implementation class required in the environment where the WAR file is deployed and operated. Google App Engine requires this class to deploy and run WAR files.

package com.example.helloworld;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

public class HelloworldServletInitializer extends SpringBootServletInitializer {

  @Override
  protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
    System.out.println("HelloworldServletInitializer#configure");
    return application.sources(HelloworldApplication.class);
  }
}

Reference: [SpringBootServletInitializer \ (Spring Boot Docs 2 \ .2 \ .0 \ .RELEASE API )](https://docs.spring.io/spring-boot/docs/2.2.0.RELEASE/api/org/ springframework / boot / web / servlet / support / SpringBootServletInitializer.html)

application.yml

A file that describes web application configuration information. It can also be application.properties. This time, only the information unique to the application is set.

application:
  message: Hello, application yaml.

helloworld.png

An image placed as a sample showing the static file storage. If you put static files in src / main / resources / static, they will be mapped to http: // hostname /. This time, when you access http://hostname/assets/helloworld.png, the file src / main / resources / static / assets / helloworld.png is delivered.

error.html

HTML Thymeleaf template file to display when an error occurs. This time, the dynamic value is not embedded, but it is possible to set the value in the error controller class as needed and customize it to be displayed on the template side.

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>404 Not Found</title>
</head>
<body>
<h1>404 Not Found</h1>
</body>
</html>

helloworld.html

An HTML Thymeleaf template file for displaying the values set by the controller class.

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
  <meta charset="UTF-8">
  <title>Hello, world.</title>
</head>
<body>
<h1>Hello, world.</h1>
<div th:text="'System Property: ' + ${systemPropertyMessage}"></div>
<div th:text="'application.yml: ' + ${applicationYamlMessage}"></div>
<div><img src="./assets/helloworld.png "></div>
</body>
</html>

appengine-web.xml

Configuration file for Google App Engine. Describe the information of the Web application.

<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">

  <!-- Java VM -->
  <runtime>java8</runtime>

  <!--Threadsafe settings-->
  <threadsafe>true</threadsafe>

  <!--Autoscale setting for the number of instances-->
  <automatic-scaling>
    <!--Launch a new instance when the CPU load factor specified here is exceeded-->
    <target-cpu-utilization>0.95</target-cpu-utilization>
    <!--Minimum number of instances. If set to 0, the number of instances will be 0 when not in use.-->
    <min-instances>0</min-instances>
    <!--Maximum number of instances-->
    <max-instances>1</max-instances>
    <!--Allowed number of simultaneous requests-->
    <max-concurrent-requests>80</max-concurrent-requests>
  </automatic-scaling>

  <!--Static file-->
  <static-files>
    <include path="/assets/**.*"/>
  </static-files>

  <!--System properties enabled when running on Google App Engine or with gradle appengineRun-->
  <system-properties>
    <property name="com.example.helloworld.message" value="Hello, system property."/>
  </system-properties>
</appengine-web-app>

reference: appengine-web.xml reference|Java 8 App Engine standard environment|  Google Cloud

HelloworldApplicationTests.java

Minimal test class. Only formal things are described. Doesn't actually test anything.

package com.example.helloworld;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class HelloworldApplicationTests {

  @Test
  void contextLoads() {
  }
}

operation

Run the test

You can run the test with gradle's test task.

$ gradle test 

Start the server locally with gradle appengineRun

You can start the local server http: // localhost: 8080 / with gradle appengineRun.

$ gradle appengineRun

Deploy to Google App Engine with gradle appengineDeploy

You can deploy to Google App Engine with gradle appengineDeploy.

$ gradle appengineDeploy

Other

Web application-wide error handling does not apply

In some cases, the error controller does not use its own error handling.

Deployment descriptor: web.xml  |Java 8 App Engine standard environment|  Google Cloud

Note: Currently, some error conditions do not allow you to configure a custom error handler. Specifically, you cannot customize an HTTP 404 response page if a Servlet mapping is not defined for a particular URL. You also can't customize the "403 Allocation Error" page or the "500 Server Error" page that appears due to an App Engine internal error.

When I actually tried it, 404 Not Found wasn't my own customization on the server I set up locally with gradle appengineRun. The gradle appengineDeploy deployed to Google App Engine displayed my own customized 404 Not Found.

Reference material

Recommended Posts

Hello World with Google App Engine (Java 8) + Spring Boot + Gradle
Hello World with Google App Engine (Java 11) + Spring Boot + Gradle
Hello World with Google App Engine (Java 8) + Servlet API 3.1 + Gradle
Google App Engine development with Docker
Java 1 1 support from Google App Engine
Building a development environment with Maven on Google App Engine [Java]
Using properties files with Flexible Environment Java 8 on Google App Engine
I can't deploy with google app engine
hello world with ctypes
Hello, World with Docker
Hello world with flask
PIL with Python on Windows 8 (for Google App Engine)
Getting Started with Google App Engine for Python & PHP
Draw hello world with mod_wsgi
Hello World with Flask + Hamlish
Until hello world with zappa
I tried Google Sign-In with Spring Boot + Spring Security REST API
Deploy your Go app on Google App Engine with GitHub Actions
Python starting with Hello world!
Hello, world! With virtual CAN communication
[Note] Hello world output with python
Use ndb.tasklet on Google App Engine
Hello World! By QPython with Braincrash
Deploy a Python app on Google App Engine and integrate it with GitHub
Hello World and face detection with opencv-python 4.2
Java --Deploy Spring Boot project to GAE
Hello World with Raspberry Pi + Minecraft Pi Edition
Access Azure Cosmos DB with Spring Boot
[Python] Run Flask on Google App Engine
[Google App Engine] User Objects (Japanese translation)
Use external modules on Google App Engine
Hello World! By QPython with Brainfu * k
I tried Java8 + Spring Boot with GAE SE (and about its development environment)
A web app that just does Hello World with Go's net / http package