Some Confusion about the source code of Tomcat

Some day before, I began to read the source code of the tomcat First, the server starts from the main method in the org.apache.catalina.startup.BootStrap, but when I go into the code bootstrap.init(), and I was confused by the following code.Like: Just for easy to debug the tomcat, I change the code like this: The code…

Tomcat的启动详解

本来,受Oracle面试官的指导。从年前就开始阅读Tomcat的源代码了,在这边就准备把自己的阅读心得拿出来共享下。 作为这个系列的第一篇文章,我们先从如何在Eclipse中运行开始。这一点上, 我是借鉴了I’m Tiger这篇文章。所以在这边,如果希望跳过这一步骤,可以参考他的和我的已经编译好的Tomcat版本。 地址是:I’m Tiger’s Tomcat或者是使用我组织好的Tomcat, Mike’s Tomcat 下载好之后,只需按照一般的Maven项目导入Eclipse就可以了。过程比较简单。 然后可以进入文章的正题了,就是Tomcat的启动过程。 首先,在Eclipse中导入工程之后会看到这样的画面: 这里面,我们可以看到两个目录,一个是tomcat-study,一个是Tomcat7.0,但是我们这边主要是阅读后者的源代码, 上一个项目其实是Tomcat7.0的上级目录,没有什么太大的意义。 然后,我们进入org.apache.catalina.startup.BootStrap类,因为这边有我们亲爱的Main函数。 在这边,我们看到的代码是Tomcat的配置代码,简单的说,就是在Tomcat启动之前去加载那些必须的类库。 在这边加一个断点,然后一步一步走下去。进入init()方法。 这边就是BootStrap函数的init().可以看到,首先就是设置CatalinaHome,CatalinaBase,这两个函数主要的 作用,就是将Catalina的路径找到,方便之后的ClassLoader进行加载。 然后进入的就是initClassLoaders(),这边不得不提一下,Java中的双亲委托模型。 因为在Tomcat中,类加载器就是按照这个模型涉及的。了解了这个之后,我们来看这边的初始化类加载器的代码。 这里就很明显的体现了双亲委托模型。首先建立了一个没有父加载器的common加载器,然后他就有了两个子 加载器:server, shared。具体的意义呢,就是在程序中,所有的类都是在他的根加载器中加载,这样可以避免同名 但却被不同加载器加载,造成误读的状况。具体的解释呢,可以看Jetty ClassLoader解析。 然后,我们继续往下(具体的一些比如createClassLoader(),就不说了,因为毕竟很浅显的名字),然后我们就进入了 init()方法的后半段 现在就是使用动态加载的技术,使用ClassLoad直接去加载.class文件,我们可以从代码,很好的理解,我们需要找到 org.apache.catalina.startup.Catalina这个类,然后将java.lang.ClassLoader作为参数,调用setParentClassLoader(ClassLoader) 方法。然后将这个动态加载的Catalina类作为一个后台的Daemon传入BootStrap的catalinaDaemon中。完成BootStrap的初始化, 同时,也开起了一个Catalina的后台Daemon。 接下来,我们就来看Main函数的后半段。 // ignore the try catch code block 在这边,我们可以看到,这是一个使用args的代码段,这也是当我们运行tomcat start时执行的函数体, 于是,我们就可以直接看command.equals(“start”)时的一些动作。即daemon.setAwait(true),daemin.load(args)和 daemon.start()这三个函数。 调用Catalina的setAwait()函数。而这个函数只有三行,作用就是典型的setter and getter方法。 之后便是daemon.load(args)。 load()函数的作用就是呼唤Catalina的load方法,而他的load就是将我们常用的配置文件conf/server.xml导入。 然后就返回了。 最后就是start()了,其实也就是Catalina的start()…,官方的Doc里的解释就是Start a new server instance….

Android上编译Native C代码

首先,我们不得不提到一个我们在Java程序开发中常使用的JNI模式, 这个模式后来也被Google采用,也能在Android下使用了,但是使用NDK的方式, 写出一个共享so库,然后使用Java JNI技术进行调用。这个方法在编写一些根本不需要 界面的程序时,就显得无比的蛋疼。 所以这边我们介绍一个简单的办法,即编写一个native的本地C程序,直接运行在Android Device上面。 其实说道这边,熟悉ARM开发的人已经知道怎么办了。Android基于Linux和ARM,所以在底层, 就是和一块普通的ARM板没有什么区别,Android只是Linux上的一个小程序。 通常的编程步骤来进行,编写源文件,用编译器编译,链接器链接,然后运行。 我们这边有两个方法获得Android上的编译工具撚,一个方法是编译Android ROM时自动生成的 还有一个办法是,使用Android NDK开发工具包中带的编译工具。 通过编译ROM获得编译工具的方法具体请看AGCC的文档,或者 可以看这篇文章Hello World C program using Android Toolchain 中文的话可以看看这篇:android编译工具agcc,这些文章包含了如何获取AGCC,和如何使用AGCC, 但是,说实话,这种方法我没有尝试,不知道结果,但是还是把地址给了出来,希望看到的可以尝试下。 现在说说我的方法,我的方法是采用借助Android NDK的方式,首先我们需要获得Agcc这个编译脚本,你可以在AGCC下载获得这个脚本,然后你也可以参照Vlad Blogs进行安装配置。值得注意的是, 这个脚本支持ndk-r4b,其他的版本我不是很清楚,因为没有测试过,而且在他的官方的注释中: 也只是说明了他支持最新的NDK的版本,ndk-r4b 好了,接下来就是部署的步骤了: 1. 安装Android SDK,这个可以到Google官方网站上进行下载。 我们需要的是SDK中的adb调试工具。这个工具在最新单版本的SDK中需要进行下载。具体可以看SDK中的文档。 之后,我们需要安装NDK,这个比较简单,都是解压即用的,在这就不细说了。不过注意的一点,SDK和NDK解压结束之后, 需要注册环境变量,在这,我只介绍Linux下的环境变量,至于win的,不说了。以下是我的环境变量,可以作为参考。 再之后,将前面下载的agcc.pl文件放到NDK目录之下,然后添加到PATH中,方便之后的调用, 同时,也可以看到该脚本存放的目录。 你可以尝试执行agcc.pl,如果出现以下结果,说明你的操作已经正确。然后就可以按照gcc的用法进行使用agcc.pl了。 编译简单的程序,你直接就可以执行agcc.pl **.c -o **,如果是复杂的程序,需要很长的参数,比如说这样: agcc.pl gemini_code/main.c -I’$NDK_HOME/build/platforms/android-8/arch-arm/usr/include/netinet’ -o gemini 好了,之后只要出现file gemini可以看到如下结果,这个文件是一个ARM的可执行文件,那就大功告成,使用adb将其push到手机上执行即可。 附: adb 传输文件命令: $ adb…

Jar代码注入

有段时间没有写博客了,主要的原因是期末考试和回老家过年,这段时间基本上没有看相关的内容,不过还是有很多可以用来分享的。 就像前一篇日志提到的一样,Java可以从字节码的方式进行玩耍。通过使用Javassist这个类库,可以很方便的进行字节码的操作。 从这个角度讲,有很多我们之前没有想到的方面也可以进行实施。比如说有这么一个jar包,需要进行引用开发,于是乎,我们可以进行一些简单的恶作剧,可以在某一个方法执行之前或之后加入一些注入代码,同时这个方法还可以进行一些简单的破解工作。 主要使用到的工具有jar打包,Javassist类库。接下来我们来讲一个简单的小例子。这个例子就是展示如何向一个已经打包好的jar注入代码。 首先,我们先就看看这个简单的类代码: 之所以包含main函数,主要是用来感受下代码注入之后的效果。 接下来我们看看运行的效果。 这就是我们在代码中包含main函数的用途。 接下来就是我们的注入代码片段。 第一点,我们将gecko.jar加入classpool的classpath,然后我们将Gecko.class从这个jar中提取出来,我们用getDeclearMethods()得到现在这个类的所有方法,这一招可以用来对付那些混淆过得代码。 第二点,我们再使用insertBefore(),和insertAfter()。将自己的注入代码写入class文件。然后将写入的class写入文件。 现在,我们可以在工程的目录下看到已经写入的class文件。 然后就是使用jar打包命令了。这个命令的使用方法很简单,主要的重点也就这两点。 因为我们的jar已经打包,所以,我们只需要解压这个包,然后把class文件进行替换。之后运行以下命令: jar cvfm gecko2.jar gecko/META-INF/MANIFEST.MF -C gecko . 然后我们就看到一堆adding的信息,表明我们的class文件,就可以在目录下看到jar文件了。 之后,我们运行java -jar gecko2.jar 好了,我们的实验成功了!然后就可以做一些其他好玩的事情了!