Tomcat源码分析 -- 4
sschrodinger
2019/01/07
参考
- 《深入剖析 Tomcat》 - 基于Tomcat 4.x
- 《Tomcat 架构解析》刘光瑞 著
- 《大话设计模式》程杰 著
- Tomcat 8.5.x 源码
- Digester 3.0 API
Digester
tomcat 使用 Digester 解析并创建应用服务。
Digester 是一款将 XML 转换为 Java 对象的事件驱动型工具,通过流读取 XML 文件,当识别出特定 XML 节点后执行特定的动作:或者创建 Java 对象;或者执行对象的某个方法。
Digester 使用匹配模式指定相关的约定,匹配模式样例如下:
匹配模式 | XML节点 | 描述 |
---|---|---|
a | <a> | 匹配所有名字为"a"的根节点,注意嵌套的同名子节点无法匹配 |
a/b | <a><b> | 匹配所有父节点为根节点"a",名称为"b"的节点。 |
Digester 使用对象栈在匹配模式满足时对对象进行操作。在文件读取中,如果遇到一个 XML 节点开始的部分,则会触发处理规则事件创建 Java 对象,并将其放入栈中。当处理该节点的子节点时,该对象都将维护在栈中,直到该节点结束,该对象才会弹出对象栈。
Digester 匹配模式确定了合适触发处理操作,处理规则定义了匹配模式时的具体操作,只需实现 org.apache.commons.digester.Rule
接口,就可以自定义事件处理方法。该接口有四个函数,如下表。
函数名 | 描述 |
---|---|
begin() | 当读取到匹配节点的开始部分时调用,会将该节点的所有属性作为参数传入 |
body() | 当读取匹配节点的嵌套内容时调用 |
end() | 当读取到匹配节点的结束部分时调用 |
finish() | 当整个parse()方法完成后调用 |
Digester 实现了部分默认的事件处理函数,部分如下:
方法及参数 | 描述 |
---|---|
addObjectCreate(String pattern, Class clazz) | 当匹配到 pattern 模式时,将 clazz 实例化并压入栈 |
addSetProperties(String pattern) | 当匹配到 pattern 模式时,利用 JavaBean 将参数赋值给对象 |
addSetNext(String pattern, String methodName) | end() 方法调用,找到位于栈顶部对象之后的对象,调用指定的方法,同时将栈顶部对象作为参数传入,用于设置父对象的子对象 |
addCallMethod(String pattern, String methodName, int paramCount, Class<?>[] paramTypes) | 用于在 end() 执行时调用栈顶部对象的某个方法 |
addCallParam(String pattern, int paramIndex, String attributeName) | 与 addCallMethod 配合使用,创建参数 |
有如下两个个类:
public class Department {
public Department() {
// TODO Auto-generated constructor stub
}
private String name;
private String code;
private Map<String, String> extension = new HashMap<>();
private List<User> users = new ArrayList<>();
public String getName() { return name;}
public void setName(String name) { this.name = name;}
public String getCode() {return code;}
public void setCode(String code) {this.code = code;}
public void addUser(User user) { this.users.add(user);}
public void putExtension(String name, String value) {this.extension.put(name, value);}
public void print() {
System.out.println("department name:" + name + ",code:" + code);
Set<String> keySet = extension.keySet();
if (keySet.size() > 0) System.out.println("extension info:");
for (String string : keySet) {
System.out.println("key:" + string + ",value:" + extension.get(string));
}
if (users.size() > 0) {
System.out.println("user info:");
for (User user : users) {
System.out.println(user);
}
}
}
}
public class User {
public User() {
// TODO Auto-generated constructor stub
}
private String name;
private String code;
public String getName() { return name;}
public void setName(String name) { this.name = name;}
public String getCode() {return code;}
public void setCode(String code) {this.code = code;}
@Override public String toString() {
return "name:" + name + ",code:" + code;
}
}
先通过如下 XML 文档将这两个类组织起来:
<?xml version="1.0" encoding="UTF-8"?>
<department name="deptname001" code="deptcode001">
<user name="username001" code="usercode001"></user>
<user name="username002" code="usercode002"></user>
<extension>
<property-name>director</property-name>
<propertry-value>joke</propertry-value>
</extension>
</department>
则我们可以通过如下的 Demo 代码自动生成树状结构的类:
public class digesterDemo {
public static void main(String[] args) {
Digester digester = new Digester();
digester.setValidating(false);
digester.addObjectCreate("department", Department.class);
digester.addSetProperties("department");
digester.addObjectCreate("department/user", User.class);
digester.addSetProperties("department/user");
digester.addSetNext("department/user", "addUser");
digester.addCallParam("department/extension/property-name", 0);
digester.addCallParam("department/extension/propertry-value", 1);
digester.addCallMethod("department/extension", "putExtension", 2);
try {
Department department = (Department) digester.parse(new File("test.xml"));
department.print();
/**
output:
department name:deptname001,code:deptcode001
extension info:
key:director,value:joke
user info:
name:username001,code:usercode001
name:username002,code:usercode002
**/
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
tomcat 启动
Tomcat 启动的主函数隐藏在or.apache.catalina.startup.Bootstrap
中。main
函数接受一个命令行参数启动或者关闭服务器,我们只关心启动过程,即 daemon.start() 分支。
public static void main(String args[]) {
if (daemon == null) {
// Don't set daemon until init() has completed
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
} else {
// When running as a service the call to stop will be on a new
// thread so make sure the correct class loader is used to prevent
// a range of class not found exceptions.
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
try {
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}
if (command.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
} else if (command.equals("stopd")) {
args[args.length - 1] = "stop";
daemon.stop();
} else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
if (null == daemon.getServer()) {
System.exit(1);
}
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else if (command.equals("configtest")) {
daemon.load(args);
if (null == daemon.getServer()) {
System.exit(1);
}
System.exit(0);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
// Unwrap the Exception for clearer error reporting
if (t instanceof InvocationTargetException &&
t.getCause() != null) {
t = t.getCause();
}
handleThrowable(t);
t.printStackTrace();
System.exit(1);
}
}
主要实现的功能
- 根据 server.xml 加载整个服务器 (Load()方法)
- 加载钩子
- await() 等待
- 等待结束 调用 stop() 方法 结束
daemon.start()
利用反射机制调用 org.apache.catalina.startup.Catalina
类的 start()
方法,启动整个服务器。
如下是 Catalina 类的 start() 方法:
public void start() {
if (getServer() == null) {
load();
}
if (getServer() == null) {
log.fatal("Cannot start server. Server instance is not configured.");
return;
}
long t1 = System.nanoTime();
// Start the new server
try {
getServer().start();
} catch (LifecycleException e) {
log.fatal(sm.getString("catalina.serverStartFail"), e);
try {
getServer().destroy();
} catch (LifecycleException e1) {
log.debug("destroy() failed for failed Server ", e1);
}
return;
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
}
// Register shutdown hook
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);
// If JULI is being used, disable JULI's shutdown hook since
// shutdown hooks run in parallel and log messages may be lost
// if JULI's hook completes before the CatalinaShutdownHook()
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
false);
}
}
if (await) {
await();
stop();
}
}
Tomcat 结合 Digester 和 LifecycleListener 做了大量的初始化工作。具体的执行函数为protected Digester createStartDigester()
。
protected Digester createStartDigester() {
long t1=System.currentTimeMillis();
// Initialize the digester
Digester digester = new Digester();
digester.setValidating(false);
digester.setRulesValidation(true);
Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
List<String> objectAttrs = new ArrayList<>();
objectAttrs.add("className");
fakeAttributes.put(Object.class, objectAttrs);
// Ignore attribute added by Eclipse for its internal tracking
List<String> contextAttrs = new ArrayList<>();
contextAttrs.add("source");
fakeAttributes.put(StandardContext.class, contextAttrs);
digester.setFakeAttributes(fakeAttributes);
digester.setUseContextClassLoader(true);
// 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.NamingResourcesImpl");
digester.addSetProperties("Server/GlobalNamingResources");
digester.addSetNext("Server/GlobalNamingResources",
"setGlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl");
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");
//Executor
digester.addObjectCreate("Server/Service/Executor",
"org.apache.catalina.core.StandardThreadExecutor",
"className");
digester.addSetProperties("Server/Service/Executor");
digester.addSetNext("Server/Service/Executor",
"addExecutor",
"org.apache.catalina.Executor");
digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
digester.addRule("Server/Service/Connector",
new SetAllPropertiesRule(new String[]{"executor", "sslImplementationName"}));
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.connector.Connector");
digester.addObjectCreate("Server/Service/Connector/SSLHostConfig",
"org.apache.tomcat.util.net.SSLHostConfig");
digester.addSetProperties("Server/Service/Connector/SSLHostConfig");
digester.addSetNext("Server/Service/Connector/SSLHostConfig",
"addSslHostConfig",
"org.apache.tomcat.util.net.SSLHostConfig");
digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
new CertificateCreateRule());
digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
new SetAllPropertiesRule(new String[]{"type"}));
digester.addSetNext("Server/Service/Connector/SSLHostConfig/Certificate",
"addCertificate",
"org.apache.tomcat.util.net.SSLHostConfigCertificate");
digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
"org.apache.tomcat.util.net.openssl.OpenSSLConf");
digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf");
digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
"setOpenSslConf",
"org.apache.tomcat.util.net.openssl.OpenSSLConf");
digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
"org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd");
digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
"addCmd",
"org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
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");
digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Connector/UpgradeProtocol");
digester.addSetNext("Server/Service/Connector/UpgradeProtocol",
"addUpgradeProtocol",
"org.apache.coyote.UpgradeProtocol");
// 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/Host/"));
addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));
// When the 'engine' is found, set the parentClassLoader.
digester.addRule("Server/Service/Engine",
new SetParentClassLoaderRule(parentClassLoader));
addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");
long t2=System.currentTimeMillis();
if (log.isDebugEnabled()) {
log.debug("Digester for server.xml created " + ( t2-t1 ));
}
return (digester);
}
getServer().start()
启动server的生命周期到LifecycleState.STARTED状态;stop()
方法关闭服务器,将生命周期改变为LifecycleState.DESTROYED。
网友评论