【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):
A1)需要传入一个模式和一个Class对象或类名来调用该方法;
看个荔枝) 如我们想让Digester对象在遇到模式employee 时,创建一个 mydiy.Employee 对象,则使用下面的代码来调用 addObjectCreate()方法:
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()方法:
 
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)); 
}
对以上代码的分析(Analysis):
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.
    }
8.1)start()方法
(干货——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());
    }
}
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐