zoukankan      html  css  js  c++  java
  • How Tomcat Works(十八)

    在前面的文章中,如果我们要启动tomcat容器,我们需要使用Bootstrap类来实例化连接器、servlet容器、Wrapper实例和其他组件,然后调用各个对象的set方法将它们关联起来;这种配置应用程序的方法有一个明显的缺陷,即所有的配置都必须硬编码。调整组件配置和属性值都必须要重新编译Bootstrap类。幸运的是,Tomcat的设计者使用了一种更加优雅的配置方式,即使用一个名为server.xml的XML文件来对应用程序进行配置。server.xml文件中的每个元素都会转换为一个java对象,元素的属性会用于设置java对象的属性,这样,就可以通过简单的编辑server.xml文件来修改tomcat的配置。

    Tomcat使用了开源库Digester来将xml文件中的元素转换成java对象。

    由于一个Context实例表示一个Web应用程序,因此配置Web应用程序是通过对已经实例化的Context实例进行配置完成的。用来配置Web应用程序的XML文件的名称是web.xml,该文件位于Web应用程序的WEB-INF目录下。

    下面来介绍Digester库,Digester库是Apache软件基金会的Jatarta项目下的子Commons项目下的一个开源项目,它的主页地址是http://commons.apache.org/proper/commons-digester/

    org.apache.commons.digester3.Digester类是Digester库中的主类,该类可用于解析XML文件,对于XML文件中的每个元素,Digester对象都会检查它是否要做事先预定义的事件,在调用Digester对象的parse()方法之前,程序员要先定义好Digester对象执行哪些动作。

    因此,程序员要先定义好模式,然后将每个模式与一条或多条规则相关联。

    模式通常是xml文件里面元素的路径,类似于xpath的语法路径

    规则指明了当Digester对象遇到了某个特殊的模式时要执行的一个或多个动作,规则是org.apache.commons.digester3.Rule类的实例,Digester类开源包含0个或多个Rule对象,在Digester实例中,这些规则和其相关联的模式都存储在由org.apache.commons.digester3.Rules接口表示的一类存储器中,每当把一条规则添加到Digester实例中时,Rule对象都会被添加到Rules对象中。

    另外,Rule类有begin()方法和end()方法,在解析xml文件时,当Digester实例遇到匹配某个模式的元素的开始标签时,它会调用相应的Rule对象的begin()方法,而当Digester实例遇到相应元素的结束标签时,它会调用Rule对象的end()方法。

    在使用Digester库时,我们需要先导入相关依赖jar

    <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-digester3</artifactId>
                <version>3.2</version>
                <classifier>with-deps</classifier>
            </dependency>

    第一个示例应用程序演示如何使用Digester库动态的创建对象,并设置相应的属性值。

    employee1.xml文件内容如下

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <employee firstName="Brian" lastName="May">
    </employee>

    我们需要根据上面的xml文件创建Employee对象,并设置相应属性,Employee类代码如下:

    public class Employee {
      private String firstName;
      private String lastName;
      private ArrayList offices = new ArrayList();
        
      public Employee() {
        System.out.println("Creating Employee");
      }
      public String getFirstName() {
        return firstName;
      }
      public void setFirstName(String firstName) {
        System.out.println("Setting firstName : " + firstName);
        this.firstName = firstName;
      }
      public String getLastName() {
        return lastName;
      }
      public void setLastName(String lastName) {
        System.out.println("Setting lastName : " + lastName);
        this.lastName = lastName;
      }
      public void addOffice(Office office) {
        System.out.println("Adding Office to this employee");
        offices.add(office);
      }
      public ArrayList getOffices() {
        return offices;
      }
      public void printName() {
        System.out.println("My name is " + firstName + " " + lastName);
      }
    }

    现在写一个测试类Test01,它使用Digester类,并为其添加创建Employee对象和设置其属性的规则。

    public class Test01 {
    
        public static void main(String[] args) {
           
            InputStream inputStream = null;
            Digester digester = new Digester();
            // add rules
            digester.addObjectCreate("employee","ex15.pyrmont.digestertest.Employee");
            digester.addSetProperties("employee");
            digester.addCallMethod("employee", "printName");
    
            try {
                inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("employee1.xml");
                Employee employee = (Employee) digester.parse(inputStream);
                System.out.println("First name : " + employee.getFirstName());
                System.out.println("Last name : " + employee.getLastName());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
    
    }

    第二个示例演示如何利用Digester库创建两个对象,并建立他们之间的关系

    employee2.xml 文件内容如下

    <?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>

    然后我们还需要创建Office类和Address类

    Office类代码如下:

    public class Office {
      private Address address;
      private String description;
      public Office() {
        System.out.println("..Creating Office");
      }
      public String getDescription() {
        return description;
      }
      public void setDescription(String description) {
        System.out.println("..Setting office description : " + description);
        this.description = description;
      }
      public Address getAddress() {
        return address;
      }
      public void setAddress(Address address) {
        System.out.println("..Setting office address : " + address);
        this.address = address;
      }
    }

    Address类代码如下:

    public class Address {
      private String streetName;
      private String streetNumber;
      public Address() {
        System.out.println("....Creating Address");
      }
      public String getStreetName() {
        return streetName;
      }
      public void setStreetName(String streetName) {
        System.out.println("....Setting streetName : " + streetName);
        this.streetName = streetName;
      }
      public String getStreetNumber() {
        return streetNumber;
      }
      public void setStreetNumber(String streetNumber) {
        System.out.println("....Setting streetNumber : " + streetNumber);
        this.streetNumber = streetNumber;
      }
      public String toString() {
        return "...." + streetNumber + " " + streetName; 
      }
    }

    下面是Test02类的定义,该类使用一个Digester对象,并为其添加规则

    public class Test02 {
    
        public static void main(String[] args) {
            
            InputStream inputStream = null;
            Digester digester = new Digester();
            // add rules
            digester.addObjectCreate("employee",
                    "ex15.pyrmont.digestertest.Employee");
            digester.addSetProperties("employee");
            digester.addObjectCreate("employee/office",
                    "ex15.pyrmont.digestertest.Office");
            digester.addSetProperties("employee/office");
            digester.addSetNext("employee/office", "addOffice");
            digester.addObjectCreate("employee/office/address",
                    "ex15.pyrmont.digestertest.Address");
            digester.addSetProperties("employee/office/address");
            digester.addSetNext("employee/office/address", "setAddress");
            try {
                inputStream = Thread.currentThread().getContextClassLoader()
                        .getResourceAsStream("employee2.xml");
                Employee employee = (Employee) digester.parse(inputStream);
                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();
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
    
        }
    }

    Rule类包含了一些方法,其中最重要的两个方法是begin()方法和end()方法,当Digester实例遇到某个XML元素的开始标签时,它会调用它所包含的匹配Rule对象的begin()方法,方法签名如下:

    public void begin( String namespace, String name, Attributes attributes ) throws Exception

    当Digester实例遇到某个XML元素的结束标签时,它会调用它所包含的匹配Rule对象的end()方法,方法签名如下:

    public void end( String namespace, String name ) throws Exception

    Digester对象是如何完成这些工作的呢?当调用Digester对象的addObjectCreate()方法、addCallMethod()方法、addSetNext()方法或其他方法时,都会间接地调用Digester类的addRule()方法;该方法将一个Rule对象和它所匹配的模式添加到Digester对象的Rules集合中。

    addRule()方法实现如下:

     public void addRule( String pattern, Rule rule )
        {
            rule.setDigester( this );
            getRules().add( pattern, rule );
        }

    查看Digester类的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 ) );
        }

    这四个重载方法都调用了addRule()方法,ObjectCreateRule类是Rule类的子类,该类的实例可作为addRule()方法的第二个参数使用。

    下面是ObjectCreateRule类的begin()方法和end()方法的实现

     @Override
        public void begin( String namespace, String name, Attributes attributes )
            throws Exception
        {
            Class<?> clazz = this.clazz;
    
            if ( clazz == null )
            {
                // 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 ( getDigester().getLogger().isDebugEnabled() )
                {
                    getDigester().getLogger().debug( format( "[ObjectCreateRule]{%s} New '%s'",
                                                             getDigester().getMatch(),
                                                             realClassName ) );
                }
    
                // Instantiate the new object and push it on the context stack
                clazz = getDigester().getClassLoader().loadClass( realClassName );
            }
            Object instance;
            if ( constructorArgumentTypes == null || constructorArgumentTypes.length == 0 )
            {
                if ( getDigester().getLogger().isDebugEnabled() )
                {
                    getDigester()
                        .getLogger()
                        .debug( format( "[ObjectCreateRule]{%s} New '%s' using default empty constructor",
                                        getDigester().getMatch(),
                                        clazz.getName() ) );
                }
    
                instance = clazz.newInstance();
            }
            else
            {
                if ( proxyManager == null )
                {
                    Constructor<?> constructor = getAccessibleConstructor( clazz, constructorArgumentTypes );
    
                    if ( constructor == null )
                    {
                        throw new SAXException(
                                       format( "[ObjectCreateRule]{%s} Class '%s' does not have a construcor with types %s",
                                               getDigester().getMatch(),
                                               clazz.getName(),
                                               Arrays.toString( constructorArgumentTypes ) ) );
                    }
                    proxyManager = new ProxyManager( clazz, constructor, defaultConstructorArguments, getDigester() );
                }
                instance = proxyManager.createProxy();
            }
            getDigester().push( instance );
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public void end( String namespace, String name )
            throws Exception
        {
            Object top = getDigester().pop();
    
            if ( proxyManager != null )
            {
                proxyManager.finalize( top );
            }
    
            if ( getDigester().getLogger().isDebugEnabled() )
            {
                getDigester().getLogger().debug( format( "[ObjectCreateRule]{%s} Pop '%s'",
                                                         getDigester().getMatch(),
                                                         top.getClass().getName() ) );
            }
        }

    begin()方法用于创建一个对象实例,并将其压入到Digester对象的内部栈中;end()方法会将内部栈的栈顶元素弹出栈

    要向Digester实例中添加Rule对象,还可以调用其addRuleSet()方法,方法实现如下:

    public void addRuleSet( RuleSet ruleSet )
        {
            String oldNamespaceURI = getRuleNamespaceURI();
            String newNamespaceURI = ruleSet.getNamespaceURI();
            if ( log.isDebugEnabled() )
            {
                if ( newNamespaceURI == null )
                {
                    log.debug( "addRuleSet() with no namespace URI" );
                }
                else
                {
                    log.debug( "addRuleSet() with namespace URI " + newNamespaceURI );
                }
            }
            setRuleNamespaceURI( newNamespaceURI );
            ruleSet.addRuleInstances( this );
            setRuleNamespaceURI( oldNamespaceURI );
        }

    org.apache.commons.digester3.RuleSet接口表示Rule对象的集合,该接口定义了两个方法,分别为addRuleInstance()和getNamespaceURI(),addRuleInstance()方法签名如下:

    public void addRuleInstance(Digester digester)

    addRuleInstance()方法用于添加定义在当前RuleSet对象中的Rule对象集合到作为该方法参数传输的Digester实例中

    getNamespaceUR()方法返回将要应用在所有Rule对象(在当前Ruleset中创建的)的命名空间的URI,该方法签名如下

    public java.lang.String getNamespaceURI()

    因此,在创建了Digester对象之后,可以创建一个RuleSet对象,并将其传输给Digester对象的addRuleSet()方法

    为了便于使用,实现RuleSet接口有一个基类RuleSetBase,RuleSetBase类为抽象类,提供了getNamespaceURI()方法的实现,我们只需要提供addRuleInstances()方法的实现就可以了

    下面是我们创建的EmployeeRuleSet类的源码(继承自RuleSetBase类)

    public class EmployeeRuleSet extends RuleSetBase  {
      public void addRuleInstances(Digester digester) {
        // add rules
        digester.addObjectCreate("employee", "ex15.pyrmont.digestertest.Employee");
        digester.addSetProperties("employee");    
        digester.addObjectCreate("employee/office", "ex15.pyrmont.digestertest.Office");
        digester.addSetProperties("employee/office");
        digester.addSetNext("employee/office", "addOffice");
        digester.addObjectCreate("employee/office/address", 
          "ex15.pyrmont.digestertest.Address");
        digester.addSetProperties("employee/office/address");
        digester.addSetNext("employee/office/address", "setAddress"); 
      }
    }

    我们注意到,EmployeeRuleSet类中的addRuleInstances()方法的实现的功能类似Test02类,将相同的Rule对象添加到Digester对象中

    下面是Test03的代码,里面会创建EmployeeRuleSet类的实例,然后将其添加到之前创建的Digester对象中

    public class Test03 {
    
      public static void main(String[] args) {
       
        InputStream inputStream = null;
        Digester digester = new Digester();
        digester.addRuleSet(new EmployeeRuleSet());
        try {
          inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("employee2.xml");
          Employee employee = (Employee) digester.parse(inputStream);
          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();
        }
        finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
      }
    }

    与其他类型的容器不同,StandardContext实例必须有一个监听器,该监听器会负责配置StandardContext实例,设置成功后会将StandardContext实例的变量configued值设置为tue。

    StandardContext类的标准监听器是org.apache.catalina.startup.ContextConfig类的实例,它会执行很对StandardContext实例来说必不可少的任务,例如安装验证器阀到StandardContext实例的管道对象中,此外还会添加许可器阀(类型为org.apache.catalina.valves.CertificateValve)到管道对象中。

    但更重要的是,ContextConfig类的实例还会读取和解析默认的web.xml文件和应用程序自定义的web.xml文件,并将xml元素转换为java对象。

    默认的web.xml文件位于CATALINE_HOME目录下的conf目录中,其中定义并映射了很多默认的servlet,配置了很多MIME类型文件的映射,定义了默认的session超时时间,以及定义了欢迎文件的列表。

    应用程序的web.xml文件是应用程序自定义的配置文件,位于应用程序目录下的WEB-INF目录中。

    ContextConfig实例会为每一个servlet元素创建StandardWrapper实例,因此,正如你在本章应用程序中看到的,配置变简单了,你不在需要实例化Wrapper实例了

    因此,我们需要在Bootstrap类中实例化一个ContextConfig类,并调用org.apache.catalina.Lifecycle接口的addLifecycleListener()方法将其添加到StandardContext对象中

    LifecycleListener listener = new ContextConfig();
    ((Lifecycle) context).addLifecycleListener(listener);

    在启动和停止StandardContext实例时,会触发相应事件,ContextConfig类会对两种事件做出响应,分别为START_EVENT 和STOP_EVENT

    每当StandardContext实例触发事件时,会调用ContextConfig实例的lifecycleEvent()方法

    public void lifecycleEvent(LifecycleEvent event) {
    
            // 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();
            else if (event.getType().equals(Lifecycle.STOP_EVENT))
                stop();
    
        }

    在上面方法中,会继续调用start()方法和stop()方法

    private synchronized void 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();
            applicationConfig();
            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);
            }
    
        }

    start()方法会进一步调用defaultConfig()方法和applicationConfig()方法

    defaultConfig()方法负责读取并解析位于%CATALINA_HOME%/conf目录下的默认的web.xml文件

    private void defaultConfig() {
    
            // Open the default web.xml file, if it exists
            File file = new File(Constants.DefaultWebXml);
            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
            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);
                } 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);
                    }
                }
            }
    
        }

    applicationConfig()方法与defaultConfig()方法类似,只不过它处理的是应用程序自定义的部署描述符,该部署描述符位于应用目录下的WEB-INF目录中

    private void 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);
            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);
                } 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);
                    }
                }
            }
    
        }

    在ContextConfig类中,使用变量webDigester来引用一个Digester类型的对象

    private static Digester webDigester = createWebDigester();

    该Digester对象用于解析默认的web.xml文件和应用程序自定义的web.xml文件,在调用createWebDigester()方法时会添加用来处理web.xml文件的规则

    /**
         * Create (if necessary) and return a Digester configured to process the
         * web application deployment descriptor (web.xml).
         */
        private static Digester 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());
            return (webDigester);
    
        }

    我们注意到,上面方法中调用了变量webDigester的addRuleSet()方法,传入一个org.apache.catalina.startup.WebRuleSet类型的对象作为参数;WebRuleSet类是org.apache.commons.digester.RuleSetBase的子类。

    下面是WebRuleSet类的addRuleInstances()方法实现:

    public void addRuleInstances(Digester digester) {
    
            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");
    
            //代码太长,后面部分略
    
        }

    --------------------------------------------------------------------------- 

    本系列How Tomcat Works系本人原创 

    转载请注明出处 博客园 刺猬的温驯 

    本人邮箱: chenying998179#163.com (#改为@

    本文链接http://www.cnblogs.com/chenying99/p/3249161.html

  • 相关阅读:
    css字体属性相关。
    子级用css float浮动 而父级div没高度不能自适应高度
    转载:基于Redis实现分布式锁
    LeetCode(53):最大子序和
    LeetCode(52):N皇后 II
    LeetCode(51):N皇后
    LeetCode(50):Pow(x, n)
    LeetCode(49): 字母异位词分组
    LeetCode(48):旋转图像
    LeetCode(47):全排列 II
  • 原文地址:https://www.cnblogs.com/chenying99/p/3249161.html
Copyright © 2011-2022 走看看