tomcat(15)Digester库
【0】README0.1)本文部分文字描述转自 “how tomcat works”,旨在学习 “tomcat(15)Digester库” 的基础知识;2)problem+solution:2.1)problem:如下面的代码,Bootstrap类实例化连接器,servlet容器,Wrapper容器和其它组件,如设置连接器的关联顶层容器,通过set方法将它们联系起来;如监听器组
·
【0】README
0.1)本文部分文字描述转自 “how tomcat works”,旨在学习 “tomcat(15)Digester库” 的基础知识;
2)problem+solution:
2.1)problem:如下面的代码,Bootstrap类实例化连接器,servlet容器,Wrapper容器和其它组件,如设置连接器的关联顶层容器,通过set方法将它们联系起来;如监听器组件通过addLifecycleListener来设置等等。这种配置应用程序的方法有一个明显的缺点:即所有的配置都必须硬编码。调整组件配置或属性值都必须要重新编译Bootstrap类。2.2)solution:Tomcat使用了一种更加优雅的配置方式,即使用一个名为server.xml 的XML 文档来对应用程序进行配置。server.xml文件中的每个元素都会转换为一个java 对象,元素的属性会用于设置java对象 的属性。这样,就可以通过简单地编辑 server.xml文件来修改tomcat的配置了;
看个荔枝)如server.xml文件中的 Context元素表示一个Context实例:<context/>;若要为 Context实例设置path属性和 docBase属性,使用这样的配置:
<context docBase="myApp" path="/myApp" />
Attention)
A1)tomcat使用了开源库Digester来将XML 文档中的元素转换成 java 对象;(干货——开源库Digester的作用)A2)用来配置web 应用程序的XML 文件的名称是 web.xml,该文件位于web 应用程序的WEB-INF 目录下;(干货——引入了大家熟悉的web.xml)
public final class Bootstrap1 {
public static void main(String[] args) {
//invoke: http://localhost:8080/app1/Primitive or http://localhost:8080/app1/Modern
System.setProperty("catalina.base", System.getProperty("user.dir"));
Connector connector = new HttpConnector();
Wrapper wrapper1 = new StandardWrapper();
wrapper1.setName("Primitive");
//wrapper1.setServletClass("servlet.PrimitiveServlet");
wrapper1.setServletClass("PrimitiveServlet");
Wrapper wrapper2 = new StandardWrapper();
wrapper2.setName("Modern");
//wrapper2.setServletClass("servlet.ModernServlet");
wrapper2.setServletClass("ModernServlet");
Context context = new StandardContext();
// StandardContext's start method adds a default mapper
context.setPath("/app1");
context.setDocBase("app1");
context.addChild(wrapper1);
context.addChild(wrapper2);
LifecycleListener listener = new SimpleContextConfig();
((Lifecycle) context).addLifecycleListener(listener);
Host host = new StandardHost();
host.addChild(context);
host.setName("localhost");
host.setAppBase("webapps");
Loader loader = new WebappLoader();
context.setLoader(loader);
// context.addServletMapping(pattern, name);
context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern");
connector.setContainer(host);
try {
connector.initialize();
((Lifecycle) connector).start();
((Lifecycle) host).start(); // 与以往的Bootstrap.java不同的是,这里是host.start() 而不是 context.start()
// make the application wait until we press a key.
System.in.read();
((Lifecycle) host).stop();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
【1】Digester库
1)intro:Digester是 Apache 下Jakarta项目下的子项目Commons项目下的一个开源项目;
2)Digester API包含3个包:三者都被打包到 commons-digester.jar 文件中;(package list)
package1)org.apache.commons.digester:该包提供了基于规则的,可处理任意XML 文档的类;package2)org.apache.commons.digester.rss:该包包含一些可以用来解析与很多新闻源使用的RSS(Rich Site Summary,富站点摘要)格式兼容的XML文档的例子;package3)org.apache.commons.digester.xmlrules:该包为 Digester库提供了一些基于XML规则的定义;
【1.1】Digester类
1)intro:Digester类可用于解析XML 文档;对于XMl 文档中的每个元素,Digester对象都会检查它是否要做事先预定义的事件。在调用Digester.parse()方法之前,需要先定义好Digester对象执行哪些动作;
2)如何定义在Digester对象遇到某个XMl 元素时它应该执行什么动作呢?—— 程序员先定义好模式,然后将每个模式与一条或多条规则相关联。XML 文档中根元素的模式与元素的名字相同。(干货——引入了模式,且XML 文档中根元素的模式与元素的名字相同)
3)看个荔枝:考虑下面的 XML文档:(example.xml)
<?xml version=1.0" encoding="ISO-8859-1">
<employee firstName="pacoson" lastName="xiao">
<office>
<address streetName="Wellington Street" streetNumber="110" />
</office>
</employee>
对上述代码的分析(Analysis):
A1)该XML文档中的根元素是 employee,employee元素有一个模式, 名为 employee;A2)office元素是 employee元素的子元素,子元素的模式是由该元素的父元素的模式再加上 “/” 符号,以及该元素名称拼接而成的,所以office元素的模式是 employee/office; (干货——我们这就了解了如何从XML 文档中推导出元素的模式)
4)下面讨论一下规则(rules):(干货——规则的定义,非常重要)
rule1)一条规则指明了当Digester 对象遇到了某个特殊的模式时要执行的一个或多个动作;规则是 org.apache.commons.digester.Rule 类;Digester类可以包含0个或多个对象;rule2)Rule类有begin()方法 和 end() 方法。在开始标签调用start()方法,结束标签调用 end() 方法;
5)自定义自己的规则:包括创建对象和设置属性值等的规则;(干货——自定义规则包括创建对象+设置属性+调用方法+创建对象间的关系+验证 XML 文档)
5.1)创建对象:若想要Digester对象在遇到某个特殊字符时创建对象,则需要调用其 addObjectCreate()方法,该方法有4个重载版本;(干货——引入addObjectCreate()方法)
public void addObjectCreate(String pattern, String className) {
addRule(pattern, new ObjectCreateRule(className));
}
public void addObjectCreate(String pattern, Class clazz) {
addRule(pattern, new ObjectCreateRule(clazz));
}
public void addObjectCreate(String pattern, String className, String attributeName) {
addRule(pattern, new ObjectCreateRule(className, attributeName));
}
public void addObjectCreate(String pattern, String attributeName, Class clazz) {
addRule(pattern, new ObjectCreateRule(attributeName, clazz));
}
对上述代码的分析(Analysis):
看个荔枝) 如我们想让Digester对象在遇到模式employee 时,创建一个 mydiy.Employee 对象,则使用下面的代码来调用 addObjectCreate()方法:A1)需要传入一个模式和一个Class对象或类名来调用该方法;
digester.addObjectCreate("employee", "mydiy.Employee.class"); 或者 digester.addObjectCreate("employee", "mydiy.Employee");
A2)addObjecdtCreate()方法的最后两个重载版本允许在xml 文档中定义类的名字,而无须将其作为参数传入,这使得类名可以在运行时决定;在上述最后两个重载方法中,参数 attributeName参数指明了 XML 元素的属性的名字,该属性包含了将要实例化的类的名字;
看个荔枝)
step1)添加创建对象的一条规则:digester.addObjectCreate("employee",null,"className");(属性名是 className);step2)传入XML 元素中的类名: <employee firstName="pacoson" lastName="xiao" className="mydiy.employee">;如果employee元素包含 className属性,那么该属性指定的值会用来作为待实例化的类的名字,如果没有包含 className属性,则会使用默认的类名;(干货——显然意思是说Emplyee类需要依赖 名为className的类对象)
Attention)addObjectCreate()方法创建的对象会被压入到一个内部栈中;
5.2)设置属性:addSetProperties()方法,该方法可以使用Digester对象为创建的对象设置属性。该方法的重载版本有:(干货——引入addSetProperties()方法)
public void addSetProperties(String pattern) {
addRule(pattern, new SetPropertiesRule());
}
public void addSetProperties( String pattern, String attributeName, String propertyName) {
addRule(pattern, new SetPropertiesRule(attributeName, propertyName));
}
public void addSetProperties(String pattern, String [] attributeNames, String [] propertyNames) {
addRule(pattern, new SetPropertiesRule(attributeNames, propertyNames));
}
看个荔枝)考虑下面的代码:
digester.addObjectCreate("employee", "mydiy.Employee");
digester.addSetProperties("employee");
对以上代码的分析(Analysis):
A1)上面的Digester有两个Rule 对象,分别用来创建对象和设置属性,他们都是通过employee模式触发的。而Rule对象按照其添加到Digester实例中的顺序逐个执行。A2)对于下面XMl 文档中的employee 元素(该元素匹配 employee模式): <employee firstName="pacoson", lastName="Xiao">;依据Digester的第一条rule,会创建 diy.Employee类的一个实例,依据第二条Rule,调用已经实例化的Employee.setFirstName() and Employee.setLastName(),分别传入pacoson 和 Xiao 来设置属性;
5.3)调用方法:
Digetser类允许通过添加一条Rule,使Digester 在遇到与该规则相关联的模式时调用内部栈最顶端对象的 某个方法。这需要用到 addCallMethod()方法,重载版本如下:
(干货——引入addCallMethod()方法)
public void addCallMethod(String pattern, String methodName) {
addRule( pattern, new CallMethodRule(methodName));
}
public void addCallMethod(String pattern, String methodName, int paramCount) {
addRule(pattern, new CallMethodRule(methodName, paramCount));
}
public void addCallMethod(String pattern, String methodName, int paramCount, String paramTypes[]) {
addRule(pattern, new CallMethodRule(
methodName,
paramCount,
paramTypes));
}
public void addCallMethod(String pattern, String methodName,
int paramCount, Class paramTypes[]) {
addRule(pattern,
new CallMethodRule(
methodName,
paramCount,
paramTypes));
}
5.4)创建对象之间的关系(干货——Digester实例有一个内部栈,用于临时存储创建的对象)(干货——引入addSetNext()方法)
5.4.1)addSetNext()方法:若栈中有两个对象,那么该方法会调用第1个对象的指定方法并将第2个对象作为参数传入该方法来创建第1个对象和第2个对象的关系;public void addSetNext(String pattern, String methodName) { addRule(pattern, new SetNextRule(methodName)); } public void addSetNext(String pattern, String methodName, String paramType) { addRule(pattern, new SetNextRule(methodName, paramType)); }
对以上代码的分析(Analysis):参数pattern 指明了触发该规则的具体模式,参数methodName 是将要调用的第1个对象的方法的名称。模式是如下格式:firstObject/secondObject;
看个荔枝)如何创建对象间的关系:
step1)创建两个对象;digester.addObjectCreate("employee", "mydiy.Employee"); digester.addObjectCreate("employee/office", "mydiy.Office");
step2)创建对象间的关系:需要另外定义一条规则,使用 addSetNext()方法来建立关系(调用addOffice()方法建立关系):digetster.addSetNext("employee/office", 'addOffice');
5.5)验证XML文档:Digester.validating属性指明了是否要对 XML 文档进行有效性验证。默认case下,其为false;setValidating()方法可以设置其值;(干货——引入setValidating()方法)
【1.2】Digester库荔枝1(如何使用 Digester库动态地创建对象,并设置相应的属性)
1)源代码
public class Test01 {
public static void main(String[] args) {
String path = System.getProperty("user.dir") + File.separator + "src";
File file = new File(path, "employee1.xml");
Digester digester = new Digester();
// add rules (为模式 employee 添加3条规则)
digester.addObjectCreate("employee",
"com.tomcat.chapter15.digestertest.Employee");
digester.addSetProperties("employee");
digester.addCallMethod("employee", "printName");
try {
Employee employee = (Employee) digester.parse(file);
System.out.println("First name : " + employee.getFirstName());
System.out.println("Last name : " + employee.getLastName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
<?xml version="1.0" encoding="ISO-8859-1"?>
<employee firstName="Brian" lastName="May">
</employee>
2)console info
Creating Employee
Setting firstName : Brian
Setting lastName : May
My name is Brian May
First name : Brian
Last name : May
3)info analysis:当调用parse()方法时,它会打开指定的xml 文档,开始解析它;(干货——当调用parse()方法时,它会打开指定的xml 文档,开始解析它,只需要parse方法就可以创建根元素(模式)的相应对象和其关联对象)
step1)Digester类查看 employee 元素的开始标签,这会触发与 employee模式关联的3条规则,按照其被添加到 Digester 对象中的顺序逐个执行;step2)第一条规则用于创建Employee对象,调用构造函数,打印Creating Employee;step3)第二条规则设置 Employee对象的属性,在employee 元素中包含两个属性:分别是 firstName 和 lastName, 这会调用调用的set方法,打印 Setting firstName : Brian Setting lastName : May;step4)第三条规则调用 Employee.printName()方法,打印M y name is Brian May;
【1.3】Digester库荔枝2(如何创建两个对象,并建立他们的关系)
1)源代码
public class Test02 {
public static void main(String[] args) {
String path = System.getProperty("user.dir") + File.separator + "src";
File file = new File(path, "employee2.xml");
Digester digester = new Digester();
// add rules,添加规则(key)
digester.addObjectCreate("employee",
"com.tomcat.chapter15.digestertest.Employee");
digester.addSetProperties("employee");
digester.addObjectCreate("employee/office",
"com.tomcat.chapter15.digestertest.Office");
digester.addSetProperties("employee/office");
digester.addSetNext("employee/office", "addOffice");
digester.addObjectCreate("employee/office/address",
"com.tomcat.chapter15.digestertest.Address");
digester.addSetProperties("employee/office/address");
digester.addSetNext("employee/office/address", "setAddress");
try {
Employee employee = (Employee) digester.parse(file);
ArrayList offices = employee.getOffices();
Iterator iterator = offices.iterator();
System.out
.println("-------------------------------------------------");
while (iterator.hasNext()) {
Office office = (Office) iterator.next();
Address address = office.getAddress();
System.out.println(office.getDescription());
System.out.println("Address : " + address.getStreetNumber()
+ " " + address.getStreetName());
System.out.println("--------------------------------");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
<?xml version="1.0" encoding="ISO-8859-1"?>
<employee firstName="Freddie" lastName="Mercury">
<office description="Headquarters">
<address streetName="Wellington Avenue" streetNumber="223"/>
</office>
<office description="Client site">
<address streetName="Downing Street" streetNumber="10"/>
</office>
</employee>
2)console info
Creating Employee
Setting firstName : Freddie
Setting lastName : Mercury
..Creating Office
..Setting office description : Headquarters
....Creating Address
....Setting streetName : Wellington Avenue
....Setting streetNumber : 223
..Setting office address : ....223 Wellington Avenue
Adding Office to this employee
..Creating Office
..Setting office description : Client site
....Creating Address
....Setting streetName : Downing Street
....Setting streetNumber : 10
..Setting office address : ....10 Downing Street
Adding Office to this employee
-------------------------------------------------
Headquarters
Address : 223 Wellington Avenue
--------------------------------
Client site
Address : 10 Downing Street
--------------------------------
Attention)本文不对 荔枝2的实例程序进行分析了,结合荔枝1的分析,理解这个不难;
【1.4】org.apache.commons.digester.Rule类(最重要的方法start() + end())
1)intro to begin() :当Digester实例 遇到某个XML 元素的开始标签时,会调用它所包含的匹配Rule 对象的begin()方法:
1)intro to begin() :当Digester实例 遇到某个XML 元素的开始标签时,会调用它所包含的匹配Rule 对象的begin()方法:
public void begin(Attributes attributes) throws Exception {
; // The default implementation does nothing
}
public void begin(String namespace, String name, Attributes attributes)
throws Exception {
begin(attributes);
}
2)intro to end():当Digester实例 遇到某个XML 元素的结束标签时,会调用它所包含的匹配Rule 对象的end()方法:
public void end() throws Exception {
; // The default implementation does nothing
}
public void end(String namespace, String name)
throws Exception {
end();
}
3)Digester对象是如何完成这些工作的? 当调用Digester.addObjectCreate()方法,addCallMethod()方法,addSetNext()方法或其他方法时,都会间接地调用 Digester.addRule()方法;
public void addRule(String pattern, Rule rule) {
rule.setDigester(this);
getRules().add(pattern, rule);
}
4)再次review Digester.addObjectCreate()方法的重载version:
public void addObjectCreate(String pattern, String className) {
addRule(pattern, new ObjectCreateRule(className));
}
public void addObjectCreate(String pattern, Class clazz) {
addRule(pattern, new ObjectCreateRule(clazz));
}
public void addObjectCreate(String pattern, String className, String attributeName) {
addRule(pattern, new ObjectCreateRule(className, attributeName));
}
public void addObjectCreate(String pattern, String attributeName, Class clazz) {
addRule(pattern, new ObjectCreateRule(attributeName, clazz));
}
A1)这4个重载方法都调用了addRule()方法,ObjectCreateRule类 是 Rule 类的子类,该类的实例都作为 addRule()方法的参数;A2)ObjectCreateRule.start()方法 和 ObjectCreateRule.end()方法的实现如下:public void begin(Attributes attributes) throws Exception { //org.apache.commons.digester.ObjectCreateRule.begin(). // Identify the name of the class to instantiate String realClassName = className; if (attributeName != null) { String value = attributes.getValue(attributeName); if (value != null) { realClassName = value; } } if (digester.log.isDebugEnabled()) { digester.log.debug("[ObjectCreateRule]{" + digester.match + "}New " + realClassName); } // Instantiate the new object and push it on the context stack Class clazz = digester.getClassLoader().loadClass(realClassName); Object instance = clazz.newInstance(); digester.push(instance); //highlight line. } public void end() throws Exception { Object top = digester.pop(); //highlight line. if (digester.log.isDebugEnabled()) { digester.log.debug("[ObjectCreateRule]{" + digester.match + "} Pop " + top.getClass().getName()); } }
对以上代码的分析(Analysis):begin()方法的最后三行会创建Digester对象的一个实例,并将其压入到 Digester对象的内部栈中。end()方法 会将内部站的栈顶元素弹出;
【1.5】Digester库荔枝3:使用RuleSet(org.apache.commons.digester.RuleSet)
1)要向Digester实例添加 Rule对象,还可以调用其 addRuleSet()方法;
2)Rule对象集合是 org.apache.commons.digester.RuleSet接口的实例,该接口定义了两个方法,分别是 addRuleInstance()方法 和 getNamespaceURI()方法;
2.1)addRuleInstance方法:将在当前RuleSet 中的Rule对象的集合作为该方法的参数添加到 Digester实例中;2.2)getNamespaceURI方法:返回将要应用在 RuleSet中所有Rule 对象的命名空间的URI;
3)RuleSetBase implements RuleSet。RuleSetBase 是一个抽象类,提供了getNamespaceURI的实现,你只需要提供addRuleInstances()方法的实现就可以了;
public abstract class RuleSetBase implements RuleSet {
protected String namespaceURI = null;
public String getNamespaceURI() {
return (this.namespaceURI);
}
public abstract void addRuleInstances(Digester digester);
}
4)测试用例
public class Test03 {
public static void main(String[] args) {
String path = System.getProperty("user.dir") + File.separator + "src";
File file = new File(path, "employee2.xml");
Digester digester = new Digester();
digester.addRuleSet(new EmployeeRuleSet());
try {
Employee employee = (Employee) digester.parse(file);
ArrayList offices = employee.getOffices();
Iterator iterator = offices.iterator();
System.out
.println("-------------------------------------------------");
while (iterator.hasNext()) {
Office office = (Office) iterator.next();
Address address = office.getAddress();
System.out.println(office.getDescription());
System.out.println("Address : " + address.getStreetNumber()
+ " " + address.getStreetName());
System.out.println("--------------------------------");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class EmployeeRuleSet extends RuleSetBase {
public void addRuleInstances(Digester digester) {
// add rules
digester.addObjectCreate("employee",
"com.tomcat.chapter15.digestertest.Employee");
digester.addSetProperties("employee");
digester.addObjectCreate("employee/office",
"com.tomcat.chapter15.digestertest.Office");
digester.addSetProperties("employee/office");
digester.addSetNext("employee/office", "addOffice");
digester.addObjectCreate("employee/office/address",
"com.tomcat.chapter15.digestertest.Address");
digester.addSetProperties("employee/office/address");
digester.addSetNext("employee/office/address", "setAddress");
}
}
Attention)参考前面做的分析,应该不难;
【2】ContextConfig类
1)在前面章节中,我们使用了 SimpleContextConfig 作为 StandardContext的监听器:其唯一用途是设置configure变量,这样StandardContext.start()方法才能继续执行;
public class SimpleContextConfig implements LifecycleListener {
public void lifecycleEvent(LifecycleEvent event) {
if (Lifecycle.START_EVENT.equals(event.getType())) {
Context context = (Context) event.getLifecycle();
context.setConfigured(true);
}
}
}
2)而tomcat 的标准监听器:是 org.apache.catalina.startup.ContextConfig类的实例;(干货——Tomcat标准监听器)
3)ContextConfig会执行很多对 StandardContext实例来说必不可少的任务。
如,与某个 StandardContext实例关联的 ContextConfig 实例会安装一个验证器阀到 StandardContext的管道中。它还会添加一个许可阀(org.apache.catalina.valves.CertificateValve)到管道对象中;
4)更重要的是:ContextConfig类的实例还要读取和解析默认的 web.xml 文件和应用程序自定义的web.xml文件,并将xml 元素转换为 java 对象;(干货——引入了默认的和自定义的web.xml文件)
4.1)默认的web.xml:位于 CATALINA_HOME/conf 目录中,其中定义并映射了很多默认的 servlet,配置了很多 MIME类型文件的映射,定义了默认的session超时时间,以及定义了欢迎文件的列表;4.2)应用程序的web.xml文件:位于 WEB-INF 目录中;
5)以上两个文件都不是必须的,即使这两个文件没有找到, ContextConfig 实例仍然会继续执行;
6)ContextConfig实例:会为每个 servlet元素创建一个 StandardWrapper类;(干货——ContextConfig的作用)
7)在BootStrap程序中,需要实例化一个 ContextConfig类,并调用 addLifecycleListener方法;
LifecycleListener listener = new ContextConfig();
((Lifecycle) context).addLifecycleListener(listener);
7.1)在启动 StandardContext实例时,会触发以下事件(events):
event1)BEFORE_START_EVENT;event2)START_EVENT;event3)AFTER_START_EVENT;
7.2)当程序停止时,会触发以下事件:
event1)BEFORE_STOP_EVENT;event2)STOP_EVENT;event3)AFTER_STOP_EVENT;
8)ContextConfig 实例会对两种事情做出相应:分别是START_EVENT and STOP_EVENT。每次 StandardContext实例触发事件时,会调用 ContextConfig.lifecycleEvent()方法;
public void lifecycleEvent(LifecycleEvent event) { //org.apache.catalina.startup.ContextConfig.lifecycleEvent().
// Identify the context we are associated with
try {
context = (Context) event.getLifecycle();
if (context instanceof StandardContext) {
int contextDebug = ((StandardContext) context).getDebug();
if (contextDebug > this.debug)
this.debug = contextDebug;
}
} catch (ClassCastException e) {
log(sm.getString("contextConfig.cce", event.getLifecycle()), e);
return;
}
// Process the event that has occurred
if (event.getType().equals(Lifecycle.START_EVENT))
start(); //highlight line.
else if (event.getType().equals(Lifecycle.STOP_EVENT))
stop(); //highlight line.
}
(干货——start方法会调用 defaultConfig() and applicationConfig()方法,两者分别用于读取和解析默认的web.xml 和 应用程序自定义的 web.xml)
private synchronized void start() { //org.apache.catalina.startup.ContextConfig.start().
if (debug > 0)
log(sm.getString("contextConfig.start"));
context.setConfigured(false);
ok = true;
// Set properties based on DefaultContext
Container container = context.getParent();
if( !context.getOverride() ) {
if( container instanceof Host ) {
((Host)container).importDefaultContext(context);
container = container.getParent();
}
if( container instanceof Engine ) {
((Engine)container).importDefaultContext(context);
}
}
// Process the default and application web.xml files
defaultConfig(); // highlight line.
applicationConfig(); // highlight line.
if (ok) {
validateSecurityRoles();
}
// Scan tag library descriptor files for additional listener classes
if (ok) {
try {
tldScan();
} catch (Exception e) {
log(e.getMessage(), e);
ok = false;
}
}
// Configure a certificates exposer valve, if required
if (ok)
certificatesConfig();
// Configure an authenticator if we need one
if (ok)
authenticatorConfig();
// Dump the contents of this pipeline if requested
if ((debug >= 1) && (context instanceof ContainerBase)) {
log("Pipline Configuration:");
Pipeline pipeline = ((ContainerBase) context).getPipeline();
Valve valves[] = null;
if (pipeline != null)
valves = pipeline.getValves();
if (valves != null) {
for (int i = 0; i < valves.length; i++) {
log(" " + valves[i].getInfo());
}
}
log("======================");
}
// Make our application available if no problems were encountered
if (ok)
context.setConfigured(true);
else {
log(sm.getString("contextConfig.unavailable"));
context.setConfigured(false);
}
}
【2.1】 defaultConfig()方法
1)intro: 该方法负责读取并解析位于 CATALINA_HOME/conf目录下的默认web.xml 文件;
private void defaultConfig() { //org.apache.catalina.startup.ContextConfig.defaultConfig().
// Open the default web.xml file, if it exists
File file = new File(Constants.DefaultWebXml);
// public static final String DefaultWebXml = "conf/web.xml";
if (!file.isAbsolute())
file = new File(System.getProperty("catalina.base"),
Constants.DefaultWebXml);
FileInputStream stream = null;
try {
stream = new FileInputStream(file.getCanonicalPath());
stream.close();
stream = null;
} catch (FileNotFoundException e) {
log(sm.getString("contextConfig.defaultMissing"));
return;
} catch (IOException e) {
log(sm.getString("contextConfig.defaultMissing"), e);
return;
}
// Process the default web.xml file (锁定webDigester变量,并解析默认的 web.xml 文件)
synchronized (webDigester) {
try {
InputSource is =
new InputSource("file://" + file.getAbsolutePath());
stream = new FileInputStream(file);
is.setByteStream(stream);
webDigester.setDebug(getDebug());
if (context instanceof StandardContext)
((StandardContext) context).setReplaceWelcomeFiles(true);
webDigester.clear();
webDigester.push(context);
webDigester.parse(is);
webDigester.push(null); // 解析结束.
} catch (SAXParseException e) {
log(sm.getString("contextConfig.defaultParse"), e);
log(sm.getString("contextConfig.defaultPosition",
"" + e.getLineNumber(),
"" + e.getColumnNumber()));
ok = false;
} catch (Exception e) {
log(sm.getString("contextConfig.defaultParse"), e);
ok = false;
} finally {
try {
if (stream != null) {
stream.close();
}
} catch (IOException e) {
log(sm.getString("contextConfig.defaultClose"), e);
}
}
}
}
【2.2】applicationConfig()方法
1)intro:该方法处理的是 应用程序自定义的web.xml,位于 WEB-INF 目录中;
private void applicationConfig() { //org.apache.catalina.startup.ContextConfig.applicationConfig().
// Open the application web.xml file, if it exists
InputStream stream = null;
ServletContext servletContext = context.getServletContext();
if (servletContext != null)
stream = servletContext.getResourceAsStream
(Constants.ApplicationWebXml); // public static final String ApplicationWebXml = "/WEB-INF/web.xml";
if (stream == null) {
log(sm.getString("contextConfig.applicationMissing"));
return;
}
// Process the application web.xml file
synchronized (webDigester) {
try {
URL url =
servletContext.getResource(Constants.ApplicationWebXml);
InputSource is = new InputSource(url.toExternalForm());
is.setByteStream(stream);
webDigester.setDebug(getDebug());
if (context instanceof StandardContext) {
((StandardContext) context).setReplaceWelcomeFiles(true);
}
webDigester.clear();
webDigester.push(context);
webDigester.parse(is);
webDigester.push(null);
} catch (SAXParseException e) {
log(sm.getString("contextConfig.applicationParse"), e);
log(sm.getString("contextConfig.applicationPosition",
"" + e.getLineNumber(),
"" + e.getColumnNumber()));
ok = false;
} catch (Exception e) {
log(sm.getString("contextConfig.applicationParse"), e);
ok = false;
} finally {
try {
if (stream != null) {
stream.close();
}
} catch (IOException e) {
log(sm.getString("contextConfig.applicationClose"), e);
}
}
}
}
【2.3】创建 Web Digester
1)在ContextConfig 类中,使用变量 webDigester来引用一个 Digester类型的对象;
private static Digester webDigester = createWebDigester();
private static Digester createWebDigester() { //org.apache.catalina.startup.ContextConfig.createWebDigester().
URL url = null;
Digester webDigester = new Digester();
webDigester.setValidating(true);
url = ContextConfig.class.getResource(Constants.WebDtdResourcePath_22);
webDigester.register(Constants.WebDtdPublicId_22,
url.toString());
url = ContextConfig.class.getResource(Constants.WebDtdResourcePath_23);
webDigester.register(Constants.WebDtdPublicId_23,
url.toString());
webDigester.addRuleSet(new WebRuleSet()); // highlight line.
return (webDigester);
}
2)这个Digester对象用来解析默认的 web.xml 文件和应用程序自定义的 web.xml 文件。在调用了 createWebDigester() 方法时会添加用来处理 web.xml 文件的规则;(干货——在调用了 createWebDigester() 方法时会添加用来处理 web.xml 文件的规则,什么是规则,你懂的,前面已经详细介绍了Rule)
Attention)
A1)createWebDigester()方法调用了变量webDigester的 addRuleSet()方法,传入了一个 org.apache.catalina.startup.WebRuleSet 类型的对象作为参数;A2)WebRuleSet 类是 org.apache.commons.digester.RuleSetBase的子类;public class WebRuleSet extends RuleSetBase { //org.apache.catalina.startup.WebRuleSet
A3)org.apache.catalina.startup.WebRuleSet的定义代码见 文末,特别要注意其addRuleInstances()方法,其添加了很多规则集合;(干货——addRuleInstances()方法添加了很多规则集合)
【3】应用程序(本测试用例重在说明如何使用ContextConfig实例作为一个监听器来配置StandardContext对象)
Attention)通过以下实例,你会发现,这与之前的Bootstrap 测试用例大有不同,以前是显式地创建StandardWrapper(创建具体的servlet实例),而下面的测试用例采用Digester从 xml 中读取 servlet的配置信息创建servlet实例,这就是为什么本文之前讲那么多 Digester库的原因;
1)测试用例
public final class Bootstrap {
// invoke: http://localhost:8080/app1/Modern or
// http://localhost:8080/app2/Primitive
// note that we don't instantiate a Wrapper here,
// ContextConfig reads the WEB-INF/classes dir and loads all servlets.
public static void main(String[] args) {
System.setProperty("catalina.base", System.getProperty("user.dir"));
Connector connector = new HttpConnector();
Context context = new StandardContext();
// StandardContext's start method adds a default mapper
context.setPath("/app1");
context.setDocBase("app1");
LifecycleListener listener = new ContextConfig();
((Lifecycle) context).addLifecycleListener(listener);
Host host = new StandardHost();
host.addChild(context);
host.setName("localhost");
host.setAppBase("webapps");
Loader loader = new WebappLoader();
context.setLoader(loader);
connector.setContainer(host);
try {
connector.initialize();
((Lifecycle) connector).start();
((Lifecycle) host).start();
Container[] c = context.findChildren();
int length = c.length;
for (int i=0; i<length; i++) {
Container child = c[i];
System.out.println(child.getName());
}
// make the application wait until we press a key.
System.in.read();
((Lifecycle) host).stop();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Supplement)本文习惯性地总结了上述测试用例的调用过程,如下:
S0)上述调用过程涉及到的变量ApplicationWebXml 和 DefaultWebXml
,其值为:
public static final String ApplicationWebXml = "/WEB-INF/web.xml";
public static final String DefaultWebXml = "conf/web.xml"; // both of them are defined in org.apache.catalina.startup.Constant;
S1)自定义的web.xml如下所示:该文件的文件路径为 System.getProperty("user.dir")\webapps\app1\WEB-INF;
S
2)(
tomcat使用了开源库Digester来将XML 文档中的元素转换成 java 对象,触发相应规则如调用设置器来配置StandardContext的子容器(StandardWrapper包装了servlet)
)
(干货——具体的规则包含在 WebRuleSet类中,见文末)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>Modern</servlet-name>
<servlet-class>ModernServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>Primitive</servlet-name>
<servlet-class>PrimitiveServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Modern</servlet-name>
<url-pattern>/Modern</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Primitive</servlet-name>
<url-pattern>/Primitive</url-pattern>
</servlet-mapping>
</web-app>
2)console info
E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;lib/catalina-5.5.4.jar;lib/naming-common.
jar;lib/commons-collections.jar;lib/naming-resources.jar;lib/commons-digester.jar;lib/catalina.jar;lib/commons-logging.jar;lib/commons-beanutils.jar;E
:\bench-cluster\cloud-data-preprocess\HowTomcatWoks\webroot com.tomcat.chapter15.startup.Bootstrap
HttpConnector Opening server socket on all host IP addresses
HttpConnector[8080] Starting background thread
WebappLoader[/app1]: Deploying class repositories to work directory E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src\work\_\localhost\app1
WebappLoader[/app1]: Deploy class files /WEB-INF/classes to E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src\webapps\app1\WEB-INF\classes
ContextConfig[/app1]: Missing default web.xml, using application web.xml only
StandardManager[/app1]: Seeding random number generator class java.security.SecureRandom
StandardManager[/app1]: Seeding of random number generator has been completed
Primitive
Modern
StandardHost[localhost]: MAPPING configuration error for request URI /Modern/app1/Modern
StandardHost[localhost]: MAPPING configuration error for request URI /favicon.ico
ModernServlet -- init
StandardHost[localhost]: MAPPING configuration error for request URI /favicon.ico
3)访问结果
public class WebRuleSet extends RuleSetBase { //org.apache.catalina.startup.WebRuleSet
protected String prefix = null;
public WebRuleSet() {
this("");
}
public WebRuleSet(String prefix) {
super();
this.namespaceURI = null;
this.prefix = prefix;
}
public void addRuleInstances(Digester digester) { // highlight.
digester.addRule(prefix + "web-app",
new SetPublicIdRule(digester, "setPublicId"));
digester.addCallMethod(prefix + "web-app/context-param",
"addParameter", 2);
digester.addCallParam(prefix + "web-app/context-param/param-name", 0);
digester.addCallParam(prefix + "web-app/context-param/param-value", 1);
digester.addCallMethod(prefix + "web-app/display-name",
"setDisplayName", 0);
digester.addRule(prefix + "web-app/distributable",
new SetDistributableRule(digester));
digester.addObjectCreate(prefix + "web-app/ejb-local-ref",
"org.apache.catalina.deploy.ContextLocalEjb");
digester.addSetNext(prefix + "web-app/ejb-local-ref",
"addLocalEjb",
"org.apache.catalina.deploy.ContextLocalEjb");
digester.addCallMethod(prefix + "web-app/ejb-local-ref/description",
"setDescription", 0);
digester.addCallMethod(prefix + "web-app/ejb-local-ref/ejb-link",
"setLink", 0);
digester.addCallMethod(prefix + "web-app/ejb-local-ref/ejb-ref-name",
"setName", 0);
digester.addCallMethod(prefix + "web-app/ejb-local-ref/ejb-ref-type",
"setType", 0);
digester.addCallMethod(prefix + "web-app/ejb-local-ref/local",
"setLocal", 0);
digester.addCallMethod(prefix + "web-app/ejb-local-ref/local-home",
"setHome", 0);
digester.addObjectCreate(prefix + "web-app/ejb-ref",
"org.apache.catalina.deploy.ContextEjb");
digester.addSetNext(prefix + "web-app/ejb-ref",
"addEjb",
"org.apache.catalina.deploy.ContextEjb");
digester.addCallMethod(prefix + "web-app/ejb-ref/description",
"setDescription", 0);
digester.addCallMethod(prefix + "web-app/ejb-ref/ejb-link",
"setLink", 0);
digester.addCallMethod(prefix + "web-app/ejb-ref/ejb-ref-name",
"setName", 0);
digester.addCallMethod(prefix + "web-app/ejb-ref/ejb-ref-type",
"setType", 0);
digester.addCallMethod(prefix + "web-app/ejb-ref/home",
"setHome", 0);
digester.addCallMethod(prefix + "web-app/ejb-ref/remote",
"setRemote", 0);
digester.addObjectCreate(prefix + "web-app/env-entry",
"org.apache.catalina.deploy.ContextEnvironment");
digester.addSetNext(prefix + "web-app/env-entry",
"addEnvironment",
"org.apache.catalina.deploy.ContextEnvironment");
digester.addCallMethod(prefix + "web-app/env-entry/description",
"setDescription", 0);
digester.addCallMethod(prefix + "web-app/env-entry/env-entry-name",
"setName", 0);
digester.addCallMethod(prefix + "web-app/env-entry/env-entry-type",
"setType", 0);
digester.addCallMethod(prefix + "web-app/env-entry/env-entry-value",
"setValue", 0);
digester.addObjectCreate(prefix + "web-app/error-page",
"org.apache.catalina.deploy.ErrorPage");
digester.addSetNext(prefix + "web-app/error-page",
"addErrorPage",
"org.apache.catalina.deploy.ErrorPage");
digester.addCallMethod(prefix + "web-app/error-page/error-code",
"setErrorCode", 0);
digester.addCallMethod(prefix + "web-app/error-page/exception-type",
"setExceptionType", 0);
digester.addCallMethod(prefix + "web-app/error-page/location",
"setLocation", 0);
digester.addObjectCreate(prefix + "web-app/filter",
"org.apache.catalina.deploy.FilterDef");
digester.addSetNext(prefix + "web-app/filter",
"addFilterDef",
"org.apache.catalina.deploy.FilterDef");
digester.addCallMethod(prefix + "web-app/filter/description",
"setDescription", 0);
digester.addCallMethod(prefix + "web-app/filter/display-name",
"setDisplayName", 0);
digester.addCallMethod(prefix + "web-app/filter/filter-class",
"setFilterClass", 0);
digester.addCallMethod(prefix + "web-app/filter/filter-name",
"setFilterName", 0);
digester.addCallMethod(prefix + "web-app/filter/large-icon",
"setLargeIcon", 0);
digester.addCallMethod(prefix + "web-app/filter/small-icon",
"setSmallIcon", 0);
digester.addCallMethod(prefix + "web-app/filter/init-param",
"addInitParameter", 2);
digester.addCallParam(prefix + "web-app/filter/init-param/param-name",
0);
digester.addCallParam(prefix + "web-app/filter/init-param/param-value",
1);
digester.addObjectCreate(prefix + "web-app/filter-mapping",
"org.apache.catalina.deploy.FilterMap");
digester.addSetNext(prefix + "web-app/filter-mapping",
"addFilterMap",
"org.apache.catalina.deploy.FilterMap");
digester.addCallMethod(prefix + "web-app/filter-mapping/filter-name",
"setFilterName", 0);
digester.addCallMethod(prefix + "web-app/filter-mapping/servlet-name",
"setServletName", 0);
digester.addCallMethod(prefix + "web-app/filter-mapping/url-pattern",
"setURLPattern", 0);
digester.addCallMethod(prefix + "web-app/listener/listener-class",
"addApplicationListener", 0);
digester.addObjectCreate(prefix + "web-app/login-config",
"org.apache.catalina.deploy.LoginConfig");
digester.addSetNext(prefix + "web-app/login-config",
"setLoginConfig",
"org.apache.catalina.deploy.LoginConfig");
digester.addCallMethod(prefix + "web-app/login-config/auth-method",
"setAuthMethod", 0);
digester.addCallMethod(prefix + "web-app/login-config/realm-name",
"setRealmName", 0);
digester.addCallMethod(prefix + "web-app/login-config/form-login-config/form-error-page",
"setErrorPage", 0);
digester.addCallMethod(prefix + "web-app/login-config/form-login-config/form-login-page",
"setLoginPage", 0);
digester.addCallMethod(prefix + "web-app/mime-mapping",
"addMimeMapping", 2);
digester.addCallParam(prefix + "web-app/mime-mapping/extension", 0);
digester.addCallParam(prefix + "web-app/mime-mapping/mime-type", 1);
digester.addCallMethod(prefix + "web-app/resource-env-ref",
"addResourceEnvRef", 2);
digester.addCallParam(prefix + "web-app/resource-env-ref/resource-env-ref-name", 0);
digester.addCallParam(prefix + "web-app/resource-env-ref/resource-env-ref-type", 1);
digester.addObjectCreate(prefix + "web-app/resource-ref",
"org.apache.catalina.deploy.ContextResource");
digester.addSetNext(prefix + "web-app/resource-ref",
"addResource",
"org.apache.catalina.deploy.ContextResource");
digester.addCallMethod(prefix + "web-app/resource-ref/description",
"setDescription", 0);
digester.addCallMethod(prefix + "web-app/resource-ref/res-auth",
"setAuth", 0);
digester.addCallMethod(prefix + "web-app/resource-ref/res-ref-name",
"setName", 0);
digester.addCallMethod(prefix + "web-app/resource-ref/res-sharing-scope",
"setScope", 0);
digester.addCallMethod(prefix + "web-app/resource-ref/res-type",
"setType", 0);
digester.addObjectCreate(prefix + "web-app/security-constraint",
"org.apache.catalina.deploy.SecurityConstraint");
digester.addSetNext(prefix + "web-app/security-constraint",
"addConstraint",
"org.apache.catalina.deploy.SecurityConstraint");
digester.addRule(prefix + "web-app/security-constraint/auth-constraint",
new SetAuthConstraintRule(digester));
digester.addCallMethod(prefix + "web-app/security-constraint/auth-constraint/role-name",
"addAuthRole", 0);
digester.addCallMethod(prefix + "web-app/security-constraint/display-name",
"setDisplayName", 0);
digester.addCallMethod(prefix + "web-app/security-constraint/user-data-constraint/transport-guarantee",
"setUserConstraint", 0);
digester.addObjectCreate(prefix + "web-app/security-constraint/web-resource-collection",
"org.apache.catalina.deploy.SecurityCollection");
digester.addSetNext(prefix + "web-app/security-constraint/web-resource-collection",
"addCollection",
"org.apache.catalina.deploy.SecurityCollection");
digester.addCallMethod(prefix + "web-app/security-constraint/web-resource-collection/http-method",
"addMethod", 0);
digester.addCallMethod(prefix + "web-app/security-constraint/web-resource-collection/url-pattern",
"addPattern", 0);
digester.addCallMethod(prefix + "web-app/security-constraint/web-resource-collection/web-resource-name",
"setName", 0);
digester.addCallMethod(prefix + "web-app/security-role/role-name",
"addSecurityRole", 0);
digester.addRule(prefix + "web-app/servlet",
new WrapperCreateRule(digester));
digester.addSetNext(prefix + "web-app/servlet",
"addChild",
"org.apache.catalina.Container");
digester.addCallMethod(prefix + "web-app/servlet/init-param",
"addInitParameter", 2);
digester.addCallParam(prefix + "web-app/servlet/init-param/param-name",
0);
digester.addCallParam(prefix + "web-app/servlet/init-param/param-value",
1);
digester.addCallMethod(prefix + "web-app/servlet/jsp-file",
"setJspFile", 0);
digester.addCallMethod(prefix + "web-app/servlet/load-on-startup",
"setLoadOnStartupString", 0);
digester.addCallMethod(prefix + "web-app/servlet/run-as/role-name",
"setRunAs", 0);
digester.addCallMethod(prefix + "web-app/servlet/security-role-ref",
"addSecurityReference", 2);
digester.addCallParam(prefix + "web-app/servlet/security-role-ref/role-link", 1);
digester.addCallParam(prefix + "web-app/servlet/security-role-ref/role-name", 0);
digester.addCallMethod(prefix + "web-app/servlet/servlet-class",
"setServletClass", 0);
digester.addCallMethod(prefix + "web-app/servlet/servlet-name",
"setName", 0);
digester.addCallMethod(prefix + "web-app/servlet-mapping",
"addServletMapping", 2);
digester.addCallParam(prefix + "web-app/servlet-mapping/servlet-name", 1);
digester.addCallParam(prefix + "web-app/servlet-mapping/url-pattern", 0);
digester.addCallMethod(prefix + "web-app/session-config/session-timeout",
"setSessionTimeout", 1,
new Class[] { Integer.TYPE });
digester.addCallParam(prefix + "web-app/session-config/session-timeout", 0);
digester.addCallMethod(prefix + "web-app/taglib",
"addTaglib", 2);
digester.addCallParam(prefix + "web-app/taglib/taglib-location", 1);
digester.addCallParam(prefix + "web-app/taglib/taglib-uri", 0);
digester.addCallMethod(prefix + "web-app/welcome-file-list/welcome-file",
"addWelcomeFile", 0);
}
}
// ----------------------------------------------------------- Private Classes
/**
* A Rule that calls the <code>setAuthConstraint(true)</code> method of
* the top item on the stack, which must be of type
* <code>org.apache.catalina.deploy.SecurityConstraint</code>.
*/
final class SetAuthConstraintRule extends Rule {
public SetAuthConstraintRule(Digester digester) {
super(digester);
}
public void begin(Attributes attributes) throws Exception {
SecurityConstraint securityConstraint =
(SecurityConstraint) digester.peek();
securityConstraint.setAuthConstraint(true);
if (digester.getDebug() > 0)
digester.log("Calling SecurityConstraint.setAuthConstraint(true)");
}
}
/**
* Class that calls <code>setDistributable(true)</code> for the top object
* on the stack, which must be a <code>org.apache.catalina.Context</code>.
*/
final class SetDistributableRule extends Rule {
public SetDistributableRule(Digester digester) {
super(digester);
}
public void begin(Attributes attributes) throws Exception {
Context context = (Context) digester.peek();
context.setDistributable(true);
if (digester.getDebug() > 0)
digester.log(context.getClass().getName() +
".setDistributable( true)");
}
}
/**
* Class that calls a property setter for the top object on the stack,
* passing the public ID of the entity we are currently processing.
*/
final class SetPublicIdRule extends Rule {
public SetPublicIdRule(Digester digester, String method) {
super(digester);
this.method = method;
}
private String method = null;
public void begin(Attributes attributes) throws Exception {
Context context = (Context) digester.peek(digester.getCount() - 1);
Object top = digester.peek();
Class paramClasses[] = new Class[1];
paramClasses[0] = "String".getClass();
String paramValues[] = new String[1];
paramValues[0] = digester.getPublicId();
Method m = null;
try {
m = top.getClass().getMethod(method, paramClasses);
} catch (NoSuchMethodException e) {
digester.log("Can't find method " + method + " in " + top +
" CLASS " + top.getClass());
return;
}
m.invoke(top, paramValues);
if (digester.getDebug() >= 1)
digester.log("" + top.getClass().getName() + "." + method +
"(" + paramValues[0] + ")");
}
}
/**
* A Rule that calls the factory method on the specified Context to
* create the object that is to be added to the stack.
*/
final class WrapperCreateRule extends Rule {
public WrapperCreateRule(Digester digester) {
super(digester);
}
public void begin(Attributes attributes) throws Exception {
Context context =
(Context) digester.peek(digester.getCount() - 1);
Wrapper wrapper = context.createWrapper();
digester.push(wrapper);
if (digester.getDebug() > 0)
digester.log("new " + wrapper.getClass().getName());
}
public void end() throws Exception {
Wrapper wrapper = (Wrapper) digester.pop();
if (digester.getDebug() > 0)
digester.log("pop " + wrapper.getClass().getName());
}
}
更多推荐
已为社区贡献7条内容
所有评论(0)