Warning: The
Java 7 runtime is deprecated.
App deployments on the Java 7 runtime are blocked, and the Java SDK version
1.9.72 or newer does not allow the building of Java 7 apps.
If your app is currently using the Java 7 runtime, it will be automatically
migrated to the Java 8 runtime.
Google will continue to support the Java 7 runtime in accordance with the
terms of service. You should
migrate to the Java 8 runtime at your earliest convenience.
Introduction
App Engine runs your Java web application using a Java 8 JVM in a safe "sandboxed" environment. App Engine invokes your app's servlet classes to handle requests and prepare responses in this environment.
The App Engine Java 7 runtime, which is based on OpenJDK 7, uses the
Java Servlet 2.5 standard for web applications. You provide your app's servlet
classes, JavaServer Pages (JSPs), static files and data files, along with the
deployment descriptor (the web.xml file) and other configuration files, in a
standard WAR directory structure. App Engine serves requests by invoking
servlets according to the deployment descriptor.
The secured "sandbox" environment isolates your application for service and security. It ensures that apps can only perform actions that do not interfere with the performance and scalability of other apps. For instance, an app cannot spawn threads in some ways, write data to the local file system or make arbitrary network connections. An app also cannot use JNI or other native code. The JVM can execute any Java bytecode that operates within the sandbox restrictions.
The App Engine platform provides many services that your code can call. Your application can also configure scheduled tasks that run at specified intervals.
The Java Guestbook app tutorial provides an introduction to developing web applications with Java technologies and App Engine.
Selecting the Java runtime
The App Engine API for Java is represented by the appengine-api-*.jar
included with the App Engine SDK as part of the Cloud SDK
(where * represents the version of the API and the App Engine SDK).
You select the version of the API your application uses by including this JAR in the
application's WEB-INF/lib/ directory, or use Maven to handle dependencies. If a new
version of the Java runtime environment is released that introduces changes that
are not compatible with existing apps, that environment will have a new
version number.
Your application will continue to use the previous version
until you replace the JAR with the new version (from a newer App Engine
SDK) and re-upload the app.
Using Maven to handle dependencies
You can use Maven to manage all of the dependencies. For example, thispom.xml entry includes the latest App Engine API
(Rappengine-api-1.0-sdk) available from Maven Central:
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-1.0-sdk</artifactId>
<version>1.9.76</version>
</dependency>
The sandbox
To allow App Engine to distribute requests for applications across multiple web servers, and to prevent one application from interfering with another, the application runs in a restricted "sandbox" environment. In this environment, the application can execute code, store and query the data in Cloud Datastore, use the App Engine mail, URL fetch and users services, and examine the user's web request and prepare the response.An App Engine application cannot:
write to the filesystem. Applications must use Cloud Datastore for storing persistent data. Reading from the filesystem is allowed, and all application files uploaded with the application are available.
respond slowly. A web request to an application must be handled within a few seconds. Processes that take a very long time to respond are terminated to avoid overloading the web server.
make other kinds of system calls.
The filesystem
A Java application cannot use any classes used to write to the filesystem, such
as java.io.FileWriter. An application can read its own files from the
filesystem using classes such as java.io.FileReader. An application can also
access its own files as "resources", such as with Class.getResource() or
ServletContext.getResource().
Only files that are considered "resource files" are accessible to the
application via the filesystem. By default, all files in the WAR are
"resource files." You can exclude files from this set using the
appengine-web.xml file.
java.lang.System
Features of the java.lang.System class that do not apply to App Engine
are disabled.
The following System methods do nothing in App Engine: exit(), gc(),
runFinalization(), runFinalizersOnExit()
The following System methods return null: inheritedChannel(), console()
An app cannot provide or directly invoke any native JNI code. The following
System methods raise a java.lang.SecurityException: load(),
loadLibrary(), setSecurityManager()
Reflection
An application is allowed full, unrestricted, reflective access to its own classes.
It can query any private members, call the method
java.lang.reflect.AccessibleObject.setAccessible(), and read/set private
members.
An application can also reflect on JRE and API classes, such as
java.lang.String and javax.servlet.http.HttpServletRequest. However, it can
only access public members of these classes, not protected or private.
An application cannot reflect against any other classes not belonging to
itself, and it can not use the setAccessible() method to circumvent these
restrictions.
Custom class loading
Custom class loading is fully supported under App Engine. An application is allowed to define its own subclass of ClassLoader that implements application-specific class loading logic. Please be aware, though, that App Engine overrides all ClassLoaders to assign the same permissions to all classes loaded by your application. If you perform custom class loading, be cautious when loading untrusted third-party code.
Class loader JAR ordering
Sometimes, it might be necessary to redefine the order in which JAR files are
scanned for classes in order to resolve collisions between class names. In
these cases, loading priority can be granted to specific JAR files by adding a
<class-loader-config> element containing <priority-specifier> elements in
the appengine-web.xml file. For example:
<class-loader-config>
<priority-specifier filename="mailapi.jar"/>
</class-loader-config>
This places "mailapi.jar" as the first JAR file to be searched for classes,
barring those in the directory war/WEB-INF/classes/.
If multiple JAR files are prioritized, their original loading order (with
respect to each other) will be used. In other words, the order of the
<priority-specifier> elements themselves does not matter.
The JRE white list
Access to the classes in the Java standard library (the Java Runtime Environment, or JRE) is limited to the classes in the App Engine JRE White List.
No signed JAR files
App Engine's precompilation isn't compatible with signed JAR files. If your application is precompiled (the default), it can't load signed JAR files. If the application tries to load a signed JAR, at runtime App Engine will generate an exception like
java.lang.SecurityException: SHA1 digest error for com/example/SomeClass.class
at com.google.appengine.runtime.Request.process-d36f818a24b8cf1d(Request.java)
at sun.security.util.ManifestEntryVerifier.verify(ManifestEntryVerifier.java:210)
at java.util.jar.JarVerifier.processEntry(JarVerifier.java:218)
at java.util.jar.JarVerifier.update(JarVerifier.java:205)
at java.util.jar.JarVerifier$VerifierStream.read(JarVerifier.java:428)
at sun.misc.Resource.getBytes(Resource.java:124)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:273)
at sun.reflect.GeneratedMethodAccessor5.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at java.lang.ClassLoader.loadClass(ClassLoader.java:266)
There are two ways to work around this:
Threads
A Java application can create a new thread, but there are some restrictions on how to do it. These threads can't "outlive" the request that creates them.
An application can
- Implement
java.lang.Runnable. - Create a thread factory by calling
com.google.appengine.api.ThreadManager.currentRequestThreadFactory(). - Call the factory's
newRequestThreadmethod, passing in theRunnable,newRequestThread(runnable), or use the factory object returned bycom.google.appengine.api.ThreadManager.currentRequestThreadFactory()with anExecutorService(e.g., callExecutors.newCachedThreadPool(factory)).
However, you must use one of the methods on ThreadManager to create your
threads. You cannot invoke new Thread() yourself or use the default thread
factory.
An application can perform operations against the current thread, such as
thread.interrupt().
Each request is limited to 50 concurrent request threads. The Java runtime will
throw a java.lang.IllegalStateException if you try to create more than 50
threads in a single request.
When using threads, use high level concurrency
objects,
such as Executor and Runnable. Those take care of many of the subtle but
important details of concurrency like
Interrupts
and scheduling and
bookkeeping.
Background threads
Code running on manual or basic scaling instances can start a background thread that can outlive the request that spawns it. This allows instances to perform arbitrary periodic or scheduled tasks or to continue working in the background after a request has returned to the user.
Java concurrency can lead to issues that are hard to debug. To avoid using threads, consider Task Queues, Scheduled Tasks, or Pub/Sub.
A background thread's logging entries are independent of those of the spawning
thread. You can read more about background threads in App Engine's
ThreadManager
documentation.
import com.google.appengine.api.ThreadManager;
import java.util.concurrent.atomic.AtomicLong;
AtomicLong counter = new AtomicLong();
Thread thread = ThreadManager.createBackgroundThread(new Runnable() {
public void run() {
try {
while (true) {
counter.incrementAndGet();
Thread.sleep(10);
}
} catch (InterruptedException ex) {
throw new RuntimeException("Interrupted in loop:", ex);
}
}
});
thread.start();
If code running in an automatic scaling service attempts to start a background thread, it raises an exception.
The maximum number of concurrent background threads created by the App Engine API is 10 per instance. (This limit doesn't apply to regular Java threads unrelated to the App Engine API.)Tools
Supported IDEs
Cloud Tools for Eclipse adds new project wizards and debug configurations to your Eclipse IDE for App Engine projects. You can deploy your App Engine projects live to production from inside Eclipse.
Cloud Tools for IntelliJ enables you to run and debug App Engine applications inside IntelliJ IDEA. You can deploy your App Engine projects live to production without leaving the IDE.
Supported build tools
To speed up your development process, you can use the App Engine plugins for Apache Maven or Gradle:
- Cloud SDK-based Apache Maven plugin
- Cloud SDK-based Gradle plugin.
- App Engine Java SDK-based Apache Maven plugin
Local development server
The development server runs your application on your local computer for development and testing. The server simulates the Cloud Datastore services. The development server can also generate configuration for Cloud Datastore indexes based on the queries the app performs during testing.
AppCfg
AppCfg is included with the standalone App Engine SDK for Java. It is a multipurpose tool that handles command-line interaction with your application running on App Engine. AppCfg can upload your application to App Engine, or just update the Cloud Datastore index configuration so you can build new indexes before updating the code. It can also download the app's log data, so you can analyze your app's performance using your own tools.
Concurrency and latency
Your application's latency has the biggest impact on the number of instances needed to serve your traffic. If you process requests quickly, a single instance can handle a lot of requests.
Single-threaded instances can handle one concurrent request. Therefore, there is a direct relationship between the latency and number of requests that can be handled on the instance per second. For example, 10ms latency equals 100 request/second/instance.Multi-threaded instances can handle many concurrent requests. Therefore, there is a direct relationship between the CPU consumed and the number of requests/second.
Java apps support concurrent requests, so a single instance can handle new requests while waiting for other requests to complete. Concurrency significantly reduces the number of instances your app requires, but you need to design your app for multithreading.
For example, if a B4 instance (approx 2.4GHz) consumes 10 Mcycles/request, you can process 240 requests/second/instance. If it consumes 100 Mcycles/request, you can process 24 requests/second/instance. These numbers are the ideal case but are fairly realistic in terms of what you can accomplish on an instance.


