日志文章

2008年03月13日 03:10:16

Tomcat源码学习(一)

作为一位Java程序员,如果您没有接触过开源软件、项目或框架的话,恐怕有些不可思议。轰轰烈烈的开源运动起源于Linux操作系统,Apache基金会在其中扮演了中流砥柱的角色,业界巨擘SUNIBM BEA Oracle等公司的积极参与,使得声势浩大的开源运动成为软件开发领域势不可挡的力量。200111月,IBMApache基金会捐献出Visual Age for Java,这个看似穷途末路的产品经众多高手的改造,演变为辉煌一时的Eclipse,直接击败了不开源的JBuilder,让做编译器起家的Borland公司几乎关张大吉。Eclipse这个产品如此经典,以至于微软的Visual Studio都得向它学习。在Apache Harmony的围追堵截下,Java的发明者Sun公司一看势头不妙,于2006年宣布Java开源,随后又公开了其旗舰级产品Solaris的源代码。今年1月,开源的死对头、冷酷自私的微软也不得不在MS-RL协议下公开.Net的源代码。但是,在这如火如荼的开源运动中,我们中国的程序员又有多少贡献呢,我们开创了哪些框架、项目和产品,为开源界添砖加瓦呢?以笔者短浅的目光看来,我们对开源界贡献的东西恐怕很少,能够与国外经典开源项目一较高下的,少之又少矣!

作为一名中国的程序员,咱们能没有遗憾吗?为什么经典的Apache Web Server不是中国人写的;为什么Linus Torvalds在大学时代就写出Linux并振臂一呼,应者云集;为什么JBoss能与巨无霸式的Websphere相抗衡;为什么MySQL能在OracleSQL Server的夹击下发展并壮大…… ?如此等等问题,在遗憾之余,我想我们应该花点时间好好思考一下,中国的软件产业怎么了,中国的程序员又怎么啦?


在笔者看来,我们的程序员对开源的理解是相当狭隘的。国学大师王国维曾说过,古往今来成大学问大事业者要经历三种境界,“昨夜西风凋碧树,独上高楼,望尽天涯路”,这是第一重境界,迷惘也;“衣带渐宽终不悔,为伊消得人憔悴”,苦苦求索之境界也;第三重境界为“众里寻他千百度,蓦然回首,那人却在灯火阑珊处”,经历多少次的失败和挫折后,终于参透真谛,领悟真理。我觉得开源也有三重境界:


首先,我们要敞开心胸,拥抱开源(
Open to Open Source)。这重境界我们大家都能做到,拿来主义嘛,谁人不会。当我们的项目需要数据库时,就去下载一个免费MySQL;需要IDE时,去下载Eclipse;需要版本控制工具时,就去下载CVS;需要写搜索引擎时,Lucene可能是我们的最爱;当我们开发J2EE Web应用时,Struts/JSF加Hibernate/iBATIS再加上Spring或许成为我们的首选架构。但是,我们绝大部分程序员都停留在这个层次上,大家下载之后,看看文档介绍,安装、配置并能运行,就以为万事大吉,一切顺利。偶尔遇到一些问题,去Google一搜,答案立马可得。

其次,我们要深入开源,了解开源(
Dig into Open Source)。要达到这个层次,就有些难度了。我们不但要知其然,还要知其所以然。“知其所以然”的最好办法就是下载源代码,仔细研读,揣摩并领会源代码的精义,看看这些经过诸多高手修改的源代码究竟藏有什么玄机,我们能从其中学习到哪些设计思想及设计模式,能复用其中哪些源代码,人家运用了哪些软件管理思想把这些来自世界各地程序员的劳动汇集成一个产品,代码架构如何,软件配置管理又是怎样进行的……,等等等等,我们从源代码中学习的东西太多了。在阅读源代码时,我们要多问自己几个为什么,这样就会收获更多。

再次,我们要融入开源,贡献开源(
Get involved in Open Source)。当我们彻底理解该项目源代码后,我们应发挥一下“人人为我,我为人人”的思想,或结合您的实际需要,或结合您的新想法,或针对Mail lists上的问题,对该开源项目加以改进和创新,并把自己的代码贡献出来,让大家评估。当然,如果您有好的想法,您完全可以创建自己的开源项目,Apache基金会中众多的开源项目不都是我们广大程序员一手创建的吗?但是,在创建新开源项目时,切忌不要重新发明轮子。

笔者才疏学浅,想以
Apache Jakarta项目包中的核心项目Tomcat为例,希望通过阅读源码,能从这个经典项目中学到更多的东西,为我们中国的开源事业起到抛砖引玉的作用。

下面我们就开始我们的
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: 产生一个无版本控制的目录树副本。

用法: 1export [-r REV] URL[@PEGREV] [PATH]

2export [-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的源代码顺利导入到目标目录。下面是源代码的目录机构,从这个目录结构中,我们可以看出该项目的开发者使用的IDEEclipse,因为我们看到了熟悉的.project.classpath文件。如果您打算开发一个Stand aloneJava应用程序,不妨借鉴一下Tomcat的目录结构,把脚本文件放在bin目录,将xmlproperties配置文件放在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的编译和EclipseJDT有什么关系?其实不然,Tomcat是在Eclipse下开发的,所以需要EclipseJDTJava 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.5Eclipse中运行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-5jakarta-tomcat-5jakarta-tomcat-catalinajakarta-tomcat-connectorsjakarta-tomcat-jasper,我觉得Tomcat的开发者可能嫌这样做太麻烦了,所以Tomcat6版本中将这些子项目都合并在一起了。但是,这种做法不利于我们阅读理解源代码。






Tags: Tomcat   源代码   学习   Java  

类别: Tomcat |  评论(41) |  浏览(5103) |  收藏
一共有 41 条评论
41楼 [楼主]Carl 2008年09月24日 09:29:23 Says:
To Googol,

Please try to access

http://localhost:8080/examples/jsp/index.html
40楼 [楼主]Carl 2008年09月24日 09:21:45 Says:
To wzm:

请手动下载http://mirror.calvin.edu/eclipse/downloads/drops/R-3.2-200606291905/eclipse-JDT-3.2.zip到本地比如说D:/temp/eclipse-JDT-3.2.zip,然后修改build.properties文件中的jdt.loc= file:///D:/temp/eclipse-JDT-3.2.zip。

39楼 [匿名]Googol 2008年09月18日 14:38:28 Says:
楼主,您好,看了您的文章很是受益,有个问题想请教您一下,我按照您在上述,将环境搭建起来,编译没有出任何的错误提示信息,可是examples却打不开,提示HTTP Status 404 - /examples/的错误,但在我编译后的tomcat应用程序中的\webapps\examples文件都有。
38楼 [匿名]wzm 2008年09月17日 23:26:31 Says:
java 版本 "1.5.0_16"
Tomcat 版本 6.0
Subversion 版本:CollabNetSubversion-server-1.4.6-4.win32

base.path和jdt.loc已经修改

运行ant download结果如下:
D:\svn\tomcat\src>ant download
Buildfile: build.xml

download:

setproxy:

testexist:
  [echo] Testing for D:/svn/tomcat/share/tomcat-native-1.1.6/tomcat-native.t
ar.gz

downloadfile:

setproxy:

testexist:
  [echo] Testing for D:/svn/tomcat/share/tomcat6-deps/dbcp/tomcat-dbcp.jar

downloadgz:

setproxy:

testexist:
  [echo] Testing for D:/svn/tomcat/share/tomcat6-deps/dbcp/tomcat-dbcp.jar

downloadgz:

setproxy:

testexist:
  [echo] Testing for D:/svn/tomcat/share/tomcat6-deps/dbcp/tomcat-dbcp.jar

downloadgz:

build-tomcat-dbcp:
  [copy] Copying 57 files to D:\svn\tomcat\share\tomcat6-deps\dbcp
  [move] Moving 57 files to D:\svn\tomcat\share\tomcat6-deps\dbcp\src\java\or
g\apache\tomcat\dbcp
  [javac] Compiling 57 source files to D:\svn\tomcat\share\tomcat6-deps\dbcp\c
lasses
  [javac] D:\svn\tomcat\share\tomcat6-deps\dbcp\src\java\org\apache\tomcat\dbc
p\dbcp\SQLNestedException.java:38: 警告:最后一个参数使用了不准确的变量类型的 va
rargs 方法的非 varargs 调用;
  [javac] 对于 varargs 调用,应使用 java.lang.Class
  [javac] 对于非 varargs 调用,应使用 java.lang.Class[],这样也可以抑制此警告
  [javac]         getCauseMethod = Throwable.class.getMethod("getCause", n
ull);
  [javac]                                             ^

  [javac] 注意:某些输入文件使用或覆盖了已过时的 API。
  [javac] 注意:要了解详细信息,请使用 -Xlint:deprecation 重新编译。
  [javac] 注意:某些输入文件使用了未经检查或不安全的操作。
  [javac] 注意:要了解详细信息,请使用 -Xlint:unchecked 重新编译。
  [javac] 1 警告
    [jar] Building jar: D:\svn\tomcat\share\tomcat6-deps\dbcp\tomcat-dbcp.jar

setproxy:

testexist:
  [echo] Testing for D:/svn/tomcat/share/eclipse/plugins/org.eclipse.jdt.cor
e_3.2.0.v_671.jar

downloadzip:
    [get] Getting: http://mirror.calvin.edu/eclipse/downloads/drops/R-3.2-2006
06291905/eclipse-JDT-3.2.zip
    [get] To: D:\svn\tomcat\share\file.zip
  [unzip] Expanding: D:\svn\tomcat\share\file.zip into D:\svn\tomcat\share

BUILD FAILED
D:\svn\tomcat\src\build.xml:554: The following error occurred while executing th
is line:
D:\svn\tomcat\src\build.xml:516: Error while expanding D:\svn\tomcat\share\file.
zip
java.util.zip.ZipException: archive is not a ZIP archive

Total time: 3 seconds


37楼 [匿名]onaaam7o 2008年08月26日 11:51:20 Says:
%E6%9C
36楼 [匿名]tt2860145 2008年08月24日 09:45:46 Says:
谢谢楼主...我试试..
35楼 [匿名]wowowo 2008年08月11日 14:28:13 Says:
谢谢楼主
34楼 [楼主]Carl 2008年08月11日 10:07:58 Says:
To wowowo:

这段代码是JMX在Tomcat中的应用,请参考我的下一篇博客,JMX在Tomcat中的应用(一、二、三、四),想摘录一段如下:

4.1 上面的代码首先使用ClassLoaderFactory工厂类创建一个ClassLoader;

4.2 然后在MBeanServerFactory这个工厂类中查找MBeanServer,如果没有发现,就使用这个工厂类创建一个MBeanServer;

4.3 第三步是给刚才创建的ClassLoader这个MBean取个名字“Name: Catalina:type=ServerClassLoader,name=common”,然后注册到MBeanServer。

您如果在JConsole中观察这个MBean,会发现这个MBean没有向管理应用程序暴露任何属性和方法,并且Classloader似乎也符合JMX命名规范,它也不是一个DynamicBean,这是为什么呢?首先,这个Classloader其实一个StandardClassloader,而不是JDK中缺省的Classloader,您如果打开ClassLoaderFactory的createClassLoader方法,马上就可以看到这一点;另外,看看StanderClassloader的类签名,我们会发现该类实现了StandardClassLoaderMBean接口,这是符合JMX命名规范的;请打开StandardClassLoaderMBean接口的源代码,您会发现这是一个空接口,这意味着实现这个接口的MBean不会向管理程序暴露任何属性和方法。
33楼 [匿名]leonard 2008年08月06日 22:45:20 Says:
呵呵,找到原因了。我直接吧basepath的地址拷贝粘帖进build.properties了,但是斜杠和反斜杠却没有改过来,所以在makedir的时候失败了。叨扰楼主了。
32楼 [匿名]leonard 2008年08月05日 22:18:04 Says:
感谢楼主的快速答复。我没有使用代理服务器,而且使用浏览器直接下载非常顺利。但是依然不知道怎么做。楼主是否能推荐一下ant的资料,能对我的编译过程的理解有帮助的。否则以我对ant的丝毫不了解,只是浪费楼主的时间。
31楼 [匿名]wowowo 2008年08月05日 10:02:23 Says:
楼主你好,看了你的文章燃起了我阅读源代码的热情,按照你的提示已经建立好了环境,并且正在以调试模式看代码,在看到BootStrap类的
private ClassLoader createClassLoader(String name, ClassLoader parent)方法时,最后的这几句不太明白:
MBeanServer mBeanServer = null;
    if (MBeanServerFactory.findMBeanServer(null).size() > 0) {
        mBeanServer =
          (MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0);
    } else {
        mBeanServer = MBeanServerFactory.createMBeanServer();
    }

    // Register the server classloader
    ObjectName objectName =
        new ObjectName("Catalina:type=ServerClassLoader,name=" + name);
    mBeanServer.registerMBean(classLoader, objectName);
这几句是要做什么?请指点一二.谢谢
30楼 [楼主]Carl 2008年08月05日 09:52:47 Says:
to 匿名]大狗:

你看看下面这篇文章对你有没有帮助?抱歉,我没有编译过Spring。

http://springide.org/project/wiki/SpringideRepository
29楼 [楼主]Carl 2008年08月05日 09:31:27 Says:
to leonard:

你的网络连上了没有?如果你使用代理服务器上网,请在Build.properties文件中加上以下几行:

  proxy.host=proxy.domain
  proxy.port=port_number
  proxy.use=on

如果还是不行,请手动从浏览器中下载下面这个文件,看看是否成功:
http://archive.apache.org/dist/tomcat/tomcat-connectors/native/tomcat-native-1.1.6-src.tar.gz



28楼 [匿名]leonard 2008年08月04日 23:03:24 Says:
楼主,我在编译的时候就遇到这个问题,一直没有找到原因,请指教。

E:\data\dev\work\eclipseWork\tomcat\src>ant download
Buildfile: build.xml

download:

proxyflags:

setproxy:

testexist:
  [echo] Testing for E:datadevworkeclipseWork     omcatshare/tomcat-native
-1.1.14/tomcat-native.tar.gz

downloadfile:

BUILD FAILED
E:\data\dev\work\eclipseWork\tomcat\src\build.xml:664: The following error occurred while executing this line:
E:\data\dev\work\eclipseWork\tomcat\src\build.xml:657: Directory E:\data\dev\work\eclipseWork\tomcat\src\datadevworkeclipseWork omcatshare\tomcat-native-1.1.14
creation was not successful for an unknown reason

Total time: 0 seconds
E:\data\dev\work\eclipseWork\tomcat\src>
27楼 [楼主]Carl 2008年08月01日 04:38:21 Says:
To tt2860145:

对你的问题,提供几个参考意见:
(1)最偷懒的办法是,在..\output\build\webapps\examples\WEB-INF目录下,找到web.xml文件的下面几行,把它们注释为:

<!--
  <listener>
    <listener-class>listeners.ContextListener</listener-class>
  </listener>
  <listener>
    <listener-class>listeners.SessionListener</listener-class>
  </listener>

-->

这样,Tomcat启动时就找不到session和context监听器,因此不需要找这两个类。

(2)如果你的Tomcat能在Eclipse中运行,请在Eclipse中找到org.apache.catalina.core.StandardContext.java文件,在其源代码的3760行(ClassLoader loader = getLoader().getClassLoader();)打个断点,然后在Eclipse调试状态下启动Tomcat,注意观察loader变量中的dirFolder或者类似的属性指向那个目录,分析以下问题所在;

(3)如果再不行,请把..\src\output\build\webapps\examples目录下所有文件打一个zip包发到我的邮箱(kww731029@gmail.com),我有时间帮你看看
26楼 [匿名]大狗 2008年07月31日 15:31:08 Says:
请问楼主,如何把spring的源码像你这样导入eclipse?
我按照spring网站上的提示用svn做check out,如下:
svn co https://springframework.svn.sourceforge.net/svnroot/springframework springframework
但是并不成功,是不是这个地址有问题?
那么应该怎么办呢?我试图把下载下来的代码版spring里的src目录当做工程目录导入eclipse,但是编译时会报很多错误,应该是其中的一些jar没有导进来,于是我试图把spring目录中lib下的jar导入工程,但是我不知道如何能把这个文件夹直接导入,难道要把其中每个文件夹里的jar一个一个的放进去?
我知道肯定是我哪儿弄的不对,请楼主帮忙!谢谢!
25楼 [匿名]tt2860145 2008年07月29日 22:51:19 Says:
很感谢您抽空看看,经过我仔细的查看您说的ContextListener.java,ContextListener.class,SessionListener.java,SessionListener.class都是存在的..不知道还会有什么原因..还是我的ant版本有问题
24楼 [楼主]Carl 2008年07月28日 16:06:02 Says:
To tt2860145:
你的错误是找不到listeners.ContextListener,这个类位于examples的webapp目录下,如果找不到,你的examples web应用将不能正常启动。请检查:

(1)在你下载的Tomcat源代码目录中,能否找到这个文件:
..\webapps\examples\WEB-INF\classes\listeners\ContextListener.java

(2)在Ant编译过的output目录中,能否找到下面这个文件:
..\output\build\webapps\examples\WEB-INF\classes\listeners\ContextListener.class

如果找不到,请把下面的源代码另存为..\webapps\examples\WEB-INF\classes\listeners\ContextListener.java,重新编译试试。
/*
* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package listeners;


import javax.servlet.ServletContext;
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;


/**
* Example listener for context-related application events, which were
* introduced in the 2.3 version of the Servlet API. This listener
* merely documents the occurrence of such events in the application log
* associated with our servlet context.
*
* @author Craig R. McClanahan
* @version $Revision: 267129 $ $Date: 2004-03-18 17:40:35 +0100 (jeu., 18 mars 2004) $
*/

public final class ContextListener
  implements ServletContextAttributeListener, ServletContextListener {


  // ----------------------------------------------------- Instance Variables


  /**
  * The servlet context with which we are associated.
  */
  private ServletContext context = null;


  // --------------------------------------------------------- Public Methods


  /**
  * Record the fact that a servlet context attribute was added.
  *
  * @param event The servlet context attribute event
  */
  public void attributeAdded(ServletContextAttributeEvent event) {

     log("attributeAdded('" + event.getName() + "', '" +
        event.getValue() + "')");

  }


  /**
  * Record the fact that a servlet context attribute was removed.
  *
  * @param event The servlet context attribute event
  */
  public void attributeRemoved(ServletContextAttributeEvent event) {

     log("attributeRemoved('" + event.getName() + "', '" +
        event.getValue() + "')");

  }


  /**
  * Record the fact that a servlet context attribute was replaced.
  *
  * @param event The servlet context attribute event
  */
  public void attributeReplaced(ServletContextAttributeEvent event) {

     log("attributeReplaced('" + event.getName() + "', '" +
        event.getValue() + "')");

  }


  /**
  * Record the fact that this web application has been destroyed.
  *
  * @param event The servlet context event
  */
  public void contextDestroyed(ServletContextEvent event) {

     log("contextDestroyed()");
     this.context = null;

  }


  /**
  * Record the fact that this web application has been initialized.
  *
  * @param event The servlet context event
  */
  public void contextInitialized(ServletContextEvent event) {

     this.context = event.getServletContext();
     log("contextInitialized()");

  }


  // -------------------------------------------------------- Private Methods


  /**
  * Log a message to the servlet context application log.
  *
  * @param message Message to be logged
  */
  private void log(String message) {

     if (context != null)
        context.log("ContextListener: " + message);
     else
        System.out.println("ContextListener: " + message);

  }


  /**
  * Log a message and associated exception to the servlet context
  * application log.
  *
  * @param message Message to be logged
  * @param throwable Exception to be logged
  */
  private void log(String message, Throwable throwable) {

     if (context != null)
        context.log("ContextListener: " + message, throwable);
     else {
        System.out.println("ContextListener: " + message);
        throwable.printStackTrace(System.out);
     }

  }


}


另外,该目录下还有一个SessionListener.java文件:
/*
* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package listeners;


import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;


/**
* Example listener for context-related application events, which were
* introduced in the 2.3 version of the Servlet API. This listener
* merely documents the occurrence of such events in the application log
* associated with our servlet context.
*
* @author Craig R. McClanahan
* @version $Revision: 267129 $ $Date: 2004-03-18 17:40:35 +0100 (jeu., 18 mars 2004) $
*/

public final class SessionListener
  implements ServletContextListener,
          HttpSessionAttributeListener, HttpSessionListener {


  // ----------------------------------------------------- Instance Variables


  /**
  * The servlet context with which we are associated.
  */
  private ServletContext context = null;


  // --------------------------------------------------------- Public Methods


  /**
  * Record the fact that a servlet context attribute was added.
  *
  * @param event The session attribute event
  */
  public void attributeAdded(HttpSessionBindingEvent event) {

     log("attributeAdded('" + event.getSession().getId() + "', '" +
        event.getName() + "', '" + event.getValue() + "')");

  }


  /**
  * Record the fact that a servlet context attribute was removed.
  *
  * @param event The session attribute event
  */
  public void attributeRemoved(HttpSessionBindingEvent event) {

     log("attributeRemoved('" + event.getSession().getId() + "', '" +
        event.getName() + "', '" + event.getValue() + "')");

  }


  /**
  * Record the fact that a servlet context attribute was replaced.
  *
  * @param event The session attribute event
  */
  public void attributeReplaced(HttpSessionBindingEvent event) {

     log("attributeReplaced('" + event.getSession().getId() + "', '" +
        event.getName() + "', '" + event.getValue() + "')");

  }


  /**
  * Record the fact that this web application has been destroyed.
  *
  * @param event The servlet context event
  */
  public void contextDestroyed(ServletContextEvent event) {

     log("contextDestroyed()");
     this.context = null;

  }


  /**
  * Record the fact that this web application has been initialized.
  *
  * @param event The servlet context event
  */
  public void contextInitialized(ServletContextEvent event) {

     this.context = event.getServletContext();
     log("contextInitialized()");

  }


  /**
  * Record the fact that a session has been created.
  *
  * @param event The session event
  */
  public void sessionCreated(HttpSessionEvent event) {

     log("sessionCreated('" + event.getSession().getId() + "')");

  }


  /**
  * Record the fact that a session has been destroyed.
  *
  * @param event The session event
  */
  public void sessionDestroyed(HttpSessionEvent event) {

     log("sessionDestroyed('" + event.getSession().getId() + "')");

  }


  // -------------------------------------------------------- Private Methods


  /**
  * Log a message to the servlet context application log.
  *
  * @param message Message to be logged
  */
  private void log(String message) {

     if (context != null)
        context.log("SessionListener: " + message);
     else
        System.out.println("SessionListener: " + message);

  }


  /**
  * Log a message and associated exception to the servlet context
  * application log.
  *
  * @param message Message to be logged
  * @param throwable Exception to be logged
  */
  private void log(String message, Throwable throwable) {

     if (context != null)
        context.log("SessionListener: " + message, throwable);
     else {
        System.out.println("SessionListener: " + message);
        throwable.printStackTrace(System.out);
     }

  }


}
23楼 [匿名]tt2860145 2008年07月27日 23:39:13 Says:
麻烦您抽空出来看看.....
22楼 [匿名]tt2860145 2008年07月27日 23:37:27 Says:
楼主:麻烦你帮我看看.一直找不到原因
严重: Error configuring application listener of class listeners.ContextListener
java.lang.ClassNotFoundException: listeners.ContextListener
     at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1354)
     at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1200)
     at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3769)
     at org.apache.catalina.core.StandardContext.start(StandardContext.java:4335)
     at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:759)
     at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:739)
     at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:524)
     at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:919)
     at org.apache.catalina.startup.HostConfig.deployDirectories(HostConfig.java:882)
     at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:491)
     at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1137)
     at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:310)
     at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:119)
     at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1021)
     at org.apache.catalina.core.StandardHost.start(StandardHost.java:718)
     at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1013)
     at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:442)
     at org.apache.catalina.core.StandardService.start(StandardService.java:450)
     at org.apache.catalina.core.StandardServer.start(StandardServer.java:709)
     at org.apache.catalina.startup.Catalina.start(Catalina.java:551)
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
     at java.lang.reflect.Method.invoke(Method.java:585)
     at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:287)
     at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:412)
2008-7-27 23:42:48 org.apache.catalina.core.StandardContext listenerStart
严重: Error configuring application listener of class listeners.SessionListener
java.lang.ClassNotFoundException: listeners.SessionListener
     at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1354)
     at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1200)
     at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3769)
     at org.apache.catalina.core.StandardContext.start(StandardContext.java:4335)
     at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:759)
     at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:739)
     at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:524)
     at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:919)
     at org.apache.catalina.startup.HostConfig.deployDirectories(HostConfig.java:882)
     at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:491)
     at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1137)
     at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:310)
     at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:119)
     at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1021)
     at org.apache.catalina.core.StandardHost.start(StandardHost.java:718)
     at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1013)
     at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:442)
     at org.apache.catalina.core.StandardService.start(StandardService.java:450)
     at org.apache.catalina.core.StandardServer.start(StandardServer.java:709)
     at org.apache.catalina.startup.Catalina.start(Catalina.java:551)
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
     at java.lang.reflect.Method.invoke(Method.java:585)
     at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:287)
     at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:412)
2008-7-27 23:42:48 org.apache.catalina.core.StandardContext listenerStart
严重: Skipped installing application listeners due to previous error(s)
2008-7-27 23:42:48 org.apache.catalina.core.StandardContext start
严重: Error listenerStart
2008-7-27 23:42:48 org.apache.catalina.core.StandardContext start
严重: Context [/examples] startup failed due to previous errors
2008-7-27 23:42:48 org.apache.coyote.http11.Http11AprProtocol start
信息: Starting Coyote HTTP/1.1 on http-8080
2008-7-27 23:42:49 org.apache.coyote.ajp.AjpAprProtocol start
信息: Starting Coyote AJP/1.3 on ajp-8009
2008-7-27 23:42:49 org.apache.catalina.startup.Catalina start
信息: Server startup in 7953 ms
« 1 23» Pages: ( 1/3 total )
发表评论
看不清楚,换一张