Tomcat 10 with Jakarta Servlet

最近小破站重装,被 Tomcat 10 坑惨了,不过最后总结就是升级两个核心依赖:javaxjersey

There is a significant breaking change between Tomcat 9.0.x and Tomcat 10.0.x. The Java package used by the specification APIs has changed from javax… to jakarta…. It will be necessary to recompile web applications against the new APIs.

问题

部署应用起不来,web 404。

原因

Tomcat 10 改用了 Jakarta EE,而不是以前的 Javax。Tomcat 9 是最后一个适配 Javax 的大版本。

修呗

依赖

ClassCastException: javax.servlet.xxx cannot be cast to jakarta.servlet.xxx

首先是这个问题,javax.servlet.* 包不再适配了,用来替代的是 jakarta.servlet.*,所以第一步是换 maven 依赖包。

老的是:

1
2
3
4
5
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>

首先我尝试了添加对应的 Jakarta 依赖:

1
2
3
4
5
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
</dependency>

并把相应的 importjavax.servlet.* 改成 jakarta.servlet.*,然后重新编译尝试部署。

ClassNotFoundException: javax.servlet.Filter

这里部署出现了个麻烦事 ↑,也打了堆栈信息,但是都没有说从哪里抛出来的。目测很底层。

那找不到从哪扔出来的,就先把所有的 javax 都给换了吧。

旧依赖:

1
2
3
4
5
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.1</version>
</dependency>

新依赖:

1
2
3
4
5
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>3.0.0</version>
</dependency>

这次涉及到的是一些 Restful service 用到的类。这里就有点坑比了,这个依赖包里类的路径没有变……依然是 javax.ws.rs.*……所以不用全局替换所有的 import ……啊……再改回去……!!

编译。部署。败了,还是同样的报错。到底是哪里调用了 Filter?(全部 log 在附录)

目测是部署 WebApp 时候调到了,但是在 IDE 里面检查 web.xml 里面也是指向的 Jakarta 的包啊?

那就暂时把老的 javax 的依赖加回去,应该能共存吧?

ClassCastException: com.sun.jersey.spi.container.servlet.ServletContainer cannot be cast to jakarta.servlet.Servlet

好家伙……目前依赖结构是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>3.0.0</version>
</dependency>

又抛出新的错了,这次我确实有显式用到这个类,在 web.xml 里面构造 servlet 的时候。目测是 jersey-bundle 的包也需要换:

1
2
3
4
5
6
7
8
9
10
11
12
13
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>fun.kaii.restful</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>

那么接下来把 jersey 相关的包进行替换,老的是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-bundle</artifactId>
<version>1.19.4</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.19.4</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
<version>1.19.4</version>
</dependency>

替换成新的(${jersey.version}=3.0.3):

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>${jersey.version}</version>
</dependency>

到这里就发现上面的一个问题了(上文划去的部分):需要把 javax.ws.rs.* 替换成 jakarta.ws.rs.*

编译!打包!部署成功!!

官宣成功

我宣布,楚不得站点的 Tomcat 升级,成功!

扫尾工作

最后清理一些遗留工作。

// TBD

附录

官网信息

Migrating from 9.0.x to 10.0.x

Full log for Filter exception

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
INFO [Catalina-utility-1] org.apache.catalina.core.ApplicationContext.log Marking servlet [Jersey Web Application] as unavailable
SEVERE [Catalina-utility-1] org.apache.catalina.core.StandardContext.loadOnStartup Servlet [Jersey Web Application] in web application [/xxx] threw load() exception
java.lang.ClassNotFoundException: javax.servlet.Filter
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1444)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1252)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at org.apache.catalina.loader.WebappClassLoaderBase.findClassInternal(WebappClassLoaderBase.java:2516)
at org.apache.catalina.loader.WebappClassLoaderBase.findClass(WebappClassLoaderBase.java:872)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1408)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1252)
at org.apache.catalina.core.DefaultInstanceManager.loadClass(DefaultInstanceManager.java:539)
at org.apache.catalina.core.DefaultInstanceManager.loadClassMaybePrivileged(DefaultInstanceManager.java:520)
at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:150)
at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1071)
at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1011)
at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4906)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5213)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:726)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:698)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:747)
at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1027)
at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:2001)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
at org.apache.catalina.startup.HostConfig.deployWARs(HostConfig.java:828)
at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:478)
at org.apache.catalina.startup.HostConfig.check(HostConfig.java:1757)
at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:316)
at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:123)
at org.apache.catalina.core.ContainerBase.backgroundProcess(ContainerBase.java:1162)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1365)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1369)
at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1347)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
深得我心!博主晚餐加鸡腿!