How Tomcat Works(十九)
本文重点关注启动tomcat时会用到的两个类,分别为Catalina类和Bootstrap类,它们都位于org.apachae.catalina.startup包下;Catalina类用于启动或关闭Server对象,并负责解析server.xml配置文件;Bootstrap类是一个入口点,负责创建Catalina实例,并调用其process()方法。
org.apachae.catalina.startup.Catalina类是启动类,它包含一个Digester对象,用于解析位于%CATALINE_HOME%/conf目录的server.xml文件
Catalina类还封装了一个Server对象,该对象持有一个Service对象(Service对象包含一个Servlet容器和一个或多个连接器)。可以使用Catalina类来启动/关闭Server对象
可以通过实例化Catalina类,并调用其process()方法来运行Tomcat,但在调用该方法时,需要传入适当的参数(如start或stop -help -config -debug -nonaming等)。
/**
* The instance main program.
*
* @param args Command line arguments
*/
public void process(String args[]) { setCatalinaHome();
setCatalinaBase();
try {
if (arguments(args))
execute();
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
上面方法会进一步调用execute()方法,execute()方法会根据传入参数调用start()方法或stop()方法
start方法会创建一个Digester对象来解析server.xml文件(Tomcat配置文件)。在解析server.xml文件之前,start方法会调用Digester对象的push方法,传入当前的Catalina对象为参数。这样,Catalina对象就成了Digester对象内部对象栈的第一个对象。解析server.xml文件后,会将变量server指向一个Server对象(默认是org.apache.catalina.core.StandardServer类型的对象)。然后,start方法会调用server的initialize和start方法。Catalina对象的start方法会调用Server对象的await方法,server对象会使用一个专用的线程来等待关闭命令。await方法会循环等待,知道接收到正确的关闭命令。当await方法返回时,Catalina对象的start方法会调用server对象的stop方法,从而关闭server对象和其他的组件。此外,start方法还会注册shutdown hook,确保服务器关闭时会执行Server对象的stop方法。
start方法的实现如下:
/**
* Start a new server instance.
*/
protected void start() { // Create and execute our Digester
Digester digester = createStartDigester();
File file = configFile();
try {
InputSource is =
new InputSource("file://" + file.getAbsolutePath());
FileInputStream fis = new FileInputStream(file);
is.setByteStream(fis);
digester.push(this);
digester.parse(is);
fis.close();
} catch (Exception e) {
System.out.println("Catalina.start: " + e);
e.printStackTrace(System.out);
System.exit(1);
} // Setting additional variables
if (!useNaming) {
System.setProperty("catalina.useNaming", "false");
} else {
System.setProperty("catalina.useNaming", "true");
String value = "org.apache.naming";
String oldValue =
System.getProperty(javax.naming.Context.URL_PKG_PREFIXES);
if (oldValue != null) {
value = value + ":" + oldValue;
}
System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);
value = System.getProperty
(javax.naming.Context.INITIAL_CONTEXT_FACTORY);
if (value == null) {
System.setProperty
(javax.naming.Context.INITIAL_CONTEXT_FACTORY,
"org.apache.naming.java.javaURLContextFactory");
}
} // If a SecurityManager is being used, set properties for
// checkPackageAccess() and checkPackageDefinition
if( System.getSecurityManager() != null ) {
String access = Security.getProperty("package.access");
if( access != null && access.length() > 0 )
access += ",";
else
access = "sun.,";
Security.setProperty("package.access",
access + "org.apache.catalina.,org.apache.jasper.");
String definition = Security.getProperty("package.definition");
if( definition != null && definition.length() > 0 )
definition += ",";
else
definition = "sun.,";
Security.setProperty("package.definition",
// FIX ME package "javax." was removed to prevent HotSpot
// fatal internal errors
definition + "java.,org.apache.catalina.,org.apache.jasper.");
} // Replace System.out and System.err with a custom PrintStream
SystemLogHandler log = new SystemLogHandler(System.out);
System.setOut(log);
System.setErr(log); Thread shutdownHook = new CatalinaShutdownHook(); // Start the new server
if (server instanceof Lifecycle) {
try {
server.initialize();
((Lifecycle) server).start();
try {
// Register shutdown hook
Runtime.getRuntime().addShutdownHook(shutdownHook);
} catch (Throwable t) {
// This will fail on JDK 1.2. Ignoring, as Tomcat can run
// fine without the shutdown hook.
}
// Wait for the server to be told to shut down
server.await();
} catch (LifecycleException e) {
System.out.println("Catalina.start: " + e);
e.printStackTrace(System.out);
if (e.getThrowable() != null) {
System.out.println("----- Root Cause -----");
e.getThrowable().printStackTrace(System.out);
}
}
} // Shut down the server
if (server instanceof Lifecycle) {
try {
try {
// Remove the ShutdownHook first so that server.stop()
// doesn't get invoked twice
Runtime.getRuntime().removeShutdownHook(shutdownHook);
} catch (Throwable t) {
// This will fail on JDK 1.2. Ignoring, as Tomcat can run
// fine without the shutdown hook.
}
((Lifecycle) server).stop();
} catch (LifecycleException e) {
System.out.println("Catalina.stop: " + e);
e.printStackTrace(System.out);
if (e.getThrowable() != null) {
System.out.println("----- Root Cause -----");
e.getThrowable().printStackTrace(System.out);
}
}
} }
其中的CatalinaShutdownHook类为关闭钩子
/**
* Shutdown hook which will perform a clean shutdown of Catalina if needed.
*/
protected class CatalinaShutdownHook extends Thread { public void run() { if (server != null) {
try {
((Lifecycle) server).stop();
} catch (LifecycleException e) {
System.out.println("Catalina.stop: " + e);
e.printStackTrace(System.out);
if (e.getThrowable() != null) {
System.out.println("----- Root Cause -----");
e.getThrowable().printStackTrace(System.out);
}
}
} }
}
Catalina对象的stop方法会关闭Server对象,其实现如下:
/**
* Stop an existing server instance.
*/
protected void stop() { // Create and execute our Digester
Digester digester = createStopDigester();
File file = configFile();
try {
InputSource is =
new InputSource("file://" + file.getAbsolutePath());
FileInputStream fis = new FileInputStream(file);
is.setByteStream(fis);
digester.push(this);
digester.parse(is);
fis.close();
} catch (Exception e) {
System.out.println("Catalina.stop: " + e);
e.printStackTrace(System.out);
System.exit(1);
} // Stop the existing server
try {
Socket socket = new Socket("127.0.0.1", server.getPort());
OutputStream stream = socket.getOutputStream();
String shutdown = server.getShutdown();
for (int i = 0; i < shutdown.length(); i++)
stream.write(shutdown.charAt(i));
stream.flush();
stream.close();
socket.close();
} catch (IOException e) {
System.out.println("Catalina.stop: " + e);
e.printStackTrace(System.out);
System.exit(1);
} }
注意,stop方法通过调用createStopDigester方法创建一个Digester对象,然后将Catalina对象push到Digester对象的内部对象栈中。
createStartDigester方法创建了一个Digester对象,然后将规则添加到其中,解析server.xml文件。添加到Digester对象中的规则是理解tomcat配置的关键。
createStartDigester方法的实现如下:
/**
* Create and configure the Digester we will be using for startup.
*/
protected Digester createStartDigester() { // Initialize the digester
Digester digester = new Digester();
if (debug)
digester.setDebug(999);
digester.setValidating(false); // Configure the actions we will be using
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
digester.addSetProperties("Server");
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server"); digester.addObjectCreate("Server/GlobalNamingResources",
"org.apache.catalina.deploy.NamingResources");
digester.addSetProperties("Server/GlobalNamingResources");
digester.addSetNext("Server/GlobalNamingResources",
"setGlobalNamingResources",
"org.apache.catalina.deploy.NamingResources"); digester.addObjectCreate("Server/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Listener");
digester.addSetNext("Server/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener"); digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service"); digester.addObjectCreate("Server/Service/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Listener");
digester.addSetNext("Server/Service/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener"); digester.addObjectCreate("Server/Service/Connector",
"org.apache.catalina.connector.http.HttpConnector",
"className");
digester.addSetProperties("Server/Service/Connector");
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.Connector"); digester.addObjectCreate("Server/Service/Connector/Factory",
"org.apache.catalina.net.DefaultServerSocketFactory",
"className");
digester.addSetProperties("Server/Service/Connector/Factory");
digester.addSetNext("Server/Service/Connector/Factory",
"setFactory",
"org.apache.catalina.net.ServerSocketFactory"); digester.addObjectCreate("Server/Service/Connector/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Connector/Listener");
digester.addSetNext("Server/Service/Connector/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener"); // Add RuleSets for nested elements
digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
digester.addRuleSet(new EngineRuleSet("Server/Service/"));
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Default"));
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/DefaultContext/"));
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/Default"));
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/DefaultContext/"));
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/")); digester.addRule("Server/Service/Engine",
new SetParentClassLoaderRule(digester,
parentClassLoader)); return (digester); }
这里需要注意的是 digester.addSetNext("Server", "setServer","org.apache.catalina.Server")方法,该方法将Server对象压入到Digester对象的内部栈中,并与栈中的下一个对象相关联;在这里,下一个对象是Catalina实例,调用其setServer()方法与Server对象相关联。
createStopDigester方法返回一个Digester对象来关闭Server对象。createStopDigester方法实现如下:
/**
* Create and configure the Digester we will be using for shutdown.
*/
protected Digester createStopDigester() { // Initialize the digester
Digester digester = new Digester();
if (debug)
digester.setDebug(999); // Configure the rules we need for shutting down
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
digester.addSetProperties("Server");
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server"); return (digester); }
与启动Digester对象不同,关闭Digester对象只对XML文件的根元素感兴趣
org.apache.catalina.startup.Bootstrap类提供了启动tomcat的切入点(还有一些其他的类也有此功能)。当使用bat或sh启动tomcat时,实际上会调用该类的main方法。在main方法中会创建三个loader,并实例化Catalina对象,然后调用Catalina对象的process方法。
Bootstrap类的定义如下所示:
public final class Bootstrap {
// ------------------------------------------------------- Static Variables
/**
* Debugging detail level for processing the startup.
*/
private static int debug = 0;
// ----------------------------------------------------------- Main Program
/**
* The main program for the bootstrap.
*
* @param args Command line arguments to be processed
*/
public static void main(String args[]) {
// Set the debug flag appropriately
for (int i = 0; i < args.length; i++) {
if ("-debug".equals(args[i]))
debug = 1;
}
// Configure catalina.base from catalina.home if not yet set
if (System.getProperty("catalina.base") == null)
System.setProperty("catalina.base", getCatalinaHome());
// Construct the class loaders we will need
ClassLoader commonLoader = null;
ClassLoader catalinaLoader = null;
ClassLoader sharedLoader = null;
try {
File unpacked[] = new File[1];
File packed[] = new File[1];
File packed2[] = new File[2];
ClassLoaderFactory.setDebug(debug);
unpacked[0] = new File(getCatalinaHome(),
"common" + File.separator + "classes");
packed2[0] = new File(getCatalinaHome(),
"common" + File.separator + "endorsed");
packed2[1] = new File(getCatalinaHome(),
"common" + File.separator + "lib");
commonLoader =
ClassLoaderFactory.createClassLoader(unpacked, packed2, null);
unpacked[0] = new File(getCatalinaHome(),
"server" + File.separator + "classes");
packed[0] = new File(getCatalinaHome(),
"server" + File.separator + "lib");
catalinaLoader =
ClassLoaderFactory.createClassLoader(unpacked, packed,
commonLoader);
unpacked[0] = new File(getCatalinaBase(),
"shared" + File.separator + "classes");
packed[0] = new File(getCatalinaBase(),
"shared" + File.separator + "lib");
sharedLoader =
ClassLoaderFactory.createClassLoader(unpacked, packed,
commonLoader);
} catch (Throwable t) {
log("Class loader creation threw exception", t);
System.exit(1);
}
Thread.currentThread().setContextClassLoader(catalinaLoader);
// Load our startup class and call its process() method
try {
SecurityClassLoad.securityClassLoad(catalinaLoader);
// Instantiate a startup class instance
if (debug >= 1)
log("Loading startup class");
Class startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance();
// Set the shared extensions class loader
if (debug >= 1)
log("Setting startup class properties");
String methodName = "setParentClassLoader";
Class paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
// Call the process() method
if (debug >= 1)
log("Calling startup class process() method");
methodName = "process";
paramTypes = new Class[1];
paramTypes[0] = args.getClass();
paramValues = new Object[1];
paramValues[0] = args;
method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
} catch (Exception e) {
System.out.println("Exception during startup processing");
e.printStackTrace(System.out);
System.exit(2);
}
}
/**
* Get the value of the catalina.home environment variable.
*/
private static String getCatalinaHome() {
return System.getProperty("catalina.home",
System.getProperty("user.dir"));
}
/**
* Get the value of the catalina.base environment variable.
*/
private static String getCatalinaBase() {
return System.getProperty("catalina.base", getCatalinaHome());
}
/**
* Log a debugging detail message.
*
* @param message The message to be logged
*/
private static void log(String message) {
System.out.print("Bootstrap: ");
System.out.println(message);
}
/**
* Log a debugging detail message with an exception.
*
* @param message The message to be logged
* @param exception The exception to be logged
*/
private static void log(String message, Throwable exception) {
log(message);
exception.printStackTrace(System.out);
}
}
Bootstrap类定义了四个静态方法,其中getCatalinaHome返回catalina.home属性的值,若没有,则返回user.dir属性的值。getCatalinaBase方法与getCatalinaHome方法类似。
Bootstrap类的main方法构造了三个loader,之所以如此做是为了防止运行WEB-INF/classes和WEB-INF/lib目录外的类。
其中commonLoader允许从%CATALINA_HOME%/common/classes,%CATALINA_HOME%/common/endorsed和%CATALINA_HOME%/common/lib目录下载入类。
catalinaLoader负责载入servlet容器需要使用的类,它只会从%CATALINA_HOME%/server/classes和%CATALINA_HOME%/server/lib目录下查找。
sharedLoader会从%CATALINA_HOME%/shared/classes和%CATALJNA_HOME%/shared/lib目录,以及对commonLoader可用的目录下查找需要的类。
然后,将sharedLoader设置为每个web应用的类载入器的父类载入器。(注意,sharedLoader并不访问catalina的内部类,或CLASSPATH中的类)
在创建了三个loader之后,main方法会载入Catalina类,实例化,并将之赋值给startupInstance变量。然后调用setParentClassLoader方法。
最后,main方法调用Catalina对象的process对象。
---------------------------------------------------------------------------
本系列How Tomcat Works系本人原创
转载请注明出处 博客园 刺猬的温驯
本人邮箱: chenying998179#163.com (#改为@)
本文链接http://www.cnblogs.com/chenying99/p/3250907.html
How Tomcat Works(十九)的更多相关文章
- How Tomcat Works(九)
本文接下来描述servlet容器是怎样管理其相关组件的生命周期的,首先本人描述一下事件监听模式,也可以称为观察者模式,该模式分为以下角色 即抽象主题角色 具体主题角色 抽象观察者角色及具体观察者角色, ...
- How Tomcat Works(十四)补充
在How Tomcat Works(十四)中,本人并没有对javax.servlet.Filter及javax.servlet.FilterChain做详细的描述,本文在这里做一下补充 FilterC ...
- How Tomcat Works(十四)
我们已经知道,在tomcat中有四种类型的servlet容器,分别为Engine.Host.Context 和Wrapper,本文接下来对tomcat中Wrapper接口的标准实现进行说明. 对于每个 ...
- How Tomcat Works(二十)
要使用一个web应用程序,必须要将表示该应用程序的Context实例部署到一个host实例中.在tomcat中,context实例可以用war文件的形式来部署,也可以将整个web应用拷贝到Tomcat ...
- How Tomcat Works(十八)
在前面的文章中,如果我们要启动tomcat容器,我们需要使用Bootstrap类来实例化连接器.servlet容器.Wrapper实例和其他组件,然后调用各个对象的set方法将它们关联起来:这种配置应 ...
- How Tomcat Works(十六)
本文接下来会介绍Host容器和Engine容器,在tomcat的实际部署中,总是会使用一个Host容器:本文介绍Host接口和Engine接口及其相关类 Host容器是org.apache.catal ...
- How Tomcat Works(十五)
本文接下来分析Context容器,Context容器实例表示一个具体的Web应用程序,其中包括一个或多个Wrapper实例:不过Context容器还需要其他的组件支持,典型的如载入器和Session管 ...
- How Tomcat Works(十二)
tomcat容器通过一个称为Session管理器的组件来管理建立的Session对象,该组件由org.apache.catalina.Manager接口表示:Session管理器必须与一个Contex ...
- How Tomcat Works(十)
本文接下来分析tomcat的日志记录器,日志记录器是用来记录消息的组件,在tomcat中,日志记录器需要与某个servlet容器相关连:在org.apache.catalina.logger包下,to ...
随机推荐
- 插入排序的JavaScript实现
思想 每次在现有已经排好的数组的基础上排入一个新的数组项. 先把第一项看做是已经排好的,第二项应该排在第一项之前还是之后呢?当前两项排好后,第三项应该排在这已排好的两项的之前还是之后还是中间呢?当前三 ...
- 解决VS2008 调试启动特别慢
Resolving Very Slow Symbol Loading with VS 2008 during debugging Recently, I was encountering insane ...
- 20位活跃在Github上的国内技术大牛
登录|注册 leon-这个程序员不闷骚的博客 喜欢leon,有追求有原则有爱心的杀手,做一个有追求的程序员,代码是程序员的朋友,虽然没有热情,但是非常忠实.希望拥有一身绝世武功,再配一把绝世好 ...
- FPGA各大厂商,不可不知
引言: FPGA市场前景诱人,但是门槛之高在芯片行业里无出其右.全球有60多家公司先后斥资数十亿美元,前赴后继地尝试登顶FPGA高地,其中不乏英特尔.IBM.德州仪器.摩托罗拉.飞利浦.东芝.三星这样 ...
- Rest之路 - Rest架构中的重要概念
资源 在Rest的架构之内,讲一切内容都是为资源.每一个资源都被定义为一个URI. 格式: <protocol>://<service-name>/<ResourceTy ...
- Maria数据库
项目上要进行数据库选型,业务上来讲,数据是非常结构化的数据,使用传统关系数据库更适合:另外项目采用微服务框架,每个服务的数据库应该尽可能轻量级, 最后考虑Maria数据库. MariaDB简介: Ma ...
- mac常用命令(随时更新)
mac 强制退出快捷键 1.使用键盘快捷键强制退出处于活跃状态的Mac程序 快捷键:Command+Option+Shift+Esc 这样按住一两秒钟,就可以强制退出当前程序了,算是最方便的一种方法. ...
- AOP(面向切面编程概念,本文为翻译)
AOP是什么 AOP为Aspect Oriented Programming的缩写.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型.利用 ...
- java成神之——安全和密码
安全和密码 加密算法 公钥和私钥加密解密 生成私钥和公钥 加密数据 解密数据 公钥私钥生成的不同算法 密钥签名 生成加密随机数 基本用法 指定算法 加密对象 SealedObject Signatur ...
- leetcode486
public class Solution { public bool PredictTheWinner(int[] nums) { // int n = nums.Length; // int[,] ...