Chapter 5: Container
A container is a module that processes the requests for a servlet and populates the response objects for web clients. A container is represented by the org.apache.catalina.Container interface and there are four types of containers: Engine, Host, Context, and Wrapper. This chapter covers Context and Wrapper and leaves Engine and Host to Chapter 13. This chapter starts with the discussion of the Container interface, followed by the pipelining mechanism in a container. It then looks at the Wrapper and Context interfaces. Two applications conclude this chapter by presenting a simple wrapper and a simple context respectively.
容器是一个处理 Servlet
请求并填充Web客户端响应对象的模块。
容器由org.apache.catalina.Container
接口表示,共有四种类型的容器:
- 引擎(
Engine
) - 主机(
Host
) - 上下文(
Context
) - 包装器(
Wrapper
)
本章将介绍上下文和包装器,引擎和主机则在第13章中介绍。
本章首先讨论容器接口,然后介绍容器中的管道机制。
接着,将详细介绍包装器和上下文接口。
最后,通过分别展示一个简单的包装器和一个简单的上下文来结束本章。
The Container Interface(容器接口)
A container must implement org.apache.catalina.Container. As you have seen in Chapter 4, you pass an instance of Container to the setContainer method of the connector, so that the connector can call the container's invoke method. Recall the following code from the Bootstrap class in the application in Chapter 4.
一个容器必须实现org.apache.catalina.Container
接口。
正如您在第四章中所看到的,您将一个 Container
实例传递给连接器(connector
)的 setContainer
方法,以便连接器可以调用容器的 invoke
方法。
请回忆一下第四章应用程序中Bootstrap
类中的以下代码。
HttpConnector connector = new HttpConnector();
SimpleContainer container = new SimpleContainer();
connector.setContainer(container);
The first thing to note about containers in Catalina is that there are four types of containers at different conceptual levels:
于 Catalina
中的容器,首先要注意的是,它有四种概念层次上的容器:
- Engine. Represents the entire Catalina servlet engine.
- Host. Represents a virtual host with a number of contexts.
- Context. Represents a web application. A context contains one or more wrappers.
- Wrapper. Represents an individual servlet.
Engine
(引擎):代表整个Catalina servlet引擎。Host
(主机):代表一个具有多个上下文的虚拟主机。Context
(上下文):代表一个Web应用程序。一个上下文包含一个或多个包装器。Wrapper
(包装器):代表一个单独的servlet。
Each conceptual level above is represented by an interface in the org.apache.catalina package. These interfaces are Engine, Host, Context, and Wrapper. All the four extends the Container interface. Standard implementations of the four containers are StandardEngine, StandardHost, StandardContext, and StandardWrapper, respectively, all of which are part of the org.apache.catalina.core package.
上述每个概念层次都由org.apache.catalina
包中的一个接口表示。
这些接口分别是 Engine
、Host
、Context
和 Wrapper
。
这四个接口都扩展自 Container
(容器)接口。这四个容器的标准实现分别是 StandardEngine
、StandardHost
、StandardContext
和 StandardWrapper
,它们都属于org.apache.catalina.core
包。
Figure 5.1 shows the class diagram of the Container interface and its sub-interfaces and implementations. Note that all interfaces are part of the org.apache.catalina package and all classes are part of the org.apache.catalina.core package.
图5.1显示了Container
接口及其子接口和实现类的类图。
请注意,所有接口都属于org.apache.catalina
包,而所有类都属于org.apache.catalina.core
包。
Figure 5.1: The class diagram of Container and its related types
图5.1:Container
及其相关类型的类图
Note All implementation classes derive from the abstract class ContainerBase.
注意:所有的实现类都继承自抽象类 ContainerBase
。
A functional Catalina deployment does not need all the four types of containers. For example, the container module in this chapter's first application consists of only a wrapper. The second application is a container module with a context and a wrapper. Neither host nor engine is needed in the applications accompanying this chapter.
一个功能完整的 Catalina
部署并不需要所有四种类型的容器。
例如,本章第一个应用程序中的容器模块只包含一个包装器。
第二个应用程序是一个包含上下文和包装器的容器模块。
本章附带的应用程序中不需要主机或引擎。
A container can have zero or more child containers of the lower level. For instance, a context normally has one or more wrappers and a host can have zero or more contexts. However, a wrapper, being the lowest in the 'hierarchy', cannot contain a child container. To add a child container to a container, you use the Container interface's addChild method whose signature is as follows.
一个容器可以拥有零个或多个较低级别的子容器。
例如,一个上下文通常有一个或多个包装器,而一个主机可以有零个或多个上下文。
然而,作为“层次结构”中最低的一层,包装器不能包含子容器。
要向容器添加子容器,可以使用 Container
接口的 addChild
方法,其签名如下。
public void addChild(Container child);
To remove a child container from a container, call the Container interface's removeChild method. The remove method's signature is as follows.
要从容器中移除子容器,请调用容器接口的 removeChild
方法。remove
方法的签名如下。
public void removeChild(Container child);
In addition, the Container interface supports the finding of a child container or a collection of all child containers through the findChild and findChildren methods. The signatures of both methods are the following.
此外,容器接口还支持通过 findChild
和 findChildren
方法查找子容器或所有子容器的集合。
这两个方法的签名如下。
public Container findChild(String name);
public Container[] findChildren();
A container can also contain a number of support components such as Loader, Logger, Manager, Realm, and Resources. We will discuss these components in later chapters. One thing worth noting here is that the Container interface provides the get and set methods for associating itself with those components. These methods include getLoader and setLoader, getLogger and setLogger, getManager and setManager, getRealm and setRealm, and getResources and setResources.
一个容器还可以包含许多支持组件,比如 Loader
、Logger
、Manager
、Realm
和 Resources
。
我们将在后面的章节中讨论这些组件。
这里值得注意的一点是,容器接口提供了用于将自身与这些组件关联的get
和set
方法。
这些方法包括 getLoader
和 setLoader
、getLogger
和 setLogger
、getManager
和 setManager
、getRealm
和 setRealm
,以及 getResources
和 setResources
。
More interestingly, the Container interface has been designed in such a way that at the time of deployment a Tomcat administrator can determine what a container performs by editing the configuration file (server.xml). This is achieved by introducing a pipeline and a set of valves in a container, which we will discuss in the next section, "Pipelining Tasks".
更有趣的是,容器接口的设计使得在部署时,Tomcat
管理员可以通过编辑配置文件(server.xml
)来确定容器的执行方式。
这是通过在容器中引入一个管道和一组阀门来实现的,我们将在下一节“管道任务”中讨论这一点。
Note The Container interface in Tomcat 4 is slightly different from that in Tomcat 5. For example, in Tomcat 4 this interface has a map method, which no longer exists in the Container interface in Tomcat 5.
注意:Tomcat 4中的Container接口与Tomcat 5中略有不同。例如,在Tomcat 4中,该接口具有一个map方法,而在Tomcat 5中的Container接口中已经不再存在。
Pipelining Tasks(流水线任务)
This section explains what happens when a container's invoke method is called by the connector. This section then discusses in the sub-sections the four related interfaces in the org.apache.catalina package: Pipeline, Valve, ValveContext, and Contained.
本节解释了当连接器调用容器的 invoke
方法时会发生什么。
然后在子节中讨论了 org.apache.catalina
包中的四个相关接口:
Pipeline
Valve
ValveContext
Contained
A pipeline contains tasks that the container will invoke. A valve represents a specific task. There is one basic valve in a container's pipeline, but you can add as many valves as you want. The number of valves is defined to be the number of additional valves, i.e. not including the basic valve. Interestingly, valves can be added dynamically by editing Tomcat's configuration file (server.xml). Figure 5.2 shows a pipeline and its valves.
管道包含容器将调用的任务。
阀门表示特定的任务。
容器的管道中有一个基本阀门,但您可以添加任意数量的阀门。
阀门的数量定义为额外阀门的数量,即不包括基本阀门。
有趣的是,可以通过编辑 Tomcat
的配置文件(server.xml
)动态添加阀门。
图5.2显示了一个管道及其阀门。
Figure 5.2: Pipeline and valves
图5.2:管道和阀门
If you understand servlet filters, it is not hard to imagine how a pipeline and its valve work. A pipeline is like a filter chain and each valve is a filter. Like a filter, a valve can manipulate the request and response objects passed to it. After a valve finishes processing, it calls the next valve in the pipeline. The basic valve is always called the
如果您了解Servlet过滤器,那么想象一下管道及其阀门的工作原理就不难了。
管道就像一个过滤器链,每个阀门都是一个过滤器。
与过滤器一样,阀门可以操作传递给它的请求和响应对象。
在阀门完成处理后,它调用管道中的下一个阀门。
基本阀门始终被称为最后一个阀门。
last.
A container can have one pipeline. When a container's invoke method is called, the
container passes processing to its pipeline and the pipeline invokes the first valve in
it, which will then invoke the next valve, and so on, until there is no more valve in
the pipeline. You might imagine that you could have the following pseudo code inside
the pipeline's invoke method:
一个容器可以有一个管道。
当调用容器的 invoke
方法时,容器将处理传递给它的管道,管道将调用其中的第一个阀门,然后依次调用下一个阀门,直到管道中没有更多的阀门。
您可以想象在管道的 invoke
方法中可以有以下伪代码:
// invoke each valve added to the pipeline
for (int n=0; n<valves.length; n++) {
valve[n].invoke( ... );
}
// then, invoke the basic valve
basicValve.invoke( ... );
However, the Tomcat designer chose a different approach by introducing the org.apache.catalina.ValveContext interface. Here is how it works.
然而,Tomcat
的设计者选择了一种不同的方法,引入了 org.apache.catalina.ValveContext
接口。
它的工作原理如下。
A container does not hard code what it is supposed to do when its invoke method is called by the connector. Instead, the container calls its pipeline's invoke method. The Pipeline interface's invoke method has the following signature, which is exactly the same as the invoke method of the Container interface.
当连接器调用容器的 invoke
方法时,容器不会硬编码其应该执行的操作。
相反,容器会调用其管道的 invoke
方法。管道接口的 invoke
方法具有以下签名,与容器接口的 invoke
方法完全相同。
public void invoke(Request request, Response response)
throws IOException, ServletException;
Here is the implementation of the Container interface's invoke method in the
org.apache.catalina.core.ContainerBase class.
public void invoke(Request request, Response response)
throws IOException, ServletException {
pipeline.invoke(request, response);
}
where pipeline is an instance of the Pipeline interface inside the container.
管道是容器内的 Pipeline
接口的一个实例。
Now, the pipeline has to make sure that all the valves added to it as well as its basic valve must be invoked once. The pipeline does this by creating an instance of the ValveContext interface. The ValveContext is implemented as an inner class of the pipeline so that the ValveContext has access to all members of the pipeline. The most important method of the ValveContext interface is invokeNext:
现在,管道必须确保所有添加到它的阀门以及它的基本阀门都会被调用一次。
管道通过创建 ValveContext
接口的一个实例来实现这一点。
ValveContext
作为管道的内部类实现,以便 ValveContext
可以访问管道的所有成员。
ValveContext
接口最重要的方法是 invokeNext
:
public void invokeNext(Request request, Response response)
throws IOException, ServletException
After creating an instance of ValveContext, the pipeline calls the invokeNext method of the ValveContext. The ValveContext will first invoke the first valve in the pipeline and the first valve will invoke the next valve before the first valve does its task. The ValveContext passes itself to each valve so that the valve can call the invokeNext method of the ValveContext. Here is the signature of the invoke method of the Valve interface.
在创建 ValveContext
实例后,管道调用 ValveContext
的 invokeNext
方法。
ValveContext
将首先调用管道中的第一个阀门,然后第一个阀门在执行其任务之前调用下一个阀门。
ValveContext
将自身传递给每个阀门,以便阀门可以调用 ValveContext
的 invokeNext
方法。
以下是Valve接口的 invoke
方法的签名。
public void invoke(Request request, Response response,
ValveContext ValveContext) throws IOException, ServletException
An implementation of a valve's invoke method will be something like the following.
阀门 invoke
方法的实现类似于下面的内容。
public void invoke(Request request, Response response,
ValveContext valveContext) throws IOException, ServletException {
// Pass the request and response on to the next valve in our pipeline
valveContext.invokeNext(request, response);
// now perform what this valve is supposed to do
...
}
The org.apache.catalina.core.StandardPipeline class is the implementation of Pipeline in all containers. In Tomcat 4, this class has an inner class called StandardPipelineValveContext that implements the ValveContext interface. Listing 5.1 presents the StandardPipelineValveContext class.
org.apache.catalina.core.StandardPipeline
类是所有容器中 Pipeline
的实现。
在 Tomcat
4中,这个类有一个内部类叫做 StandardPipelineValveContext
,实现了 ValveContext
接口。
图5.1展示了 StandardPipelineValveContext
类。
Listing 5.1: The StandardPipelineValveContext class in Tomcat 4
图5.1:Tomcat 4 中的 StandardPipelineValveContext
类
protected class StandardPipelineValveContext implements ValveContext {
protected int stage = 0;
public String getInfo() {
return info;
}
public void invokeNext(Request request, Response response)
throws IOException, ServletException {
int subscript = stage;
stage = stage + 1;
// Invoke the requested Valve for the current request thread
if (subscript < valves.length) {
valves[subscript].invoke(request, response, this);
}
else if ((subscript == valves.length) && (basic != null)) {
basic.invoke(request, response, this);
}
else {
throw new ServletException
(sm.getString("standardPipeline.noValve"));
}
}
}
The invokeNext method uses subscript and stage to remember which valve is being invoked. When first invoked from the pipeline's invoke method, the value of subscript is 0 and the value of stage is 1. Therefore, the first valve (array index 0) is invoked. The first valve in the pipeline receives the ValveContext instance and invokes its invokeNext method. This time, the value of subscript is 1 so that the second valve is invoked, and so on.
invokeNext
方法使用 subscript
和 stage
来记住正在被调用的阀门。
当首次从管道的 invoke 方法中调用时,subscript
的值为 0,stage 的值为 1。
因此,第一个阀门(数组索引为 0)被调用。
管道中的第一个阀门接收 ValveContext
实例并调用其 invokeNext
方法。
这时,subscript 的值为 1,因此第二个阀门被调用,依此类推。
When the invokeNext method is called from the last valve, the value of subscript is equal to the number of valves. As a result, the basic valve is invoked.
当从最后一个阀门调用 invokeNext
方法时,subscript
的值等于阀门的数量。
因此,基本阀门被调用。
Tomcat 5 removes the StandardPipelineValveContext class from StandardPipeline and instead relies on the org.apache.catalina.core.StandardValveContext class, which is presented in Listing 5.2.
Tomcat 5
将 StandardPipelineValveContext
类从 StandardPipeline
中移除,而是依赖于 org.apache.catalina.core.StandardValveContext
类,该类在清单 5.2 中给出。
Listing 5.2: The StandardValveContext class in Tomcat 5
清单 5.2:Tomcat 5
中的 StandardValveContext
类
package org.apache.catalina.core;
import java.io.IOException;
import javax.servlet.ServletException;
import org.apache.catalina.Request;
import org.apache.catalina.Response;
import org.apache.catalina.Valve;
import org.apache.catalina.ValveContext;
import org.apache.catalina.util.StringManager;
public final class StandardValveContext implements ValveContext {
protected static StringManager sm =
StringManager.getManager(Constants.Package);
protected String info =
"org.apache.catalina.core.StandardValveContext/1.0";
protected int stage = 0;
protected Valve basic = null;
protected Valve valves[] = null;
public String getInfo() {
return info;
}
public final void invokeNext(Request request, Response response)
throws IOException, ServletException {
int subscript = stage;
stage = stage + 1;
// Invoke the requested Valve for the current request thread
if (subscript < valves.length) {
valves[subscript].invoke(request, response, this);
}
else if ((subscript == valves.length) && (basic != null)) {
basic.invoke(request, response, this);
}
else {
throw new ServletException
(sm.getString("standardPipeline.noValve"));
}
}
void set(Valve basic, Valve valves[]) {
stage = 0;
this.basic = basic;
this.valves = valves;
}
}
Can you see the similarities between the StandardPipelineValveContext class in Tomcat 4 and the StandardValveContext class in Tomcat 5?
你能看出Tomcat 4
中的StandardPipelineValveContext
类和Tomcat 5
中的StandardValveContext
类之间的相似之处吗?
We will now explain the Pipeline, Valve, and ValveContext interfaces in more detail. Also discussed is the org.apache.catalina.Contained interface that a valve class normally implements.
我们将详细解释 Pipeline
、Valve和ValveContext
接口。
还讨论了一个阀门类通常实现的org.apache.catalina.Contained
接口。
The Pipeline Interface(管道接口)
The first method of the Pipeline interface that we mentioned was the invoke method, which a container calls to start invoking the valves in the pipeline and the basic valve. The Pipeline interface allows you to add a new valve through its addValve method and remove a valve by calling its removeValve method. Finally, you use its setBasic method to assign a basic valve to a pipeline and its getBasic method to obtain the basic valve. The basic valve, which is invoked last, is responsible for processing the request and the corresponding response. The Pipeline interface is given in Listing 5.3.
我们提到的 Pipeline
接口的第一个方法是invoke
方法,容器调用该方法来开始调用管道中的阀门和基本阀门。
Pipeline
接口允许通过其 addValve
方法添加新的阀门,并通过调用其 removeValve
方法来删除阀门。
最后,您可以使用其 setBasic
方法将基本阀门分配给管道,使用其 getBasic
方法获取基本阀门。
最后调用的基本阀门负责处理请求和相应。Pipeline
接口如代码清单5.3所示。
Listing 5.3: The Pipeline interface
代码清单5.3:Pipeline
接口
package org.apache.catalina;
import java.io.IOException;
import javax.servlet.ServletException;
public interface Pipeline {
public Valve getBasic();
public void setBasic(Valve valve);
public void addValve(Valve valve);
public Valve[] getValves();
public void invoke(Request request, Response response)
throws IOException, ServletException;
public void removeValve(Valve valve);
}
The Valve Interface(阀门接口)
The Valve interface represents a valve, the component responsible for processing a request. This interface has two methods: invoke and getInfo. The invoke method has been discussed above. The getInfo method returns information about the valve implementation. The Valve interface is given in Listing 5.4.
阀门接口表示阀门,即负责处理请求的组件。
该接口有两个方法:invoke
和 getInfo
。
invoke
方法已在上文讨论过。
getInfo
方法返回阀门实现的相关信息。阀门接口见清单 5.4。
Listing 5.4: The Valve interface
清单 5.4: 阀门接口
package org.apache.catalina;
import java.io.IOException;
import javax.servlet.ServletException;
public interface Valve {
public String getInfo();
public void invoke(Request request, Response response,
ValveContext context) throws IOException, ServletException;
}
The ValveContext Interface(阀门上下文接口)
This interface has two methods: the invokeNext method, which has been discussed above, and the getInfo method, which returns information about the ValveContext implementation. The ValveContext interface is given in Listing 5.5.
该接口有两个方法:invokeNext
方法(上文已讨论过)和 getInfo
方法(返回 ValveContext
实现的相关信息)。ValveContext
接口见清单 5.5。
Listing 5.5: The ValveContext interface
清单 5.5:ValveContext
接口
package org.apache.catalina;
import java.io.IOException;
import javax.servlet.ServletException;
public interface ValveContext {
public String getInfo();
public void invokeNext(Request request, Response response)
throws IOException, ServletException;
}
The Contained Interface(内置界面)
A valve class can optionally implement the org.apache.catalina.Contained interface. This interface specifies that the implementing class is associated with at most one container instance. The Contained interface is given in Listing 5.6.
阀门类可选择实现 org.apache.catalina.Contained
接口。
该接口指定实现类最多与一个容器实例相关联。
清单 5.6 给出了 Contained
接口。
Listing 5.6: The Contained interface
清单 5.6:Contained
接口
package org.apache.catalina;
public interface Contained {
public Container getContainer();
public void setContainer(Container container);
he Wrapper Interface(封装接口)
The org.apache.catalina.Wrapper interface represents a wrapper. A wrapper is a container representing an individual servlet definition. The Wrapper interface extends Container and adds a number of methods. Implementations of Wrapper are responsible for managing the servlet life cycle for their underlying servlet class, i.e. calling the init, service, and destroy methods of the servlet. Since a wrapper is the lowest level of container, you must not add a child to it. A wrapper throws an IllegalArgumantException if its addChild method is called.
org.apache.catalina.Wrapper
接口表示一个包装器。
包装器是表示单个servlet定义的容器。
Wrapper
接口扩展 了 Container
并添加了一些方法。
Wrapper
的实现负责管理其底层 servlet
类的 servlet
生命周期,即调用 servlet
的 init
、service
和 destroy
方法。
由于包装器是容器的最低级别,因此不能向其添加子级。
如果调用其 addChild
方法,包装器将抛出 IllegalArgumentException
异常。
Important methods in the Wrapper interface include allocate and load. The allocate method allocates an initialed instance of the servlet the wrapper represents. The allocate method must also take into account whether or not the servlet implements the javax.servlet.SingleThreadModel interface, but we will discuss this later in Chapter 11. The load method loads and initializes an instance of the servlet the wrapper represents. The signatures of the allocate and load methods are as follows.
封装程序接口中的重要方法包括分配和加载。
allocate
方法为包装器所代表的 servlet
分配一个初始化实例。
分配方法还必须考虑到 servlet
是否实现了 javax.servlet.SingleThreadModel
接口,但我们将在第 11 章后面讨论这个问题。
加载方法加载并初始化包装器所代表的 servlet
实例。allocate
和 load
方法的签名如下。
public javax.servlet.Servlet allocate() throws
javax.servlet.ServletException;
public void load() throws javax.servlet.ServletException;
The other methods will be covered in Chapter 11 when we discuss the org.apache.catalina.core.StandardWrapper class.
其他方法将在第 11 章讨论 org.apache.catalina.core.StandardWrapper
类时介绍。
The Context Interface(语境界面)
A context is a container that represents a web application. A context usually has one or more wrappers as its child containers.
Important methods include addWrapper, createWrapper, etc. This interface will be covered in more detail in Chapter 12.
上下文是表示网络应用程序的容器。
一个上下文通常有一个或多个包装器作为其子容器。
重要的方法包括 addWrapper
、createWrapper
等。
第 12 章将详细介绍该接口。
The Wrapper Application(包装应用程序)
This application demonstrates how to write a minimal container module. The core class of this application is ex05.pyrmont.core.SimpleWrapper, an implementation of the Wrapper interface. The SimpleWrapper class contains a Pipeline (implemented by the ex05.pyrmont.core.SimplePipeline class) and uses a Loader (implemented by the ex05.pyrmont.core.SimpeLoader) to load the servlet. The Pipeline contains a basic valve (ex05.pyrmont.core.SimpleWrapperValve) and two additional valves (ex05.pyrmont.core.ClientIPLoggerValve and ex05.pyrmont.core.HeaderLoggerValve). The class diagram of the application is given in Figure 5.3.
这个应用程序演示了如何编写一个最小化的容器模块。
这个应用程序的核心类是ex05.pyrmont.core.SimpleWrapper
,它是Wrapper接口的一个实现。
SimpleWrapper
类包含一个 Pipeline
(由 ex05.pyrmont.core.SimplePipeline
类实现),并使用一个 Loader
(由ex05.pyrmont.core.SimpeLoader
实现)来加载 servlet
。
Pipeline
包含一个基本的阀门(ex05.pyrmont.core.SimpleWrapperValve
)和两个附加的阀门(ex05.pyrmont.core.ClientIPLoggerValve
和ex05.pyrmont.core.HeaderLoggerValve
)。
应用程序的类图如图5.3所示。
Figure 5.3: The Class Diagram of the Wrapper Application
图5.3:包装器应用程序的类图
Note The container uses Tomcat 4's default connector.
注意:容器使用Tomcat 4的默认连接器。
The wrapper wraps the ModernServlet that you have used in the previous chapters. This application proves that you can have a servlet container consisting only of one wrapper. All classes are not fully developed, implementing only methods that must be present in the class. Let's now look at the classes in detail.
包装器包装了你在之前章节中使用过的 ModernServlet
。
这个应用程序证明了你可以只有一个包装器的servlet
容器。
所有的类都没有完全开发,只实现了必须存在于类中的方法。现在让我们详细看一下这些类。
ex05.pyrmont.core.SimpleLoader
The task of loading servlet classes in a container is assigned to a Loader implementation. In this application, the SimpleLoader class is that implementation. It knows the location of the servlet class and its getClassLoader method returns a java.lang.ClassLoader instance that searches the servlet class location. The SimpleLoader class declares three variables. The first is WEB_ROOT, which points to the directory where the servlet class is to be found.
在容器中加载 servlet
类的任务被分配给了一个Loader实现。
在这个应用程序中,SimpleLoader
类就是这个实现。
它知道servlet类的位置,它的 getClassLoader
方法返回一个java.lang.ClassLoader
实例,用于搜索servlet类的位置。
SimpleLoader
类声明了三个变量。第一个是 WEB_ROOT
,它指向servlet类所在的目录。
public static final String WEB_ROOT =
System.getProperty("user.dir") + File.separator + "webroot";
The other two variables are object references of type ClassLoader and Container:
另外两个变量是 ClassLoader
和 Container 类型的对象引用:
ClassLoader classLoader = null;
Container container = null;
The SimpleLoader class's constructor initializes the class loader so that it is ready to be returned to the SimpleWrapper instance.
SimpleLoader
类的构造函数会初始化类加载器,以便将其返回给 SimpleWrapper
实例。
public SimpleLoader() {
try {
URL[] urls = new URL[l];
URLStreamHandler streamHandler = null;
File classPath = new File(WEB_ROOT);
String repository = (new URL("file", null,
classPath.getCanonicalPath() + File.separator)).toString() ;
urls[0] = new URL(null, repository, streamHandler);
classLoader = new URLClassLoader(urls);
}
catch (IOException e) {
System.out.println(e.toString() );
}
}
The code in the constructor has been used to initialize class loaders in the applications in previous chapters and won't be explained again.
构造函数中的代码已在前几章的应用程序中用于初始化类加载器,不再赘述。
The container variable represents the container associated with this loader.
容器变量表示与该装载程序相关联的容器。
Note Loaders will be discussed in detail in Chapter 8.
注 加载器将在第 8 章中详细讨论。
ex05.pyrmont.core.SimplePipeline
The SimplePipeline class implements the org.apache.catalina.Pipeline interface. The most important method in this class is the invoke method, which contains an inner class called SimplePipelineValveContext. The SimplePipelineValveContext implements the org.apache.catalina.ValveContext interface and has been explained in the section "Pipelining Tasks" above.
SimplePipeline
类实现了org.apache.catalina.Pipeline
接口。
这个类中最重要的方法是invoke
方法,其中包含一个名为SimplePipelineValveContext
的内部类。
SimplePipelineValveContext
实现了org.apache.catalina.ValveContex
t接口,并在上面的“管道任务”部分中进行了解释。
ex05.pyrmont.core.SimpleWrapper
This class implements the org.apache.catalina.Wrapper interface and provides implementation for the allocate and load methods. Among others, this class declares the following variables:
加载器变量是用于加载 servlet
类的加载器。
父变量代表此包装器的父容器。
这意味着该封装器可以是另一个容器(如 Context
)的子容器。
private Loader loader;
protected Container parent = null;
The loader variable is a Loader that is used to load the servlet class. The parent variable represents a parent container for this wrapper. This means that this wrapper can be a child container of another container, such as a Context.
加载器变量是用于加载 servlet
类的加载器。
父变量代表此包装器的父容器。
这意味着该封装器可以是另一个容器(如 Context
)的子容器。
Pay special attention to its getLoader method, which is given in Listing 5.7.
请特别注意清单 5.7 中的 getLoader
方法。
public Loader getLoader() {
if (loader != null)
return (loader);
if (parent != null)
return (parent.getLoader());
return (null);
}
The getLoader method returns a Loader that is used to load a servlet class. If the wrapper is associated with a Loader, this Loader will be returned. If not, it will return the Loader of the parent container. If no parent is available, the getLoader method returns null.
getLoader
方法返回一个 Loader
,用于加载一个 servlet
类。如果包装器与 Loader
关联,将返回该 Loader
。
如果没有关联,则返回父容器的 Loader
。
如果没有可用的父容器,则 getLoader
方法返回 null
。
The SimpleWrapper class has a pipeline and sets a basic valve for the pipeline. You do this in the SimpleWrapper class's constructor, given in Listing 5.8.
SimpleWrapper
类具有一个 pipeline
,并为该 pipeline
设置一个基本的 valve
。
您可以在 SimpleWrapper
类的构造函数中执行此操作,如图5.8所示。
Listing 5.8: The SimpleWrapper class's constructor
图5.8:SimpleWrapper
类的构造函数
public SimpleWrapper() {
pipeline.setBasic(new SimpleWrapperValve());
}
Here, pipeline is an instance of SimplePipeline as declared in the class:
这里,pipeline
是类中声明的 SimplePipeline
的实例:
private SimplePipeline pipeline = new SimplePipeline(this);
ex05.pyrmont.core.SimpleWrapperValve
The SimpleWrapperValve class is the basic valve that is dedicated to processing the request for the SimpleWrapper class. It implements the org.apache.catalina.Valve interface and the org.apache.catalina.Contained interface. The most important method in the SimpleWrapperValve is the invoke method, given in Listing 5.9.
SimpleWrapperValve
类是专门用于处理 SimpleWrapper
类请求的基本阀门。
它实现了 org.apache.catalina.Valve
接口和 org.apache.catalina.Contained
接口。
SimpleWrapperValve
中最重要的方法是 invoke
方法,如5.9节所示。
Listing 5.9: The SimpleWrapperValve class's invoke method
5.9节:SimpleWrapperValve
类的 invoke
方法
public void invoke(Request request, Response response,
ValveContext valveContext)
throws IOException, ServletException {
SimpleWrapper wrapper = (SimpleWrapper) getContainer();
ServletRequest sreq = request.getRequest();
ServletResponse sres = response.getResponse();
Servlet servlet = null;
HttpServletRequest hreq = null;
if (sreq instanceof HttpServletRequest)
hreq = (HttpServletRequest) sreq;
HttpServletResponse hres = null;
if (sres instanceof HttpServletResponse)
hres = (HttpServletResponse) sres;
// Allocate a servlet instance to process this request
try {
servlet = wrapper.allocate();
if (hres!=null && hreq!=null) {
servlet.service(hreq, hres);
}
else {
servlet.service(sreq, sres);
}
}
catch (ServletException e) {
}
}
Because SimpleWrapperValve is used as a basic valve, its invoke method does not need to call the invokeNext method of the ValveContext passed to it. The invoke method calls the allocate method of the SimpleWrapper class to obtain an instance of the servlet the wrapper represents. It then calls the servlet's service method. Notice that the basic valve of the wrapper's pipeline invokes the servlet's service method, not the wrapper itself.
由于 SimpleWrapperValve
被用作基本阀门,因此其 invoke
方法无需调用传递给它的 ValveContext
的 invokeNext
方法。
invoke
方法调用 SimpleWrapper
类的 allocate
方法,以获得包装器所代表的 servlet
的实例。
然后它将调用 servlet
的服务方法。
请注意,封装器管道的基本阀门调用的是 servlet
的服务方法,而不是封装器本身。
ex05.pyrmont.valves.ClientIPLoggerValve
The ClientIPLoggerValve class is a valve that prints the client's IP address to the console. This class is given in Listing 5.10.
ClientIPLoggerValve
类是一个将客户端 IP 地址打印到控制台的阀门。
该类如清单 5.10 所示。
Listing 5.10: The ClientIPLoggerValve class
清单 5.10:客户端 IPLoggerValve
类
package ex05.pyrmont.valves;
import java.io.IOException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletException;
import org.apache.catalina.Request;
import org.apache.catalina.Response;
import org.apache.catalina.Valve;
import org.apache.catalina.ValveContext;
import org.apache.catalina.Contained;
import org.apache.catalina.Container;
public class ClientIPLoggerValve implements Valve, Contained {
protected Container container;
public void invoke(Request request, Response response,
ValveContext valveContext) throws IOException, ServletException {
// Pass this request on to the next valve in our pipeline
valveContext.invokeNext(request, response);
System.out.println("Client IP Logger Valve");
ServletRequest sreq = request.getRequest();
System.out.println(sreq.getRemoteAddr());
System, out.println("-----------------------------------");
}
public String getInfo() {
return null;
}
public Container getContainer() {
return container;
}
public void setContainer(Container container) {
this.container = container;
}
}
Pay attention to the invoke method. The first thing the invoke method does is call the invokeNext method of the valve context to invoke the next valve in the pipeline, if any. It then prints a few lines of string including the output of the getRemoteAddr method of the request object.
请注意 invoke
方法。
invoke
方法要做的第一件事就是调用阀门上下文的 invokeNext 方法,以调用管道中的下一个阀门(如果有的话)。
然后,它会打印几行字符串,包括请求对象的 getRemoteAddr
方法的输出。
ex05.pyrmont.valves.HeaderLoggerValve
This class is very similar to the ClientIPLoggerValve class. The HeaderLoggerValve class is a valve that prints the request header to the console. This class is given in Listing 5.11.
该类与 ClientIPLoggerValve
类非常相似。HeaderLoggerValve
类是一个将请求头打印到控制台的阀门。该类在清单 5.11 中给出。
Listing 5.11: The HeaderLoggerValve class
清单 5.11:HeaderLoggerValve
类
package ex05.pyrmont.valves;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletRequest;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.apache.catalina.Request;
import org.apache.catalina.Response;
import org.apache.catalina.Valve;
import org.apache.catalina.ValveContext;
import org.apache.catalina.Contained;
import org.apache.catalina.Container;
public class HeaderLoggerValve implements Valve, Contained {
protected Container container;
public void invoke(Request request, Response response,
ValveContext valveContext) throws IOException, ServletException {
// Pass this request on to the next valve in our pipeline
valveContext.invokeNext(request, response);
System.out.println("Header Logger Valve");
ServletRequest sreq = request.getRequest();
if (sreq instanceof HttpServletRequest) {
HttpServletRequest hreq = (HttpServletRequest) sreq;
Enumeration headerNames = hreq.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement().toString();
String headerValue = hreq.getHeader(headerName);
System.out.println(headerName + ":" + headerValue);
}
}
else
System.out.println("Not an HTTP Request");
System.out.println ("-----------------------------------");
}
public String getInfo() {
return null;
}
public Container getContainer() {
return container;
}
public void setContainer(Container container) {
this.container = container;
}
}
Again, pay special attention to the invoke method. The first thing the invoke method does is call the invokeNext method of the valve context to invoke the next valve in the pipeline, if any. It then prints the values of some headers.
请再次特别注意 invoke
方法。
invoke
方法要做的第一件事就是调用阀门上下文的 invokeNext
方法,以调用管道中的下一个阀门(如果有的话)。然后,它会打印一些标头的值。
ex05.pyrmont.startup.Bootstrap1
The Bootstrap1 class is used to start the application. It is given in Listing 5.12.
Bootstrap1
类用于启动应用程序。
该类如清单 5.12 所示。
Listing 5.12: The Bootstrap1 class
清单 5.12:Bootstrap1
类
package ex05.pyrmont.startup;
import ex05.pyrmont.core.SimpleLoader;
import ex05.pyrmont.core.SimpleWrapper;
import ex05.pyrmont.valves.ClientlPLoggerValve;
import ex05.pyrmont.valves.HeaderLoggerValve;
import org.apache.catalina.Loader;
import org.apache.catalina.Pipeline;
import org.apache.catalina.Valve;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.http.HttpConnector;
public final class Bootstrap1 {
public static void main(String[] args) {
HttpConnector connector = new HttpConnector();
Wrapper wrapper = new SimpleWrapper();
wrapper.setServletClass("ModernServlet");
Loader loader = new SimpleLoader();
Valve valve1 = new HeaderLoggerValve();
Valve valve2 = new ClientIPLoggerValve();
wrapper.setLoader(loader);
((Pipeline) wrapper).addValve(valve1);
((Pipeline) wrapper).addValve(valve2);
connector.setContainer(wrapper);
try {
connector.initialize();
connector.start();
// make the application wait until we press a key.
System.in.read();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
After creating an instance of HttpConnector and SimpleWrapper, the main method of the Bootstrap class assigns ModernServlet to the setServletClass method of SimpleWrapper, telling the wrapper the name of the class to be loaded.
创建 HttpConnector
和 SimpleWrapper
的实例后,Bootstrap
类的主方法会将 ModernServlet
指派给 SimpleWrapper
的 setServletClass
方法,告诉包装器要加载的类的名称。
wrapper.setServletClass("ModernServlet");
It then creates a loader and two valves and sets the loader to the wrapper:
然后,它会创建一个装载器和两个阀门,并将装载器设置为包装器:
Loader loader = new SimpleLoader();
Valve valve1 = new HeaderLoggerValve();
Valve valve2 = new ClientIPLoggerValve();
wrapper.setLoader(loader);
The two valves are then added to the wrapper's pipeline.
然后将这两个阀门添加到包装器的管道中。
((Pipeline) wrapper).addValve(valve1);
((Pipeline) wrapper).addValve(valve2);
Finally, the wrapper is set as the container of the connector and the connector is initialized and started.
最后,包装器被设置为连接器的容器,连接器被初始化并启动。
connector.setContainer(wrapper);
try {
connector.initialize();
connector.start();
The next line allows the user to stop the application by typing Enter in the console.
下一行允许用户在控制台中输入 Enter
来停止应用程序。
// make the application wait until we press Enter.
System.in.read();
Running the Application
To run the application in Windows, from the working directory, type the following:
要在 Windows
中运行应用程序,请在工作目录中键入以下内容:
java -classpath ./lib/servlet.jar;./ ex05.pyrmont.startup.Bootstrap1
In Linux, you use a colon to separate two libraries.
在 Linux
中,使用冒号分隔两个库。
java -classpath ./lib/servlet.jar:./ ex05.pyrmont.startup.Bootstrap1
You can invoke the servlet using the following URL:
您可以使用以下 URL
调用 servlet
:
The browser will display the response from the ModernServlet. Note also that something similar to the following is printed on the console.
浏览器将显示来自 ModernServlet
的响应。还请注意,控制台会打印出与下面类似的内容。
ModernServlet -- init
Client IP Logger Valve
127.0.0.1
-----------------------------------
Header Logger Valve
accept:image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,
application/vnd.ms-excel, application/msword, application/vnd.mspowerpoint, */*
accept-language:en-us
accept-encoding:gzip, deflate
user-agent:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR
1.1.4322)
host:localhost:8080
connection:Keep-Alive
-----------------------------------
The Context Application(背景应用)
In the first application in this chapter, you learned how to deploy a simple web application consisting of only one wrapper. This application only had one servlet. While it is possible that some applications might only need one single servlet, most web applications require more. In such applications, you need a different type of container than a wrapper. You need a context.
在本章的第一个应用中,您学习了如何部署一个简单的Web应用程序,该应用程序只包含一个包装器。
这个应用程序只有一个 servlet
。
虽然有些应用程序可能只需要一个单独的servlet,但大多数Web应用程序需要更多。
在这种应用程序中,您需要一个不同类型的容器来替代包装器。您需要一个上下文。
This second application demonstrates how to use a context with two wrappers that wrap two servlet classes. Having more than one wrapper, you need a mapper, a component that helps a container--in this case a context- select a child container that will process a particular request.
这个第二个应用程序演示了如何使用一个上下文和两个包装器来包装两个 servlet
类。
由于有多个包装器,您需要一个映射器,这是一个帮助容器(在本例中是上下文)选择一个特定请求的子容器的组件。
Note A mapper can only be found in Tomcat 4. Tomcat 5 uses another approach to finding a child container.
注意:映射器只能在Tomcat 4中找到。Tomcat 5使用另一种方法来找到子容器。
In this application your mapper is an instance of the ex05.pyrmont.core.SimpleContextMapper class, which implements the org.apache.catalina.Mapper interface in Tomcat 4. A container can also use multiple mappers to support multiple protocols. In this case, one mapper supports one request protocol. For example, a container may have a mapper for the HTTP protocol and another mapper for the HTTPS protocol. Listing 5.13 offers the Mapper interface in Tomcat 4.
在这个应用程序中,您的映射器是ex05.pyrmont.core.SimpleContextMapper
类的一个实例,在Tomcat 4
中实现了org.apache.catalina.Mapper
接口。
容器还可以使用多个映射器来支持多个协议。在这种情况下,一个映射器支持一个请求协议。
例如,一个容器可能有一个用于HTTP协议的映射器,另一个用于HTTPS协议的映射器。
图5.13展示了Tomcat 4中的 Mapper
接口。
Listing 5.13: The Mapper interface
图5.13:Mapper
接口
package org.apache.catalina;
public interface Mapper {
public Container getContainer();
public void setContainer(Container container);
public String getProtocol();
public void setProtocol(String protocol);
public Container map(Request request, boolean update);
}
The getContainer method returns the container this mapper is associated with and the setContainer method is used to associate a container with this mapper. The getProtocol method returns the protocol this mapper is responsible for and the setProtocol method is used to assign the name of the protocol this mapper is responsible for. The map method returns a child container that will process a particular request.
getContainer
方法返回与该映射器关联的容器,setContainer
方法用于将容器与该映射器关联。
getProtocol
方法返回该映射器负责的协议,setProtocol
方法用于指定该映射器负责的协议名称。
map
方法返回一个子容器,该子容器将处理特定的请求。
Figure 5.4 presents the class diagram of this application.
图5.4展示了该应用程序的类图。
Figure 5.4: The Context application class diagram.
图5.4:上下文应用程序类图。
The SimpleContext class represents a context. It uses the SimpleContextMapper as its mapper and SimpleContextValve as its basic valve. Two valves, ClientIPLoggerValve and HeaderLoggerValve, are added to the context. Two wrappers, each represented by SimpleWrapper, are added as child containers of the context. The wrappers use SimpleWrapperValve as their basic valve but do not have additional valves.
SimpleContext
类表示一个上下文。
它使用 SimpleContextMapper
作为其映射器,SimpleContextValve
作为其基本阀门。
两个阀门,ClientIPLoggerValve
和 HeaderLoggerValve
,被添加到上下文中。
两个包装器,每个由 SimpleWrapper
表示,作为上下文的子容器添加。
这些包装器使用 SimpleWrapperValve
作为其基本阀门,但没有其他阀门。
The Context application uses the same loader and the two valves. However, the loader and valves are associated with the context, not a wrapper. This way, the loader can be used by both wrappers. The context is assigned as the container for the connector. Therefore, the connector will call the invoke method of the context every time it receives an HTTP request. The rest is not hard to figure out if you recall our discussion above:
上下文应用程序使用相同的加载器和两个阀门。
然而,加载器和阀门与上下文相关联,而不是包装器。
这样,加载器可以被两个包装器使用。
上下文被分配为连接器的容器。
因此,每当连接器接收到一个HTTP请求时,连接器将调用上下文的 invoke
方法。
如果你回想一下我们之前的讨论,剩下的部分就不难理解:
- A container has a pipeline. The container's invoke method calls the pipeline's invoke method.
- The pipeline's invoke method invokes all the valves added to its container and then calls its basic valve's invoke method.
- In a wrapper, the basic valve is responsible to load the associated servlet class and respond to the request.
- In a context with child containers, the basic valve uses a mapper to find a child container that is responsible for processing the request. If a child container is found, it calls the invoke method of the child container. It then goes back to Step 1.
- 一个容器有一个管道。容器的invoke方法调用管道的invoke方法。
- 管道的
invoke
方法调用其容器中添加的所有阀门,然后调用其基本阀门的invoke
方法。 - 在包装器中,基本阀门负责加载关联的servlet类并响应请求。
- 在具有子容器的上下文中,基本阀门使用映射器找到负责处理请求的子容器。如果找到子容器,则调用子容器的invoke方法。然后返回到步骤1。
Now let's see the order of processing in the implementation.
现在让我们看看实现中的处理顺序。
The SimpleContext class's invoke method calls the pipeline's invoke method.
SimpleContext
类的 invoke
方法调用管道的 invoke
方法。
public void invoke(Request request, Response response)
throws IOException, ServletException {
pipeline.invoke(request, response);
}
As explained in the "Pipelining Tasks" section above, the code invokes all the valves added to it and then calls the basic valve's invoke method. In SimpleContext, the SimpleContextValve class represents the basic valve. In its invoke method, SimpleContextValve uses the context's mapper to find a wrapper:
如上文 "流水线任务 "部分所述,代码会调用添加到其中的所有阀门,然后调用基本阀门的 invoke
方法。
在 SimpleContext
中,SimpleContextValve
类代表基本阀门。在其 invoke
方法中,SimpleContextValve
使用上下文映射器来查找封装器:
// Select the Wrapper to be used for this Request
Wrapper wrapper = null;
try {
wrapper = (Wrapper) context.map(request, true);
}
If a wrapper is found, its invoke method is called.
如果找到封装器,就会调用其 invoke
方法。
wrapper.invoke(request, response);
A wrapper in this application is represented by the SimpleWrapper class. Here is the invoke method of SimpleWrapper, which is exactly the same as the SimpleContext class's invoke method.
本应用程序中的封装器由 SimpleWrapper
类表示。下面是 SimpleWrapper
的 invoke
方法,它与 SimpleContext
类的 invoke
方法完全相同。
public void invoke(Request request, Response response)
throws IOException, ServletException {
pipeline.invoke(request, response);
}
The pipeline is an instance of SimplePipeline whose invoke method has been listed above. The wrappers in this application do not have valves except the basic valve, which is an instance of SimpleWrapperValve. The wrapper's pipeline calls the SimpleWrapperValve class's invoke method that allocates a servlet and calls its service method, as explained in the section "The Wrapper Application" above.
管道是 SimplePipeline
的一个实例,其 invoke
方法已在上面列出。
此应用程序中的包装器除了基本阀门外没有其他阀门,基本阀门是 SimpleWrapperValve
的一个实例。
包装器的管道调用 SimpleWrapperValve
类的 invoke
方法,该方法分配一个 servlet
并调用其 service
方法,如上一节“包装器应用程序”中所解释的。
Note that the wrapper is not associated with a loader, but the context is. Therefore, the getLoader method of the SimpleWrapper class returns the parent's loader.
注意,包装器与加载器没有关联,但上下文有关联。因此,SimpleWrapper类的getLoader方法返回父级的加载器。
The four classes--SimpleContext, SimpleContextValve, SimpleContextMapper, and Bootstrap2—have not been explained in the previous section and will be discussed below.
这四个类——SimpleContext
、SimpleContextValve
、SimpleContextMapper
和 Bootstrap2
——在前一节中没有解释,将在下面进行讨论。
ex05.pyrmont.core.SimpleContextValve
This class represents the basic valve for SimpleContext. Its most important method is the invoke method, given in Listing 5.14.
该类表示 SimpleContext
的基本阀门。它最重要的方法是 invoke
方法,如清单 5.14 所示。
Listing 5.14: The SimpleContextValve class's invoke method
清单 5.14:SimpleContextValve
类的 invoke
方法
public void invoke(Request request, Response response,
ValveContext valveContext)
throws IOException, ServletException {
// Validate the request and response object types
if (!(request.getRequest() instanceof HttpServletRequest) ||
!(response.getResponse() instanceof HttpServletResponse)) {
return;
}
// Disallow any direct access to resources under WEB-INF or META-INF
HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
String contextPath = hreq.getContextPath();
String requestURI = ((HttpRequest) request).getDecodedRequestURI();
String relativeURI =
requestURI.substring(contextPath.length()).toUpperCase();
Context context = (Context) getContainer();
// Select the Wrapper to be used for this Request
Wrapper wrapper = null;
try {
wrapper = (Wrapper) context.map(request, true);
}
catch (IllegalArgumentException e) {
badRequest(requestURI, (HttpServletResponse)
response.getResponse());
return; }
if (wrapper == null) {
notFound(requestURI, (HttpServletResponse) response.getResponse());
return; }
// Ask this Wrapper to process this Request
response.setContext(context);
wrapper.invoke(request, response);
}
ex05.pyrmont.core.SimpleContextMapper
The SimpleContextMapper class, given in Listing 5.15, implements the org.apache.catalina.Mapper interface in Tomcat 4 and is designed to be associated with an instance of SimpleContext.
清单 5.15 中的 SimpleContextMapper
类实现了 Tomcat 4 中的 org.apache.catalina.Mapper
接口,设计用于与 SimpleContext
实例关联。
Listing 5.15: The SimpleContext class
清单 5.15:SimpleContext
类
package ex05.pyrmont.core;
import javax.servlet.http.HttpServletRequest;
import org.apache.catalina.Container;
import org.apache.catalina.HttpRequest;
import org.apache.catalina.Mapper;
import org.apache.catalina.Request;
import org.apache.catalina.Wrapper;
public class SimpleContextMapper implements Mapper {
private SimpleContext context = null;
public Container getContainer() {
return (context);
}
public void setContainer(Container container) {
if (!(container instanceof SimpleContext))
throw new IllegalArgumentException
("Illegal type of container");
context = (SimpleContext) container;
}
public String getProtocol() {
return null;
}
public void setProtocol(String protocol) { }
public Container map(Request request, boolean update) {
// Identify the context-relative URI to be mapped
String contextPath =
((HttpServletRequest) request.getRequest()).getContextPath();
String requestURI = ((HttpRequest) request).getDecodedRequestURI();
String relativeURI = requestURI.substring(contextPath.length());
// Apply the standard request URI mapping rules from
// the specification Wrapper wrapper = null;
String servletPath = relativeURI;
String pathInfo = null;
String name = context.findServletMapping(relativeURI);
if (name != null)
wrapper = (Wrapper) context.findChild(name);
return (wrapper);
}
}
The setContainer method throws an IllegalArgumentException if you pass a container that is not an instance of SimpleContext. The map method returns a child container (a wrapper) that is responsible for processing the request. The map method is passed two arguments, a request object and a boolean. This implementation ignores the second argument. What the method does is retrieve the context path from the request object and uses the context's findServletMapping method to obtain a name associated with the path. If a name is found, it uses the context's findChild method to get an instance of Wrapper.
setContainer
方法如果传入的容器不是SimpleContext
的实例,会抛出 IllegalArgumentException
异常。
map
方法返回一个子容器(包装器),负责处理请求。
map
方法接受两个参数,一个是请求对象,一个是布尔值。
这个实现忽略了第二个参数。
方法的作用是从请求对象中获取上下文路径,并使用上下文的 findServletMapping
方法获取与路径关联的名称。
如果找到了名称,就使用上下文的 findChild
方法获取 Wrapper
的实例。
ex05.pyrmont.core.SimpleContext
The SimpleContext class is the context implementation for this application. It is the main container that is assigned to the connector. However, processing of each individual servlet is performed by a wrapper. This application has two servlets, PrimitiveServlet and ModernServlet, and thus two wrappers. Each wrapper has a name. The name of the wrapper for PrimitiveServlet is Primitive, and the wrapper for ModernServlet is Modern. For SimpleContext to determine which wrapper to invoke for every request, you must map request URL patterns with wrappers' names. In this application, we have two URL patterns that can be used to invoke the two wrappers. The first pattern is /Primitive, which is mapped to the wrapper Primitive. The second pattern is /Modern, which is mapped to the wrapper Modern. Of course, you can use more than one pattern for a given servlet. You just need to add those patterns.
SimpleContext
类是这个应用程序的上下文实现。
它是分配给连接器的主要容器。
然而,每个单独的 servlet
的处理是由一个包装器执行的。
这个应用程序有两个 servlet
,PrimitiveServlet
和 ModernServlet
,因此有两个包装器。
每个包装器都有一个名称。
PrimitiveServlet
的包装器名称是 Primitive
,ModernServlet
的包装器是 Modern
。
为了让 SimpleContext
确定为每个请求调用哪个包装器,你必须将请求的URL模式与包装器的名称进行映射。
在这个应用程序中,有两个URL模式可以用来调用这两个包装器。
第一个模式是 /Primitive
,映射到包装器 Primitive
。
第二个模式是 u
,映射到包装器 Modern
。
当然,你可以为给定的servlet
使用多个模式。
你只需要添加这些模式。
There are a number of methods from the Container and Context interfaces that SimpleContext must implement. Most methods are blank methods, however those methods related to mapping are given real code. These methods are as follows.
SimpleContext必须实现 Container
和 Context
接口中的许多方法。大多数方法都是空方法,但与映射相关的方法是真正的代码。这些方法如下。
- addServletMapping. Adds a URL pattern/wrapper name pair. You add every pattern that can be used to invoke the wrapper with the given name.
- findServletMapping. Obtains a wrapper name given a URL pattern. This method is used to find which wrapper should be invoked for a particular URL pattern. If the given pattern has not yet been registered using the addServletMapping, the method returns null.
- addMapper. Adds a mapper to the context. The SimpleContext class declares the mapper and mappers variables. mapper is for the default mapper and mappers contains all mappers for the SimpleContext instance. The first mapper added to this context becomes the default mapper.
- findMapper. Finds the correct mapper. In SimpleContext, this returns the default mapper.
- map. Returns the wrapper that is responsible for processing this request.
addServletMapping
。添加URL模式/包装器名称对。- 你需要添加可以用来调用具有给定名称的包装器的每个模式。
findServletMapping
。给定URL模式,获取包装器名称。- 这个方法用于查找应该为特定URL模式调用哪个包装器。
- 如果使用
addServletMapping
尚未注册给定模式,该方法将返回null。
addMapper
。向上下文添加一个映射器。SimpleContext
类声明了mapper
和mappers
变量。mapper
是默认映射器,mappers
包含SimpleContext
实例的所有映射器。- 添加到此上下文的第一个映射器将成为默认映射器。
findMapper
- 查找正确的映射器。
- 在
SimpleContext
中,这将返回默认映射器。
- map。返回负责处理此请求的包装器。
In addition, SimpleContext also provides the implementation of the addChild, findChild, and findChildren methods. The addChild method is used to add a wrapper to the context, the findChild method is used to find a wrapper given a name, and findChildren returns all wrappers in the SimpleContext instance.
此外,SimpleContext
还提供了 addChild
、findChild和findChildren
方法的实现。
addChild
方法用于向上下文添加包装器,findChild
方法用于根据名称查找包装器,findChildren
返回SimpleContext实例中的所有包装器。
ex05.pyrmont.startup.Bootstrap2
The Bootstrap2 class is used to start the application. This class is similar to BootStrap1 and is given in Listing 5.16.
Bootstrap2
类用于启动应用程序。这个类类似于BootStrap1,如清单5.16所示。
Listing 5.16: The Bootstrap2 class
清单5.16:Bootstrap2
类
package ex05.pyrmont.startup;
import ex05.pyrmont.core.SimpleContext;
import ex05.pyrmont.core.SimpleContextMapper;
import ex05.pyrmont.core.SimpleLoader;
import ex05.pyrmont.core.SimpleWrapper;
import ex05.pyrmont.valves.ClientIPLoggerValve;
import exO5.pyrmont.valves.HeaderLoggerValve;
import org.apache.catalina.Connector;
import org.apache.catalina.Context;
import org.apache.catalina.Loader;
import org.apache.catalina.Mapper;
import org.apache.catalina.Pipeline;
import org.apache.catalina.Valve;
import org.apache.catalina Wrapper;
import org.apache.catalina.connector.http.HttpConnector;
public final class Bootstrap2 {
public static void main(String[] args) {
HttpConnector connector = new HttpConnector();
Wrapper wrapper1 = new SimpleWrapper();
Wrapper1.setName("Primitive");
wrapper1.setServletClass("PrimitiveServlet");
Wrapper wrapper2 = new SimpleWrapper();
wrapper2.setName("Modern");
wrapper2.setServletClass("ModernServlet");
Context context = new SimpleContext();
context.addChild(wrapper1);
context.addChild(wrapper2);
Valve valve1 = new HeaderLoggerValve();
Valve valve2 = new ClientIPLoggerValve();
((Pipeline) context).addValve(valve1);
((Pipeline) context).addValve(valve2);
Mapper mapper = new SimpleContextMapper();
mapper.setProtocol("http");
context.addMapper(mapper);
Loader loader = new SimpleLoader();
context.setLoader(loader);
// context.addServletMapping(pattern, name);
context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern");
connector.setContainer(context);
try {
connector.initialize();
connector.start();
// make the application wait until we press a key.
System.in.read();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
The main method starts by instantiating the Tomcat default connector and two wrappers, wrapper1 and wrapper2. These wrappers are given names Primitive and Modern. The servlet classes for Primitive and Modern are PrimitiveServlet and ModernServlet, respectively.
主方法首先实例化 Tomcat
默认连接器和两个包装器(wrapper1
和 wrapper2
)。
这两个封装器被命名为 Primitive
和 Modern
。
Primitive
和 Modern
的 servlet 类分别是 PrimitiveServlet
和 ModernServlet
。
HttpConnector connector = new HttpConnector();
Wrapper wrapper1 = new SimpleWrapper();
wrapper1.setName("Primitive");
wrapper1.setServletClass("PrimitiveServlet");
Wrapper wrapper2 = new SimpleWrapper();
wrapper2.setName("Modern");
wrapper2.setServletClass("ModernServlet");
Then, the main method creates an instance of SimpleContext and adds wrapper1 and wrapper2 as child containers of SimpleContext. It also instantiates the two valves, ClientIPLoggerValve and HeaderLoggerValve, and adds them to SimpleContext.
然后,main
方法创建 SimpleContext
实例,并将 wrapper1
和 wrapper2
添加为 SimpleContext
的子容器。
它还实例化了 ClientIPLoggerValve
和 HeaderLoggerValve
这两个阀门,并将它们添加到 SimpleContext
中。
Context context = new SimpleContext();
context.addChild(wrapper1);
context.addChild(wrapper2);
Valve valve1 = new HeaderLoggerValve();
Valve valve2 = new ClientIPLoggerValve();
((Pipeline) context).addValve(valve1);
((Pipeline) context).addValve(valve2);
Next, it constructs a mapper object from the SimpleMapper class and adds it to SimpleContext. This mapper is responsible for finding child containers in the context that will process HTTP requests.
接下来,它会从 SimpleMapper
类中构建一个映射器对象,并将其添加到 SimpleContext
中。该映射器负责在上下文中查找将处理 HTTP 请求的子容器。
Mapper mapper = new SimpleContextMapper();
mapper.setProtocol("http");
context.addMapper(mapper);
To load a servlet class, you need a loader. Here you use the SimpleLoader class, just like in the first application. However, instead of adding it to both wrappers, the loader is added to the context. The wrappers will find the loader using its getLoader method because the context is their parent.
要加载 servlet
类,需要一个加载器。
在这里,我们使用 SimpleLoader
类,就像在第一个应用程序中一样。
不过,加载器不是添加到两个封装器中,而是添加到上下文中。
包装器将使用 getLoader
方法找到加载器,因为上下文是它们的父类。
Loader loader = new SimpleLoader(); context.setLoader(loader);
Now, it's time to add servlet mappings. You add two patterns for the two wrappers.
现在,是时候添加 servlet
映射了。为两个封装器添加两个模式。
// context.addServletMapping(pattern, name);
context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern");
Finally, assign the context as the container for the connector and initialize and start the connector.
最后,将上下文指定为连接器的容器,并初始化和启动连接器。
connector.setContainer(context);
try {
connector.initialize();
connector.start();
Running the Application
To run the application in Windows, from the working directory, type the following:
要在 Windows
中运行应用程序,请在工作目录中键入以下内容:
java -classpath ./lib/servlet.jar;./ ex05.pyrmont.startup.Bootstrap2
In Linux, you use a colon to separate between libraries.
在 Linux
中,库与库之间用冒号分隔。
java -classpath ./lib/servlet.jar:./ ex05.pyrmont.startup.Bootstrap2
To invoke PrimitiveServlet, you use the following URL in your browser.
要调用 PrimitiveServlet
,请在浏览器中使用以下 URL。
http://localhost:8080/Primitive
To invoke ModernServlet, you use the following URL.
要调用 ModernServlet
,请使用以下 URL。
Summary(摘要)
The container is the second main module after the connector. The container uses many other modules, such as Loader, Logger, Manager, etc. There are four types of containers: Engine, Host, Context, and Wrapper. A Catalina deployment does not need all four containers to be present. The two applications in this chapter show that a deployment can have one single wrapper or a context with a few wrappers.
容器是继连接器之后的第二个主要模块。
容器使用许多其他模块,如加载器、记录器、管理器等。容器有四种类型: 引擎、主机、上下文和封装器。
Catalina
部署不需要全部四个容器。本章中的两个应用程序表明,一个部署可以只有一个包装器,或一个上下文包含几个包装器。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。