Chapter 15: Digester

Overview

As you have seen in the previous chapters, we use a Bootstrap class to instantiate a connector, a context, wrappers, and other components. Once you have those objects, you then associate them with each other by calling the set methods of various objects. For example, to instantiate a connector and a context, use the following code:

在前面的章节中,你已经看到,我们使用 Bootstrap 类来实例化连接器、上下文、包装器和其他组件。

一旦你拥有了这些对象,你可以通过调用各种对象的 set 方法来将它们相互关联。

例如,要实例化一个连接器和一个上下文,请使用以下代码:

Connector connector = new HttpConnector();
Context context = new StandardContext();

To associate the connector with the context, you then write the following code:

要将连接器与上下文关联起来,需要编写以下代码:

connector.setContainer(context);

You can also configure the properties of each object by calling the corresponding set methods. For instance, you can set the path and docBase properties of a Context object by calling its setPath and setDocBase methods:

您还可以通过调用相应的 set 方法来配置每个对象的属性。

例如,可以通过调用 Context 对象的 setPathsetDocBase 方法来设置其路径和 docBase 属性:

context.setPath("/myApp") ;
context.setDocBase("myApp");

In addition, you can add various components to the Context object by instantiating the components and call the corresponding add method on the context. For instance, here is how you add a lifecycle listener and a loader to your context object:

此外,您还可以通过实例化组件并调用上下文中相应的添加方法,将各种组件添加到上下文对象中。

例如,以下是向上下文对象添加生命周期监听器和加载器的方法:

LifecycleListener listener = new SimpleContextConfig();
((Lifecycle) context).addLifecycleListener(listener);
Loader loader = new WebappLoader();
context.setLoader(loader);

Once all necessary associations and additions have been performed, to complete the application start-up you call the initialize and start methods of the connector and the start method of the context:

完成所有必要的关联和添加后,要完成应用程序的启动,需要调用连接器的初始化和启动方法以及上下文的启动方法:

connector.initialize();
((Lifecycle) connector).start ();
((Lifecycle) context).start();

This approach to application configuration has one apparent drawback: everything is hard-coded. Changing a component or even the value of a property requires the recompilation of the Bootstrap class. Fortunately, the Tomcat designer has chosen a more elegant way of configuration, i.e. an XML document named server.xml. Each element in the server.xml file is converted to a Java object and an element's attribute is used to set a property. This way, you can simply edit the server.xml file to change Tomcat settings. For example, a Context element in the server.xml file represents a context:

这种应用配置的方法有一个明显的缺点:所有内容都是硬编码的。

改变一个组件甚至是属性的值都需要重新编译Bootstrap类。

幸运的是,Tomcat设计者选择了一种更优雅的配置方式,

即一个名为server.xml的XML文档。

server.xml文件中的每个元素都会被转换为一个Java对象,并且元素的属性用于设置属性。

这样,您只需编辑server.xml文件即可更改Tomcat的设置。

例如,server.xml文件中的一个Context元素表示一个上下文:

<context/>

To set the path and docBase properties you use attributes in the XML element:

要设置路径和 docBase 属性,需要使用 XML 元素中的属性:

<context docBase="myApp" path="/myApp"/>

Tomcat uses the open source library Digester to convert XML elements into Java objects. Digester is explained in the first section of this chapter.

Tomcat使用开源库Digester将XML元素转换为Java对象。

Digesterc在本章的第一节中进行了解释

The next section explains the configuration of a web application. A context represents a Web application, therefore the configuration of a web application is achieved through configuring the instantiated Context instance. The XML file used for configuring a web application is named web.xml. This file must reside in the WEB-INF directory of the application.

下一节解释了Web应用程序的配置。

上下文表示一个Web应用程序,因此通过配置实例化的Context实例来实现Web应用程序的配置。

用于配置Web应用程序的XML文件名为web.xml。该文件必须位于应用程序的WEB-INF目录中。

Digester

Digester is an open source project under the subproject Commons under the Apache's Jakarta project. You can download Digester it from http://jakarta.apache.org/commons/digester/. The Digester API comes in three packages, which are packaged into the commons-digester.jar file:

Digester是Apache Jakarta项目下的子项目Commons中的一个开源项目。

你可以从

http://jakarta.apache.org/commons/digester/

下载Digester。Digester API分为三个包,打包到commons-digester.jar文件中:

  • org.apache.commons.digester. This package provides for rules-based processing of arbitrary XML documents.
  • org.apache.commons.digester.rss. Example usage of Digester to parse XML documents compatible with the Rich Site Summary format used by many newsfeeds.
  • org.apache.commons.digester.xmlrules. This package provides for XML-based definition of rules for Digester.
  • org.apache.commons.digester. 这个包提供了基于规则的任意XML文档处理。
  • org.apache.commons.digester.rss. 使用Digester解析与许多新闻源使用的Rich Site Summary格式兼容的XML文档的示例用法。
  • org.apache.commons.digester.xmlrules. 这个包提供了用于Digester的基于XML的规则定义。

We will not cover all members in the three packages. Instead, we will concentrate on several important types used by Tomcat. We will start this section by presenting the Digester class, the most important type in the Digester library.

我们不会涵盖三个包中的所有成员。

相反,我们将专注于Tomcat使用的几个重要类型。

我们将从介绍Digester类开始,这是Digester库中最重要的类型。

The Digester Class

The org.apache.commons.digester.Digester class is the main class in the Digester library. You use it to parse an XML document. For each element in the document, the Digester object will check if it needs to do something. You, the programmer, decide what the Digester instance must do before you call its the parse method.

org.apache.commons.digester.Digester 类是 Digester 库中的主类。您可以用它来解析 XML 文档。对于文档中的每个元素,Digester 对象都会检查它是否需要做些什么。在调用解析方法之前,程序员要决定 Digester 实例必须做什么。

How do you tell the Digester object what to do when it encounters an XML element? Easy. You define patterns and associate each pattern with one or more rules. The root element in an XML document has a pattern that is the same as the name of the element. For example, consider the XML document in Listing 15.1.

如何告诉 Digester 对象在遇到 XML 元素时该做什么?很简单。您可以定义模式,并将每个模式与一个或多个规则关联起来。XML 文档中的根元素有一个与元素名称相同的模式。例如,请看清单 15.1 中的 XML 文档。

Listing 15.1: The example.xml file

清单 15.1: example.xml 文件

<?xml version="1.0" encoding="ISO-8859-1"?>
<employee firstName="Brian" lastName="May">
 <office>
 <address streeName="Wellington Street" streetNumber="110"/>
 </office>
</employee>

The root document of the XML document is employee. The employee element has the pattern employee. The office element is a subelement of . The pattern of a subelement is the name of the subelement prefixed by the pattern of its containing element plus /. Therefore, the pattern for the office element is employee/office. The pattern for the address element is equal to:

XML文档的根文档是employee。employee元素具有employee的模式。

office元素是的子元素。子元素的模式是其包含元素的模式加上/前缀的子元素的名称。

因此,office元素的模式是employee/office。地址元素的模式等于:

the parent element's pattern + "/" + the name of the element

父元素的模式 + "/" + 元素的名称

The parent of the address element is , and the pattern for is employee/office. Therefore, the pattern for is employee/office/address.

地址元素的父级是,的模式是employee/office。因此,的模式是employee/office/address。

Now that you understand how a pattern derives from an XML element, let's talk about rules.

现在您了解了模式是如何从XML元素派生出来的,让我们谈谈规则。

A rule specifies an action or a couple of actions that the Digester must do upon encountering a particular pattern. A rule is represented by the org.apache.commons.digester.Rule class. The Digester class contains zero or more Rule objects. Inside a Digester instance, these rules and their corresponding patterns are stored in a type of storage represented by the org.apache.commons.digester.Rules interface. Every time you add a rule to a Digester instance, the Rule object is added to the Rules object.

规则指定Digester在遇到特定模式时必须执行的一个或多个操作。

规则由org.apache.commons.digester.Rule类表示。

Digester类包含零个或多个Rule对象。

在Digester实例中,这些规则及其对应的模式存储在org.apache.commons.digester.Rules接口表示的一种存储类型中。

每次向Digester实例添加规则时,Rule对象都会添加到Rules对象中。

Among others, the Rule class has the begin and end methods. When parsing an XML document, a Digester instance calls the begin method of the Rule object(s) added to it when it encounters the start element with a matching pattern. The end method of the Rule object is called when the Digester sees an end element.

Rule类除了其他方法外,还有begin和end方法。

在解析XML文档时,Digester实例在遇到匹配模式的开始元素时调用Rule对象的begin方法。

当Digester看到结束元素时,调用Rule对象的end方法。

When parsing the example.xml document in Listing 15.1, here is what the Digester object does:

在解析列表15.1中的example.xml文档时,Digester对象的操作如下:

  • It first encounters the employee start element, therefore it checks if there is a rule (rules) for the pattern employee. If there is, the Digester executes the begin method of the Rule object(s), starting from the begin method of the first rule added to the Digester.
  • It then sees the office start element, so the Digester object checks if there is a rule (rules) for the pattern employee/office. If there is, it executes the begin method(s) implemented by the rule(s).
  • Next, the Digester instance encounters the address start element. This makes it check if there is a rule (rules) for the pattern employee/office/address. If one or more rule is found, execute the begin method(s) of the rule(s).
  • Next, the Digester encounters the address end element, causing the end method(s) of the matching rules to be executed.
  • Next, the Digester encounters the office end element, causing the end method(s) of the matching rules to be run.
  • Finally, the Digester encounters the employee end element, causing the end method(s) of the matching rules to be executed.
  • 首先遇到employee开始元素,因此检查是否存在模式employee的规则。如果有,Digester执行Rule对象的begin方法,从添加到Digester的第一个规则的begin方法开始。
  • 然后看到office开始元素,因此Digester对象检查是否存在模式employee/office的规则。如果有,执行规则实现的begin方法。
  • 接下来,Digester实例遇到地址开始元素。这使其检查模式employee/office/address是否存在规则。如果找到一个或多个规则,执行规则的begin方法。
  • 接下来,Digester遇到地址结束元素,导致执行匹配规则的end方法。
  • 接下来,Digester遇到office结束元素,导致执行匹配规则的end方法。
  • 最后,Digester遇到employee结束元素,导致执行匹配规则的end方法。

What rules can you use? Digester has predefined a number of rules. You can use these rules without even having to understand the Rule class. However, if these rules are not sufficient, you can make your own rules. The predefined rules include the rules for creating objects, setting the value of a property, etc.

您可以使用哪些规则?Digester预定义了许多规则。

您甚至可以在不了解Rule类的情况下使用这些规则。

但是,如果这些规则不够用,您可以制作自己的规则。

预定义的规则包括用于创建对象、设置属性值等规则。

Creating Objects

If you want your Digester instance to create an object upon seeing a particular pattern, call its addObjectCreate method. This method has four overloads. The signatures of the two more frequently used overloads are as follows:

如果希望 Digester 实例在看到特定模式时创建一个对象,可调用其 addObjectCreate 方法。

该方法有四个重载。两个较常用重载的签名如下:

public void addObjectCreate(java.lang.String pattern,
 java.lang.Class clazz)
public void addObjectCreate(java.lang.String pattern,
 java.lang.String className)

You pass the pattern and a Class object or a class name. For instance, if you want the Digester to create an Employee object (whose class is ex15.pyrmont.digestertest.Employee) upon encountering the pattern employee, you call one of the following lines of code:

您需要传递模式、类对象或类名称。例如,如果您想让 Digester 在遇到 employee 模式时创建一个 Employee 对象(其类名为 ex15.pyrmont.digestertest.Employee),您可以调用以下代码之一:

digester.addObjectCreate("employee",
 ex15.pyrmont.digestertest.Employee.class)

or

digester.addObjectCreate("employee",
 "ex15.pyrmont.digestertest.Employee");

The other two overloads of addObjectCreate allow you to define the name of the class in the XML element, instead of as an argument to the method. This is a very powerful feature because the class name can be determined at runtime. Here are the signatures of those method overloads:

addObjectCreate 的其他两个重载允许在 XML 元素中定义类名,而不是将其作为方法的参数。

这是一个非常强大的功能,因为类名可以在运行时确定。以下是这些方法重载的签名:

public void addObjectCreate(java.lang.String pattern,
 java.lang.String className, java.lang.String attributeName)
public void addObjectCreate(java.lang.String pattern,
 java.lang.String attributeName, java.lang.Class clazz)

In these two overloads, the attributeName argument specifies the name of the attribute of the XML element that contains the name of the class to be instantiated. For example, you can use the following line of code to add a rule for creating an object:

在这两个重载中,attributeName 参数指定了 XML 元素中包含要实例化类名称的属性名称。

例如,您可以使用以下代码行添加一条创建对象的规则:

digester.addObjectCreate("employee", null, "className");

where the attribute name is className.

其中的属性名称为 className。

You then pass the class name in the XML element.

然后在 XML 元素中传递类名。

<employee firstName="Brian" lastName="May"
 className="ex15.pyrmont.digestertest.Employee">

Or, you can define the default class name in the addObjectCreate method as follows:

或者,也可以在 addObjectCreate 方法中定义默认类名,如下所示:

digester.addObjectCreate("employee",
 "ex15.pyrmont.digestertest.Employee", "className");

If the employee element contains a className attribute, the value specified by this attribute will be used as the name of the class to instantiate. If no className attribute is found, the default class name is used.

如果 employee 元素包含 className 属性,该属性指定的值将被用作要实例化的类名。

如果没有找到 className 属性,则使用默认的类名。

The object created by addObjectCreate is pushed to an internal stack. A number of methods are also provided for you to peek, push, and pop the created objects.

addObjectCreate 创建的对象将被推入内部堆栈。

此外,该程序还提供了许多方法,供您偷看、推送和弹出创建的对象。

Setting Properties

Another useful method is addSetProperties, which you can use to make the Digester object set object properties. One of the overloads of this method has the following signature:

另一个有用的方法是 addSetProperties,用它可以使 Digester 对象设置对象属性。该方法的重载之一具有以下签名:

public void addSetProperties(java.lang.String pattern)

You pass a pattern to this method. For example, consider the following code:

您可以向该方法传递一个模式。

例如,请看下面的代码:

digester.addObjectCreate("employee",
 "ex15.pyrmont.digestertest.Employee");
digester.addSetProperties("employee");

The Digester instance above has two rules, object create and set properties. Both are set to be triggered by the pattern employee. The rules are executed in the order they are added to the Digester instance. For the following employee element in an XML document (which corresponds to the pattern employee):

上述 Digester 实例有两条规则,即对象创建和属性设置。

这两条规则都被设置为由模式雇员触发。

这些规则按照添加到 Digester 实例的顺序执行。

对于 XML 文档中的以下雇员元素(与模式雇员相对应):

<employee firstName="Brian" lastName="May">

The Digester instance first creates an instance of ex15.pyrmont.digestertest.Employee, thanks to the first rule added to it. The Digester instance then responds to the second rule for the pattern employee by calling the setFirstName and setLastName properties of the instantiated Employee object, passing Brian and May, respectively. An attribute in the employee element corresponds to a property in the Employee object. An error will occur if the Employee class does not define any one of the properties.

Digester实例首先根据其添加的第一个规则创建ex15.pyrmont.digestertest.Employee的实例。

然后,Digester实例通过调用实例化的Employee对象的setFirstName和setLastName属性来响应与模式employee相关的第二个规则,分别传递Brian和May。

员工元素中的属性对应于Employee对象中的属性。

如果Employee类未定义任何一个属性,则会发生错误。

Calling Methods

The Digester class allows you to add a rule that calls a method on the topmost object in the stack upon seeing a corresponding pattern. This method is addcallMethod. One of its overloads has the following signature:

Digester类允许您添加一个规则,当看到相应模式时,调用堆栈中顶部对象的方法。

这个方法是addcallMethod。

其中一个重载具有以下签名:

public void addCallMethod (java.lang.String pattern,
 java.lang.String methodName)

Creating Relationships between Objects

A Digester instance has an internal stack for storing objects temporarily. When the addObjectCreate method instantiates a class, the result is pushed into this stack. Imagine the stack as a well. To push an object into the stack is like dropping a round object having the same diameter as the well into it. To pop an object means to lift the top most object from the well.

Digester实例有一个内部堆栈,用于临时存储对象。

当addObjectCreate方法实例化一个类时,结果会被推入这个堆栈中。

可以将堆栈想象成一个井。

将一个对象推入堆栈就像将一个与井直径相同的圆形物体放入其中。

弹出一个对象意味着从井中提起最上面的对象。

When two addObjectCreate methods are invoked, the first object is dropped to the well first, followed by the second object. The addSetNext method is used to create a relationship between the first and the second object by calling the specified method on the first object and passing the second object as an argument to the method. Here is the signature of the addSetNext method:

当调用两个addObjectCreate方法时,第一个对象首先被放入井中,然后是第二个对象。

addSetNext方法用于通过调用指定方法来创建第一个对象与第二个对象之间的关系,并将第二个对象作为参数传递给该方法。

下面是addSetNext方法的签名:

public void addSetNext(java.lang.String pattern,
 java.lang.String methodName)

The pattern argument specifies the pattern that triggers this rule, the methodName argument is the name of the method on the first object that will be called. The pattern should be of the form firstObject/secondObject.

模式参数指定触发此规则的模式,methodName参数是将被调用的第一个对象上的方法的名称。

模式应为 firstObject/secondObject 形式。

For example, an employee can have an office. To create a relationship between an employee and his/her office, you will first need to use two addObjectCreate methods, such as the following:

例如,员工可以拥有一个办公室。

要在员工和他/她的办公室之间建立关系,您首先需要使用两个addObjectCreate方法,如下所示:

digester.addObjectCreate("employee",
 "ex15.pyrmont.digestertest.Employee");
digester.addObjectCreate("employee/office",
 "ex15.pyrmont.digestertest.Office");

The first addObjectCreate method creates an instance of the Employee class upon seeing an employee element. The second addObjectCreate method creates an instance of Office on seeing under .

第一个 addObjectCreate 方法在遇到 employee 元素时创建一个 Employee 类的实例。

第二个 addObjectCreate 方法在遇到 office 元素时创建一个 Office 的实例。

The two addObjectCreate methods push two objects to the stack. Now, the Employee object is at the bottom and the Office object on top. To create a relationship between them, you define another rule using the addSetNext method, such as the following:

这两个 addObjectCreate 方法将两个对象推入栈中。

现在,Employee 对象在底部,Office 对象在顶部。

为了创建它们之间的关系,您可以使用 addSetNext 方法定义另一条规则,例如下面这样:

digester.addSetNext("employee/office", "addOffice");

in which addOffice is a method in the Employee class. This method must accept an Office object as an argument. The second Digester example in this section will clarify the use of addSetNext.

在这个例子中,addOffice 是 Employee 类中的一个方法。

这个方法必须接受一个 Office 对象作为参数。

本节中的第二个 Digester 示例将阐明 addSetNext 的用法。

Validating the XML Document

The XML document a Digester parses can be validated against a schema. Whether or not the XML document will be validated is determined by the validating property of the Digester. By default, the value of this property is false.

Digester解析的XML文档可以根据模式进行验证。

XML文档是否进行验证取决于Digester的validating属性。

默认情况下,该属性的值为false。

The setValidating method is used to indicate if you want validation to be performed. The setValidating method has the following signature:

使用setValidating方法可以指示是否要执行验证。

setValidating方法的签名如下:

public void setValidating(boolean validating)

If you want the well-formedness of your XML document to be validated, pass true to the setValidating method.

如果您希望验证 XML 文档的良好格式,请向 setValidating 方法传递 true。

Digester Example 1

The first example explains how to use Digester to create an object dynamically and set its properties. Consider the Employee class in Listing 15.2 that we will instantiate using Digester.

第一个示例说明了如何使用 Digester 动态创建一个对象并设置其属性。

请看清单 15.2 中的 Employee 类,我们将使用 Digester 将其实例化。

Listing 15.2: The Employee Class

清单 15.2: 雇员类

  
package ex15.pyrmont.digestertest;  
        import java.util.ArrayList;  
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);  
    }  
}

The Employee class has three properties: firstName, lastName, and office. The firstName and lastName properties are strings, and office is of type ex15.pyrmont.digester.Office. The office property will be used in the second example of Digester.

Employee类有三个属性:firstName,lastName和office。

firstName和lastName属性是字符串类型,office是ex15.pyrmont.digester.Office类型。

office属性将在Digester的第二个示例中使用。

The Employee class also has one method: printName that simply prints the first name and last name properties to the console.

Employee类还有一个方法:printName,它只是将firstName和lastName属性打印到控制台。

We will now write a test class that uses a Digester and adds rules for creating an Employee object and setting its properties. The Test01 class in Listing 15.3 can be used for this purpose.

现在我们将编写一个测试类,使用Digester并添加规则来创建Employee对象并设置其属性。

15.3节中的Test01类可用于此目的。

Listing 15.3: The Test01 Class

15.3节:Test01类

  
package ex15.pyrmont.digestertest;  
        import java.io.File;  
        import org.apache.commons.digester.Digester;  
public class Test01 {  
    public static void main(String[] args) {  
        String path = System.getProperty("user.dir") + File.separator +  
                "etc";  
        File file = new File(path, "employee1.xml");  
        Digester digester = new Digester();  
        // add rules  
        digester.addObjectCreate("employee",  
                "ex15.pyrmont.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();  
        }  
    }  
}

You first define the path containing the location of your XML document and pass it the File class's constructor. You then create a Digester object and add there rules having the pattern employee:

首先定义包含 XML 文档位置的路径,并将其传递给文件类的构造函数。

然后创建一个 Digester 对象,并在其中添加具有雇员模式的规则:

digester.addObjectCreate("employee",
 "ex15.pyrmont.digestertest.Employee");
 digester.addSetProperties("employee");
 digester.addCallMethod("employee", "printName");

Next, you call the parse method on the Digester object passing the File object referencing the XML document. The return value of the parse method is the first object in the Digester's internal stack:

接下来,通过引用 XML 文档的文件对象,调用 Digester 对象的解析方法。

解析方法的返回值是 Digester 内部堆栈中的第一个对象:

Employee employee = (Employee) digester.parse(file);

This gives you an Employee object instantiated by the Digester. To see if the Employee object's properties have been set, call the getFirstName and getLastName methods of the Employee object:

这样就得到了一个由 Digester 实例化的 Employee 对象。要查看 Employee 对象的属性是否已设置,请调用 Employee 对象的 getFirstName 和 getLastName 方法:

System.out.println("First name : " + employee.getFirstName());
 System.out.println("Last name : " + employee.getLastName());

Now, Listing 15.4 offers the employee1.xml document with the root element employee. The element has two attributes, firstName and lastName

现在,清单 15.4 提供了带有根元素 employee 的 employee1.xml 文档。

该元素有两个属性:firstName 和 lastName

Listing 15.4: The employee1.xml file

清单 15.4: employee1.xml 文件

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

The result of running the Test01 class is as follows:

运行 Test01 类的结果如下:

Creating Employee
Setting firstName : Brian
Setting lastName : May
My name is Brian May
First name : Brian
Last name : May

Here is what happened.

这里是发生的事情。

When you call the parse method on the Digester object, it opens the XML document and starts parsing it. First, the Digester sees the employee start element. This triggers the three rules for the pattern employee in the order the rules were added. The first rule is for creating an object. Therefore, the Digester instantiates the Employee class, resulting the calling of the Employee clas's constructor. This constructor prints the string Creating Employee.

当你在Digester对象上调用parse方法时,它会打开XML文档并开始解析。

首先,Digester看到了employee开始元素。

这触发了employee模式的三条规则,按照它们被添加的顺序执行。

第一条规则是用于创建对象。

因此,Digester实例化了Employee类,从而调用了Employee类的构造函数。

这个构造函数打印了字符串“创建员工”。

The second rule sets the attribute of the Employee object. There are two attributes in the employee element, firstName and lastName. This rule causes the set methods of the firstName and lastName properties to be invoked. The set methods print the following strings:

第二条规则设置了Employee对象的属性。在employee元素中有两个属性,firstName和lastName。这条规则导致调用firstName和lastName属性的set方法。这些set方法打印了以下字符串:

Setting firstName : Brian
Setting lastName : May

The third rule calls the printName method, which prints the following text:

第三条规则调用 printName 方法,打印出以下文本:

My name is Brian May

我的名字是布莱恩-梅

Then, the last two lines are the result of calling the getFirstName and getLastName methods on the Employee object:

然后,最后两行是在 Employee 对象上调用 getFirstName 和 getLastName 方法的结果:

First name : Brian
Last name : May

Digester Example 2

The second Digester example demonstrates how to create two objects and create a relationship between them. You define the type of relationship created. For example, an employee works in one or more office. An office is represented by the Office class. You can create an Employee and an Office object, and create a relationship between the Employee and Office objects. The Office class is given in Listing 15.5.

第二个Digester示例演示了如何创建两个对象并在它们之间建立关系。

您可以定义创建的关系类型。例如,一个员工在一个或多个办公室工作。

办公室由Office类表示。您可以创建一个Employee对象和一个Office对象,并在它们之间建立关系。

Office类如清单15.5所示。

Listing 15.5: The Office Class

清单15.5:Office类

  
package ex15.pyrmont.digestertest;  
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;  
    }  
}

You create a relationship by calling a method on the parent object. Note that this example uses the Employee class in Listing 15.2. The Employee class has the addOffice method to add an Office object to its offices collection.

您可以通过调用父对象上的方法来创建关系。

请注意,本示例使用了清单 15.2 中的 Employee 类。

Employee 类有 addOffice 方法,用于将 Office 对象添加到其 offices 集合中。

Without the Digester, your Java code would look like this:

如果没有 Digester,您的 Java 代码将如下所示:

Employee employee = new Employee();
Office office = new Office();
employee.addOffice(office);

An office has an address and an address is represented by the Address class, given in Listing 15.6.

办公室有地址,地址由 Address 类表示,见清单 15.6。

Listing 15.6: The Address Class

清单 15.6:地址类



package ex15.pyrmont.digestertest;
public class Address {
    private String streetName;
    private String streetNumber;
    public Adress () {
        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;
    }
}

To assign an address to an office, you can call the setAddress method of the Office class. With no help from Digester, you would have the following code:

要为办公室指定地址,可以调用 Office 类的 setAddress 方法。

如果没有 Digester 的帮助,您将得到以下代码:

Office office = new Office();
Address address = new Address();
office.setAddress (address);

The second Digester example shows how you can create objects and create relationships between them. We will use the Employee, Office, and Address classes. The Test02 class (in Listing 15.7) uses a Digester and adds rules to it.

第二个 Digester 示例展示了如何创建对象并在它们之间建立关系。

我们将使用 EmployeeOfficeAddress 类。

Test02 类(在清单 15.7 中)使用 Digester 并向其添加规则。

Listing 15.7: The Test02 Class

清单 15.7:Test02


package ex15.pyrmont.digestertest;
import java.io.File;
import java.util.*;
import org.apache.commons.digester.Digester;
public class Test02 {
    public static void main(String[] args) {
        String path = System.getProperty("user.dir") + File.separator +
                "etc";
        File file = new File(path, "employee2.xml");
        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 {
            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();
        }
    }
}

To see the Digester in action, you can use the XML document employee2.xml in Listing 15.8.

要查看 Digester 的运行情况,可以使用清单 15.8 中的 XML 文档 employee2.xml

Listing 15.8: The employee2.xml file

清单 15.8: 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>

The result when the Test02 class is run is as follows:

运行 Test02 类的结果如下:

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

The Rule Class

The Rule class has several methods, the two most important of which are begin and end. When a Digester instance encounters the beginning of an XML element, it calls the begin method of all matching Rule objects it contains. The begin method of the Rule class has the following signature:

规则类有几个方法,其中最重要的两个是 beginend

当 Digester 实例遇到一个 XML 元素的开头时,它会调用它所包含的所有匹配 Rule 对象的 begin 方法。

规则类的 begin 方法具有以下签名:

public void begin(org.xml.sax.Attributes attributes)
 throws java.lang.Exception

When the Digester instance encounters the end of an XML element, it calls the end method of all matching Rule instances it contains. The signature of the end method of the Rule class is as follows.

Digester 实例遇到XML元素的结尾时,它会调用其包含的所有匹配Rule实例的end方法。

Rule类的end方法签名如下。

public void end() throws java.lang.Exception

How do the Digester objects in the preceding examples do the wonder? Every time you call the addObjectCreate, addCallMethod, addSetNext, and other methods of the Digester, you indirectly invoke the addRule method of the Digester class, which adds a Rule object and its matching pattern to the Rules collection inside the Digester.

在前面的示例中,Digester对象是如何实现奇迹的呢?

每当您调用DigesteraddObjectCreateaddCallMethodaddSetNext 等方法时,实际上间接调用了Digester类的addRule方法,该方法会向Digester内部的Rules集合中添加 Rule 对象及其匹配模式。

The signature of the addRule method is as follows:

addRule 方法的签名如下:

public void addRule(java.lang.String pattern, Rule rule)

The implementation of the addRule method in the Digester class is as follows:

Digester 类中 addRule 方法的实现如下:

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

Take a look at the Digester class source code for the addObjectCreate method overloads:

请查看 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));
    }

The four overloads call the addRule method. The ObjectCreateRule class--whose instance gets created as the second argument to the addRule method--is a subclass of the Rule class. You may be interested in the begin and end method implementations in the ObjectCreateRule class:

这四个重载调用 addRule 方法。

ObjectCreateRule 类(其实例作为 addRule 方法的第二个参数被创建)是 Rule 类的子类。

你可能会对 ObjectCreateRule 类中的 begin 和 end 方法的实现感兴趣:

    public void begin(Attributes attributes) throws Exception {
        // 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);
    }
    public void end() throws Exception {
        Object top = digester.pop();
        if (digester.log.isDebugEnabled()) {
            digester.log.debug("[ObjectCreateRule]{" + digester.match +
                    "} Pop " + top.getdass().getName());
        }
    }

The last three lines in the begin method creates an instance of the object and then pushes it to the internal stack inside the Digester. The end method pops the object from the stack.

begin 方法的最后三行创建了一个对象实例,然后将其推入 Digester 的内部堆栈。

end 方法会从堆栈中弹出对象。

The other subclass of the Rule class works similarly. You can open the source code if you are keen to know what is behind each rule.

规则类的另一个子类也是类似的工作方式。如果你想知道每条规则背后的内容,可以打开源代码。

Digester Example 3: Using RuleSet

Another way of adding rules to a Digester instance is by calling its addRuleSet method. The signature of this method is as follows:

向 Digester 实例添加规则的另一种方法是调用其 addRuleSet 方法。该方法的签名如下

public void addRuleSet(RuleSet ruleSet)

The org.apache.commons.digester.RuleSet interface represents a set of Rule objects. This interface defines two methods, addRuleInstance and getNamespaceURI. The signature of the addRuleInstance is as follows:

org.apache.commons.digester.RuleSet 接口表示一组规则对象。

该接口定义了两个方法:addRuleInstance 和 getNamespaceURI。addRuleInstance 的签名如下:

public void addRuleInstance(Digester digester)

The addRuleInstance method adds the set of Rule objects defined in the current RuleSet to the Digester instance passed as the argument to this method.

addRuleInstance 方法将当前 RuleSet 中定义的规则对象集添加到作为该方法参数传递的 Digester 实例中。

The getNamespaceURI returns the namespace URI that will be applied to all Rule objects created in this RuleSet. Its signature is as follows:

getNamespaceURI 返回名称空间 URI,该名称空间 URI 将应用于在此 RuleSet 中创建的所有 Rule 对象。其签名如下

public java.lang.String getNamespaceURI()

Therefore, after you create a Digester object, you can create a RuleSet object and pass the RuleSet object to the addRuleSet method on the Digester.

因此,在创建 Digester 对象后,可以创建一个 RuleSet 对象,并将 RuleSet 对象传递给 Digester 上的 addRuleSet 方法。

A convenience base class, RuleSetBase, implements RuleSet. RuleSetBase is an abstract class that provides the implementation of the getNamespaceURI. You only need to provide the implementation of the addRuleInstances method

一个方便的基类 RuleSetBase 实现了 RuleSet。RuleSetBase 是一个抽象类,提供了 getNamespaceURI 的实现。

您只需提供 addRuleInstances 方法的实现。

As an example, let's modify the Test02 class in the previous example by introducing the EmployeeRuleSet class in Listing 15.9.

作为示例,让我们通过引入清单 15.9 中的 EmployeeRuleSet 类来修改上一个示例中的 Test02 类。

Listing 15.9: The EmployeeRuleSet Class

清单 15.9:EmployeeRuleSet 类

package ex15.pyrmont.digestertest;  
        import org.apache.commons.digester.Digester;  
        import org.apache.commons.digester.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");  
    }  
}

Notice that the implementation of the addRuleInstances method in the EmployeeRuleSet class adds the same rules to the Digester as the Test02 class does. The Test03 class in Listing 15.10 creates an instance of the EmployeeRuleSet and then adds it to the Digester it created earlier.

请注意,EmployeeRuleSet 类中 addRuleInstances 方法的实现将与 Test02 类相同的规则添加到 Digester 中。

清单 15.10 中的 Test03 类创建了 EmployeeRuleSet 的实例,然后将其添加到之前创建的 Digester 中。

Listing 15.10: The Test03 Class

清单 15.10:Test03 类

  
package ex15.pyrmont.digestertest;  
        import java.io.File;  
        import java.util.ArrayList;  
        import java.util.Iterator;  
        import org.apache.commons.digester.Digester;  
public class Test03 {  
    public static void main(String[] args) {  
        String path = System.getProperty("user.dir") +  
                File.separator + "etc";  
        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 ();  
        }  
    }  
}

When run, the Test03 class produces the same output as the Test02 class. Note however, that the Test03 is shorter because the code for adding Rule objects is now hidden inside the EmployeeRuleSet class.

运行时,Test03 类的输出结果与 Test02 类相同。

但请注意,Test03 的时间更短,因为添加规则对象的代码现在隐藏在 EmployeeRuleSet 类中。

As you will see later, Catalina uses subclasses of RuleSetBase for initializing its server and other components. In the next sections, you will see how Digester plays a very important role in Catalina

稍后您将看到,Catalina 使用 RuleSetBase 的子类来初始化其服务器和其他组件。

在接下来的章节中,您将看到 Digester 如何在 Catalina 中发挥非常重要的作用

ContextConfig

Unlike other types of containers, a StandardContext must have a listener. This listener configures the StandardContext instance and upon successfully doing so sets the StandardContext's configured variable to true. In previous chapters, we used the SimpleContextConfig class as the StandardContext's listener. This class was a very simple one whose sole purpose is to set the configured variable so that the start method of StandardContext can continue.

与其他类型的容器不同,StandardContext必须具有一个监听器。

这个监听器配置StandardContext实例,并在成功配置后将StandardContext的configured变量设置为true。在前面的章节中,我们使用SimpleContextConfig类作为StandardContext的监听器。这个类非常简单,其唯一目的是设置configured变量,以便StandardContext的start方法可以继续执行。

In a real Tomcat deployment, the standard listener for StandardContext is an instance of org.apache.catalina.startup.ContextConfig class. Unlike our humble SimpleContextConfig class, ContextConfig does a lot of useful stuff that the StandardContext instance cannot live without it. For example, a ContextConfig instance associated with a StandardContext installs an authenticator valve in the StandardContext's pipeline. It also adds a certificate valve (of type org.apache.catalina.valves.CertificateValve) to the pipeline.

在真实的Tomcat部署中,StandardContext的标准监听器是org.apache.catalina.startup.ContextConfig类的一个实例。与我们谦虚的SimpleContextConfig类不同,ContextConfig执行了许多StandardContext实例所不能缺少的有用操作。例如,与StandardContext关联的ContextConfig实例在StandardContext的管道中安装了一个认证器阀门。它还向管道中添加了一个证书阀门(类型为org.apache.catalina.valves.CertificateValve)。

More importantly, however, the ContextConfig instance also reads and parses the default web.xml file and the application web.xml file and convert the XML elements to Java objects. The default web.xml file is located in the conf directory of CATALINE_HOME. It defines and maps default servlets, maps file extensions with MIME types, defines the default session timeout, and list welcome files. You should open the file now to see its contents.

然而,更重要的是,ContextConfig实例还会读取和解析默认的web.xml文件和应用程序的web.xml文件,并将XML元素转换为Java对象。默认的web.xml文件位于CATALINE_HOME的conf目录中。它定义和映射默认的servlet,将文件扩展名与MIME类型映射,定义默认的会话超时时间,并列出欢迎文件。你现在可以打开这个文件来查看其内容。

The application web.xml file is the application configuration file, located in the WEB-INF directory of an application. Both files are not required. ContextConfig will continue even if none of these files is found

应用程序的web.xml文件是应用程序的配置文件,位于应用程序的WEB-INF目录中。这两个文件都不是必需的。即使没有找到这些文件,ContextConfig也会继续执行。

The ContextConfig creates a StandardWrapper instance for each servlet element. Therefore, as you can see in the application accompanying this chapter, configuration is made easy. You are no longer required to instantiate a wrapper anymore.

ContextConfig为每个servlet元素创建一个StandardWrapper实例。因此,正如你可以在本章附带的应用程序中看到的那样,配置变得非常简单。你不再需要手动实例化一个包装器。

Therefore, somewhere in your bootstrap class, you must instantiate the ContextConfig class and add it to the StandardContext by calling the addLifecycleListener method of the org.apache.catalina.Lifecycle interface.

因此,在你的引导类中的某个地方,你必须实例化ContextConfig类,并通过调用org.apache.catalina.Lifecycle接口的addLifecycleListener方法将其添加到StandardContext中。

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

The StandardContext fires the following events when it is started:

StandardContext 启动时会触发以下事件:

  • BEFORE_START_EVENT
  • START_EVENT
  • AFTER_START_EVENT

When stopped, the StandardContext fires the following events:

停止时,StandardContext 会触发以下事件:

  • BEFORE_STOP_EVENT
  • STOP_EVENT
  • AFTER_STOP_EVENT

The ContextConfig class responds to two events: START_EVENT and STOP_EVENT. The lifecycleEvent method is invoked every time the StandardContext triggers an event. This method is given in Listing 15.11. We have added comments to Listing 15.11 so that the stop method is easier to understand.

ContextConfig 类响应两个事件: START_EVENT 和 STOP_EVENT。每次 StandardContext 触发事件时,都会调用 lifecycleEvent 方法。该方法在清单 15.11 中给出。我们在清单 15.11 中添加了注释,以便更容易理解 stop 方法。

Listing 15.11: The lifecycleEvent method of ContextConfig

清单 15.11: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();  
}

As you can see in the end of the lifecycleEvent method, it calls either its start method or its stop method. The start method is given in Listing 15.12. Notice that somewhere in its body the start method calls the defaultConfig and applicationConfig methods. Both are explained in the sections after this.

正如您在 lifecycleEvent 方法的末尾所看到的,它调用了 start 方法或 stop 方法。启动方法见清单 15.12。请注意,在其主体的某处,start 方法调用了 defaultConfig 和 applicationConfig 方法。这两个方法将在后面的章节中解释。

Listing 15.12: The start method of ContextConfig


    private synchronized void start() {
        if (debug > 0)
            log(sm.getString("ContextConfig.start"));
        // reset the configured boolean
        context.setConfigured(false);
        // a flag that indicates whether the process is still
        // going smoothly
        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);
        }
    }

The defaultConfig Method

The defaultConfig method reads and parses the default web.xml file in the %CATALINA_HOME%/conf directory. The defaultConfig method is presented in Listing 15.13.

Listing 15.13: The defaultConfig method


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

The defaultConfig method begins by creating a File object that references the default web.xml file.

File file = new File(Constants.DefaultWebXml);

The value of DefaultWebXML can be found in the org.apache.catalina.startup.Constants class as follows:

public static final String DefaultWebXml = "conf/web.xml";

The defaultConfig method then processes the web.xml file. It locks the webDigester object variable, then parses the 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);

The webDigester object variable references a Digester instance that have been populated with rules for processing a web.xml file. It is discussed in the subsection, "Creating Web Digester" later in this section.

The applicationConfig Method

The applicationConfig method is similar to the defaultConfig method, except that it processes the application deployment descriptor. A deployment descriptor resides in the WEB-INF directory of the application directory.

The applicationConfig method is given in Listing 15.14.

Listing 15.14: The applicationConfig method of ContextConfig


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

Creating Web Digester

A Digester object reference called webDigester exists in the ContextConfig class:

ContextConfig 类中存在一个名为 webDigester 的 Digester 对象引用:

private static Digester webDigester = createWebDigester();

This Digester is used to parse the default web.xml and application web.xml files. The rules for processing the web.xml file are added when the createWebDigester method is invoked. The createWebDigester method is given in Listing 15.15.

这个解析器用于解析默认的web.xml和应用程序的web.xml文件。当调用createWebDigester方法时,会添加处理web.xml文件的规则。createWebDigester方法如清单15.15所示。

Listing 15.15: The createWebDigester method

清单15.15:createWebDigester方法



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

Notice that createWebDigester method calls the addRuleSet on webDigester by passing an instance of org.apache.catalina.startup.WebRuleSet. The WebRuleSet is a subclass of the org.apache.commons.digester.RuleSetBase class. If you are familiar with the syntax of a servlet application deployment descriptor and you have read the Digester section at the beginning of this chapter, you sure can understand how it works.

注意,createWebDigester方法调用webDigester的addRuleSet方法,并传递org.apache.catalina.startup.WebRuleSet的实例。WebRuleSet是org.apache.commons.digester.RuleSetBase类的子类。如果您熟悉servlet应用程序部署描述符的语法,并且已经阅读了本章开头的Digester部分,那么您肯定可以理解它是如何工作的。

The WebRuleSet class is given in Listing 15.16. Note that we have removed some parts of the addRuleInstances method to save space.

WebRuleSet类在清单15.16中给出。请注意,我们已经删除了addRuleInstances方法的一些部分以节省空间。

Listing 15.16: The WebRuleSet class

清单15.16:WebRuleSet类



package org.apache.catalina.startup;
        import java.lang.reflect.Method;
        import org.apache.catalina.Context;
        import org.apache.catalina.Wrapper;
        import org.apache.catalina.deploy.SecurityConstraint;
        import org.apache.commons.digester.Digester;
        import org.apache.commons.digester.Rule;
        import org.apache.commons.digester.RuleSetBase;
        import org.xml.sax.Attributes;
/**
 * <p><strong>RuleSet</strong> for processing the contents of a web
 application
 * deployment descriptor (<code>/WEB-INF/web.xml</code>) resource.</p>
 *
 * @author Craig R. McClanahan
 * @version $Revision: 1.1 $ $Date: 2001/10/17 00:44:02 $
 */
public class WebRuleSet extends RuleSetBase {
    // ------------------------------------- Instance Variables
    /**
     * The matching pattern prefix to use for recognizing our elements.
     */
    protected String prefix = null;
    // ------------------------------------------- Constructor
    /**
     * Construct an instance of this <code>RuleSet</code> with
     * the default matching pattern prefix.
     */
    public WebRuleSet () {
        this("");
    }
    /**
     * Construct an instance of this <code>RuleSet</code> with
     * the specified matching pattern prefix.
     *
     * @param prefix Prefix for matching pattern rules (including the
     * trailing slash character)
     */
    public WebRuleSet(String prefix) {
        super();
        this.namespaceURI = null;
        this.prefix = prefix;
    }
    // ------------------------------------------- Public Methods
    /**
     * <p>Add the set of Rule instances defined in this RuleSet to the
     * specified <code>Digester</code> instance, associating them with
     * our namespace URI (if any). This method should only be called
     * by a Digester instance.</p>
     *
     * @param digester Digester instance to which the new Rule instances
     * should be added.
     */
    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/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.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",
                "setServletdass", 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)");
    }
}
...
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());
    }
}

The Application

This chapter's application shows how to use a ContextConfig instance as a listener to configure the StandardContext object. It consists of only one class, Bootstrap, which is presented in Listing 15.17.

Listing 15.17: The Bootstrap class



package ex15.pyrmont.startup;
        import org.apache.catalina.Connector;
        import org.apache.catalina.Container;
        import org.apache.catalina.Context;
        import org.apache.catalina.Host;
        import org.apache.catalina.Lifecycle;
        import org.apache.catalina.LifecycleListener;
        import org.apache.catalina.Loader;
        import org.apache.catalina.connector.http.HttpConnector;
        import org.apache.catalina.core.StandardContext;
        import org.apache.catalina.core.StandardHost;
        import org.apache.catalina.loader.WebappLoader;
        import org.apache.catalina.startup.ContextConfig;
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();
        }
    }
}

Running the Applications

To run the application in Windows, from the working directory, type the following:

java -classpath ./lib/servlet.jar;./lib/commonscollections.jar;./lib/commons-digester.jar;./lib/commonslogging.jar;./lib/commons-beanutils.jar;./
ex15.pyrmont.startup.Bootstrap

In Linux, you use a colon to separate two libraries.

java -classpath ./lib/servlet.jar:./lib/commonscollections.jar:./lib/commons-digester.jar:./lib/commonslogging.jar:./lib/commons-beanutils.jar:./
ex15.pyrmont.startup.Bootstrap

To invoke PrimitiveServlet, use the following URL in your browser.

http://localhost:8080/app1/Primitive

To invoke ModernServlet, use the following URL.

http://localhost:8080/app1/Modern

Summary

Tomcat is used in different configurations. Easy configuration using a server.xml file is achieved through the use of Digester objects that converts XML elements to Java objects. In addition, a web.xml document is used to configure a servlet/JSP application. Tomcat must be able to parse this web.xml document and configure a Context object based on the elements in the XML document. Again, Digester solves this problem elegantly.


Xander
195 声望50 粉丝