转自:http://hi.baidu.com/hateeyes/blog/item/7f44942a20ad8f9d023bf66d.html
下面我们就开始我们的 Tomcat源码学习之旅。
1. 下载 Tomcat6.0的源代码
首先,我们得下载 Tomcat6.0的源代码。Tomcat源代码的版本控制工具不是CVS,而是Subversion,如果您的机器上没有安装Subversion,请从 http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=91 下载并安装这个开源的版本控制工具。当然,如果您想从 Eclipse 中直接导入 Tomcat 源代码,请从 http://subclipse.tigris.org/update_1.0.x 下载 Subversion 插件,即可导入 Tomcat 源代码。安装完成后,请在 MS-DOS 窗口中键入 svn export help ,您将会看到:
C:\Documents and Settings\carlwu>svn help export
export: 产生一个无版本控制的目录树副本。
用法 : 1 、 export [-r REV] URL[@PEGREV] [PATH]
2 、 export [-r REV] PATH1[@PEGREV] [PATH2]
1 、从 URL 指定的仓库,导出一个干净的目录树到 PATH 。如果有指定
REV 的话,内容即为该版本的,否则就是 HEAD 版本。如果 PATH
被省略的话, URL 的最后部份会被用来当成本地的目录名称。
2 、在工作副本中,从指定的 PATH1 导出一个干净的目录树到 PATH2 。如果
有指定 REV 的话,会从指定的版本导出,否则从工作副本导出。如果
PATH2 被省略的话, PATH1 的最后部份会被用来当成本地的目录名称。
如果没有指定 REV 的话,所有的本地修改都保留,但是未纳入版本控制
的文件不会被复制。
如果指定了 PEGREV ,将从指定的版本本开始查找。
有效选项 : 。。。。。。
我们看到 Subversion 给我们提供了非常友好的帮助,并且是中文的,看来中国程序员对这个开源项目有所贡献。接下来,请在 MS-DOS 下键入:
svn export http://svn.apache.org/repos/asf/tomcat/tc6.0.x/tags/TOMCAT_6_0_0/ D:\carl_wu\tomcat\src\
这个命令的意思是把 Tomcat6.0 的源代码从 Subversion 库中导入到本机的 D:\carl_wu\tomcat\src\ 目录,命令运行后,您稍等几分钟,就会看到 Tomcat 的源代码顺利导入到目标目录。下面是源代码的目录机构,从这个目录结构中,我们可以看出该项目的开发者使用的 IDE 是 Eclipse ,因为我们看到了熟悉的 .project 及 .classpath 文件。如果您打算开发一个 Stand alone 的 Java 应用程序,不妨借鉴一下 Tomcat 的目录结构,把脚本文件放在 bin 目录,将 xml 和 properties 配置文件放在 conf 目录中,把 Java 源码文件放在 java 或者 src 目录中,资源文件比如说图片文件, ini 文件及其它的一些静态资源文件可以放在 res 目录,测试源代码可以放在 test 目录中。这是一个典型的 Java 应用程序的目录机构,笔者以前曾接触到一个来自美国的产品,其源代码目录结构和 Tomcat 及其相像。
2. 编译并运行
代码下载后,我们接下来就是要编译并运行 Tomcat 。一提编译,我们不禁会想到可爱的 Ant 。不错, Tomcat 正是以 Ant 作为编译工具,如果您还没有安装,请从 http://ant.apache.org/bindownload.cgi 处下载并安装它。然后,请从 Tomcat 的源代码文件找到 build.properties.default 文件,并将该文件复制到 build.properties ,然后打开 build.properties ,找到下面这行:
base.path=/usr/share/java
将它改为:
base.path= D:/carl_wu/tomcat/share
在 Tomcat 编译过程中, Ant 会让我们下载一些必要的依赖项目, base.path 目录就是用来保存这些项目文件的,我们可以将这个属性指向一个已经存在的目录。修改完 base.path 后,我们回到 MS-DOS 窗口,切换到 Tomcat 源代码所在目录,然后运行 ant download 命令,如下图所示:
一分钟未到, Ant 就告诉我们一个错误并提示我们编译失败,具体错误信息如下:
downloadzip:
[get] Getting: http://sunsite.informatik.rwth-aachen.de/eclipse/downloads/drops/R-3.2-200606291905/eclipse-JDT-3.2.zip
[get] To: D:\carl_wu\tomcat\share\file.zip
[get] Error opening connection java.io.FileNotFoundException: http://sunsite.informatik.rwth-aachen.de:3080/eclipse/downloads/drops/R-3.2-200606291905/eclipse-JDT-3.2.zip
[get] Error opening connection java.io.FileNotFoundException: http://sunsite.informatik.rwth-aachen.de:3080/eclipse/downloads/drops/R-3.2-200606291905/eclipse-JDT-3.2.zip
[get] Error opening connection java.io.FileNotFoundException: http://sunsite.informatik.rwth-aachen.de:3080/eclipse/downloads/drops/R-3.2-200606291905/eclipse-JDT-3.2.zip
[get] Can't get http://sunsite.informatik.rwth-aachen.de/eclipse/downloads/drops/R-3.2-200606291905/eclipse-JDT-3.2.zip to D:\carl_wu\tomcat\share\file.zip
BUILD FAILED
D:\carl_wu\tomcat\src\build.xml:554: The following error occurred while executing this line:
D:\carl_wu\tomcat\src\build.xml:514: Can't get http://sunsite.informatik.rwth-aachen.de/eclipse/downloads/drops/R-3.2-200606291905/eclipse-JDT-3.2.zip to D:\carl_wu\tomcat\share\file.zip
Total time: 41 seconds
这个编译错误非常简单,就是找不到 http://sunsite.informatik.rwth-aachen.de/eclipse/downloads/drops/R-3.2-200606291905/eclipse-JDT-3.2.zip 文件。有人可能会想, Tomcat 的编译和 Eclipse 的 JDT 有什么关系?其实不然, Tomcat 是在 Eclipse 下开发的,所以需要 Eclipse 的 JDT ( Java Development tooling )插件来编译 Tomat 源代码。既然找不到,我们只好自己动手,上 Google 一搜,马上发现这个文件的有效下载地址为: http://mirror.calvin.edu/eclipse/downloads/drops/R-3.2-200606291905/eclipse-JDT-3.2.zip 。我们 打开 刚才的 build.properties 文件,将其 34 行修改为:
jdt.loc= http://mirror.calvin.edu/eclipse/downloads/drops/R-3.2-200606291905/eclipse-JDT-3.2.zip
修改保存 build.properties 文件后,重新开始 ant download 任务。这次我们等的时间较长,因为 eclipse-JDT-3.2.zip 大约有 19M ,下载需要一段时间。我们可乘此机会去泡杯茶弄点咖啡什么的,等我们品茶回来,发现敬业的蚂蚁 Ant 告诉我们编译成功,虽然编译器给出几个警告。这时我们可发现刚才创建的 base.path 目录( D:\carl_wu\tomcat\share )中已经下载了 6 个依赖项目,它们都是 Tomcat 编译所必须的。
下面就开始真正的编译任务了,请在 MS-DOS 窗口内键入 ant 并回车, Ant 将在 2 分钟内编译 1000 多个源文件并将 Tomcat 部署到 output 目录。编译顺利完成后,请打开 Tomcat 的源代码目录,会发现多了一个 output 目录,这是 Ant 的编译后的输出目录。请打开 Tomcat 源代码的 output\build\bin 子目录,双击 startup.bat 文件,我们即可成功启动 Tomcat6.0 ,此时我们的编译工作就算顺利完成了。
3. 导入源代码到 Eclipse
3.1 请打开 Eclipse ,新建一个 Java 项目,然后点击“ Next ”按钮,请选择“ Create project from existing source ”, 并在 Directory 文本框内填入我们刚才下载的 Tomcat 源代码目录( i.e. D:\carl_wu\tomcat\src) ,然后点击“ Next ”直至结束。
3.2 我们将会看到 Eclipse 拒绝编译,这是因为 Eclipse 找不到该项目指定的库文件。请右击该项目,在弹出菜单中选择“ Properties ” à “ Libraries ”,然后删除两个以 TOMCAT_LIBS 开头的两个库文件,只保留一个 JRE 库文件,然后点击“ OK ”按钮,这时 Eclipse 开始编译 Tomcat 源代码,但是发现一堆错误,这是因为我们没有为该项目添加编译所必须的 Jar 包。
3.3 准备好 Tomcat 项目所必须的 jar 文件,其实,刚才我们运行 ant download 任务时,已经下载过这些 jar 文件包。
ant.jar (请在 ant 安装目录的 lib 子目录中拷贝)
commons-collections-3.1.jar (从刚才 Ant 下载的 commons-collections-3.1 子目录中拷贝)
commons-dbcp-1.2.1.jar (从刚才 Ant 下载的 commons-dbcp-1.2.1 子目录中拷贝)
commons-logging-1.1.jar (如果您本机没有这个 jar 包,请从 http://commons.apache.org/downloads/download_logging.cgi 处下载)
commons-pool-1.2.jar (从刚才 Ant 下载的 commons-pool-1.2 子目录中拷贝)
org.eclipse.jdt.core_3.2.0.v_671.jar (从刚才 Ant 下载的 eclipse\plugins 子目录中拷贝)
3.4 当我们准备好这些 jar 文件后,将这些文件拷贝到某一目录(比如说 D:\carl_wu\tomcat\tomcat_lib 目录),然后在 Eclipse 中新建一个 User Libraries ,我们将这个新建的 User Libraries 命名为 TOMCAT_LIBS ,并把这些文件加到 TOMCAT_LIBS 。然后将我们新建的 TOMCAT_LIBS 添加到 Tomcat6 项目。另外,别忘了把 JUnit 库也加到 Tomcat6 项目。这时 Eclipse 开始重新编译,编译过程顺利通过,所有错误均消失,此时 Tomcat6 项目的目录结构如下:
还有,请把 test 目录也加入到源代码中,方法是在 Eclipse 中右击 ”test” 目录,然后在弹出菜单中选择“ Build path” à ”Use as Source Folder ”,之后我们会看到 test 目录上就多了个源代码的符号,如上图所示。
3.5 在 Eclipse 中运行 Tomcat 。请找到 Tomcat 的启动主类 org.apache.catalina.startup.Bootstrap ,右击这个类,在弹出菜单中选择“ Run As…” à ”Open Run Dialog…” ,然后在弹出的“ Run ”窗口中填入程序运行参数“ start” 和 JVM 运行参数 catalina.home ,如下面窗口所示:
然后点击“ Run ”按钮,我们将会看到 Tomcat 正常启动。恭喜,咱们的 Tomcat 源码已经成功导入 Eclipse ,这时,可视化的 UML 分析工具及 Debug 工具就能派上用场了。
3.5 调试 Tomcat ,请打开 org.apache.jasper.compiler.Compiler 类的源代码,在 generateJava() 方法的第一行打一个断点,然后在 Eclipse 的调试状态下运行 Tomcat ,等 Tomcat 运行后,打开我们的浏览器,在地址栏中输入 http://localhost:8080/examples/jsp/jsp2/el/basic-comparisons.jsp 并回车,然后我们可观察到 Eclipse 此时切换至调试视图:
上面的小实验表明我们可以在 Eclipse 中通过 Debugger 观察 Tomcat 的内部运行机理。另外补充一点,上面的 generateJava 方法是将 jsp 动态编译至 java class ,这个方法只是在第一次请求或者 Jsp 源码发生变化时执行,如果您再次在浏览器中发送同样的请求,您将看不到上图的 Debug 界面,因为该方法不再执行。
另外,还有一点很有意思。 Tomcat6 以前版本的源代码分散在好几个子项目中,他们分别叫做 jakarta-servletapi-5 , jakarta-tomcat-5 , jakarta-tomcat-catalina , jakarta-tomcat-connectors 和 jakarta-tomcat-jasper ,我觉得 Tomcat 的开发者可能嫌这样做太麻烦了,所以 Tomcat6 版本中将这些子项目都合并在一起了。但是,这种做法不利于我们阅读理解源代码