I finally migrated a long-running Google App Engine Java 8 project to Java 11+ standard environment before loss of Java 8 support in Jan 2024.
But I need to use the legacy bundled services including Datastore, Blobstore and Task Queues. I should do this by 'Installing the App Engine API Jar.'
I followed the following Google tutorial https://cloud.google.com/appengine/docs/standard/java-gen2/services/access
In particular I need to 'Install the App Engine API JAR' by adding the following to the app-engine.web.xml file:
<app-engine-apis>true</app-engine-apis>
But trying to build/deploy the project via the Gradle plugin in Android Studio gets the exception 'Unrecognized element <app-engine-apis>'.
So alas - I can't deploy to the new Java 11 server environment while preserving the old bundled legacy functions.
Note: because I use the Gradle plugin with Android Studio to build and deploy the server, I don't have a pom.xml file used by Maven.
Any advice on how to build the gradle file using the necessary '<app-engine-apis>true' would be greatly appreciated! Specs below:
Android Studio Giraffe 2022.3.1 Patch 4 with Gradle 7.4.2
Running server on GAE Standard Environment with Objectify 5
AppEngine Gradle file
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'com.google.cloud.tools.appengine'
apply plugin: 'com.google.cloud.tools.endpoints-framework-server'
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.google.cloud.tools:endpoints-framework-gradle-plugin:2.1.0'
classpath 'com.google.cloud.tools:appengine-gradle-plugin:2.4.2'
}
}
repositories {
google()
mavenCentral()
}
appengine {
deploy {
version = "GCLOUD_CONFIG"
projectId = "GCLOUD_CONFIG"
}
}
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
dependencies {
implementation (group: 'com.google.appengine', name: 'appengine-api-1.0-sdk', version: '2.0.0')
implementation 'com.google.endpoints:endpoints-framework:2.2.2'
implementation ('com.google.http-client:google-http-client-gson:1.40.1') {
exclude (group: 'httpclient', module: 'httpclient')
}
implementation 'javax.inject:javax.inject:1'
implementation 'javax.servlet:servlet-api:3.0-alpha-1'
implementation 'javax.activation:javax.activation-api:1.2.0'
implementation ('com.google.firebase:firebase-admin:8.1.0') {
exclude (group: 'com.google.guava')
}
implementation 'com.google.guava:guava:31.0.1-jre'
implementation (group: 'com.google.appengine.tools', name: 'appengine-gcs-client', version: '0.8.2')
implementation 'com.googlecode.objectify:objectify:5.1.25'
implementation "com.stripe:stripe-java:20.90.0"
}
app-engine.web.xml
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>my-fashion-project</application>
<version>1</version>
<runtime>java11</runtime>
<app-engine-apis>true</app-engine-apis> // THIS IS THE LINE THAT CAUSES EXCEPTION
<threadsafe>true</threadsafe>
<url-stream-handler>urlfetch</url-stream-handler>
<system-properties>
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties" />
</system-properties>
<sessions-enabled>
true
</sessions-enabled>
<resource-files>
<include path="/**.json" />
</resource-files>
<automatic-scaling>
<min-instances>1</min-instances>
</automatic-scaling>
</appengine-web-app>
web.xml
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
<app-engine-apis>true</app-engine-apis>
<filter>
<filter-name>ObjectifyFilter</filter-name>
<filter-class>com.googlecode.objectify.ObjectifyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ObjectifyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>EndpointsServlet</servlet-name>
<servlet-class>com.google.api.server.spi.EndpointsServlet</servlet-class>
<init-param>
<param-name>services</param-name>
<param-value>
com.myfashionserver.entities.MemberEndpoint,
............ (list of entity endpoints excluded) ............
</param-value>
</init-param>
</servlet>
<servlet-mapping>
............ (list of servlets excluded) ............
<security-constraint>
<web-resource-collection>
<web-resource-name>queue</web-resource-name>
<url-pattern>/queue/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
<welcome-file-list>
<welcome-file>blog.html</welcome-file>
</welcome-file-list>
</web-app>
Android Studio Gradle build exception log
com.google.apphosting.utils.config.AppEngineConfigException: Unrecognized element <app-engine-apis>
at com.google.apphosting.utils.config.AppEngineWebXmlProcessor.processSecondLevelNode(AppEngineWebXmlProcessor.java:243)
at com.google.apphosting.utils.config.AppEngineWebXmlProcessor.processXml(AppEngineWebXmlProcessor.java:64)
at com.google.apphosting.utils.config.AppEngineWebXmlReader.processXml(AppEngineWebXmlReader.java:132)
at com.google.apphosting.utils.config.AppEngineWebXmlReader.readAppEngineWebXml(AppEngineWebXmlReader.java:76)
at com.google.api.server.spi.tools.AppEngineUtil.getAppProperty(AppEngineUtil.java:138)
at com.google.api.server.spi.tools.AppEngineUtil.getApplicationId(AppEngineUtil.java:57)
at com.google.api.server.spi.tools.AppEngineUtil.getApplicationDefaultHostname(AppEngineUtil.java:96)
at com.google.api.server.spi.tools.EndpointsToolAction.getHostname(EndpointsToolAction.java:233)
at com.google.api.server.spi.tools.GetDiscoveryDocAction.execute(GetDiscoveryDocAction.java:85)
at com.google.api.server.spi.tools.EndpointsTool.execute(EndpointsTool.java:84)
at com.google.cloud.tools.gradle.endpoints.framework.server.task.EndpointsArtifactTask.generateEndpointsArtifact(EndpointsArtifactTask.java:208)
at java.base@17.0.6/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base@17.0.6/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at java.base@17.0.6/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.base@17.0.6/java.lang.reflect.Method.invoke(Unknown Source)
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:125
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:58)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:51)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:29)
at org.gradle.api.internal.tasks.execution.TaskExecution$3.run(TaskExecution.java:236)
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:157)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:68)
at org.gradle.api.internal.tasks.execution.TaskExecution.executeAction(TaskExecution.java:221)
at org.gradle.api.internal.tasks.execution.TaskExecution.executeActions(TaskExecution.java:204)
at org.gradle.api.internal.tasks.execution.TaskExecution.executeWithPreviousOutputFiles(TaskExecution.java:187)
at org.gradle.api.internal.tasks.execution.TaskExecution.execute(TaskExecution.java:165)
at org.gradle.internal.execution.steps.ExecuteStep.executeInternal(ExecuteStep.java:89)
at org.gradle.internal.execution.steps.ExecuteStep.access$000(ExecuteStep.java:40)
at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:53)
at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:50)
App Engine server exception log when trying to call API methods that use Datastore with Objectify 5. Error occurs when deployed the project in the Java 11+ environment without the <app-engine-apis>true<app-engine-apis> line in app-engine.web.xml.
exception occurred while calling backend method
com.google.apphosting.api.ApiProxy$FeatureNotEnabledException: datastore_v3.RunQuery
at com.google.apphosting.utils.runtime.ApiProxyUtils.convertApiError(ApiProxyUtils.java:81)
at com.google.apphosting.utils.runtime.ApiProxyUtils.getApiError(ApiProxyUtils.java:198)
at com.google.apphosting.runtime.ApiProxyImpl$AsyncApiFuture.success(ApiProxyImpl.java:684)
at com.google.apphosting.runtime.ApiProxyImpl$AsyncApiFuture.success(ApiProxyImpl.java:581)
at com.google.apphosting.runtime.http.HttpApiHostClient.receivedResponse(HttpApiHostClient.java:296)
at com.google.apphosting.runtime.http.JettyHttpApiHostClient$Listener.onComplete(JettyHttpApiHostClient.java:206)
at org.eclipse.jetty.client.ResponseNotifier.notifyComplete(ResponseNotifier.java:218)
at org.eclipse.jetty.client.ResponseNotifier.notifyComplete(ResponseNotifier.java:210)
at org.eclipse.jetty.client.HttpReceiver.terminateResponse(HttpReceiver.java:481)
at org.eclipse.jetty.client.HttpReceiver.terminateResponse(HttpReceiver.java:461)
at org.eclipse.jetty.client.HttpReceiver.responseSuccess(HttpReceiver.java:424)
at org.eclipse.jetty.client.http.HttpReceiverOverHTTP.messageComplete(HttpReceiverOverHTTP.java:374)
at org.eclipse.jetty.http.HttpParser.handleContentMessage(HttpParser.java:596)
at org.eclipse.jetty.http.HttpParser.parseContent(HttpParser.java:1723)
at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:1552)
at org.eclipse.jetty.client.http.HttpReceiverOverHTTP.parse(HttpReceiverOverHTTP.java:208)
at org.eclipse.jetty.client.http.HttpReceiverOverHTTP.process(HttpReceiverOverHTTP.java:148)
at org.eclipse.jetty.client.http.HttpReceiverOverHTTP.receive(HttpReceiverOverHTTP.java:80)
at org.eclipse.jetty.client.http.HttpChannelOverHTTP.receive(HttpChannelOverHTTP.java:131)
at org.eclipse.jetty.client.http.HttpConnectionOverHTTP.onFillable(HttpConnectionOverHTTP.java:172)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105)
at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:829)
I assume this GAE exception is thrown because the server can't access the legacy Datastore due to failure to 'Install the App Engine API JAR'. The alternative explanation is that I am not using Objectify 6, but I have read that others are running Java 11+ GAE servers with Objectify 5.