|
一个container是一种模块,处理对servlet的请求并且返回response对象给web client端. 一个container由org.apache.catalina.Container接口定义.一共有四种类型的container:engine,host,context和wrapper.本章将会介绍context和wrapper,剩下两种会在13章中介绍.本章开始于对container接口的讨论,接着讲述在container中pipelining的原理.然后具体介绍wrapper和context接口.本章结尾给出了两个应用程序,分别实现了简单的wrapper和context.
1 Container接口
Container必须实现org.apache.catalina.Container接口。有四种不同概念层次的container,
Engine:代表整个Catalina servlet引擎。
Host:代表一个虚拟host,其中包含若干context。
Context:代表一个web application,其中可包含若干wrapper。
Wrapper:代表一个独立的servlet。
在org.apache.catalina.core包中,StandardEngine, StandardHost, StandardContext, StandardWrapper是四个标准实现。
这四种引擎不都是必须的。比如本章的第一个application只包含一个wrapper。第二个application包含一个context和wrapper。
一个Container可以有0个或多个低一层的child container。比如,一个context可以有若干个wrapper,host可以有若干context。因为wrapper在最底层,所以是没有child container的。加减一个孩子的方法在接口中有定义,
public void addChild(Container child)
public void removeChild(Container child)
Container接口还可以查找child container,
public Container findChild(String name)
public Container[ ] findChildren()
Container还有一些辅助性的组件,比如Loader, Logger, Manager, Realm, Resources,后面几章会有介绍。Container接口对这些组件有相应的get set方法。
需要强调的是,Tomcat管理员可以在configuration文件(server.xml)中设置container的性能。这是通过container中的pipeline和一组valve(阀门,管子)来实现的。下一节“Pipelining Tasks”会有讨论。
2、Pipelining Tasks
这部分内容讲解了connector调用的container的invoke方法都具体做了什么。这一节会介绍4个相关的接口:Pipeline, Valve, ValveContext, Contained。他们都在包org.apache.catalina中。
Pipeline包含Container调用的所有的任务。一个valve代表一个具体的任务。在container的pipeline中有一个基本的valve,但是你可以加入任意多的valve。valve的个数是指后加入的个数,并不包括那个基本的valve。有趣的是,valve可以动态的加入到Tomcat的configuration文件(server.xml)中。
pipeline和valve理解起来很象是servlet filter。pipeline是一个filter chain,每一个valve是一个filter。一个valve的任务完成后,下一个valve的任务会被调用。那个basic valve总是被最后调用。可以想象,这个过程可以用下面的伪代码描述:
for (int n=0;n<valve.length;n++) {
valve[n].invoke(...);
}
basicValve.invoke(...);
但是,Tomcat并没有用上述方式,而是引入了org.apache.catalina.ValveContext接口。Container的invoke()方法被调用时,里面调用了pipeline的invoke()方法。pipeline里的invoke方法有着和Container中一样的定义:
public void invoke(Request request, Response response) throws...
pipeline作为一个属性关联到一个Container对象。
现在pipeline需要确定每一个关联到他的valve都被执行一次。pipeline通过ValveContext接口的一个实例来完成这个任务。ValveContext作为pipeline的一个内部类实现,所以他可以访问到pipeline中的每一个属性。
ValveContext中最重要的方法是:
public void invokeNext(Request request, Response response) throws....
Valve中的invoke()方法为:
public void invoke(Request request, Response response, ValveContext vc)....
valveContext的invokeNext先调用第一个valve的invoke方法,并且他自己传给valve的invoke方法,然后再由valve的invoke来调用valveContext的invokeNext()。
简言之,valvecontext, pipeline, valve 类之间的关系如下:
class pipeline {
valve[] avalve;
function invoke(request, response) {
new valvecontext().invokeNext();
}
inner class valvecontext {
invokeNext(request, response) {
avalve.invoke(request, response, this);
}
}
}
class valve {
invoke(request, response, valvecontext) {
// TO DO ...
this.invokeNext(request, response);
}
}
pipeline和container的关系是:pipeline是container的一个成员变量
用这种方式,多个valve依次被调用。
==========================================
PS: 上述3个类 和 container 又有什么关系?
==========================================
3、The Wrapper Application
这里需要提一下将在12章中介绍的Context接口。这个接口代表了一个web application。他通常含有多个wrapper作为他的孩子。重要的方法包括addWrapper, createWrapper...
转入正题。
本章wrapper的一个简单的实现是 ex05.pyrmont.core.SimpleWrapper。包含了一个Pipeline(ex05.pyrmont.core.SimplePipeline), 一个Loader(ex05.pyrmont.core.SimpeLoader)来加载一个servlet。
这个pipeline中包含了一个基本的valve(SimpleWrapperValve)和两个additional的valve(ClientIPLoggerValve , HeaderLoggerValve)。
这个loader的工作过程 和 前几章的加载类的过程完全一样。只是功能给抽离出来而已。
SimpeWrapperValve的重要作用。
作为基本的valve。SimpleWrapperValve的做用是 调用 servlet的service方法。方法调用的地方是在valve 的 invoke方法中。
另外两个附加的valve,作用就是在simplewrappervalve调用invoke之前,做点事情。(filter的功能应该就是在这里实现的吧)。
ex05.pyrmont.startup.Bootstrap1 启动类的实现如下:
HttpConnector connector = new HttpConnector();//新建连结器
Wrapper wrapper = new SimpleWrapper();//新建容器,初始化方法会设置basicValve
wrapper.setServletClass("ModernServlet");//设置容器中的servlet
Loader loader = new SimpleLoader();//新建类装载器
Valve valve1 = new HeaderLoggerValve();//新建additional valve
Valve valve2 = new ClientIPLoggerValve();//新建additional valve
wrapper.setLoader(loader);// 设置容器的类装载器
((Pipeline) wrapper).addValve(valve1);//设置容器的additional valve
((Pipeline) wrapper).addValve(valve2);//设置容器的additional valve
connector.setContainer(wrapper);//设置连结器对应的容器
try {
connector.initialize();
connector.start();
}
catch (Exception e) {
}
4、 The Context Application
这个应用程序展示了包含两个wrapper的容器(也即两个servelt)。一个以上的wrapper就需要引入一种新的组件,叫做mapper。mapper组件的作用是根据特定的request来选择哪个wrapper来处理。
mapper的接口定义如下:
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);//用来返回一个child container
}
回忆一下,从container层次的角度来说,wrapper在最底层,context在wrapper层之上。
本章中这个context的例子,用了一个loader和两个valves。这三个组件都和context相关联。这样他们可以被底层的wrapper所共享。这个context被指定为connector的容器。下面列出的是这个容器(即context)的执行顺序:
a. context有一个pipeline。这个container的invoke方法调用pipeline的invoke方法。
b. pipeline的invoke方法调用所有context的valve的invoke方法。
c. 在context层的basic valve中,用mapper找到一个child wrapper来处理特定的请求
d. 找到了child wrapper后,wrapper的各个valve在被依次调用。
ex05.pyrmont.core.SimpleContextValve
ex05.pyrmont.core.SimpleContextMapper
ex05.pyrmont.core.SimpleContext
这三个类分别就是 context层的basic valve、context的孩子wrapper的影射、context层 的具体的实现类。
启动类bootstrap2.java的主要部分如下:
HttpConnector connector = new HttpConnector();//新建连结器
Wrapper wrapper1 = new SimpleWrapper();//新建一个wrapper
wrapper1.setName("Primitive");
wrapper1.setServletClass("PrimitiveServlet");
Wrapper wrapper2 = new SimpleWrapper();//新建另一个wrapper
wrapper2.setName("Modern");
wrapper2.setServletClass("ModernServlet");
Context context = new SimpleContext();//新建一个context
context.addChild(wrapper1);//为context添加一个wrapper
context.addChild(wrapper2);//为context再添加一个wrapper
Valve valve1 = new HeaderLoggerValve();//新建valve
Valve valve2 = new ClientIPLoggerValve();//新建valve
((Pipeline) context).addValve(valve1);// context层添加valve
((Pipeline) context).addValve(valve2);// context层添加valve
Mapper mapper = new SimpleContextMapper();//新建一个mapper
mapper.setProtocol("http");
context.addMapper(mapper);
Loader loader = new SimpleLoader();//新建一个loader
context.setLoader(loader);
// context.addServletMapping(pattern, name);
context.addServletMapping("/Primitive", "Primitive");//建立wrapper的mapping
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();
}
运行启动类后,在ie中请求 http://localhost:8080/Modern http://localhost:8080/Primitive 。容器context接受请求。在执行完context层的valve后,会根据 /Modern /Primitive 来找到对应的wrapper,来继续执行,进而调用具体的servlet。
-- 完毕 欢迎各位同仁共同交流:) |
|
|