Coder 爱翻译 How Tomcat Works 第五章 第二部分

[size=large]The Pipeline Interface[/size]

我们提到的Pipeline接口的第一个方法invoke方法,这个方法是容器用来调用在pipeline中的valve和basic valve的。Pipeline接口允许你使用它的addValve方法来添加一个新的valve和使用removeValve方法来移除一个valve。最后,你使用它的setBasic方法给pipeline来指定一个basic valve和它的getBasic方法获取basic valve。basic valve是在最后被调用的,它负责处理request和相应的response。下面是Pipeline接口:

Listing 5.3: The Pipeline interface

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);
}


[size=large]The Valve Interface[/size]

Valve接口代表了一个valve,这个组件负责处理一个request。这个接口有两个方法:invoke和getInfo。invoke方法在前面已经讨论了。getInfo方法返回关于valve实现的信息。


Listing 5.4: The Valve interface
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;
}


[size=large]The ValveContext Interface[/size]

这个接口有两个方法:invokeNext方法在前面讨论了,getInfo方法返回ValveContext实现的信息。

Listing 5.5: The ValveContext interface

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;
}


[size=large]The Contained Interface[/size]

一个valve类可以选择性地实现org.apache.catalina.Contained接口。这个接口表明实现了该接口的类是最多和一个容器实例相关联的。

Listing 5.6: The Contained interface

package org.apache.catalina;
public interface Contained {
public Container getContainer();
public void setContainer(Container container);
}


[size=large]The Wrapper Interface[/size]

org.apache.catalina.Wrapper接口代表了一个wrapper。一个wrapper代表了一个独立的servlet定义的容器。Wrapper接口继承自Container,再添加了一些其他方法。Wrapper的实现负责管理它们底层的servlet类的servlet生命周期。例如:调用servlet的init,service和destroy方法。一个wrapper是容器的最底层,你就不能向它里面添加子容器。如果你向它里面调用addChild方法添加子容器,这时Wrapper会抛出一个IllegalArgumantException异常。

Wrapper接口的重要方法包括:allocate和load。allocate方法分配一个这wrapper所代表的servlet初始化实例。allocate方法必须考虑到这个servlet是否是实现了javax.servlet.SingleThreadModel接口,我们在后面十一章讨论。load方法加载和实例化一个wrapper所代表的servlet。

public javax.servlet.Servlet allocate() throws
javax.servlet.ServletException;

public void load() throws javax.servlet.ServletException;


[size=large]The Context Interface[/size]

一个context是代表了一个web应用的容器。一个context通常有一个或多个子容器。
重要的方法包括addWrapper,createWrapper等。这接口将在十二章详细讨论。

[size=large]The Wrapper Application[/size]

这个应用程序演示怎么编写一个小的容器模块。核心类是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)

类图:
[img]http://dl.iteye.com/upload/attachment/359700/e761b13a-b788-37ed-93c4-bd26443f9b1f.jpg[/img]

wrapper包装了在前几章使用过的ModernServlet。这个应用程序证实了你可以使用一个只有一个wrapper组成的servlet容器。

[size=large]ex05.pyrmont.core.SimpleLoader[/size]

在容器中加载servlet类的任务分配给Loader来完成这个功能。在这个应用中,SimpleLoader类就是Loader实现。它知道servlet类的路径,它的getClassLoader方法返回一个java.lang.ClassLoader实例来搜索servlet类的路径。SimpleLoader类声明了三个变量。第一个是WEB_ROOT,它指向能找到servlet类所在的目录。

public static final String WEB_ROOT =
System.getProperty("user.dir") + File.separator + "webroot";

其他两个变量时ClassLoader和Container的对象引用。

ClassLoader classLoader = null;
Container container = null;

SimpleLoader类的构造函数初始化类加载器,并返回给SimpleWrapper实例。

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() );
}
}

构造函数里面的代码用来初始化应用程序中的类加载器。
容器的变量代表了与这个加载器相关联的容器。Loader会在第八章讨论。

[size=large]ex05.pyrmont.core.SimplePipeline[/size]

SimplePipeline类实现了org.apache.catalina.Pipeline接口。最重要的方法是invoke方法,里面包含一个内部类SimplePipelineValveContext。SimplePipelineValveContext实现了org.apache.catalina.ValveContext接口。

[size=large]ex05.pyrmont.core.SimpleWrapper[/size]

这个类实现了org.apache.catalina.Wrapper接口和提供了allocate和load方法的实现。它还声明了下面的变量:

private Loader loader;
protected Container parent = null;

loader变量是一个Loader,它用来加载servlet类。parent变量代表一个这个wrapper的父容器。这意味着wrapper可以是另一个容器的子容器(就像是Context的子容器)。注意一下getLoader方法:

public Loader getLoader() {
if (loader != null)
return (loader);
if (parent != null)
return (parent.getLoader());
return (null);
}

getLoader方法返回一个用来加载servlet类的Loader。如果wrapper和一个Loader相关联,这个Loader将被返回。如果没有相关的,就返回Loader的父容器。如果没有preant变量,getLoader就返回null。

SimpleWrapper类有一个pipeline并在pipeline中设置了一个basic valve。

public SimpleWrapper() {
pipeline.setBasic(new SimpleWrapperValve());
}

这里,pipeline是一个在类中声明的SimplePipeline实例:

private SimplePipeline pipeline = new SimplePipeline(this);


[size=large]ex05.pyrmont.core.SimpleWrapperValve[/size]

SimpleWrapperValve类是basic valve用来为SimpleWrapper类处理请求。它实现了org.apache.catalina.Valve接口和org.apache.catalina.Contained接口。在SimpleWrapperValve中最重要的方法invoke方法。

Listing 5.9: The SimpleWrapperValve class's invoke method

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)
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方法。invoke方法调用SimpleWrapper类的allocate方法获取一个代表wrapper的servlet实例。注意wrapper中pipeline的basic valve调用servlet的service方法。而不是调用wrapper它自己。

[size=large]ex05.pyrmont.valves.ClientIPLoggerValve[/size]

ClientIPLoggerValve类是一个valve,它是打印客户端的IP地址到控制台。

Listing 5.10: The ClientIPLoggerValve class
package ex05.pyrmont.valves;

import java.io.IOException;
import javax.servlet.ServletRequest;
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.Contained;
import org.apache.catalina.Container;

public class ClientIPLoggerValve implements Valve, Contained {
protected Container container;
public void invoke(Request request, Response response,
ValveContext valveContext) throws IOException, ServletException {
// Pass this request on to the next valve in our pipeline
valveContext.invokeNext(request, response);
System.out.println("Client IP Logger Valve");
ServletRequest sreq = request.getRequest();
System.out.println(sreq.getRemoteAddr());
System, out.println("-----------------------------------");
}
public String getInfo() {
return null;
}
public Container getContainer() {
return container;
}
public void setContainer(Container container) {
this.container = container;
}
}

注意invoke方法。第一件要做的事是invoke方法调用valve context 的invokeNext方法来调用pipeline中的下一个valve。然后打印几行字符串,包括请求对象的getRemoteAddress方法的输出。

[size=large]ex05.pyrmont.valves.HeaderLoggerValve[/size]

这个类和ClientIPLoggerValve类相似。HeaderLoggerValve类是打印请求头部信息到控制台。

Listing 5.11: The HeaderLoggerValve class

package ex05.pyrmont.valves;

import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletRequest;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.apache.catalina.Request;
import org.apache.catalina.Response;
import org.apache.catalina.Valve;
import org.apache.catalina.ValveContext;
import org.apache.catalina.Contained;
import org.apache.catalina.Container;

public class HeaderLoggerValve implements Valve, Contained {
protected Container container;
public void invoke(Request request, Response response,
ValveContext valveContext) throws IOException, ServletException {
// Pass this request on to the next valve in our pipeline
valveContext.invokeNext(request, response);
System.out.println("Header Logger Valve");
ServletRequest sreq = request.getRequest();
if (sreq instanceof HttpServletRequest) {
HttpServletRequest hreq = (HttpServletRequest) sreq;
Enumeration headerNames = hreq.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement().toString();
String headerValue = hreq.getHeader(headerName);
System.out.println(headerName + ":" + headerValue);
}

}
else
System.out.println("Not an HTTP Request");

System.out.println ("-----------------------------------");
}

public String getInfo() {
return null;
}
public Container getContainer() {
return container;
}
public void setContainer(Container container) {
this.container = container;
}
}

再一次,注意invoke方法。第一件事是invoke方法调用valve context 的invokeNext方法调用pipeline的下一个valve。然后打印头部信息值。

[size=large]ex05.pyrmont.startup.Bootstrap1[/size]

Bootstrap1类用来启动这个应用程序。

Listing 5.12: The Bootstrap1 class

package ex05.pyrmont.startup;
import ex05.pyrmont.core.SimpleLoader;
import ex05.pyrmont.core.SimpleWrapper;
import ex05.pyrmont.valves.ClientlPLoggerValve;
import ex05.pyrmont.valves.HeaderLoggerValve;
import org.apache.catalina.Loader;
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 Bootstrap1 {
public static void main(String[] args) {
HttpConnector connector = new HttpConnector();
Wrapper wrapper = new SimpleWrapper();
wrapper.setServletClass("ModernServlet");
Loader loader = new SimpleLoader();
Valve valve1 = new HeaderLoggerValve();
Valve valve1 = new HeaderLoggerValve();
Valve valve2 = new ClientIPLoggerValve();
wrapper.setLoader(loader);
((Pipeline) wrapper).addValve(valve1);
((Pipeline) wrapper).addValve(valve2);
connector.setContainer(wrapper);
try {
connector.initialize();
connector.start();
// make the application wait until we press a key.
System.in.read();
} catch (Exception e) {
e.printStackTrace();
}
}
}

创建一个HttpConnector和SimpleWrapper实例后,Bootstrap类的主方法把ModernServlet指配给SimpleWrapper的setServlet方法,通知wrapper,类的名字被加载。

wrapper.setServletClass("ModernServlet");

然后创建一个loader和两个valve,把loader设置给wrapper:

Loader loader = new SimpleLoader();
Valve valve1 = new HeaderLoggerValve();
Valve valve2 = new ClientIPLoggerValve();
wrapper.setLoader(loader);

两个valve被加入到wrapper的pipeline中:

((Pipeline) wrapper).addValve(valve1);
((Pipeline) wrapper).addValve(valve2);

最后,wrapper被设置为连接器的容器,然后连接器初始化并启动。

connector.setContainer(wrapper);
try {
connector.initialize();
connector.start();

下面一行允许用户通过在控制台按Enter键停止应用程序。

// make the application wait until we press Enter.
System.in.read();


[size=large]Running the Application[/size]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值