[ JagoPG Site ]
This publication is more than a year old. The information can be out-dated.

Deploy an application on AppEngine with Gradle and Java 11

Last week I had to deploy an application written with Java 11 to Google's App Engine. The project has Gradle as automation system. I found that following the guidelines of Google I was not able to deploy my application to the platform.

Then, in this post I provide the settings that worked for me, and a brief explanation about how the deployment works. In this guide I am going to consider that you have an understanding of the basic concepts of Google Cloud.

The theory

Check that you are running Java 11, and your JAVA_HOME environment variable is not empty.

When running the deployment task, the application is built, the plugin copies the packaged application and the app.yaml configuration file, and copies them to the build/staged-app folder. After that, both files are uploaded to Google Cloud.

AppEngine will clone an image of Java and will include you deployed files to generate a new service release. After that the service is launched.

If you require another version of Java, or a custom implementation, you can define a Dockerfile with your setup. Check the Google's documentation to check the steps to follow [4].

Project folder structure

Your project should look similar to the following folder structure:

MyApp
├── app.yaml
├── build
│   └── staged-app
│       ├── MyApp-1.0-SNAPSHOT.war
│       └── app.yaml
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
└── src
    ├── main
    │   ├── appengine
    │   │   └── app.yaml
    │   ├── java
    │   │   └── com
    │   │       └── example
    │   │           └── myapp
    │   │               └── MyApplication.java
    │   └── resources
    │       └── application.properties
    └── test
        └── java
            └── com
                └── example
                    └── myapp
                        └── MyApplicationTests.java

Configuration files

Modify the build.gradle file to contain a similar configuration as shown below. The app.yaml file just requires to have both the runtime and entrypoint properties.

  • build.grade file:
buildscript {
    repositories {
        maven {
            url "https://plugins.gradle.org/m2/"
        }
    }
    dependencies {
        classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.2.0'
        classpath "org.springframework.boot:spring-boot-gradle-plugin:2.2.5.RELEASE"
    }
}

apply plugin: 'org.springframework.boot'
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'com.google.cloud.tools.appengine'

dependencies {
    providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
    providedCompile 'com.google.appengine:appengine:+'
    compile 'jstl:jstl:1.2'

    runtime group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.3.1'
    runtime group: 'com.squareup.okhttp3', name : 'logging-interceptor', version :'4.3.1'
    runtime group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: '1.3.0'

    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }

    testCompile group: 'org.mockito', name: 'mockito-core', version: '2.+'
    testImplementation 'org.springframework.amqp:spring-rabbit-test'
    testCompile 'com.google.truth:truth:0.33'

    testCompile 'com.google.appengine:appengine-testing:+'
    testCompile 'com.google.appengine:appengine-api-stubs:+'
    testCompile 'com.google.appengine:appengine-tools-sdk:+'
}

test {
    useJUnitPlatform()
}

sourceSets {
    main {
        java.srcDirs = ['src/main/java']
    }
}

appengine {
    deploy {
        stopPreviousVersion = true
        promote = true
        projectId = System.getenv('GOOGLE_CLOUD_PROJECT')
        version = '1'
    }
}


group   = "com.example.myapp"
version = "1.0-SNAPSHOT"

sourceCompatibility = 1.11
targetCompatibility = 1.11
  • app.yaml file: notice that the extension of the executable file at the entrypoint property is war instead of jar. If you check the build\staged-app folder, you will find that a war file is created.
runtime: java11
entrypoint: java -noverify -jar MyApp-1.0-SNAPSHOT.war
service: 'myapp'
env_variables:
  SERVER_PORT: 8080
  ELASTICSEARCH_HOST: 127.0.0.1
  ELASTICSEARCH_PORT: 9200

Deployment commands

You can deploy directly using the Google SDK, or use an automation system as Gradle or Maven. The first method is useful to check if the app is working properly, the second method is a more production ready way to deploy the app.

Method 1: Using SDK

There is no need to set up Gradle. Just for checking that the application works in the platform. You can also set in the command to read the app.yaml file that contains the settings about how to run the app.

GOOGLE_CLOUD_PROJECT=flashdeals-dev-37743 gcloud app deploy build/libs/MyApp-1.0-SNAPSHOT.war --verbosity=info

Method 2: Using Gradle

Requires to set up build.gradle and app.yaml files. You have also to use same folder structure as shown in following sections.

GOOGLE_CLOUD_PROJECT=your-project-id gradle appengineDeploy --info

References

[1] Testing and deploying your application, https://cloud.google.com/appengine/docs/standard/java11/testing-and-deploying-your-app

[2] app.yaml configuration file, https://cloud.google.com/appengine/docs/standard/java11/config/appref

[3] GoogleCloudPlatform/gradle-appengine-plugin, https://github.com/GoogleCloudPlatform/gradle-appengine-plugin

[4] Using Gradle and App Engine Plugin, https://cloud.google.com/appengine/docs/standard/java11/using-gradle