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

Overview

--Create a simple Hello World display web application with Google App Engine Java 11 standard environment + Spring Boot configuration --The official release of the Google App Engine Java 11 standard environment was announced on October 30, 2019. --Java 11 release announced on September 25, 2018

environment

--Google App Engine Java 11 standard environment

Basic changes from Java 8 standard environment to Java 11

--The memory of the instance class has doubled (for example, F1 has changed from 128MB to 256MB) --Application configuration file changed from appengine-web.xml to app.yaml --You need to deploy an executable jar file, not a war file

Source code

Source code list

├── build.gradle
├── settings.gradle
└── src
    ├── main
    │   ├── appengine
    │   │   └── app.yaml
    │   ├── java
    │   │   └── com
    │   │       └── example
    │   │           └── helloworld
    │   │               ├── HelloworldApplication.java
    │   │               ├── HelloworldController.java
    │   │               └── HelloworldErrorController.java
    │   └── resources
    │       ├── application.yml
    │       ├── static
    │       │   └── assets
    │       │       └── helloworld.png
    │       └── templates
    │           ├── error.html
    │           └── helloworld.html
    └── 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'
  // 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'
  // 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 11
sourceCompatibility = '11'
targetCompatibility = '11'

//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'

app.yaml

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

#Use Java 11
runtime: java11

#Java 8 runtime F1 had 128MB of memory, but Java 11 has 256MB
instance_class: F1

#Autoscale setting for the number of instances
automatic_scaling:
  max_instances: 1 #Maximum number of instances
  min_instances: 0 #Minimum number of instances. If set to 0, the number of instances will be 0 when not in use.
  target_cpu_utilization: 0.95 #CPU load factor that triggers the launch of a new instance(0.5 to 0.Specified between 95)
  max_concurrent_requests: 80 #Allowed number of simultaneous requests(The maximum value that can be specified is 80)

#Set environment variables
env_variables:
  JAVA_TOOL_OPTIONS: "-XX:MaxRAM=256m -XX:ActiveProcessorCount=2 -Xmx32m"

reference:

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");

    //Set the data to be displayed
    ModelAndView mav = new ModelAndView();
    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.
    mav.setStatus(HttpStatus.NOT_FOUND);
    mav.setViewName("error"); // error.html
    return mav;
  }
}

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 accessing 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="'application.yml: ' + ${applicationYamlMessage}"></div>
<div><img src="./assets/helloworld.png "></div>
</body>
</html>

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 with gradle test

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

$ gradle test 

Start the server locally with gradle bootRun

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

$ gradle bootRun

The bootRun task is a feature of the Spring Boot Gradle Plugin, so it doesn't load the configuration file app.yaml for Google App Engine.

Deploy to Google App Engine with gradle appengineDeploy

You can deploy to Google App Engine with gradle appengineDeploy.

$ gradle appengineDeploy

Tasks available in Gradle

You can see the list of tasks available in gradle tasks.

$ gradle tasks

> Task :tasks

------------------------------------------------------------
Tasks runnable from root project
------------------------------------------------------------

App Engine app.yaml based projects tasks
----------------------------------------
appengineCloudSdkLogin - Login and set the Cloud SDK common configuration user
appengineDeploy - Deploy an App Engine application
appengineDeployAll - Deploy an App Engine application and all of its config files
appengineDeployCron - Deploy Cron configuration
appengineDeployDispatch - Deploy Dispatch configuration
appengineDeployDos - Deploy Dos configuration
appengineDeployIndex - Deploy Index configuration
appengineDeployQueue - Deploy Queue configuration
appengineShowConfiguration - Show current App Engine plugin configuration
appengineStage - Stage an App Engine app.yaml based project for deployment
checkCloudSdk - Validates the Cloud SDK
downloadCloudSdk - Download the Cloud SDK

Application tasks
-----------------
bootRun - Runs this project as a Spring Boot application.
(The following is omitted)

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
Using properties files with Flexible Environment Java 8 on Google App Engine
I can't deploy with google app engine
Hello, World with Docker
Hello world with flask
PIL with Python on Windows 8 (for Google App Engine)
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
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