In addition to supporting the Java 8 runtime, App Engine now supports hosting your applications using the Java 11 runtime. Learn how to migrate your existing App Engine app from Java 8 to Java 11 on the standard environment.
Key differences between the Java 8 and Java 11 runtimes
Starting with the Java 11 runtime, the App Engine standard environment no longer includes bundled App Engine services such as Memcache and Task Queues. Instead, Google Cloud provides standalone products that are equivalent to most of the services that were previously bundled with the Java 8 runtime. Each of these Google Cloud products provides idiomatic Cloud Java Client Libraries. For the bundled services that are not available as separate products in Google Cloud, such as image processing, search, and messaging, you can use third-party providers or other workarounds as suggested in this migration guide.
Removing the bundled App Engine services enables the Java 11 runtime to support a fully idiomatic Java development experience. In the Java 11 runtime, you write a standard Java app that is fully portable and can run in any standard Java environment, including App Engine.
In the Java 11 runtime, configuration files are in the YAML format instead of the XML format. For example, you specify settings for your app in the
app.yaml
file instead of theappengine-web.xml
file. You need to manually convertappengine-web.xml
file toapp.yaml
file. For more information, see Creating anapp.yaml
file.For information about converting other types of App Engine configuration files to YAML, see Migrating XML to YAML file formats
The Java 11 runtime can execute any Java framework as long as you package a web server that is configured to respond to HTTP requests on the port specified by the
PORT
environment variable (recommended) or on port 8080. For example, the Java 11 runtime can run a Spring Boot UberJAR as is.
Creating an app.yaml
file
In the Java 11 runtime, application settings are stored in the app.yaml
file. You must remove the appengine-web.xml
file from your existing
application and replace it with an app.yaml
file.
The only required element in an app.yaml
file is the runtime
element:
App Engine provides default values for all other settings, including the F1 instance class, which determines the memory and CPU resources that are available to your app, and automatic scaling, which controls how and when new instances of your app are created.
The gcloud app deploy
command can create an app.yaml
file when you
deploy your app.
The app.yaml
file that App Engine creates contains only the runtime
entry, and your app will use default values for all settings.
If you need to override the default settings, create an app.yaml
file and
specify the settings you need. For more information, see
app.yaml
file reference.
For a Maven project, the standard location for app.yaml
file is under the
src/main/appengine
directory. The App Engine Maven plugin will
create a correct target/appengine-staging
directory containing your JAR
artifacts and this app.yaml
file, ready for deployment.
The following is a sample Maven project structure:
MyDir/
pom.xml
[index.yaml]
[cron.yaml]
[dispatch.yaml]
src/main/
appengine/
app.yaml
java/com.example.mycode/
MyCode.java
If you have more than one JAR file in your project directory or want to
specify a custom entrypoint, you must specify it in the entrypoint
element of
your app.yaml
file.
Re-packaging a WAR file into a JAR file
To migrate to the Java 11 runtime, you must re-package your App Engine Java 8 web application into an executable JAR file.
Your application must have a Main
class that starts a web server which
responds to HTTP requests on the port specified by the PORT
environment
variable, typically 8081.
For example:
WAR migration example
The following instructions demonstrate how to repackage an App Engine
Java 8 hello-world
application as a JAR to run on the Java 11 runtime.
The migration uses the appengine-simple-jetty-main
artifact. This provides a
Main
class with a simple Jetty web server that loads a WAR file and packages
your app into an executable JAR file:
Clone the Embedded Jetty Server artifact to your local machine:
git clone https://github.com/GoogleCloudPlatform/java-docs-samples
Alternatively, you can download the sample as a zip file and extract it.
Change to the directory that contains the sample code:
cd java-docs-samples/appengine-java11/appengine-simple-jetty-main/
Install the dependency locally:
mvn install
Add the following code to your project
pom.xml
file:appengine-simple-jetty-main
dependency:maven-dependency
plugin: App Engine deploys files located in the${build.directory}/appengine-staging
directory. By adding themaven-dependency
plugin to your build, App Engine installs your specified dependencies to the correct folder.
Create an
entrypoint
element in yourapp.yaml
file to call theappengine-simple-jetty-main
object and pass your WAR file as an argument. For example, see thehelloworld-servlet
sampleapp.yaml
file:To run your application locally:
Package your application:
mvn clean package
Start the server with your WAR file as an argument.
For example, you can start the server in the
helloworld-servlet
sample by running the following command from yourjava-docs-samples/appengine-java11/appengine-simple-jetty-main/
folder:mvn exec:java -Dexec.args="../helloworld-java8/target/helloworld.war"
In your web browser, enter the following address:
To deploy your application:
gcloud tooling
gcloud app deploy
Maven plugin
mvn package appengine:deploy -Dapp.deploy.projectId=PROJECT_ID
Replace PROJECT_ID with the ID of your Cloud project. If your
pom.xml
file already specifies your project ID, you don't need to include the-Dapp.deploy.projectId
property in the command you run.
Framework flexibility
The Java 11 runtime does not include any web-serving framework, meaning you're no longer limited to servlet-based frameworks.
There are hello world
samples using popular Java web frameworks on the
Google Cloud GitHub repository:
Migrating from the App Engine Java SDK
The Java 11 runtime does not support the App Engine Java SDK. The following lists some changes you may have to make to your existing App Engine Java 8 app and your deployment process in order to use the App Engine Java 11 runtime:
- Download Cloud SDK.
- Migrate from AppCfg to gcloud command line.
- Migrate to the Cloud SDK-based Maven plugin.
- Migrate to the Cloud SDK-based Gradle plugin.
- Migrate your
xml
toyaml
files automatically using thegcloud beta app migrate-config
tool or migrate manually.
Migrating from the App Engine-specific APIs
Most of the functionality provided by the App Engine-specific APIs is now provided by the Google Cloud Client Library for Java. For more information, see the recommended alternatives listed below.
Blobstore
To store and retrieve data, use Cloud Storage through the Cloud Client Libraries. To get started, see Using Cloud Storage.
Datastore
For an alternative to a NoSQL key/value database like the Datastore API, use Cloud Firestore in Datastore mode. Firestore is the newest version of Datastore, and Datastore mode is recommended for databases that will be used primarily by App Engine apps.Images
You can serve images from Cloud Storage, serve them directly, or use a third-party content delivery network (CDN).
To resize, convert, and manipulate images, use an image processing library such as ImageJ2, imgscalr, or thumbnailator. To use one of these third-party libraries, add the library as a dependency and update your code to call the library's APIs.
The App Engine Images service also provided functionality to avoid dynamic requests to your application by handling image resizing using a serving URL. If you want similar functionality, you can generate the re-sized images ahead of time and upload them to Cloud Storage for serving. Alternatively, you could use a third-party content delivery network (CDN) service that offers image resizing.
Logging
We recommend that you update your app to use Cloud Logging, which supports features such as viewing logs in the Logs Viewer, downloading logs, filtering messages by severity, and correlating app messages with specific requests. As an alternative, you can enable these features by writing log messages that contain specific data structured in a JSON object.
For more information, see Writing and viewing logs.To send email, use a third-party mail provider such as SendGrid, Mailgun, or Mailjet. All of these services offer APIs to send emails from applications.
Memcache
To cache application data, use Memorystore for Redis.Modules
To obtain information and modify your application's running services, use a combination of environment variables and the App Engine Admin API:
Service information | How to access |
---|---|
Current application ID | GAE_APPLICATION environment variable |
Current project ID | GOOGLE_CLOUD_PROJECT environment variable |
Current service name | GAE_SERVICE environment variable |
Current service version | GAE_VERSION environment variable |
Current instance ID | GAE_INSTANCE environment variable |
Default hostname | Admin API apps.get method |
List of services | Admin API apps.services.list method |
List of versions for a service | Admin API apps.services.versions.list method |
Default version for a service, including any traffic splits | Admin API apps.services.get method |
List of running instances for a version | Admin API apps.services.versions.instances.list method |
Namespaces
The Namespaces API enabled multitenant apps to partition data across tenants simply by specifying a unique namespace string for each tenant.
While Datastore supports multitenancy directly, other Google Cloud services do not. If your multitenant app uses other Google Cloud services, you will need to handle multitenancy manually. To have completely isolated instances of services, you can create new projects programmatically using the Cloud Resource Manager API and access resources across projects.
OAuth
Instead of using the App Engine OAuth service to verify OAuth 2.0 tokens,
use the
oauth2.tokeninfo
method of the
OAuth 2.0 API.
Search
Host any full-text search database such as Elasticsearch on Compute Engine and access it from your service.
Task queue
Queue tasks for asynchronous code execution using the Cloud Tasks REST API, RPC API, or the Google Cloud Client library, and use a Java 11 App Engine standard service as a Push target. For more information, see Migrating from Task Queues to Cloud Tasks.In many cases where you might use pull queues, such as queuing up tasks or messages that will be pulled and processed by separate workers, Pub/Sub can be a good alternative as it offers similar functionality and delivery guarantees. See the following sample program interacting with the Cloud Tasks API.
User authentication
For an alternative to the Users API, use any HTTP-based authentication mechanism such as:
- OAuth 2.0 and OpenID Connect which provide federated identity from the provider of your choice. Google is an OpenID Connect identity provider. There are also several other providers available. This sample implements an OAuth 2.0 flow using the Google OAuth Client Library for Java.
- Firebase Authentication, which provides authentication using username/password and federated identity using Google, Facebook, Twitter, and more.
- Google Identity Platform, which provides many options for authentication and authorization of Google user accounts.
- Auth0, which provides authentication with various identity providers and single sign-on features.
Migrating XML to YAML file formats
Cloud SDK does not support the following file formats:
cron.xml
datastore-index.xml
dispatch.xml
queue.xml
The following examples demonstrate how to migrate your xml
files to
yaml
files.
Migrating your files automatically
To migrate your xml
files automatically:
You must have Cloud SDK version 226.0.0 or later. To update to the latest version:
gcloud components update
For each file you'd like to migrate, specify one of the following subcommands (
cron-xml-to-yaml
,datastore-indexes-xml-to-yaml
,dispatch-xml-to-yaml
,queue-xml-to-yaml
) and file name:gcloud beta app migrate-config queue-xml-to-yaml MY-QUEUE-XML-FILE.xml
Manually double-check the converted file before deploying to production.
For a successful sample
xml
toyaml
file conversion, see the Migrating your files manually tabs.
Migrating your files manually
To manually migrate your xml
files to yaml
files:
cron.yaml
Create a cron.yaml
file with a cron
object containing a list of objects,
each with fields that correspond to each of the <cron>
tag attributes in
your cron.xml
file, as shown below.
Converted cron.yaml
file:
cron:
- url: '/recache'
schedule: 'every 2 minutes'
description: 'Repopulate the cache every 2 minutes'
- url: '/weeklyreport'
schedule: 'every monday 08:30'
target: 'version-2'
timezone: 'America/New_York'
description: 'Mail out a weekly report'
Original cron.xml
file:
<?xml version="1.0" encoding="UTF-8"?>
<cronentries>
<cron>
<url>/recache</url>
<description>Repopulate the cache every 2 minutes</description>
<schedule>every 2 minutes</schedule>
</cron>
<cron>
<url>/weeklyreport</url>
<description>Mail out a weekly report</description>
<schedule>every monday 08:30</schedule>
<timezone>America/New_York</timezone>
<target>version-2</target>
</cron>
</cronentries>
For more information, see the cron.yaml
reference
documentation.
dispatch.yaml
Create a dispatch.yaml
file with a dispatch
object containing a list of
objects, each with fields that correspond to each of the <dispatch>
tag
attributes in your dispatch.xml
file, as shown below.
Converted dispatch.yaml
file:
dispatch:
- url: '*/favicon.ico'
module: default
- url: 'simple-sample.uc.r.appspot.com/'
module: default
- url: '*/mobile/*'
module: mobile-frontend
Original dispatch.xml
file
<?xml version="1.0" encoding="UTF-8"?>
<dispatch-entries>
<dispatch>
<url>*/favicon.ico</url>
<module>default</module>
</dispatch>
<dispatch>
<url>simple-sample.uc.r.appspot.com/</url>
<module>default</module>
</dispatch>
<dispatch>
<url>*/mobile/*</url>
<module>mobile-frontend</module>
</dispatch>
</dispatch-entries>
For more information, see the dispatch.yaml
reference
documentation
index.yaml
Create an index.yaml
file with an indexes
object containing a list of
objects, each with fields that correspond to each of the <datastore-index>
tag attributes in your datastore-indexes.xml
file, as shown below.
Converted index.yaml
file:
indexes:
- ancestor: false
kind: Employee
properties:
- direction: asc
name: lastName
- direction: desc
name: hireDate
- ancestor: false
kind: Project
properties:
- direction: asc
name: dueDate
- direction: desc
name: cost
Original datastore-index.xml
file:
<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes
autoGenerate="true">
<datastore-index kind="Employee" ancestor="false">
<property name="lastName" direction="asc" />
<property name="hireDate" direction="desc" />
</datastore-index>
<datastore-index kind="Project" ancestor="false">
<property name="dueDate" direction="asc" />
<property name="cost" direction="desc" />
</datastore-index>
</datastore-indexes>
For more information, see the index.yaml
reference
documentation.
queue.yaml
Create a queue.yaml
file with a queue
object containing a list of
objects, each with fields that correspond to each of the <queue>
tag
attributes in your queue.xml
file, as shown below.
Converted queue.yaml
file:
queue:
- name: fooqueue
mode: push
rate: 1/s
retry_parameters:
task_retry_limit: 7
task_age_limit: 2d
- name: barqueue
mode: push
rate: 1/s
retry_parameters:
min_backoff_seconds: 10
max_backoff_seconds: 200
max_doublings: 0
Original queue.xml
file:
<queue-entries>
<queue>
<name>fooqueue</name>
<rate>1/s</rate>
<retry-parameters>
<task-retry-limit>7</task-retry-limit>
<task-age-limit>2d</task-age-limit>
</retry-parameters>
</queue>
<queue>
<name>barqueue</name>
<rate>1/s</rate>
<retry-parameters>
<min-backoff-seconds>10</min-backoff-seconds>
<max-backoff-seconds>200</max-backoff-seconds>
<max-doublings>0</max-doublings>
</retry-parameters>
</queue>
<queue-entries>