Migrating to the Java 11/17 runtimes allow you to use up-to-date language features and build apps that are more portable, with idiomatic code.
Understanding your migration options
To reduce runtime migration effort and complexity, the App Engine standard environment allows you to access many of legacy bundled services and APIs in the Java 11/17 runtimes, such as Memcache. Your Java 11/17 app can call the bundled services APIs through the App Engine API JAR, and access most of the same functionality as on the Java 8 runtime.
You also have the option to use Google Cloud products that offer similar functionality as the legacy bundled services. These Google Cloud products provide idiomatic Cloud Client Libraries for Java. 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.
To learn more about migrating to unbundled services, see Migrating from bundled services.
There are some differences in how you perform the runtime migration, based on whether you choose to use legacy bundled services:
Migrating to Java 11/17 with bundled services | Migrating to Java 11/17 without bundled services |
---|---|
Access bundled services using the App Engine APIs JAR. | Optionally, use recommended Google Cloud products or third-party services. |
Use
You may also need to configure additional YAML files depending on the features your app uses. |
Use
You may also need to configure additional YAML files depending on the features your app uses. |
Apps are deployed via Jetty. Use WAR format to package your app. | Apps are deployed using your own server. Use JAR format to package your app. To learn more about converting your existing WAR file to an executable JAR, see Re-packaging a WAR file. |
Overview of the migration process
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/17 runtimes:
- Download Google Cloud CLI.
- Migrate from the standalone App Engine Maven plugin to the gcloud CLI-based Maven plugin or the gcloud CLI-based Gradle plugin.
- Install the App Engine API JAR if you are using the legacy bundled services.
Key differences between the Java 8 and Java 11/17 runtimes
The following is a summary of the differences between the Java 8 and Java 11/17 runtimes on App Engine standard environment:
Java 8 runtime | Java 11/17 runtimes | |
---|---|---|
Server deployment | Server deployed for you via Jetty | If your app does not use the legacy bundled services, you have to deploy a server yourself.1 |
App Engine legacy bundled services | Provided | Provided |
Ability to use Cloud Client Libraries for Java | Yes | Yes |
Language extension and system library support | Yes | Yes |
External network access | Yes | Yes |
Files system access | Read/write access to /tmp
|
Read/write access to /tmp
|
Language runtime | Modified for App Engine | Unmodified, open-source runtime |
Isolation mechanism | gVisor-based container sandbox | gVisor-based container sandbox |
Testing with local development server | Supported | Supported |
Thread safety configuration | Can be specified in the appengine-web.xml file.
|
Can't be specified in the configuration files. All apps are presumed to be thread safe.3 |
Logging | Uses a java.util.logging.
ConsoleHandler , which writes to
stderr and flushes the stream
after each record.
|
Standard Cloud Logging 2 |
Notes:
If your app is not using the legacy bundled services, the Java 11/17 runtimes 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/17 runtimes can run a Spring Boot Uber JAR as is. For more examples, see the Framework flexibility section.If your app is using the legacy bundled services, App Engine deploys it using Jetty in the same way as in the Java 8 runtime.
Logging in the Java 11/17 runtimes follows the logging standard in Cloud Logging. In the Java 11/17 runtimes, app logs are no longer bundled with the request logs but are separated in different records. To learn more about reading and writing logs in the Java 11/17 runtimes, see the logging guide.
To configure a non-threadsafe app in the Java 11/17 runtime, similar to setting
<threadsafe>false</threadsafe>
in Java 8, set max concurrency to 1 in either yourapp.yaml
file, orappengine-web.xml
file if using the legacy bundled services.
Framework flexibility
The Java 11/17 runtimes do not include any web-serving framework unless you are using the legacy bundled services. This means that you can use a framework other than a servlet-based framework. If you are using the legacy bundled services, the Java 11/17 runtimes provide the Jetty web-serving framework.
There are hello world
samples using popular Java web frameworks on the
Google Cloud GitHub repository:
Migrating XML to YAML file formats
gcloud CLI 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 gcloud CLI 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>