《How Tomcat Works》翻译(3)之Context容器
来源:互联网 发布:淘宝联盟网 编辑:程序博客网 时间:2024/06/05 08:32
http://he-wen.iteye.com/blog/833969
一、The Context应用
在这章的第一个应用程序(Wrapper容器),你已经学会了怎样部署一个仅仅只有一个wrapper容器的简单Web应用程序。这个程序只有一个servlet。尽管在一些应用程序中可能只需要仅仅一个servelt,但是大部分应用程序是需要多个servlet.因此在本应用程序中有增加了一个类型的容器(Context容器,它是Wrapper的父类容器)。
本应用程序阐明了怎样用一个Context容器(该容器包含两个Wrapper容器即两个servlet类)。因此,你拥有了更多的Wrapper容器,就需要借助一个组件(Mapper),帮助容器(Context容器)选择一个要处理特定请求的Request的孩子容器(Wrapper容器)。
注意:A Mapper组件出现在Tomcat4中,但是在Tomcat5中该组件移除了,引用了另外一种方式来查找孩子容器(Wrapper容器)。
在本应用程序中,你的Mapper实例是ex05.pyrmont.core.SimpleContextMapper类,它实现了org.apache.catalina.Mapper接口(这是在Tomcat4)。一个容器也能够用多个mappers(为了支持不同种类的协议)。在本应用程序中一个mapper支持一个request协议。比如,一个容器可能一个mapper对应Http协议,而另外一个mapper对应了the Https协议。下面代码是Tomcat4提供的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方法返回就是容器与mapper相关联,The setContainer方法就是设置the mapper与 the Container相关联。The getProtocol方法返回the mapper负责处理的协议,setProtocol方法就是把协议的名称赋给负责处理协议的the mapper.The map方法返回一个将要处理特定请求的孩子容器。
下面是本应用程序的类图:
注解:一定要把类图和下面的一段看明白,才能够真正的理解父容器和孩子容器之间的关系和设计者的思路,这个十分重要。他们是借助the mapper组件来进行联系。。。
The SimpleContext代表一个Context容器,The SimpleContextMapper作为mapper组件,the SimpleContextValve作为一个basic valve.两个Valve分别是:ClientIPLogger、HeaderLoggerValve全部添加到The Context容器。两个Wrapper(都是由SimpleWrapper组成)被添加到The COntext容器里(就是作为它的孩子容器)。The wrappers使用SimpleWrapperValve作为他们的basic valve但是没有添加其他的valves.
The Context 应用程序使用了上个应用程序一样的的Loader和两个Valves。然而,The Loader和Valves是与the Context容器相关联而不是与Wrapper容器相关。注解:理解这里十分重要。因此
the Loader能够被两个Wrapper容器公用。The Context被赋给了The connector。因此,只要The Connector接收到一个Http请求,它就会调用the Context的invoke方法。如果你已经对上个程序已经领悟到了其中的精髓,那么剩下的调用关系就很容易理解了。
1、一个Container要有一个pipeline.The Container的Invoke方法就会调用the pipeline的Invoke方法。
2、The pipeline的Invoke方法就会调用添加整个Container的所有Valve中的Invoke方法。
3、在一个Wrapper容器里,The basic valve负责加载与之关联的servlet的类以及相应The Request请求。
4、在拥有孩子容器的Context,The basic Valve使用mapper组件目的就是找出负责处理The request请求的孩子容 器。如果能够找到孩子容器,那么the Context 容器就会调用孩子容器的the invoke方法。然后有返回到第一步。
注解:这四个过程十分的重要,一定充分的了解。不理解的时候要反复的看,或者就是跟踪源码,这种设计思想非常值得学习。
现在让我们看看实现处理的顺序。
The SimpleContext类中的Invoke方法调用了the pipeline的invoke方法
public void invoke(Request request,Response response)
throws IOException,ServletException{
pipeline.invoke(request,response);
}
The pipeline是有the SimplePipeline类代表,他的invoke方法调用方式如下面的代码:
- /***
- * 该方法交给内部类中的invokeNext(Request,Response)处理
- */
- public void invoke(Request request, Response response) throws IOException,
- ServletException {
- (new SimplePipelineValveContext()).invokeNext(request, response);
- }
这个在“Pipelining Task”部分已经解释了,这里就不解释。这个代码调用了所有添加在子容器的(Wrapper)的Valves的the invoke方法然后又调用了the basic valve的Invoke方法。在 SimpleContext容器中,The SimpleContextValve类代表the basic Valve。在调用这个基本阀门中的invoke方法时,the SimpleContextValve使用了the context 中的组件the mapper 查找一个wrapper:
Wrapper wrapper=null;
try{
wrapper=(Wrapper)context.map(request,true);
}
如果the Wrapper找到了,就会调用The Wrapper的invoke方法:
wrapper.invoke(request,response);
在本应用程序中一个Wrapper是由the SimpleWrapper类代表的。下面SimpleWrapper中的the invoke方法代码与the SimpleContext类的invoke方法的代码是一样的.
public void invoke(Request request,Response response)
throws IOException,ServletException{
pipeline.invoke(request,response);
}
The pipeline是The SimplePipeline的实例(他的invoke方法已经在上面讨论了)。特别注意的是,本应用程序的The Wrappers没有the valves除了有the basic valve(它由The SimpleWrapperValve类代表)。The wrapper的pipeline调用了the SimpleWrapperValve类的invoke方法(主要是分配一个servlet以及调用该servlet的service方法)详细解释已经在“The Wrapper应用程序”解说了。
还需要注意的是the wrapper容器是没有和a loader关联,而是与the context 容器关联的。因此the SimpleWrapper的getLoader方法返回父类中的loader.
这里有四个类(SimpleContext,SimpleContextValve,SimpleContextMapper,Bootstrap2)在第一个应用程序没有解释,下面将会详细讨论这四个类。
二、ex05.pyrmont.core.SimpleContextValve
这个类代表了the SimpleContext容器的the basic valve,他的the 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; // NOTE - Not much else we can do generically
- }
- // 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类(它实现了org.apache.catalina.Mapper接口注意这是在Tomcat4)主要设计的思想就是与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 {
- /**
- * The Container with which this Mapper is associated.
- */
- 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) {
- }
- /**
- * Return the child Container that should be used to process this Request,
- * based upon its characteristics. If no such child Container can be
- * identified, return <code>null</code> instead.
- *
- * @param request Request being processed
- * @param update Update the Request to reflect the mapping selection?
- *
- * @exception IllegalArgumentException if the relative portion of the
- * path cannot be URL decoded
- */
- 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 Container不是the SimpleContext实例,那么The setContainer方法就会抛出一个 IllegalAgumentException异常。the map 方法返回了一个孩子容器(A wrapper只要负责处理the Request请求)。The Map 方法需要传两个参数(a request对象和一个布尔值)。这个实现忽略了第二个参数。该方法主要任务就是检索来自the request 对象的the context路径,然后使用了the Context的findServletMapping方法获得与路径相关联的名字。如果名字找到了,那么就是用the Context中的findChild方法获得一个Wrapper实例。
四、ex05.prymont.core.SimpleContext
在本应用程序中The SimpleContext类实现了The context接口。它是主要的容器,并且把自己赋给了the Connector.然而,在处理每个servlet是由a wrapper容器负责。本应用程序有两个servlet(PrimitiveServlet,ModernServlet),因此就需要两个Wrapper,他们的名字分别为Primitive、Modern。对于The SimpleContext决定每一次The request请求需要哪个wrapper的条件是:一定要the request URL 模式要与the wrapper的名字相匹配。在本应用程序中,我们有两个URL模式分别是用来调用两个wrapper的先决条件。第一个模式是/Primitive,它与the wrapper Primitive相匹配。第二个模式是/Modern,它与The Wrapper Modern相匹配。当然,你能够使用更多的模式给一个确定的servlet。你仅仅需要添加这些模式。
The SimpleContext类实现了The Container和Context接口,所有存在许多方法。但是大部分方法是空实现的方法,然而与mapping相关的方法才是真正的代码(也就是本应用程序所需要的代码),这些方法可以看下面的描述.
1、addServletMapping 添加一个URL pattern/wrapper匹配对。你添加的每个模式对是要调用哪个wrapper的一个先决条件。即The Context容器会根据模式匹配对来决断用哪个Wrapper容器(他的孩子容器)
2、findServletMapping 通过一个URL模式获得the Wrapper的名字。这个方法就是根据URL模式找出要调用哪个Wrapper。如果用户用的URL模式没有在addServleyMapping方法添加,那么使用此方法返回null.
3、 addMapper 添加一个组件mapper到the Context容器中。The SimpleContext类拥有the mapper和mappers两个成员变量。mapper是一个默认的mapper组件,mappers包含了The SimpleContext实例所有的mapper
。The first mapper添加到了这个SimpleContext中变成了一个默认的mapper组件。
4、findMapper 找出一个正确的mapper组件。在 SimpleContext类中该方法是返回一个默认的组件mapper.
5、map 返回一个负责处理的request请求的wrapper容器
除此之外,SimpleContext类也实现了addChild,findChild,findChildren方法。The addChild方法就是把一个wrapper添加到the context中,the findChild方法通过一个具体的名字找出一个wrapper,findChildren方法是返回在SimpleContext实例中的所有的wrapper容器即它的所有孩子容器。
四、ex05.pyrmont.startup.Bootstrap2
The Bootstrap2类是一个启动本应用程序,这个类与BootStrap1功能代码十分相似,下面是其代码:
- 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 ex05.pyrmont.valves.HeaderLoggerValve;
- 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();
- }
- }
- }
主方法开始实例化Tomcat 默认的连接器,两个wrappers,wrapper1,wrapper2.这些Wrappers的确定的名字是Primitive和Modern,而Primitiv,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");
然后,主方法创建了一个SimpleContext实例,SimpleContext容器又添加了wrapper1,wrapper2作为孩子容器。也实例化两个valve:ClientIPLogger,HeaderLoggerValve并且把两个Valve添加到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);
接下来,主方法就构造一个mapper对象(The SimpleMapper类),并把该组件添加到SimpleContext容器中。这个mapper组件主要负责找出在Context容器中的孩子容器(专门处理Http请求)
- Mapper mapper = new SimpleContextMapper();
- mapper.setProtocol("http");
- context.addMapper(mapper);
对于加载一个servlet你就需要一个Loader.在这里你使用了SimpleLoader类,正如第一个应用程序那样。然而,The Loader 添加到The Context 容器中,这样就可以为两个wrappers共同使用这个加载器了。The Wrappers将使用getLoader找出the loader,因为the Context是the Wrapper的父容器。
- Loader loader = new SimpleLoader();
- context.setLoader(loader);
现在,该是时候添加servlet的匹配模式了。你要添加两个模式给这两个wrappers
- context.addServletMapping("/Primitive", "Primitive");
- context.addServletMapping("/Modern", "Modern");
最后,要把the Context容器传给the connector,然后启动the connector
- 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 connector后,The Container是第二个主要的模块。The Container使用许多的其他模块,如:Loader,Logger,Manager等等。这里也有四种类型的容器:Engine,Host,Context,Wrapper.一个Catalina部署是不会把四个容器全部的展现出来。在这章中的两个应用表明了一个部署可以有单个的wrapper容器或者是由多个Wrapper容器组成的Context容器。
- 《How Tomcat Works》翻译(3)之Context容器
- How Tomcat works 翻译(2) 之 Wrapper容器
- 《how tomcat works》翻译开篇
- How Tomcat Works 3
- 《How To Tomcat Works》-第五章 容器
- how tomcat works 五 servlet容器 上
- how tomcat works 5 servlet容器 下
- How tomcat works——5 容器
- How Tomcat works 之The Application 应用
- How Tomcat works 之 StandardWrapper 标准包装
- How Tomcat works之StandardContext 标准上下文
- How Tomcat works之 Host and Engine
- How Tomcat Works之ex01时序图
- 《How Tomcat Works》读书笔记(四):容器初探
- 《How Tomcat Works》读书笔记(四):容器初
- 《How Tomcat Works》读书笔记(四):容器初探
- how tomcat works 读书笔记(二)----------一个简单的servlet容器
- 《How Tomcat Works》读书笔记(四)-容器附加
- stm32DMA通道 ADC通道
- Tomcat Context配置
- char *p = "123"和char a[] = "123"根本就不同
- UVA-1388
- struts2文件上传突破2M限制
- 《How Tomcat Works》翻译(3)之Context容器
- Robotium中调用getActivity()方法导致程序挂起的研究浅析
- DirectX 11游戏编程学习笔记之9: 第7章Lighting(光照)
- oracle日志
- jsp取不到值栈的值
- UVA 10881
- 2、类变量@protected ,@private,@public,@package,声明各有什么含义?
- 欢迎进群大家以后一起交流 CSharp、.Net技术交流群 242497960
- 全球速卖通教你怎么样分析数据纵横-行业情报选