0.1.1)servlet容器:是 org.apache.catalina.Container接口的實例;0.1.2)在Tomcat中,共有四種容器(types):
t1)Engine;t2)Host;t3)Context;t4)Wrapper;
t1)Engine:表示整個Catalina servlet 引擎;t2)Host:表示包含有一個或多個 Context容器的虛擬主機;t3)Context:表示一個web 應用程序,一個Context 能夠有多個 Wrapper;t4)Wrapper:表示一個獨立的servlet;
A1)全部的實現類都繼承自抽象類 ContainerBase ;A2)Container接口的設計知足如下條件:在部署應用時,Tomcat管理員能夠經過編輯配置文件(server.xml)來決定使用哪一種容器。這是經過引入容器中的管道(pipeline)和閥(valve)的集合實現的; (乾貨——引入了容器中的管道和閥)
A1)管道就想過濾器鏈條同樣,而閥則好似過濾器;A2)當一個閥執行完成後,會調用下一個閥繼續執行。基礎閥老是最後一個執行; (乾貨——當一個閥執行完成後,會調用下一個閥繼續執行。基礎閥老是最後一個執行)
// invoke each valve added to the pipeline,先是非基礎閥調用 invoke方法 for(;;){ valve[i].invoke(); } // then, invoke the basic valve, 後是基礎閥調用 invoke方法(基礎閥最後一個調用invoke方法) basicValve.invoke(...); public void invoke(Request request, Response response) // SimplePipeline.invoke() throws IOException, ServletException { // Invoke the first Valve in this pipeline for this request (new SimplePipelineValveContext()).invokeNext(request, response); } public void invokeNext(Request request, Response response) // SimplePipeline.invokeNext() 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("No valve"); } } } // end of inner class
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); }
public interface Valve { public String getInfo();
public void invoke(Request request, Response response,ValveContext context) throws IOException, ServletException;
public interface Contained { public Container getContainer(); public void setContainer(Container container); }
public interface Wrapper extends Container { public long getAvailable(); public void setAvailable(long available); public String getJspFile(); public void setJspFile(String jspFile); public int getLoadOnStartup(); public void setLoadOnStartup(int value); public String getRunAs(); public void setRunAs(String runAs); public String getServletClass(); public void setServletClass(String servletClass); public boolean isUnavailable(); public void addInitParameter(String name, String value); public void addInstanceListener(InstanceListener listener); public void addSecurityReference(String name, String link); public Servlet allocate() throws ServletException; public void deallocate(Servlet servlet) throws ServletException; public String findInitParameter(String name); public String[] findInitParameters(); public String findSecurityReference(String name); public String[] findSecurityReferences(); public void load() throws ServletException; public void removeInitParameter(String name); public void removeInstanceListener(InstanceListener listener); public void removeSecurityReference(String name); public void unavailable(UnavailableException unavailable); public void unload() throws ServletException; }
public class SimpleLoader implements Loader { public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot"; ClassLoader classLoader = null; Container container = null; public SimpleLoader() { try { URL[] urls = new URL[1]; 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() ); } }
public class SimplePipeline implements Pipeline { public SimplePipeline(Container container) { setContainer(container); } // The basic Valve (if any) associated with this Pipeline. protected Valve basic = null; // The Container with which this Pipeline is associated. protected Container container = null; // the array of Valves protected Valve valves[] = new Valve[0]; public void setContainer(Container container) { this.container = container; } public Valve getBasic() { return basic; } public void setBasic(Valve valve) { this.basic = valve; ((Contained) valve).setContainer(container); } public void addValve(Valve valve) { if (valve instanceof Contained) ((Contained) valve).setContainer(this.container); synchronized (valves) { Valve results[] = new Valve[valves.length +1]; System.arraycopy(valves, 0, results, 0, valves.length); results[valves.length] = valve; valves = results; } } public Valve[] getValves() { return valves; } public void invoke(Request request, Response response) throws IOException, ServletException { // Invoke the first Valve in this pipeline for this request (new SimplePipelineValveContext()).invokeNext(request, response); } public void removeValve(Valve valve) { } // this class is copied from org.apache.catalina.core.StandardPipeline class's // StandardPipelineValveContext inner class. protected class SimplePipelineValveContext implements ValveContext { protected int stage = 0; public String getInfo() { return null; } 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("No valve"); } } } // end of inner class }
public class SimpleWrapper implements Wrapper, Pipeline { // the servlet instance private Servlet instance = null; private String servletClass; private Loader loader; private String name; private SimplePipeline pipeline = new SimplePipeline(this); protected Container parent = null; public SimpleWrapper() { pipeline.setBasic(new SimpleWrapperValve()); } public synchronized void addValve(Valve valve) { pipeline.addValve(valve); } public Servlet allocate() throws ServletException { // Load and initialize our instance if necessary if (instance==null) { try { instance = loadServlet(); } catch (ServletException e) { throw e; } catch (Throwable e) { throw new ServletException("Cannot allocate a servlet instance", e); } } return instance; } private Servlet loadServlet() throws ServletException { if (instance!=null) return instance; Servlet servlet = null; String actualClass = servletClass; if (actualClass == null) { throw new ServletException("servlet class has not been specified"); } Loader loader = getLoader(); // Acquire an instance of the class loader to be used if (loader==null) { throw new ServletException("No loader."); } ClassLoader classLoader = loader.getClassLoader(); // Load the specified servlet class from the appropriate class loader Class classClass = null; try { if (classLoader!=null) { classClass = classLoader.loadClass(actualClass); } } catch (ClassNotFoundException e) { throw new ServletException("Servlet class not found"); } // Instantiate and initialize an instance of the servlet class itself try { servlet = (Servlet) classClass.newInstance(); } catch (Throwable e) { throw new ServletException("Failed to instantiate servlet"); } // Call the initialization method of this servlet try { servlet.init(null); } catch (Throwable f) { throw new ServletException("Failed initialize servlet."); } return servlet; } public String getInfo() { return null; } public Loader getLoader() { if (loader != null) return (loader); if (parent != null) return (parent.getLoader()); return (null); }
public class SimpleWrapperValve implements Valve, Contained { protected Container container; 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) { } } public String getInfo() { return null; } public Container getContainer() { return container; } public void setContainer(Container container) { this.container = 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; } }
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; } }
step1)建立 HttpConnector 和 SimpleWrapper實例,並將須要加載的 servlet name 賦值給 Wrapper實例;step2)建立一個載入器和兩個閥,將載入器設置到Wrapper實例中 ;step3)將上述建立的兩個閥添加到 Wrapper的管道中;step4)將Wrapper 實例設置爲 鏈接器的servlet容器,並初始化並啓動鏈接器;
package com.tomcat.chapter5.startup; 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; import com.tomcat.chapter5.core.SimpleLoader; import com.tomcat.chapter5.core.SimpleWrapper; import com.tomcat.chapter5.valves.ClientIPLoggerValve; import com.tomcat.chapter5.valves.HeaderLoggerValve; 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("servlet.ModernServlet"); // 設置servlet的相對路徑 Loader loader = new SimpleLoader(); // 類加載器 Valve valve1 = new HeaderLoggerValve(); // 把請求頭信息output到 console Valve valve2 = new ClientIPLoggerValve();// 用來將client的IP 地址輸出到控制檯上 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(); } } }
E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\webroot com.tomcat.chapter5.startup/Bootstrap1 HttpConnector Opening server socket on all host IP addresses HttpConnector[8080] Starting background thread ModernServlet -- init Client IP Logger Valve 127.0.0.1 ------------------------------------ Header Logger Valve host:localhost:8080 connection:keep-alive accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 user-agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 accept-encoding:gzip, deflate, sdch accept-language:zh-CN,zh;q=0.8,en;q=0.6 ------------------------------------ Client IP Logger Valve 127.0.0.1 ------------------------------------ Header Logger Valve host:localhost:8080 connection:keep-alive accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 user-agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 accept-encoding:gzip, deflate, sdch accept-language:zh-CN,zh;q=0.8,en;q=0.6 ------------------------------------
public interface Mapper { public Container getContainer(); // 返回與該映射器相關聯的servlet容器的實例; public void setContainer(Container container); // 設置與該映射器相關聯的servlet容器; public String getProtocol(); // 返回該映射器負責處理的協議 public void setProtocol(String protocol); //指定該映射器負責處理哪一種協議 public Container map(Request request, boolean update); // 返回要處理某個特定請求的子容器的實例; }
step1)容器包含一個管道,容器的invoke方法會調用管道的invoke方法;step2)管道的invoke方法會調用全部添加到其容器中的閥,而後再調用其基礎閥的invoke方法;step3)在Wrapper實例中, 基礎閥負責載入相關聯的servlet類,並對請求進行響應;step4)在包含子容器的 Context實例中, 基礎閥使用映射器來查找一個子容器,該子容器負責處理接收到的請求。若找到了相應的子容器,則調用其invoke方法,轉到step1繼續執行;
step1)SimpleContext類的invoke方法調用管道的invoke方法:![]()
step2)管道SimplePipeline的invoke以下:
public void invoke(Request request, Response response) throws IOException, ServletException { // Invoke the first Valve in this pipeline for this request (new SimplePipelineValveContext()).invokeNext(request, response); // 會調用全部添加到Context 實例中的閥,而後再調用基礎閥的invoke方法; }
step3)SimpleContext類中,基礎閥是 SimpleContextValve類的實例。在SimpleContextValve類的 invoke方法中, SimpleContextValve實例使用了 Context實例的映射器來查找 Wrapper容器;public class SimpleContext implements Context, Pipeline { public SimpleContext() { pipeline.setBasic(new SimpleContextValve()); } public void invoke(Request request, Response response, ValveContext valveContext) // SimpleContextValve.invoke() 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); // attention for this line. } 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); }
A1)Wrapper實例的管道會調用 SimpleWrapperValve類的 invoke方法,它會分配servlet實例,並調用其 service方法;A2)Wrapper實例中:並無與載入器相關聯,可是Context 實例關聯了類載入器,所以,SimpleWrapper類的 getLoader() 方法會返回父容器的載入器;
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); } }
1)intro to SimpleContext: 該類是 Context容器 的實例,是與鏈接器相關聯的主容器;2)本應用程序有兩種URL模式:用來調用兩個 Wrapper實例,如/Primitive 和 /Modern 模式;固然,也能夠將多個 URL模式映射到一個Wrapper實例上。只須要添加這些模式便可;3)SimpleContext類必須實現 Container 和 Context接口,實現的方法包括如下幾個(methods):method1)addServletMapping(): 添加一個 URL模式 / Wrapper實例的名稱對;經過給定的名稱添加用於調用Wrapper實例的每種模式;method2)findServletMapping():經過URL模式 查找對應的Wrapper 實例名稱;該方法用來查找某個特殊URL 模式對應的Wrapper實例;method3)addMapper():在Context容器中添加一個映射器。SimpleContext類聲明有兩個變量: mapper and mappers 。mapper表示程序使用的默認映射器,mappers包含SimpleContext 實例中全部可用的映射器。第一個被添加到 Context容器中的映射器稱爲默認映射器;method4)findMapper():找到正確的映射器,在 SimpleContext類中,它返回默認映射器;method5)map(): 返回負責處理當前請求的 Wrapper實例;
step1)首先實例化Tomcat的默認鏈接器,建立兩個Wrapper實例,並指定名稱。HttpConnector connector = new HttpConnector(); Wrapper wrapper1 = new SimpleWrapper(); wrapper1.setName("Primitive"); wrapper1.setServletClass("servlet.PrimitiveServlet"); Wrapper wrapper2 = new SimpleWrapper(); wrapper2.setName("Modern"); wrapper2.setServletClass("servlet.ModernServlet");
step2)main() 方法建立一個 SimpleContext實例,並將 wrapper1 和 wrapper2 做爲子容器添加到 SimpleContext 實例中。此外,它還會實例化兩個閥:ClientIPLoggerValve 和 HeaderLoggerValve,並將它們添加到 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);
step3)接下來,它會從SimpleMapper類建立一個映射器對象,將其添加到 SimpleContext 實例中。映射器負責查找Context 實例中的子容器來處理 HTTP請求Mapper mapper = new SimpleContextMapper(); mapper.setProtocol("http"); context.addMapper(mapper);
step4)要載入servlet類,還須要一個載入器。並將其添加到 Context實例中。Wrapper實例能夠經過 其 getLoader方法來獲取載入器,由於Wrapper實例是 Context實例的子容器:Loader loader = new SimpleLoader(); context.setLoader(loader);
step5)添加servlet映射。爲 兩個Wrapper 實例添加兩種模式:// context.addServletMapping(pattern, name); context.addServletMapping("/Primitive", "Primitive"); context.addServletMapping("/Modern", "Modern");
step6)將Context容器與 鏈接器相關聯,並初始化鏈接器,調用其 start方法;connector.setContainer(context); try { connector.initialize(); connector.start(); // make the application wait until we press a key. System.in.read(); }
E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\webroot com.tomcat.chapter5.startup. Bootstrap2 HttpConnector Opening server socket on all host IP addresses HttpConnector[8080] Starting background thread Client IP Logger Valve 127.0.0.1 ------------------------------------ Header Logger Valve host:localhost:8080 connection:keep-alive accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 user-agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 accept-encoding:gzip, deflate, sdch accept-language:zh-CN,zh;q=0.8,en;q=0.6 ------------------------------------ init from service Client IP Logger Valve 127.0.0.1 ------------------------------------