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:
true
But trying to build/deploy the project via the Gradle plugin in Android Studio gets the exception ‘Unrecognized element ’.
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 ‘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.<strong>*VERSION_11*</strong>
targetCompatibility = JavaVersion.<strong>*VERSION_11*</strong>
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 true 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.