Poison

本地使用 IDEA 编译调试 Tomcat 代码

由于有时需要验证 Tomcat 的问题或者调试其内部代码,所以需要在本地 IDEA 搭建 Tomcat 的开发环境,在此简单记录。

整个过程根据官方的构建文档调整而来,参考:Apache Tomcat 8 (8.5.70) - Building Tomcat

首先确认机器安装了 Java SE Development Kit 8Apache Ant,我本地使用的版本是 JDK 1.8.0_291 及 Ant 1.10.8_1。

从 GitHub 上 clone Apache Tomcat 的源码:

1
git clone https://github.com/apache/tomcat.git

clone 完成后切换到 tomcat 工程目录并切换到指定的 tag,我使用 8.5.70

1
2
cd tomcat
git checkout 8.5.70

先使用 ant 构建一次,把缺失的 jar 下载到本地:

1
ant

再使用 ant 生成 Intellij IDEA 所需的项目环境信息:

1
ant ide-intellij

部分输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
downloadfile:
[get] Getting: https://repo.maven.apache.org/maven2/com/unboundid/unboundid-ldapsdk/6.0.0/unboundid-ldapsdk-6.0.0.jar
[get] To: /Users/Poison/tomcat-build-libs/download-588515216.tmp
[echo] Checksum check for unboundid-ldapsdk-6.0.0.jar, algorithm MD5|SHA-1: OK
[mkdir] Created dir: /Users/Poison/tomcat-build-libs/unboundid-6.0.0
[move] Moving 1 file to /Users/Poison/tomcat-build-libs/unboundid-6.0.0

ide-intellij:
[copy] Copying 10 files to /Users/Poison/IdeaProjects/tomcat/.idea
[echo] IntelliJ IDEA project directory created. Please create PATH VARIABLES for
[echo]
[echo] ANT_HOME = /usr/local/Cellar/ant/1.10.8_1/libexec
[echo] TOMCAT_BUILD_LIBS = /Users/Poison/tomcat-build-libs
[echo]

BUILD SUCCESSFUL
Total time: 53 seconds

可以看到 Intellij IDEA 项目所需的 .idea 目录已经生成,此时我们使用 Intellij IDEA 打开该目录,根据文档配置以上两个 PATH VARIABLES,配置 PATH VARIABLES 的文档可以参考:Path variables | IntelliJ IDEA,在我的电脑上配置后的截图如下:
PATH VARIABLES

然后在 IDEA 中从类入口 org.apache.catalina.startup.Bootstrap#main 运行 Tomcat,随即发现编译报错:

1
2
/Users/Poison/IdeaProjects/tomcat/test/util/TestCookieFilter.java:19:17
java: package org.junit does not exist

原因为 tag 为 8.5.70 的 ant target: ide-intellij 中使用的 JUnit 版本为 4.13,而 Tomcat 实际使用的 JUnit 版本已经升级至 4.13.2,且 TOMCAT_BUILD_LIBS 对应的目录下的 JUnit 版本也是 4.13.2,所以我们需要手动修改 IDEA 中使用的 JUnit 版本,在 .idea/tomtat.iml 中调整 JUnit 的版本:

1
2
3
4
5
6
7
8
9
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="jar://$TOMCAT_BUILD_LIBS$/junit-4.13/junit-4.13.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</orderEntry>

将其中的 jar://$TOMCAT_BUILD_LIBS$/junit-4.13/junit-4.13.jar!/ 替换为 jar://$TOMCAT_BUILD_LIBS$/junit-4.13.2/junit-4.13.2.jar!/ 后重新运行 org.apache.catalina.startup.Bootstrap#main,我的电脑的启动输出日志如下:

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
Aug 26, 2021 5:26:33 PM org.apache.catalina.startup.ClassLoaderFactory validateFile
WARNING: Problem with directory [/Users/Poison/IdeaProjects/tomcat/lib], exists: [false], isDirectory: [false], canRead: [false]
Aug 26, 2021 5:26:33 PM org.apache.catalina.startup.ClassLoaderFactory validateFile
WARNING: Problem with directory [/Users/Poison/IdeaProjects/tomcat/lib], exists: [false], isDirectory: [false], canRead: [false]
Aug 26, 2021 5:26:33 PM org.apache.catalina.startup.ClassLoaderFactory validateFile
WARNING: Problem with directory [/Users/Poison/IdeaProjects/tomcat/lib], exists: [false], isDirectory: [false], canRead: [false]
Aug 26, 2021 5:26:33 PM org.apache.catalina.startup.ClassLoaderFactory validateFile
WARNING: Problem with directory [/Users/Poison/IdeaProjects/tomcat/lib], exists: [false], isDirectory: [false], canRead: [false]
Aug 26, 2021 5:26:33 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server version name: Apache Tomcat/@VERSION@
Aug 26, 2021 5:26:33 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server built: @VERSION_BUILT@
Aug 26, 2021 5:26:33 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Server version number: @VERSION_NUMBER@
Aug 26, 2021 5:26:33 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: OS Name: Mac OS X
Aug 26, 2021 5:26:33 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: OS Version: 10.16
Aug 26, 2021 5:26:33 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Architecture: x86_64
Aug 26, 2021 5:26:33 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Java Home: /Library/Java/JavaVirtualMachines/jdk1.8.0_291.jdk/Contents/Home/jre
Aug 26, 2021 5:26:33 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: JVM Version: 1.8.0_291-b10
Aug 26, 2021 5:26:33 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: JVM Vendor: Oracle Corporation
Aug 26, 2021 5:26:33 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: CATALINA_BASE: /Users/Poison/IdeaProjects/tomcat
Aug 26, 2021 5:26:33 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: CATALINA_HOME: /Users/Poison/IdeaProjects/tomcat
Aug 26, 2021 5:26:33 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=62955:/Applications/IntelliJ IDEA.app/Contents/bin
Aug 26, 2021 5:26:33 PM org.apache.catalina.startup.VersionLoggerListener log
INFO: Command line argument: -Dfile.encoding=UTF-8
Aug 26, 2021 5:26:33 PM org.apache.catalina.core.AprLifecycleListener lifecycleEvent
INFO: The Apache Tomcat Native library which allows using OpenSSL was not found on the java.library.path: [/Users/Poison/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.]
Aug 26, 2021 5:26:33 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-nio-8080"]
Aug 26, 2021 5:26:34 PM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
INFO: Using a shared selector for servlet write/read
Aug 26, 2021 5:26:34 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 683 ms
Aug 26, 2021 5:26:34 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service [Catalina]
Aug 26, 2021 5:26:34 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet engine: [Apache Tomcat/@VERSION@]
Aug 26, 2021 5:26:34 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory [/Users/Poison/IdeaProjects/tomcat/webapps/docs]
Aug 26, 2021 5:26:34 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deployment of web application directory [/Users/Poison/IdeaProjects/tomcat/webapps/docs] has finished in [350] ms
Aug 26, 2021 5:26:34 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory [/Users/Poison/IdeaProjects/tomcat/webapps/manager]
Aug 26, 2021 5:26:34 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deployment of web application directory [/Users/Poison/IdeaProjects/tomcat/webapps/manager] has finished in [66] ms
Aug 26, 2021 5:26:34 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory [/Users/Poison/IdeaProjects/tomcat/webapps/examples]
Aug 26, 2021 5:26:34 PM org.apache.catalina.core.StandardContext listenerStart
SEVERE: Error configuring application listener of class [listeners.ContextListener]
java.lang.ClassNotFoundException: listeners.ContextListener
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1415)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1223)
at org.apache.catalina.core.DefaultInstanceManager.loadClass(DefaultInstanceManager.java:537)
at org.apache.catalina.core.DefaultInstanceManager.loadClassMaybePrivileged(DefaultInstanceManager.java:518)
at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:149)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4686)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5232)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:753)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:727)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:695)
at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1177)
at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1925)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)

Aug 26, 2021 5:26:34 PM org.apache.catalina.core.StandardContext listenerStart
SEVERE: Error configuring application listener of class [listeners.SessionListener]
java.lang.ClassNotFoundException: listeners.SessionListener
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1415)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1223)
at org.apache.catalina.core.DefaultInstanceManager.loadClass(DefaultInstanceManager.java:537)
at org.apache.catalina.core.DefaultInstanceManager.loadClassMaybePrivileged(DefaultInstanceManager.java:518)
at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:149)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4686)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5232)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:753)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:727)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:695)
at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1177)
at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1925)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)

Aug 26, 2021 5:26:34 PM org.apache.catalina.core.StandardContext listenerStart
SEVERE: Error configuring application listener of class [async.AsyncStockContextListener]
java.lang.ClassNotFoundException: async.AsyncStockContextListener
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1415)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1223)
at org.apache.catalina.core.DefaultInstanceManager.loadClass(DefaultInstanceManager.java:537)
at org.apache.catalina.core.DefaultInstanceManager.loadClassMaybePrivileged(DefaultInstanceManager.java:518)
at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:149)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4686)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5232)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:753)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:727)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:695)
at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1177)
at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1925)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)

Aug 26, 2021 5:26:34 PM org.apache.catalina.core.StandardContext listenerStart
SEVERE: Error configuring application listener of class [websocket.drawboard.DrawboardContextListener]
java.lang.ClassNotFoundException: websocket.drawboard.DrawboardContextListener
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1415)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1223)
at org.apache.catalina.core.DefaultInstanceManager.loadClass(DefaultInstanceManager.java:537)
at org.apache.catalina.core.DefaultInstanceManager.loadClassMaybePrivileged(DefaultInstanceManager.java:518)
at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:149)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4686)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5232)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:753)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:727)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:695)
at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1177)
at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1925)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)

Aug 26, 2021 5:26:34 PM org.apache.catalina.core.StandardContext listenerStart
SEVERE: Skipped installing application listeners due to previous error(s)
Aug 26, 2021 5:26:34 PM org.apache.catalina.core.StandardContext startInternal
SEVERE: One or more listeners failed to start. Full details will be found in the appropriate container log file
Aug 26, 2021 5:26:34 PM org.apache.catalina.core.StandardContext startInternal
SEVERE: Context [/examples] startup failed due to previous errors
Aug 26, 2021 5:26:34 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deployment of web application directory [/Users/Poison/IdeaProjects/tomcat/webapps/examples] has finished in [94] ms
Aug 26, 2021 5:26:34 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory [/Users/Poison/IdeaProjects/tomcat/webapps/ROOT]
Aug 26, 2021 5:26:34 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deployment of web application directory [/Users/Poison/IdeaProjects/tomcat/webapps/ROOT] has finished in [92] ms
Aug 26, 2021 5:26:34 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory [/Users/Poison/IdeaProjects/tomcat/webapps/host-manager]
Aug 26, 2021 5:26:34 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deployment of web application directory [/Users/Poison/IdeaProjects/tomcat/webapps/host-manager] has finished in [92] ms
Aug 26, 2021 5:26:34 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-nio-8080"]
Aug 26, 2021 5:26:34 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 875 ms

看起来好像出错了,是 webapps/examples 目录引起的,查看该目录下的内容,原来是原生的 Java 类,没有进行编译,而之前我们执行的 ant 编译出的目录位于 output/build/webapps,所以我们对源码进行简单的调整以处理本地环境使用的 webapps 目录不正确的问题:

1
2
3
4
// 将 org.apache.catalina.core.StandardHost#getAppBaseFile 中的代码进行调整,调整前:
File file = new File(getAppBase());
// 调整后
File file = new File("output/build/" + getAppBase());

调整完成后,启动不再报错,就可以 DEBUG 源码了。