《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接口:

 

Java代码  收藏代码
  1. package org.apache.catalina;  
  2.   
  3. public interface Mapper {  
  4.     public Container getContainer();  
  5.     public void setContainer(Container container);  
  6.     public String getProtocol();  
  7.     public void setProtocol(String protocol);  
  8.     public Container map(Request request, boolean update);  
  9.   
  10.   
  11. }  

 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方法调用方式如下面的代码:

 

Java代码  收藏代码
  1. /*** 
  2.      * 该方法交给内部类中的invokeNext(Request,Response)处理 
  3.      */  
  4.     public void invoke(Request request, Response response) throws IOException,  
  5.             ServletException {  
  6.         (new SimplePipelineValveContext()).invokeNext(request, response);  
  7.     }  

 这个在“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方法是十分关键的,可以看下面代码:

 

Java代码  收藏代码
  1. public void invoke(Request request, Response response, ValveContext valveContext)  
  2.   throws IOException, ServletException {  
  3.   // Validate the request and response object types  
  4.   if (!(request.getRequest() instanceof HttpServletRequest) ||  
  5.     !(response.getResponse() instanceof HttpServletResponse)) {  
  6.     return;     // NOTE - Not much else we can do generically  
  7.   }  
  8.   
  9.   // Disallow any direct access to resources under WEB-INF or META-INF  
  10.   HttpServletRequest hreq = (HttpServletRequest) request.getRequest();  
  11.   String contextPath = hreq.getContextPath();  
  12.   String requestURI = ((HttpRequest) request).getDecodedRequestURI();  
  13.   String relativeURI =  
  14.     requestURI.substring(contextPath.length()).toUpperCase();  
  15.   
  16.   Context context = (Context) getContainer();  
  17.   // Select the Wrapper to be used for this Request  
  18.   Wrapper wrapper = null;  
  19.   try {  
  20.     wrapper = (Wrapper) context.map(request, true);  
  21.   }  
  22.   catch (IllegalArgumentException e) {  
  23.     badRequest(requestURI, (HttpServletResponse) response.getResponse());  
  24.     return;  
  25.   }  
  26.   if (wrapper == null) {  
  27.     notFound(requestURI, (HttpServletResponse) response.getResponse());  
  28.     return;  
  29.   }  
  30.   // Ask this Wrapper to process this Request  
  31.   response.setContext(context);  
  32.   wrapper.invoke(request, response);  
  33. }  

 三、ex05.pyrmont.core.SimpleContextMapper

   The SimpleContextMapper类(它实现了org.apache.catalina.Mapper接口注意这是在Tomcat4)主要设计的思想就是与SimpleContext实例相关联。

Java代码  收藏代码
  1. package ex05.pyrmont.core;  
  2.   
  3. import javax.servlet.http.HttpServletRequest;  
  4. import org.apache.catalina.Container;  
  5. import org.apache.catalina.HttpRequest;  
  6. import org.apache.catalina.Mapper;  
  7. import org.apache.catalina.Request;  
  8. import org.apache.catalina.Wrapper;  
  9.   
  10. public class SimpleContextMapper implements Mapper {  
  11.   
  12.   /** 
  13.    * The Container with which this Mapper is associated. 
  14.    */  
  15.   private SimpleContext context = null;  
  16.   
  17.   public Container getContainer() {  
  18.     return (context);  
  19.   }  
  20.   
  21.   public void setContainer(Container container) {  
  22.     if (!(container instanceof SimpleContext))  
  23.       throw new IllegalArgumentException  
  24.         ("Illegal type of container");  
  25.     context = (SimpleContext) container;  
  26.   }  
  27.   
  28.   public String getProtocol() {  
  29.     return null;  
  30.   }  
  31.   
  32.   public void setProtocol(String protocol) {  
  33.   }  
  34.   
  35.   
  36.   /** 
  37.    * Return the child Container that should be used to process this Request, 
  38.    * based upon its characteristics.  If no such child Container can be 
  39.    * identified, return <code>null</code> instead. 
  40.    * 
  41.    * @param request Request being processed 
  42.    * @param update Update the Request to reflect the mapping selection? 
  43.    * 
  44.    * @exception IllegalArgumentException if the relative portion of the 
  45.    *  path cannot be URL decoded 
  46.    */  
  47.   public Container map(Request request, boolean update) {  
  48.     // Identify the context-relative URI to be mapped  
  49.     String contextPath =  
  50.       ((HttpServletRequest) request.getRequest()).getContextPath();  
  51.     String requestURI = ((HttpRequest) request).getDecodedRequestURI();  
  52.     String relativeURI = requestURI.substring(contextPath.length());  
  53.     // Apply the standard request URI mapping rules from the specification  
  54.     Wrapper wrapper = null;  
  55.     String servletPath = relativeURI;  
  56.     String pathInfo = null;  
  57.     String name = context.findServletMapping(relativeURI);  
  58.     if (name != null)  
  59.       wrapper = (Wrapper) context.findChild(name);  
  60.     return (wrapper);  
  61.   }  
  62. }  

 如果你传过来的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功能代码十分相似,下面是其代码:

 

Java代码  收藏代码
  1. package ex05.pyrmont.startup;  
  2.   
  3. import ex05.pyrmont.core.SimpleContext;  
  4. import ex05.pyrmont.core.SimpleContextMapper;  
  5. import ex05.pyrmont.core.SimpleLoader;  
  6. import ex05.pyrmont.core.SimpleWrapper;  
  7. import ex05.pyrmont.valves.ClientIPLoggerValve;  
  8. import ex05.pyrmont.valves.HeaderLoggerValve;  
  9. import org.apache.catalina.Context;  
  10. import org.apache.catalina.Loader;  
  11. import org.apache.catalina.Mapper;  
  12. import org.apache.catalina.Pipeline;  
  13. import org.apache.catalina.Valve;  
  14. import org.apache.catalina.Wrapper;  
  15. import org.apache.catalina.connector.http.HttpConnector;  
  16.   
  17. public final class Bootstrap2 {  
  18.   public static void main(String[] args) {  
  19.     HttpConnector connector = new HttpConnector();  
  20.     Wrapper wrapper1 = new SimpleWrapper();  
  21.     wrapper1.setName("Primitive");  
  22.     wrapper1.setServletClass("PrimitiveServlet");  
  23.     Wrapper wrapper2 = new SimpleWrapper();  
  24.     wrapper2.setName("Modern");  
  25.     wrapper2.setServletClass("ModernServlet");  
  26.   
  27.     Context context = new SimpleContext();  
  28.     context.addChild(wrapper1);  
  29.     context.addChild(wrapper2);  
  30.   
  31.     Valve valve1 = new HeaderLoggerValve();  
  32.     Valve valve2 = new ClientIPLoggerValve();  
  33.   
  34.     ((Pipeline) context).addValve(valve1);  
  35.     ((Pipeline) context).addValve(valve2);  
  36.   
  37.     Mapper mapper = new SimpleContextMapper();  
  38.     mapper.setProtocol("http");  
  39.     context.addMapper(mapper);  
  40.     Loader loader = new SimpleLoader();  
  41.     context.setLoader(loader);  
  42.     // context.addServletMapping(pattern, name);  
  43.     context.addServletMapping("/Primitive""Primitive");  
  44.     context.addServletMapping("/Modern""Modern");  
  45.     connector.setContainer(context);  
  46.     try {  
  47.       connector.initialize();  
  48.       connector.start();  
  49.   
  50.       // make the application wait until we press a key.  
  51.       System.in.read();  
  52.     }  
  53.     catch (Exception e) {  
  54.       e.printStackTrace();  
  55.     }  
  56.   }  
  57. }  

 主方法开始实例化Tomcat 默认的连接器,两个wrappers,wrapper1,wrapper2.这些Wrappers的确定的名字是Primitive和Modern,而Primitiv,Modern对应的Servlet类分别是PrimitiveServlet,ModernServlet

 

 

Java代码  收藏代码
  1. HttpConnector connector = new HttpConnector();  
  2.    Wrapper wrapper1 = new SimpleWrapper();  
  3.    wrapper1.setName("Primitive");  
  4.    wrapper1.setServletClass("PrimitiveServlet");  
  5.    Wrapper wrapper2 = new SimpleWrapper();  
  6.    wrapper2.setName("Modern");  
  7.    wrapper2.setServletClass("ModernServlet");  

 然后,主方法创建了一个SimpleContext实例,SimpleContext容器又添加了wrapper1,wrapper2作为孩子容器。也实例化两个valve:ClientIPLogger,HeaderLoggerValve并且把两个Valve添加到SimpleContext容器中。

 

Java代码  收藏代码
  1. Context context = new SimpleContext();  
  2. context.addChild(wrapper1);  
  3. context.addChild(wrapper2);  
  4.   
  5. Valve valve1 = new HeaderLoggerValve();  
  6. Valve valve2 = new ClientIPLoggerValve();  
  7.   
  8. ((Pipeline) context).addValve(valve1);  
  9. ((Pipeline) context).addValve(valve2);  

 接下来,主方法就构造一个mapper对象(The SimpleMapper类),并把该组件添加到SimpleContext容器中。这个mapper组件主要负责找出在Context容器中的孩子容器(专门处理Http请求)

 

 

Java代码  收藏代码
  1. Mapper mapper = new SimpleContextMapper();  
  2. mapper.setProtocol("http");  
  3. context.addMapper(mapper);  

 对于加载一个servlet你就需要一个Loader.在这里你使用了SimpleLoader类,正如第一个应用程序那样。然而,The Loader 添加到The Context 容器中,这样就可以为两个wrappers共同使用这个加载器了。The Wrappers将使用getLoader找出the loader,因为the Context是the Wrapper的父容器。

Java代码  收藏代码
  1. Loader loader = new SimpleLoader();  
  2.     context.setLoader(loader);  
 

现在,该是时候添加servlet的匹配模式了。你要添加两个模式给这两个wrappers

 

 

Java代码  收藏代码
  1. context.addServletMapping("/Primitive""Primitive");  
  2. context.addServletMapping("/Modern""Modern");  

 最后,要把the Context容器传给the connector,然后启动the connector

 

Java代码  收藏代码
  1. connector.setContainer(context);  
  2.    try {  
  3.      connector.initialize();  
  4.      connector.start();  
  5.   
  6.      // make the application wait until we press a key.  
  7.      System.in.read();  
  8.    }  
  9.    catch (Exception e) {  
  10.      e.printStackTrace();  
  11.    }  
  12.  }  

 五、运行这个应用程序

     这里翻译我就不详细说了

 

 六、总结

     介绍完the connector后,The Container是第二个主要的模块。The Container使用许多的其他模块,如:Loader,Logger,Manager等等。这里也有四种类型的容器:Engine,Host,Context,Wrapper.一个Catalina部署是不会把四个容器全部的展现出来。在这章中的两个应用表明了一个部署可以有单个的wrapper容器或者是由多个Wrapper容器组成的Context容器。

0 0
原创粉丝点击