Chapter 11: StandardWrapper
You have learned in Chapter 5 that there are four types of containers: engine, host, context, and wrapper. You have also built your own simple contexts and wrappers in previous chapters. A context normally has one or more wrappers, in which each wrapper represents a servlet definition. This chapter will now look at the standard implementation of the Wrapper interface in Catalina. It starts by explaining the sequence of methods that get invoked for each HTTP request and continues with the javax.servlet.SingleThreadModel interface. The chapter concludes by explaining the StandardWrapper and StandardWrapperValve classes. The application that accompanies this chapter uses StandardWrapper instances to represents servlets.
在第5章中,您已经了解到有四种类型的容器:引擎(engine
)、主机(host
)、上下文(context
)和包装器(wrapper)。
在之前的章节中,您还构建了自己的简单上下文和包装器。
一个上下文通常有一个或多个包装器,其中每个包装器代表一个servle
t定义。
本章将介绍Catalina
中Wrapper
接口的标准实现。
它从解释每个HTTP请求调用的方法序列开始,并继续介绍 javax.servlet.SingleThreadModel
接口。
本章最后解释了StandardWrapper
和StandardWrapperValve
类。
本章附带的应用程序使用 StandardWrapper
实例来表示 servlet
。
Sequence of Methods Invocation
For each incoming HTTP request, the connector calls the invoke method of the associated container. The container will then call the invoke methods of all its child containers. For example, if the connector is associated with an instance of StandardContext, the connector will call the invoke method of the StandardContext instance, which then call the invoke methods of all its child containers (in this case, the child containers will be of type StandardWrapper). Figure 11.1 explains what happens when the connector receives an HTTP request. (Recall from Chapter 5 that a container has a pipeline with one or more valves.)
对于每个传入的HTTP请求,连接器调用关联容器的 invoke
方法。
容器将调用其所有子容器的 invoke
方法。
例如,如果连接器与 StandardContext
实例相关联,则连接器将调用 StandardContext
实例的 invoke
方法,然后调用其所有子容器的 invoke
方法(在这种情况下,子容器的类型将为 StandardWrapper
)。
图11.1解释了当连接器接收到HTTP请求时会发生什么。
(回想一下第5章,容器具有一个具有一个或多个阀门的管道。)
Figure 11.1: The collaboration diagram of methods invocation
图11.1:方法调用的协作图
- The connector creates the request and response objects
- The connector calls the StandardContext instance's invoke method.
- The StandardContext's invoke method in turn calls the invoke method of the context's pipeline. The basic valve in a StandardContext's pipeline is a StandardContextValve, therefore the StandardContext's pipeline calls the StandardContextValve's invoke method.
- The StandardContextValve's invoke method obtains the appropriate wrapper to serve the request and calls the wrapper's invoke method.
- The StandardWrapper is the standard implementation of a wrapper. The StandardWrapper instance's invoke method calls its pipeline's invoke method.
The basic valve in the StandardWrapper's pipeline is a StandardWrapperValve. Therefore, the invoke method of the StandardWrapperValve is called. The invoke method of the StandardWrapperValve calls the wrapper's allocate method to obtain an instance of the servlet. - The allocate method calls the load method to load the servlet, if the servlet needs to be loaded.
- The load method calls the servlet's init method.
- The StandardWrapperValve calls the servlet's service method.
- 连接器创建请求和响应对象
- 连接器调用
StandardContext
实例的invoke方法。 StandardContext
的invoke
方法再调用上下文的管道的invoke方法。StandardContext
的管道中的基本阀门是StandardContextValve
,因此StandardContext
的管道调用StandardContextValve
的invoke
方法。
StandardContextValve
的invoke
方法获取适当的包装器来处理请求,并调用包装器 的invoke
方法。StandardWrapper
是包装器的标准实现。StandardWrapper
实例的invoke
方法调用其管道的invoke
方法。StandardWrapper
的管道中的基本阀门是StandardWrapperValve
。- 因此,调用
StandardWrapperValve
的invoke
方法。StandardWrapperValve
的invoke
方法调用包装器的allocate
方法来获取一个servlet
的实例。
- 因此,调用
allocate
方法调用load
方法来加载servlet
,如果需要加载servlet
的话。load
方法调用servlet
的init
方法。StandardWrapperValve
调用servlet
的service
方法。
Note The StandardContext class's constructor sets an instance of StandardContextValve as its basic valve:
注意:StandardContext
类的构造函数将 StandardContextValve
的实例设置为其基本阀门。
publicStandardContext() {
super();
pipeline.setBasic(new StandardContextValve());
namingResources.setContainer(this);
}
Note The StandardWrapper class's constructor sets an instance of
StandardWrapperValve as its basic valve:
public StandardWrapper() {
super();
pipeline.setBasic(new StandardWrapperValve());
}
In this chapter we are interested in the details on how a servlet is invoked. We will therefore look at the StandardWrapper and StandardWrapperValve classes. Before we do that, however, let's look at the javax.servlet.SingleThreadModel interface first. Understanding this interface is crucial in understanding how a wrapper loads a servlet.
在本章中,我们关注的是如何调用 servlet
的细节。
因此,我们将研究 StandardWrapper 和 StandardWrapperValve
类。
不过,在此之前,我们先来看看 javax.servlet.SingleThreadModel
接口。
了解这个接口对于理解封装程序如何加载 servlet
至关重要。
SingleThreadModel(单线程模型)
A servlet can implement the javax.servlet.SingleThreadModel interface, and a servlet implementing this interface is colloquially called a SingleThreadModel (STM) servlet. According to the Servlet specification, the purpose of implementing this interface is to guarantee that the servlet handles only one request at a time. Quoting the section SRV.14.2.24 of the Servlet 2.4 specification (Servlet 2.3 has a similar explanation on the SingleThreadModel interface):
一个Servlet可以实现 javax.servlet.SingleThreadModel
接口,实现了这个接口的 Servlet
通常被称为 SingleThreadModel
(STM
)Servlet
。
根据 Servlet
规范,实现这个接口的目的是确保 Servlet
一次只处理一个请求。
引用Servlet 2.4规范(Servlet 2.3对SingleThreadModel接口有类似的解释)的SRV.14.2.24节:
If a servlet implements this interface, you are guaranteed that no two threads will execute concurrently in a servlet's service method. The servlet container can guarantee this by synchronizing access to a single instance of the servlet, or by maintaining a pool of servlet instances and dispatching each new request to a free servlet. This interface does not prevent synchronization problems that result from servlets accessing shared resources such as static class variables or classes outside the scope of the servlet.
如果一个 Servlet
实现了这个接口,你可以保证在 Servlet
的 service
方法中不会有两个线程同时执行。
Servlet
容器可以通过同步访问 Servlet
的单个实例或维护一个 Servlet
实例池并将每个新请求分派给一个空闲的 Servlet
来保证这一点。
这个接口无法防止由于 Servlet
访问共享资源(如静态类变量或 Servlet
范围之外的类)而导致的同步问题。
Many programmers do not read this carefully, and think that implementing SingleThreadModel will make their servlet thread-safe. This is not the case. Read the quotation above once again.
很多程序员没有仔细阅读这一点,认为实现 SingleThreadModel
会使他们的 Servlet
线程安全。
事实并非如此,请再次阅读上面的引述。
It is true that by implementing SingleThreadModel no two threads will execute a servlet's service method at the same time. However, to enhance performance the servlet container can create multiple instances of an STM servlet. That means, the STM servlet's service method can be executed concurrently in different instances. This will introduce synchronization problems if the servlet need to access static class variables or other resources outside the class.
实现 SingleThreadModel
确实可以保证在同一时间内没有两个线程执行一个 Servlet
的 service
方法。
然而,为了提高性能,Servlet
容器可以创建多个STM Servlet
的实例。
这意味着,在不同的实例中,STM Servlet
的 service
方法可以并发执行。
如果 Servlet
需要访问静态类变量或其他类之外的资源,这将引入同步问题。
False Sense of Multi-Thread Safety
错误的多线程安全意识
The SingleThreadModel interface is deprecated in Servlet 2.4 because it gives servlet programmers false sense of multi-thread safety. However, both Servlet 2.3 and Servlet 2.4 containers must still support this interface.
Servlet 2.4中已经弃用了 SingleThreadModel
接口,因为它给 Servlet
程序员带来了多线程安全的虚假感觉。
然而,无论是Servlet 2.3
还是Servlet 2.4
的容器都必须继续支持这个接口。
Note You can find an interesting discussion about SingleThreadModel on
注意:你可以在链接中找到关于 SingleThreadModel
的有趣讨论。
http://w4.metronet.com/~wjm/tomcat/ToFeb11/msg02655.html.
(已失效)
StandardWrapper
The main responsibilities of a StandardWrapper object are to load the servlet it represents and allocates an instance of it. The StandardWrapper, however, does not call the servlet's service method. This task is left to the StandardWrapperValve object, the basic valve in the StandardWrapper instance's pipeline. The StandardWrapperValve object obtains the servlet instance from StandardWrapper by calling its allocate method. Upon receiving the servlet instance, the StandardWrapperValve calls the servlet's service method.
StandardWrapper
对象的主要职责是加载它所代表的 servlet
并分配一个实例。
然而,StandardWrapper
不会调用 servlet
的 service
方法,这个任务留给了 StandardWrapperValve
对象,它是 StandardWrapper
实例管道中的基本阀门。
StandardWrapperValve
对象通过调用 StandardWrapper
的 allocate
方法来获取 servlet
实例。
在接收到servlet实例后,StandardWrapperValve
调用 servlet
的 service
方法。
The StandardWrapper loads the servlet class when the servlet is requested the first time. The StandardWrapper instance loads a servlet dynamically, therefore it needs to know the fully qualified name of the servlet class. You tell the StandardWrapper this by passing the servlet class name to the setServletClass method of the StandardWrapper. In addition, you calls its setName method to pass the name the servlet will be referred to.
当第一次请求 servle
t时,StandardWrapper
会加载 servlet
类。
StandardWrapper
实例以动态方式加载 servlet
,因此需要知道servlet类的完全限定名称。
通过将 servlet
类名传递给 StandardWrapper
的 setServletClass
方法,告诉 StandardWrapper
这一点。
此外,通过调用其 setName
方法传递 servlet
将被引用的名称。
With regard to allocating the servlet instance when the StandardWrapperValve requests it, the StandardWrapper must take into account whether or not the servlet implements the SingleThreadModel interface.
关于在 StandardWrapperValve
请求时分配 servlet
实例,StandardWrapper
必须考虑 servlet
是否实现了 SingleThreadModel
接口。
For a servlet that does not implement the SingleThreadModel interface, StandardWrapper will load the servlet class once and keep returning the same instance for subsequent requests. The StandardWrapper instance does not need multiple instances of the servlet because it is assumed safe to call the servlet's service method from many threads. It is the responsibility of the servlet programmer to synchronize access to a common resource, if necessary.
对于不实现 SingleThreadModel
接口的 servlet
,StandardWrapper
将加载 servlet
类一次,并为后续的请求保持返回相同的实例。
StandardWrapper
实例不需要多个 servlet
实例,因为假设可以从多个线程调用 servlet
的 service
方法是安全的。
如果需要,servlet
程序员有责任同步访问共享资源。
For an STM servlet, things are different. The StandardWrapper instance must guarantee that no two threads will execute concurrently in the STM servlet's service method. If the StandardWrapper were to maintain a single instance of the STM servlet, here is how it would invoke the STM servlet's service method:
对于 STM
(SingleThreadModel
)servlet
,情况就不同了。
StandardWrapper
实例必须保证在STM servlet
的service
方法中没有两个线程同时执行。
如果 StandardWrapper
维护单个STM servlet
的实例,它将如何调用STM servlet
的 service
方法:
Servlet instance = <get an instance of the servlet>;
if ((servlet implementing SingleThreadModel>) {
synchronized (instance) {
instance.service(request, response);
}
}
else {
instance.service(request, response);
}
However, for the sake of performance, StandardWrapper maintains a pool of STM servlet instances.
不过,为了提高性能,StandardWrapper
会维护一个 STM servlet
实例池。
A wrapper is also responsible for preparing a javax.servlet.ServletConfig instance that can be obtained from inside the servlet. The two next sections discuss the allocation and loading of the servlet.
封装器还负责准备一个 javax.servlet.ServletConfig
实例,该实例可从 servlet
内部获取。
接下来的两节将讨论 servlet
的分配和加载。
Allocating the Servlet(分配 Servlet)
As mentioned at the beginning of this section, the StandardWrapperValve's invoke method calls the wrapper's allocate method to obtain an instance of the requested servlet. The StandardWrapper class therefore must have the implementation of this method.
如本节开头所述,StandardWrapperValve
的 invoke
方法调用包装器的 allocate
方法来获取请求的 servlet
实例。
因此,StandardWrapper
类必须实现该方法。
The signature of the allocate method is as follows:
allocate
方法的签名如下:
public javax.servlet.Servlet allocate() throws ServletException;
Notice that the allocate method returns an instance of the requested servlet.
请注意,allocate
方法返回的是请求的 servlet
的实例。
The necessity for supporting STM servlets makes the allocate method a bit more complex. In fact, there are two parts in the allocate method, one to cater for non-STM servlets and the other for STM servlets. The first part has the following skeleton.
由于必须支持 STM servlets
,分配方法就变得复杂了一些。
事实上,allocate
方法有两个部分,一个是非 STM servlets
,另一个是 STM servlets
。第一部分的骨架如下。
if (!singleThreadModel) {
// returns a non-STM servlet instance
}
The singleThreadModel is a boolean that indicates whether the servlet represented by this StandardWrapper is an STM servlet. The initial value for singleThreadModel is false, but the loadServlet method tests the servlet it is loading and set this boolean if the servlet is a STM servlet. The loadServlet method is explained in the section "Loading the Servlet" below.
singleThreadModel
是一个布尔值,用于指示此 StandardWrapper
所代表的 servlet
是否是 STM
servlet
。
singleThreadModel
的初始值是 false,但 loadServlet
方法会测试正在加载的 servlet
,如果该 servlet
是 STM servlet
,就会设置该布尔值。
loadServlet
方法将在下文 "加载 Servlet
"一节中解释。
The second part of the allocate method is executed if singleThreadModel is true. The skeleton of the second part is as follows:
如果 singleThreadModel
为 true
,则执行 allocate
方法的第二部分。第二部分的骨架如下:
synchronized (instancepool) {
// returns an instance of the servlet from the pool
}
We'll take a look at the first and the second parts now.
我们现在来看看第一部分和第二部分。
For non-STM servles, the StandardWrapper defines a variable named instance of type javax.servlet.Servlet:
对于非 STM
服务,StandardWrapper
定义了一个变量,名为 javax.servlet.Servlet
类型的实例:
private Servlet instance = null;
The allocate method checks if instance is null. If it is, the allocate method calls the loadServlet method to load the servlet. It then increments the countAllocated integer and returns the instance.
allocate
方法会检查实例是否为空。如果为空,allocate
方法会调用 loadServlet
方法加载 servlet
。
然后递增 countAllocated
整数并返回实例。
if (!singleThreadModel) {
// Load and initialize our instance if necessary
if (instance == null) {
synchronized (this) {
if (instance == null) {
try {
instance = loadServlet();
}
catch (ServletException e) {
throw e;
}
catch (Throwable e) {
throw new ServletException
(sm.getString("standardWrapper.allocate"), e);
}
}
}
}
if (!singleThreadModel) {
if (debug >= 2)
log(" Returninq non-STM instance");
countAllocated++;
return (instance);
}
}
If the servlet represented by StandardWrapper is a STM servlet, the allocate method attempt to return an instance from a pool. The instancePool variable of type java.util.Stack references to a stack of STM servlet instances.
如果 StandardWrapper
所代表的 servlet
是 STM servlet
,则分配方法会尝试从池中返回一个实例。
instancePool
变量的类型为 java.util.Stack
,指向 STM servlet
实例堆栈。
private Stack instancePool = null;
This variable is instantiated inside the loadServlet method, which we -will discuss in the next section.
我们将在下一节讨论 loadServlet
方法,该变量将在 loadServlet
方法中实例化。
The allocate method will allocate an instance of the STM servlet as long as the number of instances does not exceed the specified maximum number. The maxInstances integer holds the maximum number of STM instances and by default its value is 20.
只要实例数量不超过指定的最大数量,分配方法就会分配一个 STM
servlet
实例。
maxInstances
整数表示 STM 实例的最大数量,默认值为 20。
private int maxInstances = 20;
To track down the current number of STM instances, the StandardWrapper class uses the nInstances integer:
为了追踪当前 STM 实例的数量,StandardWrapper
类使用了 nInstances
整数:
private int nInstances = 0;
Here is the second part of the allocate method.
下面是分配方法的第二部分。
synchronized (instancePool) {
while (countAllocated >= nInstances) {
// Allocate a new instance if possible, or else wait
if (nInstances < maxInstances) {
try {
instancePool.push(loadServlet());
nInstances++;
}
catch (ServletException e) {
throw e;
}
catch (Throwable e) {
throw new ServletException
(sm.getString("StandardWrapper.allocate"), e);
}
}
else {
try {
instancePool.wait();
}
catch (InterruptedException e) {
;
}
}
}
if (debug >= 2)
log(" Returning allocated STM instance");
countAllocated++;
return (Servlet) instancePool.pop();
}
The code above uses a while loop to wait until nInstances is less than or equal to the value of countAllocated. Inside the while loop, the allocate method checks the value of nInstance, and if it is lower than maxInstances, the allocate method calls the loadServlet method and pushes the new instance to the pool and increments nInstances. If the number of nInstance is equal to maxInstance or greater, it waits by calling the instancePool stack's wait method, waiting for an instance to be returned to the stack.
上面的代码使用 while
循环来等待,直到 nInstances
小于或等于 countAllocated
的值。
在 while
循环内部,allocate
方法检查 nInstance
的值,如果它小于 maxInstances
,则调用 loadServlet
方法并将新实例推入池中,并增加 nInstances
的值。
如果nInstance
的数量等于或大于maxInstance
,则通过调用instancePool
堆栈的wait
方法进行等待,等待实例返回到堆栈中。
Loading the Servlet(加载 Servlet
)
The StandardWrapper class implements the load method of the Wrapper interface. The load method calls the loadServlet method that loads the servlet and calls its init method, passing a javax.servlet.ServletConfig instance. Here is how the loadServlet method works.
StandardWrapper
类实现了 Wrapper
接口的load方法。
load
方法调用 loadServlet
方法来加载 servlet
并调用其 init
方法,传递一个javax.servlet.ServletConfig
实例。以下是loadServlet方法的工作原理。
The loadServlet method starts by checking if the current StandardWrapper represents an STM servlet. If it does not and the variable instance is not null (which means the servlet has been previously loaded), it simply returns the instance.
loadServlet
方法首先检查当前的 StandardWrapper
是否表示一个 STM
servlet
。
如果不是,并且变量 instance
不为 null
(这意味着 servlet
已经被预先加载),它简单地返回该实例。
// Nothing to do if we already have an instance or an instance pool
if (!singleThreadModel && (instance != null))
return instance;
If instance is null or it is a STM servlet, it runs the rest of the method.
如果实例为空或为 STM servlet,则运行该方法的其余部分。
First, it captures the output of System.out and System.err, so that it can log any message later by using the log method of javax.servlet.ServletContext.
首先,它会捕获 System.out
和 System.err
的输出,以便以后使用 javax.servlet.ServletContext
的日志方法记录任何信息。
PrintStream out = System.out;
SystemLogHandler.startCapture();
It then defines the variable servlet of type javax.servlet.Servlet. This represents the instance of the loaded servlet that will be returned by the loadServlet method.
然后,它定义了 javax.servlet.Servlet
类型的变量 servlet
。
这代表了 loadServlet
方法将返回的已加载 Servlet
的实例。
Servlet servlet = null;
The loadServlet method is responsible for loading the servlet class. The name of the class should have been assigned to the servletClass class variable. The method assigns the value of this variable to the String actualClass.
loadServlet
方法负责加载 servlet
类。类的名称应已分配给 servletClass
类变量。
该方法会将该变量的值赋值给字符串 actualClass
。
String actualClass = servletclass;
However, since Catalina is also a JSP container, the loadServlet method must also find out if the request servlet is a JSP page. If it is, the loadServlet method attempts to obtain the actual class for the JSP page.
不过,由于 Catalina
也是一个 JSP 容器,因此 loadServlet
方法还必须查明请求的 servlet
是否是一个 JSP 页面。
如果是,则 loadServlet
方法会尝试获取 JSP 页面的实际类。
if ((actualClass == null) && (jspFile != null)) {
Wrapper jspWrapper = (Wrapper)
((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);
if (jspWrapper != null)
actualClass = jspWrapper.getServletClass();
}
If the name of the servlet for the JSP page cannot be found, the value of the servletclass variable will be used. However, if servletclass has not been set by calling the StandardWrapper class's setServletClass method, an exception will be thrown and the rest of the method will not be executed
如果找不到 JSP 页面的 servlet
名称,将使用 servletclass
变量的值。
但是,如果尚未通过调用 StandardWrapper
类的 setServletClass
方法设置 servletclass
,则会出现异常,该方法的其余部分将不会执行
// Complain if no servlet class has been specified
if (actualClass == null) {
unavailable(null);
throw new ServletException
(sm.getString("StandardWrapper.notClass", getName()));
}
At this stage, the servlet class name has been resolved, so the loadServlet method obtains the loader. If no loader is found, it throws an exception and the method stops here.
在此阶段,Servlet
类名已被解析,因此 loadServlet
方法会获取加载器。
如果找不到加载器,就会抛出异常,方法到此为止。
// Acquire an instance of the class loader to be used
Loader loader = getLoader();
if (loader == null) {
unavailable(null);
throw new ServletException
(sm.getString("StandardWrapper.missingLoader", getName()));
}
If a loader can be found, the loadServlet method calls its getClassLoader method to obtain a ClassLoader
如果能找到加载器,则 loadServlet 方法会调用其 getClassLoader
方法来获取 ClassLoader
。
ClassLoader classLoader = loader.getClassLoader();
Catalina provides special servlets that belong to the org.apache.catalina package. These special servlets have access to the internal body of the servlet container. If the servlet is a special servlet, i.e. if the isContainerProvidedServlet method returns true, the classLoader variable is assigned another ClassLoader instance, so access to the internal part of Catalina is possible.
Catalina
提供属于 org.apache.catalina
包的特殊 servlet
。
这些特殊的 servlet
可以访问 servlet
容器的内部主体。
如果 servlet
是特殊 servlet
,即 isContainerProvidedServlet
方法返回 true
,classLoader
变量就会被分配给另一个 ClassLoader
实例,这样就可以访问 Catalina
的内部部分。
// Special case class loader for a container provided servlet
if (isContainerProvidedServlet(actualClass)) {
ClassLoader = this.getClass().getClassLoader();
log(sm.getString
("standardWrapper.containerServlet", getName()));
}
Having a class loader and the servlet name to load, the loadServlet method can now load the servlet.
有了类加载器和要加载的 servlet
名称,loadServlet
方法就可以加载 servlet
了。
// Load the specified servlet class from the appropriate class
// loader Class classClass = null;
try {
if (ClassLoader != null) {
System.out.println("Using classLoader.loadClass");
classClass = classLoader.loadClass(actualClass);
}
else {
System.out.println("Using forName");
classClass = Class.forName(actualClass);
}
}
catch (ClassNotFoundException e) {
unavailable(null);
throw new ServletException
(sm.getstring("standardWrapper.missingClass", actualClass), e);
}
if (classClass == null) {
unavailable(null);
throw new ServletException
(sm.getString("standardWrapper.missingClass", actualClass));
}
Then, it can instantiate the servlet.
然后,它就可以实例化 servlet
。
// Instantiate and initialize an instance of the servlet class
// itself
try {
servlet = (Servlet) classClass.newInstance();
}
catch (ClassCastException e) {
unavailable(null);
// Restore the context ClassLoader
throw new ServletException
(sm.getString("standardWrapper.notServlet", actualClass), e);
}
catch (Throwable e) {
unavailable(null);
// Restore the context ClassLoader
throw new ServletException
(sm.getstring("standardWrapper.instantiate", actualClass), e);
}
However, before the loadServlet method initializes the servlet, it checks if loading this servlet is allowed by calling the isServletAllowed method.
不过,在 loadServlet
方法初始化 servlet
之前,它会通过调用 isServletAllowed
方法来检查是否允许加载此 servlet。
// Check if loading the servlet in this web application should be
// allowed
if (!isServletAllowed(servlet)) {
throw new SecurityException
(sm.getString("standardWrapper.privilegedServlet",
actualClass));
}
If the security check was passed, it checks if the servlet is a ContainerServlet. A ContainerServlet is a servlet that implements the org.apache.catalina.ContainerServlet interface and has access to Catalina internal functionality. If the servlet is a ContainerServlet, the loadServlet method calls the ContainerServlet's setWrapper method, passing this StandardWrapper instance
如果安全检查通过,则检查 servlet
是否是 ContainerServlet
。
ContainerServlet
是一种实现了 org.apache.catalina.ContainerServlet
接口并能访问 Catalina
内部功能的 servlet
。
如果 servlet
是 ContainerServlet
,则 loadServlet
方法会调用 ContainerServlet
的 setWrapper
方法,并传递此 StandardWrapper
实例
// Special handling for ContainerServlet instances
if ((servlet instanceof ContainerServlet) &&
isContainerProvidedServlet(actualClass)) {
((ContainerServlet) servlet).setWrapper(this);
}
Next, the loadServlet method fires the BEFORE_INIT_EVENT and calls the sender's init method.
接下来,loadServlet
方法会触发 BEFORE_INIT_EVENT
,并调用发送者的 init
方法。
try {
instanceSupport.fireInstanceEvent(
InstanceEvent.BEFORE_INIT_EVENT, servlet);
servlet.init(facade);
Note that the init method is passed the facade variable that references a javax.servlet.ServletConfig object. Find out how the ServletConfig object is created in the section "Creating ServletConfig" later in this chapter.
请注意,init 方法传递的是引用 javax.servlet.ServletConfig
对象的门面变量。
本章后面的 "创建 ServletConfig
"一节将介绍如何创建 ServletConfig
对象。
If the loadOnStartup variable is assigned an integer value and the servlet is a JSP page, call the service method of the servlet as well.
如果 loadOnStartup
变量被赋值为整数且 servlet
是一个 JSP 页面,则也要调用 servlet
的 service
方法。
// Invoke jspInit on JSP pages
if ((loadOnStartup > 0) && (jspFile != null)) {
// Invoking jspInit
HttpRequestBase req = new HttpRequestBase();
HttpResponseBase res = new HttpResponseBase();
req.setServletPath(jspFile};
req.setQueryString("jsp_precompile=true");
servlet.service(req, res);
}
Next, the loadServlet method fires the AFTER_INIT_EVENT event.
接下来,loadServlet
方法会触发 AFTER_INIT_EVENT
事件。
instanceSupport.firelnstanceEvent (InstanceEvent.AFTER_INIT_EVENT,
servlet);
If the servlet represented by this StandardWrapper object is a STM servlet, the servlet instance will be added to the instance pool. Therefore, if the instancePool variable is still null, it is assigned a Stack object.
如果该 StandardWrapper
对象所代表的 servlet
是 STM servlet
,该 servlet 实例将被添加到实例池中。
因此,如果 instancePool
变量仍为空,它将被分配给一个 Stack
对象。
// Register our newly initialized instance
singleThreadModel = servlet instanceof SingleThreadModel;
if (singleThreadModel) {
if (instancePool == null)
instancePool = new Stack();
}
fireContainerEvent("load", this);
}
In the finally block, the loadServlet stops the capturing of the System.out and System.err and logs any message during the loading process to the log method of the ServletContext.
在 finally
代码块中,loadServlet
停止捕获 System.out
和 System.err
,并将加载过程中的任何信息记录到 ServletContext
的日志方法中。
finally {
String log = SystemLogHandler.stopCapture();
if (log != null && log.length() > 0) {
if (getServletContext() != null) {
getServletContext().log(log);
}
else {
out.println(log);
}
}
}
And, finally, the loadServlet method returns the servlet instance.
最后,loadServlet
方法会返回 servlet
实例。
return servlet;
The ServletConfig Object(ServletConfig 对象)
The loadServlet method of the StandardWrapper class calls the sender's init method after the servlet is loaded. The init method is passed an instance of javax.servlet.ServletConfig. You may be wondering how the StandardWrapper object obtains the ServletConfig object.
StandardWrapper
类的 loadServlet
方法在加载servlet后调用发送者的init方法。
init
方法接收一个javax.servlet.ServletConfig
的实例。
你可能想知道 StandardWrapper
对象是如何获取 ServletConfig
对象的。
Look no further than the StandardWrapper class itself. This class implements the javax.servlet.ServletConfig interface, in addition to the Wrapper interface.
不用再往前看了,就在 StandardWrapper
类本身。
这个类实现了javax.servlet.ServletConfig
接口,除了Wrapper
接口。
The ServletConfig interface has the following four methods: getServletContext, getServletName, getInitParameter, and getInitParameterNames. Let's now see how these methods are implemented in the StandardWrapper class.
ServletConfig
接口有以下四个方法:getServletContext
、getServletName
、getInitParameter
和 getInitParameterNames
。
现在让我们看看这些方法在 StandardWrapper
类中是如何实现的。
Note The StandardWrapper class does not pass itself to the servlet's init method, however. Instead, it wraps itself in a StandardWrapperFacade instance to hide most of its public method from a servlet programmer. See the section "StandardWrapperFacade" after this section.
注意:StandardWrapper类并不直接将自身传递给servlet的init方法。相反,它将自身包装在一个StandardWrapperFacade实例中,以隐藏大部分公共方法,使其对servlet程序员不可见。请参阅本节后面的"StandardWrapperFacade"部分。
getServletContext
The signature of this method is as follows:
该方法的签名如下
public ServletContext getServletContext()
A StandardWrapper instance must be a child container of a StandardContext. This is to say, a StandardWrapper's parent is a StandardContext. From the StandardContext object, you can obtain a ServletContext object by calling its getServletContext method. Here is the implementation of the getServletContext method in the StandardWrapper class:
StandardWrapper
实例必须是 StandardContext
的子容器。
也就是说,StandardWrapper
的父对象是 StandardContext
。
通过调用 StandardContext
的 getServletContext
方法,可以从 StandardContext
对象获得 ServletContext
对象。
下面是 StandardWrapper
类中 getServletContext
方法的实现:
public ServletContext getServletContext() {
if (parent == null)
return (null);
else if (!(parent instanceof Context))
return (null);
else
return (((Context) parent).getServletContext());
}
Note Now you know that it is not possible to deploy a stand alone wrapper that represents a servlet definition. The wrapper must reside in a context so that the ServletConfig object can return a ServletContext instance when its getServletContext method is called.
注意 现在你知道了,不可能部署一个独立的封装器来表示 Servlet 定义。封装器必须位于上下文中,这样 ServletConfig 对象才能在调用其 getServletContext 方法时返回 ServletContext 实例。
getServletName
This method returns the name of the servlet. Its signature is as follows:
该方法返回 servlet
的名称。其签名如下
public java.lang.String getServletName()
Here is the implementation of the getServletName method in the StandardWrapper class:
下面是 StandardWrapper
类中 getServletName
方法的实现:
public String getServletName() {
return (getName());
}
It simply calls the getName method of the ContainerBase class, the parent class of StandardWrapper. The getName method is implemented as follows in ContainerBase:
它只需调用 StandardWrapper
的父类 ContainerBase
类的 getName
方法。ContainerBase
中的 getName
方法实现如下:
public String getName() {
return (name);
}
You can set the value of name by calling the setName method. Recall that you call the setName method of the StandardWrapper instance by passing the name of the servlet?
你可以通过调用 setName
方法来设置 name
的值。
还记得通过传递 servlet
的名称来调用 StandardWrapper
实例的 setName
方法吗?
getInitParameter
This method returns the value of the specified initialization parameter. Its signature is as follows:
该方法返回指定初始化参数的值。
其签名如下
public java.lang.String getInitParameter(java.lang.String nam
In StandardWrapper, the initialization parameters are stored in a HashMap named parameters.
在 StandardWrapper
中,初始化参数存储在名为 parameters
的 HashMap
中。
private HashMap parameters = new HashMap();
You populate parameters by calling the StandardWrapper class's addInitParameter method, passing the name and the value of the parameter:
您可以通过调用 StandardWrapper
类的 addInitParameter
方法来填充参数,并传递参数的名称和值:
public void addInitParameter(String name, String value) {
synchronized (parameters) {
parameters.put(name, value);
}
fireContainerEvent("addInitParameter", name);
}
Here is the implementation of getInitParameter in StandardWrapper:
下面是 StandardWrapper
中 getInitParameter
的实现:
public String getInitParameter(String name) {
return (findInitParameter(name));
}
The findInitParameter method accepts the parameter name and calls the get method of the parameters HashMap. Here is the implementation of findInitParameter.
findInitParameter
方法接受参数名,并调用参数 HashMap
的 get
方法。以下是 findInitParameter
的实现。
public String findInitParameter(String name) {
synchronized (parameters) {
return ((String) parameters.get(name));
}
}
getInitParameterNames
This method returns an Enumeration containing all the names of the initialization parameters. Its signature is as follows:
该方法返回一个枚举,其中包含所有初始化参数的名称。其签名如下:
public java.util.Enumeration getInitParameterNames()
Here is the implementation of the getInitParameterNames in StandardWrapper:
下面是 StandardWrapper
中 getInitParameterNames
的实现:
public Enumeration getInitParameterNames() {
synchronized (parameters) {
return (new Enumerator(parameters.keyset()));
}
}
The Enumerator class implements java.util.Enumeration and is part of the org.apache.catalina.util package.
Enumerator
类实现了 java.util.Enumeration
,是 org.apache.catalina.util
包的一部分。
Parent and Children
A wrapper represents a container for each individual servlet. As such, a wrapper cannot have a child, and its addChild method should not be called. If you did, you would get a java.lang.IllegalStateException. Here is the implementation of the addChild method in the StandardWrapper class:
封装器代表每个单独 servlet
的容器。因此,封装器不能有子节点,也不应调用其 addChild
方法。
如果调用,就会出现 java.lang.IllegalStateException
异常。下面是 StandardWrapper
类中 addChild
方法的实现:
public void addChild(Container child) {
throw new IllegalStateException
(sm.getString("StandardWrapper.notChild"));
}
A wrapper's parent can only be an implementation of Context. Its setParent method throws a java.lang.IllegalArgumentException if you pass a non-Context container to it.
包装器的父类只能是 Context
的实现。如果向其传递一个非 Context
容器,则其 setParent
方法会抛出 java.lang.IllegalArgumentException
异常。
public void setParent(Container container) {
if ((container != null) && !(container instanceof Context))
throw new IllegalArgumentException
(sm.getString("standardWrapper.notContext"));
super.setParent(container);
}
StandardWrapperFacade
The StandardWrapper instance calls the init method of the servlet it loads. The init method requires a javax.servlet.ServletConfig and the StandardWrapper class itself implements the ServletConfig interface, so in theory a StandardWrapper object could pass itself to the init method. However, the StandardWrapper class needs to hide most of its public method from the servlet. To achieve this, the StandardWraper class wraps itself in a StandardWrapperFacade instance. Figure 11.2 shows the relationship between StandardWrapper and StandardWrapperFacade. Both implement the javax.servlet.ServletConfig interface.
StandardWrapper
实例调用加载的 servlet
的 init
方法。
init
方法需要一个javax.servlet.ServletConfig
对象,而StandardWrapper
类本身实现了ServletConfig
接口,因此理论上一个StandardWrapper
对象可以将自身传递给 init
方法。
然而,StandardWrapper类需要隐藏大部分公共方法,以防止servlet访问。
为了实现这一点,StandardWrapper类将自身封装在一个 StandardWrapperFacade
实例中。
图11.2展示了 StandardWrapper
和 StandardWrapperFacade
之间的关系。
两者都实现了javax.servlet.ServletConfig
接口。
Figure 11.2: The relationship between StandardWrapper and StandardWrapperFacade
图 11.2:StandardWrapper
和 StandardWrapperFacade
之间的关系
The following line can be found in the StandardWrapper class in which the StandardWrapper passes itself to the constructor of StandardWrapperFacade:
在 StandardWrapper
类中可以找到以下一行,其中 StandardWrapper
将自身传递给 StandardWrapperFacade
的构造函数:
private StandardWrapperFacade facade = new StandardWrapperFacade(this);
The StandardWrapperFacade class provides the class-level variable config of type ServletConfig:
StandardWrapperFacade
类提供了 ServletConfig
类型的类级变量 config
:
private ServletConfig config = null;
When an instance of StandardWrapperFacade is created from inside a StandardWrapper object, the StandardWrapperFacade class's constructor is passed the containing StandardWrapper object, which the constructor assigns to the config variable:
当从 StandardWrapper
对象内部创建 StandardWrapperFacade
实例时,StandardWrapperFacade
类的构造函数会传递包含 StandardWrapper
的对象,构造函数会将该对象赋值给 config 变量:
public StandardWrapperFacade(StandardWrapper config) {
super();
this.config = (ServletConfig) config;
}
Thus, when the StandardWrapper object calls the init method of a servlet instance, it passes an instance of StandardWrapperFacade. Calls to the ServletConfig's getServletName, getInitParameter, and getInitParameterNames methods from inside the servlet are simply passed to the corresponding methods implemented by the StandardWrapper class.
因此,当 StandardWrapper
对象调用 servlet
实例的 init
方法时,它会传递一个 StandardWrapperFacade
实例。
从 Servlet
内部调用 ServletConfig
的 getServletName
、getInitParameter
和 getInitParameterNames
方法时,只需将其传递给 StandardWrapper
类实现的相应方法即可。
public String getServletName() {
return config.getServletName();
}
public String getInitParameter(String name) {
return config.getInitParameter(name);
}
public Enumeration getInitParameterNames() {
return config.getInitParameterNames();
}
Calls to the getServletContext method is a bit more complex:
对 getServletContext
方法的调用要复杂一些:
public ServletContext getServletContext() {
ServletContext theContext = config.getServletContext();
if ((theContext != null) && (theContext instanceof
ApplicationContext))
theContext = ((ApplicationContext) theContext).getFacade();
return (theContext);
}
The method calls the getServletContext method in the StandardWrapper class, but it returns the ServletContext's façade, instead of the ServletContext object itself.
该方法调用了 StandardWrapper
类中的 getServletContext
方法,但返回的是 ServletContext
的外观,而不是 ServletContext
对象本身。
StandardWrapperValve
The StandardWrapperValve class is the basic valve in a StandardWrapper instance. This valve does two things:
StandardWrapperValve类是StandardWrapper实例中的基本阀门。该阀门有两个作用:
- Executes all filters associated with the servlet
- Calls the sender's service method.
- 执行与
servlet
关联的所有过滤器 - 调用发送者的
service
方法。
To do these, here are what the StandardWrapperValve must do in its invoke method implementation:
为了实现这些功能,StandardWrapperValve在其invoke方法中必须执行以下操作:
- Obtain an instance of the servlet that the containing StandardWrapper represents by calling the StandardWrapper's allocate method.
- Create a filter chain by calling the private createFilterChain method.
- Call the filter chain's doFilter method. This includes calling the servlet's service method.
- Release the filter chain.
- Call the wrapper's deallocate method.
- Call the wrapper's unload method if the servlet is permanently unavailable.
- 通过调用
StandardWrapper
的allocate
方法获取包含的StandardWrapper
表示的servlet
的实例。 - 通过调用私有的
createFilterChain
方法创建一个过滤器链。 - 调用过滤器链的
doFilter
方法。这包括调用servlet
的service
方法。 - 释放过滤器链。
- 调用包装器的
deallocate
方法。 - 如果
servlet
永久不可用,则调用包装器的unload
方法。
The following is the highlight of the invoke method:
以下是 invoke
方法的重点:
// Allocate a servlet instance to process this request
try {
if (!unavailable) {
servlet = wrapper.allocate();
}
}
...
// Acknowlege the request
try {
response.sendAcknowledgement();
}
...
// Create the filter chain for this request
ApplicationFilterChain filterChain = createFilterChain(request,
servlet);
// Call the filter chain for this request
// This also calls the servlet's servicet() method try {
String jspFile = wrapper.getJspFile();
if (jspFile != null)
sreq.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
else sreq.removeAttribute(Globals.JSP_FILE_ATTR);
if ((servlet != null) && (filterChain != null)) {
filterChain.doFilter(sreq, sres);
}
sreq.removeAttribute(Globals.JSP_FILE_ATTR);
}
...
// Release the filter chain (if any) for this request
try {
if (filterChain != null)
filterChain.release();
}
...
// Deallocate the allocated servlet instance
try {
if (servlet != null) {
wrapper.deallocate(servlet);
}
}
...
// If this servlet has been marked permanently unavailable,
// unload it and release this instance try {
if ((servlet != null) && (wrapper.getAvailable() ==
Long.MAX_VALUE)) {
wrapper.unload();
}
}
...
Of utmost importance is the call to the createFilterChain method and the call to the doFilter method of the filter chain. The createFilterChain method creates an instance of ApplicationFilterChain and adds all filters that should be applied to the servlet represented by the StandardWrapper. The ApplicationFilterChain class is explained in detail in the section, "ApplicationFilterChain", later in this chapter. To fully understand this class, however, you need to understand the FilterDef and the ApplicationFilterConfig classes. Both are given in the sections "FilterDef" and "ApplicationFilterConfig", respectively.
最重要的是调用 createFilterChain
方法和过滤器链的 doFilter
方法。
createFilterChain
方法创建一个 ApplicationFilterChain
的实例,并将所有应用于由 StandardWrapper
表示的servlet的过滤器添加到其中。
ApplicationFilterChain
类将在本章后面的 "ApplicationFilterChain
" 一节中详细介绍。
然而,要完全理解这个类,您需要了解 FilterDef
和 ApplicationFilterConfig
类。
它们分别在"FilterDef"和"ApplicationFilterConfig"两节中给出。
FilterDef
The org.apache.catalina.deploy.FilterDef class represents a filter definition as defined by the filter element in the deployment descriptor. Listing 11.1 presents this class.
org.apache.catalina.deploy.FilterDef
类表示部署描述符中filter元素定义的过滤器定义。
清单11.1展示了这个类。
Listing 11.1: The FilterDef class
清单11.1:FilterDef
类
package org.apache.catalina.deploy;
import java.util.HashMap;
import java.util.Map;
public final class FilterDef {
/**
* The description of this filter. */ private String description = null;
public String getDescription() {
return (this.description);
}
public void setDescription(String description) {
this.description = description;
}
/**
* The display name of this filter. */ private String displayName = null;
public String getDisplayName() {
return (this.displayName);
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
/**
* The fully qualified name of the Java class that implements this * filter. */ private String filterClass = null;
public String getFilterClass() {
return (this.filterClass);
}
public void setFilterclass(String filterClass) {
this.filterClass = filterClass;
}
/**
* The name of this filter, which must be unique among the filters * defined for a particular web application. */ private String filterName = null;
public String getFilterName() {
return (this.filterName);
}
public void setFilterName(String filterName) {
this.filterName = filterName;
}
/**
* The large icon associated with this filter. */ private String largeIcon = null;
public String getLargeIcon() {
return (this.largeIcon);
}
public void setLargeIcon(String largeIcon) {
this.largeIcon = largeIcon;
}
/**
* The set of initialization parameters for this filter, keyed by * parameter name. */ private Map parameters = new HashMap();
public Map getParameterMap() {
return (this.parameters);
}
/**
* The small icon associated with this filter. */ private String smallIcon = null;
public String getSmallIcon() {
return (this.smallIcon);
}
public void setSmallIcon(String smallIcon) {
this.smallIcon = smallIcon;
}
public void addInitParameter(String name, String value) {
parameters.put(name, value);
}
/**
* Render a String representation of this object. */ public String toString() {
StringBuffer sb = new StringBuffer("FilterDef[");
sb.append("filterName=");
sb.append(this.filterName);
sb.append(", filterClass=");
sb.append(this.filterClass);
sb.append("]");
return (sb.toString());
}
}
Each property in the FilterDef class represents a sub-element that can appear under the filter element. The class also has a Map named parameters that represents a Map containing all the initial parameters for this filter. The addInitParameter method adds a pair of initial parameter name/value
ApplicationFilterConfig
The org.apache.catalina.core.ApplicationFilterConfig implements the javax.servlet.FilterConfig interface. ApplicationFilterConfig manages the filter instances that are created when the web application is first started.
org.apache.catalina.core.ApplicationFilterConfig实现了javax.servlet.FilterConfig接口。ApplicationFilterConfig在Web应用程序首次启动时管理创建的过滤器实例。
You create an ApplicationFilterConfig object bypassing an org.apache.catalina.Context object and a FilterDef object to the ApplicationFilterConfig class's constructor:
您可以通过将org.apache.catalina.Context对象和FilterDef对象传递给ApplicationFilterConfig类的构造函数来创建ApplicationFilterConfig对象:
public ApplicationFilterConfig(Context context, FilterDef filterDef)
throws ClassCastException, ClassNotFoundException,
IllegalAccessException, InstantiationException, ServletException
The Context object represents a web application and the FilterDef object is the filter definition. The ApplicationFilterConfig class has the getFilter method that returns a javax.servlet.Filter object. This method loads the filter class and instantiates it.
Context对象表示一个Web应用程序,FilterDef对象是过滤器定义。ApplicationFilterConfig类具有getFilter方法,该方法返回一个javax.servlet.Filter对象。此方法加载过滤器类并实例化它。
Filter getFilter() throws ClassCastException, ClassNotFoundException,
IllegalAccessException, InstantiationException, ServletException {
// Return the existing filter instance, if any
if (this.filter != null)
return (this.filter);
// Identify the class loader we will be using
String filterClass = filterDef.getFilterClass();
ClassLoader classLoader = null;
if (filterClass.startsWith("org.apache.catalina."))
classLoader = this.getClass().getClassLoader();
else
classLoader = context.getLoader().getClassLoader();
ClassLoader oldCtxClassLoader =
Thread.currentthread().getContextClassLoader();
// Instantiate a new instance of this filter and return it
Class clazz = classLoader.loadClass(filterClass);
this.filter = (Filter) clazz.newInstance();
filter.init(this);
return (this.filter);
}
ApplicationFilterChain
The org.apache.catalina.core.ApplicationFilterChain class is the implementation of the javax.servlet.FilterChain interface. The invoke method in the StandardWrapperValve class creates an instance of this class and calls its doFilter method. The ApplicationFilterChain class's doFilter method calls the doFilter method of the first filter in the chain. The Filter interface's doFilter method has the following signature:
org.apache.catalina.core.ApplicationFilterChain
类是javax.servlet.FilterChain
接口的实现。
StandardWrapperValve
类中的i nvoke
方法创建了这个类的一个实例,并调用它的 doFilter
方法。
ApplicationFilterChain
类的 doFilter
方法调用了链中第一个过滤器的 doFilter
方法。
Filter
接口的 doFilter
方法具有以下签名:
public void doFilter(ServletRaquest request, ServletResponse response,
FilterChain chain) throws java.io.IOException, ServletException
The doFilter method of the ApplicationFilterChain class passes itself as the third argument to the filter's doFilter method.
ApplicationFilterChain
类的 doFilter
方法将自身作为第三个参数传递给过滤器的 doFilter
方法。
From its doFilter method, a filter can cause the invocation of another filter by explicitly calling the doFilter method of the FilterChain object. Here is an example of the doFilter method implementation in a filter.
从它的 doFilter
方法中,过滤器可以通过显式调用 FilterChain
对象的 doFilter
方法来引发对另一个过滤器的调用。
以下是一个过滤器中 doFilter
方法的实现示例。
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// do something here
...
chain.doFilter(request, response);
}
As you can see, the last line of the doFilter method is a call to the doFilter method of the FilterChain. If the filter is the last filter in the chain, this will cause the invocation of the requested servlet's service method. If the filter does not call chain.doFilter, the next filter will not be invoked.
如您所见,doFilter
方法的最后一行是对 FilterChain
的 doFilter
方法的调用。
如果过滤器是链中的最后一个过滤器,这将导致调用请求的 servlet
服务方法。
如果过滤器没有调用 chain.doFilter
,则不会调用下一个过滤器。
The Application
The application consists of two classes, ex11.pyrmont.core.SimpleContextConfig and ex11.pyrmont.startup.Bootstrap. The SimpleContextConfig class is a copy of the previous chapter's application. The Bootstrap class is given in Listing 11.2.
应用程序由两个类组成:ex11.pyrmont.core.SimpleContextConfig
和 ex11.pyrmont.startup.Bootstrap
。
SimpleContextConfig
类是上一章应用程序的副本。
清单 11.2 给出了 Bootstrap
类。
Listing 11.2: The Bootstrap class
清单 11.2:Bootstrap
类
package ex11.pyrmont.startup;
//use StandardWrapper
import ex11.pyrmont.core.SimpleContextConfig;
import org.apache.catalina.Connector;
import org.apache.catalina.Context;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Loader;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.http.HttpConnector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardWrapper;
import org.apache.catalina.loader.WebappLoader;
public final class Bootstrap {
public static void main(String[] args) {
System.setProperty("catalina.base",
System.getProperty("user.dir"));
Connector connector = new HttpConnector();
Wrapper wrapper1 = new StandardWrapper();
wrapper1.setName("Primitive");
wrapper1.setServletClass("PrimitiveServlet");
Wrapper wrapper2 = new StandardWrapper();
wrapper2.setName("Modern");
wrapper2.setServletClass("ModernServlet");
Context context = new StandardContext();
// StandardContext's start method adds a default mapper
context.setPath("/myApp");
context.setDocBase("myApp");
LifecycleListener listener = new SimpleContextConfig();
((Lifecycle) context).addLifecycleListener(listener);
context.addChild(wrapper1);
context.addChild(wrapper2);
// for simplicity, we don't add a valve, but you can add
// valves to context or wrapper just as you did in Chapter 6 Loader loader = new WebappLoader();
context.setLoader(loader);
// context.addServletMapping(pattern, name);
context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern");
// add ContextConfig. This listener is important because it
// configures StandardContext (sets configured to true), otherwise // StandardContext won't start connector.setContainer(context);
try {
connector.initialize();
((Lifecycle) connector).start();
((Lifecycle) context).start();
// make the application wait until we press a key.
System.in.read();
((Lifecycle) context).stop();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
The Bootstrap class creates an instance of StandardContext and calls it myApp. To the StandardContext, the Bootstrap class adds two StandardWrapper instances: Primitive and Modern
Bootstrap类创建了StandardContext的一个实例,并将其命名为myApp。在StandardContext中,Bootstrap类添加了两个 StandardWrapper
实例:Primitive
和 Modern
。
Running the Applications 运行应用程序
To run the application in Windows, from the working directory, type the following:
在 Windows
中,从工作目录中输入以下命令来运行应用程序:
java -classpath ./lib/servlet.jar;./lib/commons-collections.jar;./
ex11.pyrmont.startup.Bootstrap
In Linux, you use a colon to separate two libraries.
在Linux中,使用冒号来分隔两个库。
java -classpath ./lib/servlet.jar:./lib/commons-collections.jar:./
ex11.pyrmont.startup.Bootstrap
To invoke PrimitiveServlet, use the following URL in your browser.
要调用 PrimitiveServlet
,请在浏览器中使用以下 URL。
http://localhost:8080/Primitive
To invoke ModernServlet, use the following URL.
要调用 ModernServlet
,请使用以下 URL。
Summary
In this chapter you learned about the StandardWrapper class, the standard implementation of the Wrapper interface in Catalina. Also discussed was the filter and filter-related classes. An application that uses the StandardWrapper class was presented at the end of the chapter.
在本章中,您将了解到 StandardWrapper
类,它是 Catalina
中 Wrapper
接口的标准实现。
此外,还讨论了过滤器和过滤器相关类。
本章最后介绍了一个使用 StandardWrapper 类的应用程序。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。