深度剖析tomcat简介
2016-06-21 16:11
731 查看
5.1 Container接口
container必须实现org.apache.catalina.Container接口。然后将container实例设置到connector的setContainer方法中。这样,connector就可以调用container的invoke方法了。示例代码如下:java代码:
查看复制到剪贴板打印
HttpConnector connector = new HttpConnector();
SimpleContainer container = new SimpleContainer();
connector.setContainer(container);
首先要注意的是,在tomcat中,共有四种类型的container,分别由不同的概念层次:
l engine:表示tomcat的整个servlet引擎;
l host:表示包含有一个或多个context的虚拟机;
l context:表示一个web应用。一个context中可以有多个wrapper;
l wrapper:表示一个独立的servlet。
上述的每个概念层级由org.apache.catalina包内的一个接口表示,分别是Engine,Host,Context和Wrapper,它们都继承自Container接口。这四个接口的标准实现分别是StandardEngine,StandardHost,StandardContext和StandardWrapper,都在org.apache.catalina.core包内。下面的uml图展示了各类的关系:
图表 10 Container接口及其实现类的UML图示
注意,所有的实现类都继承自ContainerBase类。
catalina中的部署并不是必须将所有四种container都包括在内。例如,本章中第一个应用的container模块仅仅用到了wrapper。
一个container可以有0个或多个低层级的子container。例如,一般情况下,一个context中会有一个或多个wrapper,一个host中会有0个或多个context。但是,wrapper处理层级的最底层,因此,它无法再包含子container了。可以通过Container接口的addChild方法添加子container,removeChild删除一个子container,此外,Container接口也支持对子container的查找,方法是findChild和findChildren。
container中可以包含一些支持的组件,如Loader,logger,Manger,Realm和Resources等,提供了getter和setter方法进行访问。这些组件将在后续的章节中讨论。
有趣的是,Container接口被设计成,在部署应用是,tomcat管理员可以通过编辑选项文件(server.xml)来决定使用哪种container。这是通过引入流水线(pipeline)和container中阀(valve)的集合实现的。
5.2 流水线(pipeline)任务
本节说明在connector调用了container的invoke方法后发生了什么事。按照相关的四个接口分为四个小节讨论,这四个接口都在org.apache.catalina包中,分别是Pipeline,Valve,ValveContext和Contained。pipeline包括了container中要执行的任务。一个阀(valve)表示一个指定的任务。在pipeline中有一个基础valve(basic valve),但是使用者可根据自己的需要添加其他的valve。注意,vavle的数量被定义为额外添加的valve的数量,不包括basic valve。有趣的是,valve可以通过修改server.xml来动态添加。
如果对servlet的过滤器有所了解的话,就不难理解pipeline和valve是如何工作的。pipeline就像是filter链,每个valve就像是一个过滤器。实际上,valve与过滤器类似,它可以控制传递给它的request和response对象。当一个valve处理结束后,它就调用pipeline中的下一个valve进行处理。basic valve总是最后被调用的。
container中有一个pipeline。当调用了container的invoke方法后,container将处理过程交给它的pipeline,而pipeline会 调用它的第一个valve,valve执行完后会调用后续的valve,知道所有的valve都调用结束。下面是伪代码:
java代码:
查看复制到剪贴板打印
// invoke each valve added to the pipeline for (int n=0; n<valves.length; n++) {
valve
.invoke( ... );
}
// then, invoke the basic valve
basicValve.invoke( ... );
但是,tomcat的设计者选择了另一种实现方法,通过引入接口org.apache.catalina.ValveContext来实现。当connector调用container的invoke方法后,container要做的事并没有硬编码写在invoke方法中,而是调用pipeline的invoke方法,该方法的签名与container的invoke方法相同:
java代码:
查看复制到剪贴板打印
public void invoke(Request request, Response response) throws IOException, ServletException;
下面是org.apache.catalina.core.ContainerBase的invoke实现:
public void invoke(Request request, Response response) throws IOException, ServletException {
pipeline.invoke(request, response);
}
其中pipeline是Pipeline接口的实例。
pipeline必须保证添加到其中的所有valve和basic valve都被调用一次。pipeline是通过创建一个ValveContext实例来实现的。ValveContext是作为pipeline的一个内部类实现的,这样,ValveContext实例就可以访问pipeline的所有成员了。ValveContext接口中最重要的方法是invokeNext:
java代码:
查看复制到剪贴板打印
public void invokeNext(Request request, Response response) throws IOException, ServletException;
在所有的container中,org.apache.catalina.core.StandardPipeline类实现了Pipeline接口,在该类中有一个内部类StandardPipelineValveContext实现了ValveContext接口。StandardPipelineValveContext的代码如下所示:
java代码:
查看复制到剪贴板打印
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。当第一个valve在pipeline的invoke方法中被调用时,subscript的值是0,stage的值为1。因此,第一个valve被调用。pipeline中的第一个valve接收ValveContext的实例,调用它的invokeNext方法。这时subscript的值为1,可以调用第二个valve。当invokeNext方法被最后一个valve调用时,subscript等于valve的数量,然后,调用basic valve。
tomcat5中从StandardPipeline取消了StandardPipelineValveContext,取而代之的是org.apache.catalina.core.StandardValveContext。代码如下:
java代码:
查看复制到剪贴板打印
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;
}
}
5.2.1 Pipeline
container调用pipeline的invoke方法开始对valve进行逐个调用。使用Pipeline接口的addValve方法可以添加新的valve,或调用removeValve方法删除对某个valve的调用。使用setBasicValve和getBasicValve可以对basic valve进行设置。Pipeline接口定义如下:
java代码:
查看复制到剪贴板打印
package org.apache.catalina;
import java.io.IOException;
import javax.servlet.ServletException;
public interface Pipeline {
public Valve getBasic();
public void setBasic(Valve valve);
public void addValve(Valve valve);
public Valve[] getValves();
public void invoke(Request request, Response response) throws IOException, ServletException;
public void removeValve(Valve valve);
}
5.2.2 Valve接口
Valve接口用于处理一个请求,包含了两个方法,invoke和getInfo。getInfo方法返回valve的实现信息Valve接口的定义如下:java代码:
查看复制到剪贴板打印
package org.apache.catalina;
import java.io.IOException;
import javax.servlet.ServletException;
public interface Valve {
public String getInfo();
public void invoke(Request request, Response response,
ValveContext context) throws IOException, ServletException;
}
5.2.3 ValveContext接口
该接口有两个方法,invokeNext和getInfo。接口定义如下:java代码:
查看复制到剪贴板打印
package org.apache.catalina;
import java.io.IOException;
import javax.servlet.ServletException;
public interface ValveContext {
public String getInfo();
public void invokeNext(Request request, Response response)
throws IOException, ServletException;
}
5.2.4 Contained接口
一个valve也可以实现org.apache.catalina.Contained接口,该接口将valve与某个container绑定。接口定义如下:java代码:
查看复制到剪贴板打印
package org.apache.catalina;
public interface Contained {
public Container getContainer();
public void setContainer(Container container);
5.3 Wrapper应用程序
org.apache.catalina.Wrapper接口表示一个wrapper层级的container,表示一个独立的servlet定义。Wrapper继承自Container接口,又添加了一些额外的方法。Wrapper接口的实现要负责管理servlet的生民周期,例如,调用init,service,destroy等方法。若是向Wrapper的addChild方法被调用,则抛出IllegalArgumantException异常。Wrapper接口中比较重要的方法是load和allocate方法。allocate方法会为wrapper分配一个servlet实例,而且,allocate方法还要考虑下servlet类是否实现了javax.servlet.SingleThreadModel接口(在11章讨论)。load方法载入并初始化servlet类。方法签名如下:
java代码:
查看复制到剪贴板打印
public javax.servlet.Servlet allocate() throws javax.servlet.ServletException;
public void load() throws javax.servlet.ServletException;
5.4 Context接口
Context表示了一个web应用,一个context中可以有一个或多个wrapper。Context接口比较重要的方法是addWrapper和createWrapper(在第十二章讨论)。
5.5 Wrapper程序实例
该程序展示了如何写一个最小的container模块。核心类是ex05.pyrmont.core.SimpleWrapper,实现了Wrapper接口。SimpleWrapper类中包含了一个Pipeline(由ex05.pyrmont.core.SimplePipeline实现),使用一个Loader(由ex05.pyrmont.core.SimpeLoader实现)载入servlet。pipeline中有一个basic valve(ex05.pyrmont.core.SimpleWrapperValve),和两个额外的valve(ex05.pyrmont.core.ClientIPLoggerValve和ex05.pyrmont.core.HeaderLoggerValve)。程序的uml图如下所示:图表 11 Wrapper程序UML示意图
注意:程序使用tomcat4默认的connector。
5.5.1 ex05.pyrmont.core.SimpleLoader
该类用于在container中载入servlet类。该类通过变量WEB_ROOT指明要在哪里查找servlet类。使用ClassLoader和Container,分别指明类载入器和container。SimpleLoader类的构造函数会初始化一个class loader,以便SimpleWrapper实例使用。代码如下:
java代码:
查看复制到剪贴板打印
public SimpleLoader() {
try {
URL[] urls = new URL[l];
URLStreamHandler streamHandler = null;
File classPath = new File(WEB_ROOT);
String repository = (new URL("file", null,
classPath.getCanonicalPath() + File.separator)).toString() ;
urls[0] = new URL(null, repository, streamHandler);
classLoader = new URLClassLoader(urls);
}
catch (IOException e) {
System.out.println(e.toString() );
}
}
5.5.2 ex05.pyrmont.core.SimplePipeline
SimplePipeline类实现了org.apache.catalina.Pipeline接口,最重要的是invoke方法,该方法中包含了一个内部类,SimplePipelineValveContext,该类实现了org.apache.catalina.ValveContext接口。
5.5.3 ex05.pyrmont.core.SimpleWrapper
该类实现了org.apache.catalina.Wrapper接口,提供了allocate和load方法的实现,声明了两个变量:private Loader loader;
protected Container parent = null;
loader变量指明了载入servlet要使用的loader实例。parent变量指明该wrapper的父container。注意其中的getLoader方法,代码如下:
java代码:
查看复制到剪贴板打印
public Loader getLoader() {
if (loader != null)
return (loader);
if (parent != null)
return (parent.getLoader());
return (null);
}
该方法返回一个用于servlet类的loader,若wrapper已经关联了一个loader,则直接将其返回,否则从父container处获取并返回,若还是没有,返回null。
SimpleWrapper有一个pipeline,需要通过setBasic方法为其设置一个basic valve。
5.5.4 ex05.pyrmont.core.SimpleWrapperValve
SimpleWrapperValve类实现了org.apache.catalina.Valve接口和org.apache.catalina.Contained接口,是一个basic valve,专用于为SimpleWrapper处理请求。其最重要的方法invoke如下所示:java代码:
查看复制到剪贴板打印
public void invoke(Request request, Response response, ValveContext valveContext) throws IOException, ServletException {
SimpleWrapper wrapper = (SimpleWrapper) getContainer();
ServletRequest sreq = request.getRequest();
ServletResponse sres = response.getResponse();
Servlet servlet = null;
HttpServletRequest hreq = null;
if (sreq instanceof HttpServletRequest)
hreq = (HttpServletRequest) sreq;
HttpServletResponse hres = null;
if (sres instanceof HttpServletResponse)
hres = (HttpServletResponse) sres;
//Allocate a servlet instance to process this request
try {
servlet = wrapper.allocate();
if (hres!=null && hreq!=null) {
servlet.service(hreq, hres);
} else {
servlet.service(sreq, sres);
}
} catch (ServletException e) { }
}
SimpleWrapperValve类作为basic valve使用,因此,其invoke方法不需要调用valveContext的invokeNext方法,它调用servlet的service方法,而不是wrapper类的。
5.5.5 ex05.pyrmont.valves.ClientIPLoggerValve
该类打印用户的ip信息。代码如下:注意其invoke方法,它先调用valveContext的invokeNext方法,然后再打印ip。
5.5.6 ex05.pyrmont.valves.HeaderLoggerValve
该类与ClientIPLoggerValve类似,打印请求头信息。代码如下:
5.5.7 ex05.pyrmont.startup.Bootstrap1
该类用于启动应用程序,代码如下:
5.6 Context程序实例
本节的程序展示了如何使用context和wrapper。在程序中是了一个mapper(一个组件)来帮助context选择某个wrapper来处理特殊的请求。注意:mapper组件仅在tomcat4中,tomcat5使用了其他的方法。
本例中,mapper是ex05.pyrmont.core.SimpleContextMapper类的实例,该类实现了org.apache.catalina.Mapper接口。container中可以包含有多个mapper来支持不同的请求协议。例如,一个mapper处理HTTP协议请求,另一个mapper处理HTTPS协议的请求。org.apache.catalina.Mapper接口定义如下所示:
java代码:
查看复制到剪贴板打印
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);
}
其中setProtocol和getProtocol指明了该mapper负责处理哪种协议,map方法返回使用哪个子container处理特殊的请求。相关类的UML图如下所示 :
图表 12 Context应用程序相关类 UML示意图
SimpleContext类表示context,SimpleContextMapper作为其mapper,SimpleContextValve作为其basic valve。context中有两个valve,ClientIPLoggerValve和HeaderLoggerValve,还有两个wrapper,都是SimpleWrapper,这两个wrapper都使用SimpleWrapperValve作为其basic valve,且不再添加其他valve。
在Context应用程序中,使用了相同的loader和valve,但是它们之间是通过context,而不是wrapper关联的,这样,所有的wrapper就都可以使用一个loader了。context作为connector的container使用,因此,当connector接收到一个http请求时,会调用context的invoke方法。剩下的步骤如下所示:
(1)container中包含一个pipeline,container的方法会调用pipeline的invoke方法;
(2)pipeline调用所有添加到其中的valve,最后调用basic valve;
(3)在wrapper中,basic valve负责载入servlet类,并相应http请求;
(4)在包含子container的context中,basic valve使用mapper找到某个子container负责处理http请求;若找到了这样的子container,则调用其invoke方法,调用第一步。
5.6.1 ex05.pyrmont.core.SimpleContextValve
该类中最重要的方法是invoke方法,具体实现如下:java代码:
查看复制到剪贴板打印
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;
}
// 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);
}
5.6.2 ex05.pyrmont.core.SimpleContextMapper
SimpleContextMapper类实现了org.apache.catalina.Mapper接口。代码如下:
5.6.3 ex05.pyrmont.core.SimpleContext
SimpleContext类是本程序的context实现,是分配给connector的主container,但是对每个独立servlet的处理是由wrapper完成的。本程序中有两个servlet,两个wrapper,它们都有对应的名字,PrimitiveServlet对应的wrapper的名字是Primitive,ModernServlet对应的wrapper的名字是Modern。SimpleContext是通过url映射来决定调用哪个wrapper的。本程序有两个url可以使用,其中“/Primitive”会调用Primitive,“/Modern”会调用Modern。你也可以添加自己的映射。Container和Context接口中有很多方法,在本程序的实现中,大部分都是空方法,但与映射有关的方法都实现了,这些方法是:
(1)addServletMapping,添加一个url和wrapper的映射;
(2)findServletMapping,通过url查找对应的wrapper;
(3)addMapper,在context中添加一个mapper。SimpleContext类中有两个变量mapper和mappers。mapper表示程序使用的默认mapper,mappers包含了SimpleContext实例中所有的mapper。第一个被添加到mappers中的mapper成为默认mapper;
(4)findMapper,找到正确的mapper,在SimpleContext中,它返回默认mapper;
(5)map,返回负责处理当前请求的wrapper。
5.6.4 ex05.pyrmont.startup.Bootstrap2
转自:http://sishuok.com/forum/blogPost/list/4089.html注:有一本书,《深入剖析tomcat》这本书很好,大家可以看一下。
相关文章推荐
- java-模拟tomcat服务器
- i-jetty环境搭配与编译
- Android Native 绘图方法
- Python动态类型的学习---引用的理解
- 实现单Tomcat多Server配置
- 生产环境下的Tomcat配置
- Linux部署Tomcat服务器
- jenkins------结合maven将svn项目自动部署到tomcat下
- 如何搞定tomcat这只喵~
- 土人系列AS入门教程 -- 对象篇
- C#托管堆对象实例包含内容分析
- C#中struct和class的区别详解
- C#实现获取不同对象中名称相同属性的方法
- javascript asp教程第十一课--Application 对象
- PowerShell中使用Out-String命令把对象转换成字符串输出的例子
- VBS ArrayList Class vbs中的数组类
- VBS教程:对象-正则表达式(RegExp)对象
- 大家看了就明白了css样式中类class与标识id选择符的区别小结
- C#检查指定对象是否存在于ArrayList集合中的方法