将含旧版捆绑服务的GAE Java8应用升级至Java17部署失败
我们有一个App Engine标准Java Web应用,基于Java 8运行时,大量使用App Engine SDK调用Datastore、Search API、Memcache、BigQuery、Cloud Storage等核心API;同时使用DataNucleus App Engine插件增强JDO类对接Cloud Datastore,REST服务基于Jersey 1.7开发。
因GAE终止Java 8支持,我们升级到Java 17运行时,按GCP文档配置了appengine-web.xml:
<runtime>java17</runtime> <app-engine-apis>true</app-engine-apis>
Servlet代码升级到Java 17后,旧版捆绑服务运行正常。但迁移Jersey到3.x(Java 17兼容版)时,其依赖jakarta.*命名空间而非javax.*,我们遂将项目中所有javax.*替换为jakarta.*,编译通过且解决了依赖冲突,但部署到GAE Java 17并启用旧版捆绑服务时,出现以下错误:
Request failed: Unexpected exception from servlet: java.lang.IllegalStateException: class com.myCompany.security.ApplicationSecurityFilter is not a javax.servlet.Filter
Request failed: Unexpected exception from servlet: javax.servlet.UnavailableException: Servlet class com.myCompany.server.someServlet is not a javax.servlet.Servlet
尽管WAR包的WEB-INF/lib中包含所有jakarta.*库,GAE运行时仍无法识别这些类。
核心矛盾在于:启用旧版捆绑服务(app-engine-apis: true)时,GAE Java 17运行时仍以传统javax.servlet API作为容器基础规范;而你已将代码和依赖切换到Jakarta EE的jakarta.servlet命名空间,两者属于不同的API体系,容器无法将jakarta.servlet.Filter/Servlet实例识别为它期望的javax.servlet类型,因此触发类型转换错误。
路径1:保留旧版捆绑服务,回退到javax.*生态
- 放弃Jersey 3.x,改用兼容Java 17的Jersey 2.x版本(Jersey 2.x仍基于
javax.*命名空间,支持Java 8至17) - 恢复代码中所有
jakarta.*到javax.*的替换,移除项目内jakarta.servlet等相关依赖 - 确保所有第三方库均使用基于
javax.*的版本,避免混合javax与jakarta依赖
路径2:迁移到GAE Java 17现代运行时(推荐)
若业务允许逐步替换旧版GAE SDK依赖,建议完全迁移到GCP现代客户端库,不再依赖旧版捆绑服务:
- 移除
appengine-web.xml中的<app-engine-apis>true</app-engine-apis>配置,使用GAE Java 17标准运行时 - 替换所有旧版App Engine SDK调用为对应GCP客户端库:
- Datastore:使用
com.google.cloud:google-cloud-datastore - Memcache:使用
com.google.cloud:google-cloud-memcache或Cloud Memorystore(Redis) - BigQuery:使用
com.google.cloud:google-cloud-bigquery - Cloud Storage:使用
com.google.cloud:google-cloud-storage
- Datastore:使用
- 保留Jersey 3.x与
jakarta.*命名空间的代码,此时GAE运行时会使用Jakarta EE兼容的Servlet容器,可正常识别jakarta.servlet相关类 - 针对JDO/DataNucleus:升级到支持Jakarta EE的版本(如DataNucleus 6.x),替换
javax.jdo依赖为jakarta.jdo,并调整相关配置
内容的提问来源于stack exchange,提问作者Pratap Singh

