Migrating to the Java 11 runtime

Migrating to the Java 11 runtime allows 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, App Engine standard environment allows you to access many of App Engine bundled services and APIs in the Java 11 runtime, such as Memcache. Your Java 11 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 App Engine 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 App Engine bundled services:

Migrating to Java 11 with bundled services Migrating to Java 11 without bundled services
Access bundled services using the App Engine APIs JAR. Optionally, use recommended Google Cloud products or third-party services.

Use appengine-web.xml and web.xml for app configuration.

You may also need to configure additional YAML files depending on the features your app uses.

Use app.yaml for app configuration.

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 runtime:

Key differences between the Java 8 and Java 11 runtimes

The following is a summary of the differences between the Java 8 and Java 11 runtimes on App Engine standard environment:

Java 8 runtime Java 11 runtime
Server deployment Server deployed for you via Jetty If your app does not use App Engine bundled services, you have to deploy a server yourself 1
Bundled services Provided Provided
Ability to use Cloud Client Libraries for Java No 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 Proprietary gVisor-based container sandbox
Testing with local development server Supported Supported
Logging Uses a java.util.logging.
ConsoleHandler, which writes to
stderr and flushes the stream
after each record.
Standard Cloud Logging 2

Notes:

  1. If your app is not using App Engine bundled services, 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 Uber JAR as is. For more examples, see the Framework flexibility section.

    If your app is using App Engine bundled services, App Engine deploys it using Jetty in the same way as in the Java 8 runtime.

  2. Logging in the Java 11 runtime follows the logging standard in Cloud Logging. In the Java 11 runtime, 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 runtime, see the logging guide.

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 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:

  1. You must have Cloud SDK version 226.0.0 or later. To update to the latest version:

    gcloud components update
    
  2. 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
    
  3. Manually double-check the converted file before deploying to production.

    For a successful sample xml to yaml 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>