Coder 爱翻译 How Tomcat Works 第五章 第一部分

[size=x-large]Chapter 5: Container[/size]

一个容器是一个为servlet处理请求和给客户端填充response对象的模块。一个容器可以用
org.apache.catalina.Container接口表示。这里有四种类型的容器:Engine, Host, Context 和Wrapper。这章包含了Context和Wrapper。把其它两个容器放在后面第十三章讲解。

[size=large]The Container Interface[/size]

一个容器必须实现了org.apache.catalina.Container。你看到了在第四章中,你传递一个Container实例给连接器的toContainer方法,这样连接器可以调用容器的invoke方法。回顾下面的Bootstrap类的代码:

HttpConnector connector = new HttpConnector();
SimpleContainer container = new SimpleContainer();
connector.setContainer(container);

第一件要注意的事情是在Catalina里面的容器在不同概念层次上的划分有下面四种类型的容器:

 Engine:
Represents the entire Catalina servlet engine. (代表整个Catalina的servlet engine)
 Host:
Represents a virtual host with a number of contexts.(代表一个有许多context的虚拟主机)
 Context:
Represents a web application. A context contains one or more wrappers.(代表一个web应用,一个context包含一个或更多wrapper)
 Wrapper:
Represents an individual servlet.(代表一个单独的servlet)

每个概念上的层次用org.apache.catalina包中的一个接口表示。这些接口是:Engine, Host, Context和 Wrapper。这四个接口都继承了Container接口。这四个容器的标准实现分别是:
StandardEngine, StandardHost, StandardContext和StandardWrapper。很具有代表性,他们都是org.apache.catalina.core包的一部分。

一个能完成应用功能的Catalina的部署不需要包含所有的这四种容器。例如:在这章的第一个应用程序的容器模块只包含了一个wrapper。第二个应用程序是一个拥有一个context和一个wrapper的容器模块。这章的应用程序中既没有host也没有engine。

一个容器可以有0个或更多低层次的的子容器。例如:一个context通常有一个或更多wrapper,一个host可以有0个或更多context。但是,一个wrapper是位于层次结构的最底层,他就不能包含子容器。增加一个子容器到一个容器中,你使用Container接口的addChild方法:

public void addChild(Container child);

从一个容器中移除一个子容器使用Container接口的removeChild方法:

public void removeChild(Container child);

此外,Container接口支持使用findChild和FindChildren方法找到一个容器的一个子容器和一个容器的所有子容器集合:

public Container findChild(String name);
public Container[] findChildren();

一个容器也可以包含很多个像后面要讲到的支持组件:Loader, Logger, Manager, Realm和Resources.这里不得不提一下Container接口提供的get和set方法来把容器自己和组件联系起来的方法:getLoader 和setLoader, getLogger 和setLogger, getManager 和
setManager, getRealm 和setRealm,getResources 和 setResources。

更有趣的是,Container接口通过这种方式来指定:在部署的时候,一个Tomcat的管理员(administrator)通过修改配置文件(server.xml)决定一个容器是完成什么事情的。而这又是通过在一个容器中引入一个pipeline和一套valve来实现的。

注意:Tomcat 4 的Container接口和Tomcat 5的稍有一点不同。例如:Tomcat 4 接口有一个map方法,而在Tomcat 5中就没有。

[size=large]Pipelining Tasks[/size]

一个pipeline包含了容器将要调用的任务(tasks)。一个valve代表一个具体的任务。一个容器的pipeline有一个(basic valve)基本的valve(阀),但是你还是可以添加任意多你想要添加的valve。valve的数量是由增加的valve的数量决定,但不包含basic valve。有趣的是,valve可以通过修改Tomcat的配置文件(server.xml)来动态地添加。

如果你了解servlet的filter,你就不难想象一个pipeline的valve是怎么工作的了。一个pipeline就像是一个filter chain,而每一个valve就是一个filter。就像filter一样,一个valve可以处理传递给它的request和response对象。当一个valve完成了处理工作,它就调用在pipeline中的下一个valve。而basic valve总是在最后被调用。

一个容器可以有一个pipeline。当一个容器的invoke方法被调用,容器传递处理任务到它的pipeline,这个pipeline就调用它的第一个valve,然后调用它后面的valve,依次调用到最后。
直到在pipeline中再也没有valve。你可以想象的到,你可以跟着下面的pipeline的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( ... );

但是,Tomcat的设计者选择了不同的方式,通过引入了org.apache.catalina.ValveContext接口。下面介绍它怎么工作的。

一个容器当它的invoke方法被连接器调用时,它打算做什么事情不能通过(hard code)硬编码实现。而是容器调用它的pipeline的invoke方法。Pipeline接口的invoke方法和Container接口的invoke方法相同:

public void invoke(Request request, Response response)
throws IOException, ServletException;

下面是Container接口的invoke方法在org.apache.catalina.core.ContainerBase类的实现

public void invoke(Request request, Response response)
throws IOException, ServletException {
pipeline.invoke(request, response);
}

pipeline是容器内部的Pipeline接口的实例。

现在,pipeline必须确认所有的valve被添加,basic valve必须被调用一次。pipeline通过创建一个ValveContext接口的实例来完成valve的添加确认。ValveContext是作为一个pipeline的内部类实现的,所以ValveContext可以访问pipeline的所有成员。ValveContext接口的最重要的方法是invokeNext方法:

public void invokeNext(Request request, Response response)
throws IOException, ServletException

创建了一个ValveContext实例后,pipeline调用ValveContext的invokeNext方法。ValveContext将首先调用pipeline中的第一个valve,然后第一个valve在它完成了任务前将调用下一个valve。ValveContext把它自己传递给每一个valve,所以valve可以调用ValveContext的invokeNext方法。

public void invoke(Request request, Response response,
ValveContext ValveContext) throws IOException, ServletException

一个valve的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
...
}

org.apache.catalina.core.StandardPipeline类是在所有容器的Pipeline的实现。在Tomcat 4中,这个类有一个叫做StandardPipelineValveContext的实现了ValveContext接口的内部类。

Listing 5.1: The StandardPipelineValveContext class in Tomcat 4

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

invokeNext方法使用下标(subscript)和状态(stage)来明确指出哪一个valve被调用过。当在pipeline的invoke方法第一个valve被调用时,下标值是0,状态值是1。因此,第一个valve(数组下标是0)被调用。在pipeline中的第一个valve接收ValveContext实例和调用它的invokeNext方法。这时,下标值是1,当然第二个valve被调用,以此类推。

当最后一个valve的invokeNext方法被调用时,下标值是等于valve的数量。最后,basic valve被调用。

Tomcat 5从StandardPipeline中移除了StandardPipelineValveContext类,而依靠org.apache.catalina.core.StandardValveContext。

Listing 5.2: The StandardValveContext class in Tomcat 5

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

你可以看见Tomcat 4中的StandardPipelineValveContext类和Tomcat 5 中的StandardValveContext类相似。

下面们将要详细讨论Pipeline, Valve和 ValveContext接口:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值