The Eclipse App Engine plugin is no longer supported. To upgrade my apps from Java 8 to Java 11, I opted for the Maven App Engine plugin. To ensure a smooth process, my plan is to first redeploy the existing Java 8 code (using Maven, not the Eclipse plugin) without any changes. After that, I'll proceed with updating the project to Java 11.
Despite successfully building the code without any errors, I encounter a server-side error when running the app in both production and the local environment. The exception is as follows:
com.google.apphosting.runtime.jetty9.JettyLogger warn: Failed startup of context c.g.a.r.j.AppEngineWebAppContext@1ac03879{/,file:///base/data/home/apps/s~MY_APP_ID/20240128t073546.458064643040632226/,UNAVAILABLE}{/base/data/home/apps/s~MY_APP_ID/20240128t073546.458064643040632226}
MultiException[javax.servlet.UnavailableException: Servlet class my.package_name.PurgeServlet is not a javax.servlet.Servlet, javax.servlet.UnavailableException: Servlet class my.package_name.VersionServlet is not a javax.servlet.Servlet, javax.servlet.UnavailableException: Servlet class ...]
at org.eclipse.jetty.util.MultiException.ifExceptionThrow(MultiException.java:122)
at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:776)
at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:379)
at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1449)
at com.google.apphosting.runtime.jetty9.AppEngineWebAppContext.startWebapp(AppEngineWebAppContext.java:274)
at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1414)
at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:916)
at org.eclipse.jetty.servlet.ServletContextHandler.doStart(ServletContextHandler.java:288)
at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:524)
at com.google.apphosting.runtime.jetty9.AppEngineWebAppContext.doStart(AppEngineWebAppContext.java:218)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:73)
at com.google.apphosting.runtime.jetty9.AppVersionHandlerFactory.doCreateHandler(AppVersionHandlerFactory.java:206)
at com.google.apphosting.runtime.jetty9.AppVersionHandlerFactory.createHandler(AppVersionHandlerFactory.java:124)
at com.google.apphosting.runtime.jetty9.AppVersionHandlerMap.getHandler(AppVersionHandlerMap.java:82)
at com.google.apphosting.runtime.jetty9.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:176)
at com.google.apphosting.runtime.RequestRunner.dispatchServletRequest(RequestRunner.java:262)
at com.google.apphosting.runtime.RequestRunner.dispatchRequest(RequestRunner.java:227)
at com.google.apphosting.runtime.RequestRunner.run(RequestRunner.java:193)
at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:273)
at java.lang.Thread.run(Thread.java:750)
Suppressed: javax.servlet.UnavailableException: Servlet class my.package_name.PurgeServlet is not a javax.servlet.Servlet
at org.eclipse.jetty.servlet.ServletHolder.checkServletType(ServletHolder.java:514)
at org.eclipse.jetty.servlet.ServletHolder.doStart(ServletHolder.java:386)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:73)
at org.eclipse.jetty.servlet.ServletHandler.lambda$initialize$0(ServletHandler.java:749)
at java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:483)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:313)
at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:647)
at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:774)
... 18 more
Suppressed: javax.servlet.UnavailableException: Servlet class my.package_name.VersionServlet is not a javax.servlet.Servlet
at org.eclipse.jetty.servlet.ServletHolder.checkServletType(ServletHolder.java:514)
at org.eclipse.jetty.servlet.ServletHolder.doStart(ServletHolder.java:386)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:73)
at org.eclipse.jetty.servlet.ServletHandler.lambda$initialize$0(ServletHandler.java:749)
at java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:483)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:313)
at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:647)
at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:774)
... 18 more
[... the same exception for each of my servlets]
Caused by: [CIRCULAR REFERENCE: javax.servlet.UnavailableException: Servlet class my.package_name.PurgeServlet is not a javax.servlet.Servlet]
Failed startup of context c.g.a.r.j.AppEngineWebAppContext@1ac03879{/,file:///base/data/home/apps/s~MY_APP_ID/20240128t073546.458064643040632226/,UNAVAILABLE}{/base/data/home/apps/s~MY_APP_ID/20240128t073546.458064643040632226}
MultiException[javax.servlet.UnavailableException: Servlet class my.package_name.PurgeServlet is not a javax.servlet.Servlet, javax.servlet.UnavailableException: Servlet class my.package_name.VersionServlet is not a javax.servlet.Servlet, ...]
at org.eclipse.jetty.util.MultiException.ifExceptionThrow(MultiException.java:122)
at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:776)
at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:379)
at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1449)
at com.google.apphosting.runtime.jetty9.AppEngineWebAppContext.startWebapp(AppEngineWebAppContext.java:274)
at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1414)
at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:916)
at org.eclipse.jetty.servlet.ServletContextHandler.doStart(ServletContextHandler.java:288)
at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:524)
at com.google.apphosting.runtime.jetty9.AppEngineWebAppContext.doStart(AppEngineWebAppContext.java:218)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:73)
at com.google.apphosting.runtime.jetty9.AppVersionHandlerFactory.doCreateHandler(AppVersionHandlerFactory.java:206)
at com.google.apphosting.runtime.jetty9.AppVersionHandlerFactory.createHandler(AppVersionHandlerFactory.java:124)
at com.google.apphosting.runtime.jetty9.AppVersionHandlerMap.getHandler(AppVersionHandlerMap.java:82)
at com.google.apphosting.runtime.jetty9.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:176)
at com.google.apphosting.runtime.RequestRunner.dispatchServletRequest(RequestRunner.java:262)
at com.google.apphosting.runtime.RequestRunner.dispatchRequest(RequestRunner.java:227)
at com.google.apphosting.runtime.RequestRunner.run(RequestRunner.java:193)
at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:273)
at java.lang.Thread.run(Thread.java:750)
Suppressed: javax.servlet.UnavailableException: Servlet class my.package_name.PurgeServlet is not a javax.servlet.Servlet
at org.eclipse.jetty.servlet.ServletHolder.checkServletType(ServletHolder.java:514)
at org.eclipse.jetty.servlet.ServletHolder.doStart(ServletHolder.java:386)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:73)
at org.eclipse.jetty.servlet.ServletHandler.lambda$initialize$0(ServletHandler.java:749)
at java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:483)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:313)
at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:647)
at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:774)
... 18 more
Suppressed: javax.servlet.UnavailableException: Servlet class my.package_name.VersionServlet is not a javax.servlet.Servlet
at org.eclipse.jetty.servlet.ServletHolder.checkServletType(ServletHolder.java:514)
at org.eclipse.jetty.servlet.ServletHolder.doStart(ServletHolder.java:386)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:73)
at org.eclipse.jetty.servlet.ServletHandler.lambda$initialize$0(ServletHandler.java:749)
at java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:483)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:313)
at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:647)
at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:774)
... 18 more
[... the same exception for all of my servlets]
Caused by: [CIRCULAR REFERENCE: javax.servlet.UnavailableException: Servlet class my.package_name.PurgeServlet is not a javax.servlet.Servlet]
Uncaught exception from servlet
MultiException[javax.servlet.UnavailableException: Servlet class my.package_name.PurgeServlet is not a javax.servlet.Servlet, javax.servlet.UnavailableException: Servlet class my.package_name.VersionServlet is not a javax.servlet.Servlet, ...]
at org.eclipse.jetty.util.MultiException.ifExceptionThrow(MultiException.java:122)
at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:776)
at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:379)
at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1449)
at com.google.apphosting.runtime.jetty9.AppEngineWebAppContext.startWebapp(AppEngineWebAppContext.java:274)
at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1414)
at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:916)
at org.eclipse.jetty.servlet.ServletContextHandler.doStart(ServletContextHandler.java:288)
at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:524)
at com.google.apphosting.runtime.jetty9.AppEngineWebAppContext.doStart(AppEngineWebAppContext.java:218)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:73)
at com.google.apphosting.runtime.jetty9.AppVersionHandlerFactory.doCreateHandler(AppVersionHandlerFactory.java:206)
at com.google.apphosting.runtime.jetty9.AppVersionHandlerFactory.createHandler(AppVersionHandlerFactory.java:124)
at com.google.apphosting.runtime.jetty9.AppVersionHandlerMap.getHandler(AppVersionHandlerMap.java:82)
at com.google.apphosting.runtime.jetty9.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:176)
at com.google.apphosting.runtime.RequestRunner.dispatchServletRequest(RequestRunner.java:262)
at com.google.apphosting.runtime.RequestRunner.dispatchRequest(RequestRunner.java:227)
at com.google.apphosting.runtime.RequestRunner.run(RequestRunner.java:193)
at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:273)
at java.lang.Thread.run(Thread.java:750)
Suppressed: javax.servlet.UnavailableException: Servlet class my.package_name.PurgeServlet is not a javax.servlet.Servlet
at org.eclipse.jetty.servlet.ServletHolder.checkServletType(ServletHolder.java:514)
at org.eclipse.jetty.servlet.ServletHolder.doStart(ServletHolder.java:386)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:73)
at org.eclipse.jetty.servlet.ServletHandler.lambda$initialize$0(ServletHandler.java:749)
at java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:483)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:313)
at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:647)
at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:774)
... 18 more
Suppressed: javax.servlet.UnavailableException: Servlet class my.package_name.VersionServlet is not a javax.servlet.Servlet
at org.eclipse.jetty.servlet.ServletHolder.checkServletType(ServletHolder.java:514)
at org.eclipse.jetty.servlet.ServletHolder.doStart(ServletHolder.java:386)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:73)
at org.eclipse.jetty.servlet.ServletHandler.lambda$initialize$0(ServletHandler.java:749)
at java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:483)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:313)
at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:647)
at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:774)
... 18 more
[... the same exception for all of my servlets]
I.e., I am getting javax.servlet.UnavailableException: Servlet class my.package_name.<Any>Servlet is not a javax.servlet.Servlet
for all of my servlets. As I mentioned before, the source code hasn't been touched, everything works fine when deployed via eclipse plugin and all of my servlets are extending javax.servlet.http.HttpServlet
.
When deployed, welcome file is served correctly, so I would assume that web.xml
is not causing the problem. However, when run locally, while the same exceptions are printed to the console, both the welcome file and the admin console at http://localhost:8080/_ah/admin
display HTTP ERROR 503 Service Unavailable
.
My pom.xml
file is the following:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>Version_Server</groupId>
<artifactId>Version_Server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<!-- The same error happens when using -->
<!--
<artifactId>servlet-api</artifactId>
<version>2.4</version>
-->
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.googlecode.objectify</groupId>
<artifactId>objectify</artifactId>
<version>5.1.22</version>
</dependency>
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-1.0-sdk</artifactId>
<version>1.9.34</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20231013</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<resources>
<resource>
<directory>src</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.3</version>
<configuration>
<warSourceDirectory>war</warSourceDirectory>
<failOnMissingWebXml>true</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>appengine-maven-plugin</artifactId>
<version>2.4.4</version>
</plugin>
</plugins>
</build>
</project>
war/WEB_INF/web.xml
is the following:
<?xml version="1.0" encoding="utf-8"?>
<web-app
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"
>
<security-constraint>
<web-resource-collection>
<web-resource-name>purge</web-resource-name>
<url-pattern>/purge</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
<servlet>
<servlet-name>PurgeServlet</servlet-name>
<servlet-class>my.package_name.PurgeServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>PurgeServlet</servlet-name>
<url-pattern>/purge</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>VersionServlet</servlet-name>
<servlet-class>my.package_name.VersionServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>VersionServlet</servlet-name>
<url-pattern>/latestVersion</url-pattern>
</servlet-mapping>
<!-- the rest of the servlets -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
PurgeServlet.java
package my.package_name;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import my.package_name.data.Request;
import com.googlecode.objectify.ObjectifyService;
/**
*
*
*/
@SuppressWarnings("serial")
public class PurgeServlet extends HttpServlet {
/**
*
*/
private void setupObjectify() {
ObjectifyService.begin();
ObjectifyService.register(Request.class);
}
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) {
setupObjectify();
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.YEAR, -1);
Date date = calendar.getTime();
List<Request> requests =
ObjectifyService.ofy().load().type(Request.class).
filter("date <", date).limit(300).list();
ObjectifyService.ofy().delete().entities(requests);
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) {
doPost(req, resp);
}
}
VersionServlet.java
package my.package_name;
import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import my.package_name.data.LatestVersion;
import my.package_name.data.Request;
import my.package_name.util.StringUtil;
import com.google.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.appengine.api.taskqueue.TaskOptions;
import com.googlecode.objectify.ObjectifyService;
/**
*
*
*/
@SuppressWarnings("serial")
public class VersionServlet extends HttpServlet {
/**
*
*/
private void setupObjectify() {
ObjectifyService.begin();
ObjectifyService.register(Request.class);
ObjectifyService.register(LatestVersion.class);
}
/**
*
* @param request
*/
private void persistRequest(Request request) {
ObjectifyService.ofy().save().entity(request);
}
/**
*
* @param packageName
* @return
*/
private LatestVersion getLatestVersion(String packageName) {
LatestVersion latestVersion =
ObjectifyService.ofy().load().type(LatestVersion.class).
id(packageName).now();
if (latestVersion == null) {
latestVersion = new LatestVersion("", "0", "");
}
return latestVersion;
}
/**
*
* @param latestVersion
* @param response
*/
private void printLatestVersion
(
LatestVersion latestVersion,
HttpServletResponse response
) {
if (latestVersion != null) {
try {
response.setContentType("text/plain");
response.getWriter().println(latestVersion.toJSON());
} catch (IOException e) {
// do nothing
}
}
}
/**
*
*/
private void purgeOldRecords() {
Queue queue = QueueFactory.getDefaultQueue();
queue.add(TaskOptions.Builder.withUrl("/purge"));
}
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) {
String packageName = req.getParameter("packageName");
String versionCode = req.getParameter("versionCode");
if (!StringUtil.isDefined(packageName) ||
!StringUtil.isDefined(versionCode)) {
return;
}
setupObjectify();
Request request = new Request(packageName, versionCode);
persistRequest(request);
printLatestVersion(getLatestVersion(packageName), resp);
purgeOldRecords();
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) {
doPost(req, resp);
}
}
Switching between JDK 1.8.0 and JDK 11.0.9 doesn't change anything. How can I resolve this problem?
war/WEB_INF/appengine-web.xml
is the following:
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<runtime>java8</runtime>
<threadsafe>true</threadsafe>
<!-- Configure java.util.logging -->
<system-properties>
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
</system-properties>
</appengine-web-app>
It seems that my IDE (eclipse) was constantly rebuilding everything in the war/WEB-INF/classes
directory, while maven's output was in target/classes
. Exiting the IDE and deleting the war/WEB-INF/classes
directory resolved the issue.