Wrapper应用
这个应用创建了一个最小的容器模型。
类图如上。
SimpleLoader
在容器Container中加载一个servlet类的任务由Loader的实现类来完成。这里的实现类为SimpleLoader,这个类知道servlet类的class文件的位置,getClassLoader方法返回一个java.lang.ClassLoader类的实例。SimpleLoader类定义了三个变量,第一个是WEB_ROOT,指向存在servlet class文件的目录。
public static final String WEB_ROOT =
System.getProperty("user.dir") + File.separator + "webroot";
其它两个变量是ClassLoader和Container类型的引用对象:
ClassLoader classLoader = null;
Container container = null;
SimplePipeline
SimplePipeline实现了org.apache.catalina.Pipeline接口。在这个类中最重要的就是invoke方法,这个方法中使用了一个名为SimplePipelineValveContext的内部类,这个内部类实现了org.apache.catalina.ValveContext接口,这些都在前面论述过。
SimpleWrapper
这个类实现了org.apache.catalina.Wrapper接口,并提供了allocate和load方法的实现。其中有两个变量:
private Loader loader;
protected Container parent = null;
loader变量用于加载servlet类。parent变量表示这个wrapper的父容器,也就是说wrapper可以是其它容器的一个子容器,比如,Context。
这里我们需要特别注意一个getLoader方法:
public Loader getLoader() {
if (loader != null)
return (loader);
if (parent != null)
return (parent.getLoader());
return (null);
}
getLoader方法返回一个Loader类型来加载servlet类。如果wrapper已经关联了一个Loader,那直接返回这个Loader;如果没有,则返回它父容器的Loader;如果没有父容器,那么getLoader方法直接返回一个null。
SimpleWrapper拥有一个pipeline实例,同时它还会为这个pipeline设置它的基本Value。这一切都是在SimpleWrapper类的构造方法中完成的。
public SimpleWrapper() {
pipeline.setBasic(new SimpleWrapperValve());
}
pipeline是SimplePipeline类的一个实例。
SimpleWrapperValve
SimpleWrapperValve是一个基本Value,它主要为SimpleWrapper类处理request请求。它实现了org.apache.catalina.Valve接口和org.apache.catalina.Contained接口。SimpleWrapperValve类最主要的方法就是invoke。
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是一个基本Value,所以它的invoke方法不需要调用传递给它的ValueContext中的invokeNext方法。invoke方法SimpleWrapper类的allocate方法获取一个由wrapper表示的servlet类。然后调用servlet的service方法。需要注意的是,servlet的service方法是由wrapper管道中的基本Value来调用,而不是wrapper本身来调用。
ClientIPLoggerValve
ClientIPLoggerValve类是一个Value,它用来在终端输出客户端的IP地址。invoke是它主要的方法。
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("------------------------------------");
}
在invoke方法中的第一件事就是调用ValueContext的invokeNext方法来请求管道中的下一个Value,然后它会打印request类中getRemotAddr方法返回的字符串。
HeaderLoggerValve
这个类同上一个类十分相似。HeaderLoggerValve也是一个Value,它用来打印request请求的header到终端。
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("------------------------------------");
}
同上一个Vaule一样,invoke方法做的第一件事就是调用传递给的ValueContext中的invokeNext方法来请求下一个Value。然后再打印请求的header。
Bootstrap1
public final class Bootstrap1 {
public static void main(String[] args) {
/*
* call by using http://localhost:8080/ModernServlet, but could be
* invoked by any name
*/
HttpConnector connector = new HttpConnector();
Wrapper wrapper = new SimpleWrapper();
wrapper.setServletClass("ModernServlet");
Loader loader = new SimpleLoader();
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的实例之后,main方法调用SimpleWrapper类的setServletClass方法传递参数“ModernServlet”,通知wrapper需要加载的类。
wrapper.setServletClass("ModernServlet");
然后创建Loader和两个Value,将Loader分配给wrapper,两个Value分配给wrapper的管道。
Loader loader = new SimpleLoader();
Valve valve1 = new HeaderLoggerValve();
Valve valve2 = new ClientIPLoggerValve();
wrapper.setLoader(loader);
((Pipeline) wrapper).addValve(valve1);
((Pipeline) wrapper).addValve(valve2);
最后,将wrapper设置为connector的容器(Container),初始化并启动connector.
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();
}
运行程序
运行Bootstrap1类中的main方法,在浏览器中输入
localhost:8080
终端Console输出如下:
Client IP Logger Valve
0:0:0:0:0:0:0:1
------------------------------------
Header Logger Valve
host:localhost:8080
connection:keep-alive
accept:*/*
user-agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36
accept-encoding:gzip,deflate,sdch
accept-language:zh-CN,zh;q=0.8
------------------------------------
我们可以看到两个Value分别都被执行了。
浏览器显示ModernServlet的返回结果如下: