一、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容器。