tomcat(5)servlet容器

【0】README
0.0)本文部分文字描述轉自:「深刻剖析tomcat」,旨在學習  tomcat(5)servlet容器 的基礎知識;
0.1)intro to servlet容器:servlet容器是用來處理請求servlet資源,併爲web客戶端填充response 對象的模塊;
0.1.1)servlet容器:是 org.apache.catalina.Container接口的實例;
0.1.2)在Tomcat中,共有四種容器(types):
t1)Engine;
t2)Host;
t3)Context;
t4)Wrapper;
 
【1】Container接口
1)在Tomcat中,共有四種容器(types): (乾貨——Tomcat中共有4種容器)
t1)Engine:表示整個Catalina servlet 引擎;
t2)Host:表示包含有一個或多個 Context容器的虛擬主機;
t3)Context:表示一個web 應用程序,一個Context 能夠有多個 Wrapper;
t4)Wrapper:表示一個獨立的servlet;
2)以上4中容器都是 org.apache.catalina包下的接口:分別爲Engine,Host, Context, Wrapper,他們都繼承自Container接口。這4個接口的標準實現是 StandardEngine類,StandardHost類,StandardContext類,StandardWrapper類,他們都在 org.apache.catalina.core 包內;
Attention)
A1)全部的實現類都繼承自抽象類 ContainerBase ;
A2)Container接口的設計知足如下條件:在部署應用時,Tomcat管理員能夠經過編輯配置文件(server.xml)來決定使用哪一種容器。這是經過引入容器中的管道(pipeline)和閥(valve)的集合實現的; (乾貨——引入了容器中的管道和閥)
 
【2】管道任務
1)本節旨在說明:當鏈接器調用了servlet容器的invoke方法後會發生什麼事情,並討論org.apache.catalina 包中的4個相關接口,Pipeline, Valve, ValveContext 和 Contained;
2)管道和閥:
2.1)管道:包含該servlet容器將要調用的任務;
2.2)一個閥:表示一個具體的任務。
2.3)在servlet容器的管道中,有一個基礎閥,可是,能夠添加任意數量的閥。閥的數量指的是額外添加的閥數量,即,不包括基礎閥。有意思的是, 能夠經過編輯tomcat 的 配置文件(server.xml)來動態地添加閥;
2.4)一條管道和閥的示意圖以下:

Attention)
A1)管道就想過濾器鏈條同樣,而閥則好似過濾器;
A2)當一個閥執行完成後,會調用下一個閥繼續執行。基礎閥老是最後一個執行; (乾貨——當一個閥執行完成後,會調用下一個閥繼續執行。基礎閥老是最後一個執行)
3)管道的invoke方法:一個servlet容器能夠有一條管道,當調用了容器的invoke方法後,容器會將處理工做交由管道完成,而管道會調用其中的第一個閥開始處理。當第一個閥處理完後,它會調用後續的閥繼續執行任務,直到管道中全部的閥都處理完成。 下面是invoke方法的僞代碼:
// 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

 

4)實現閥的遍歷:Tomcat引入接口 org.apache.catalina.ValveContext 來實現閥的遍歷執行;
4.1)管道必須保證添加到其中的全部閥和基礎閥都被調用一次:這是經過調用一個 ValveContext接口實例來實現的。
4.2)ValveContext接口中最重要的方法是 invokeNext方法:在建立了ValveContext實例後,管道會調用ValveContext實例的 invokeNext方法。ValveContext實例會首先調用管道中的 第一個閥,第一個閥執行完後,會調用後面的閥繼續執行。ValveContext實例會將自身傳給每一個閥,所以,每一個閥均可以調用 ValveContext實例的 invokeNext方法;
5)org.apache.catalina.core.StandardPipeline類:是全部servlet容器中的Pipeline接口的實現,Tomcat4中有一個實現了ValveContext接口的內部類,名爲StandardPipelineValveContext;
6)Tomcat5 從 StandardPipeline類中移除了 StandardPipelineValveContext類:卻使用 org.apache.catalina.core.StandardValveContext類來調用閥;
 
【2.1】Pipeline接口
1)對於Pipeline接口:首先要提到的一個方法是 invoke方法,servlet容器調用invoke方法來開始調用管道中的閥和基礎閥;
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); 
} 
2)getBasic和setBasic:setBasic方法將基礎閥設置到管道中,getBasic獲取基礎閥; (乾貨——管道中能夠指定基礎閥)
3)addValve和removeValve:新增閥和刪除閥; (乾貨——在管道中能夠新增和刪除非基礎閥)
【2.2】Valve接口
1)閥是Valve接口的實例,用來處理接收到的請求,有兩個方法:invoke方法和getinfo方法;
public interface Valve {
    public String getInfo();                                                                              
   public void invoke(Request request, Response response,ValveContext context) throws IOException, ServletException;

 

【2.3.】ValveContext接口
1)有兩個方法:invokeNext方法和 getInfo方法;
 
【2.4】Contained接口
public interface Contained {   
    public Container getContainer();
    public void setContainer(Container container);
} 
【3】Wrapper接口
1)intro to Wrapper: Wrapper級的servlet容器是一個 org.apache.catalina.Wrapper接口的實例,表示一個獨立的servlet定義。Wrapper接口繼承自 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;
}

 

2)Wrapper接口的實現類:要負責管理繼承servlet類的servlet生命週期,即,調用 servlet的 init(), service(), destroy()方法;
3)因爲Wrapper已是最低級的容器了,不能再向其中添加子容器;(乾貨——Wrapper已是最低級的servlet容器)
4)Wrapper接口有兩個方法:load方法 和 allocate方法;
4.1)load方法:載入並初始化servlet類;
4.2)allocate方法:會分配一個已經初始化的servlet實例;
 
【4】Context接口
1)intro to Context:Context接口是一個web 應用程序,一個Context實例能夠有一個或多個Wrapper實例做爲其子容器;
2)比較重要的方法: addWrapper() and createWrapper();
 
【5】Wrapper 應用程序(demonstrate how to build a smallest servlet container)
1)SimpleWrapper類:該類實現了Wrapper接口,包含一個Pipeline實例,並使用Loader實例載入servlet類。Pipeline實例包含一個基礎閥和兩個額外的閥。
【5.1】SimpleLoader類
1)SimpleLoader:負責完成類的載入工做,它知道servlet類的位置,經過調用其getClassLoader能夠返回一個 java.lang.ClassLoader實例,能夠用來搜索servlet類的位置;
2)SimpleLoader的構造函數:會初始化類加載器,供 SimpleWrapper實例使用;
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() );
    }
  }

 

【5.2】SimplePipeline類
1)該類最重要的方法是 invoke方法;
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
}

 

【5.3】SimpleWrapper類
1)該類實現了Wrapper接口:並提供了 allocate 和 load 方法的實現;
2)getLoader()方法:該方法返回一個用於載入servlet 類的載入器。若Wrapper實例已經關聯了一個載入器,則直接將其返回;不然,它將返回父容器的載入器。若沒有父容器,getLoader方法會返回null;
3)SimpleWrapper類:有一個Pipeline實例,並該爲Pipeline實例設置基礎閥;
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);
  }

 

【5.4】SimpleWrapperValve類
1)SimpleWrapperValve是一個基礎閥:用於處理讀iSimpleWrapper類的請求,其最主要的方法是 invoke方法;
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;
  }
}

 

【5.5】ClientIPLoggerValve類
1)ClientIPLoggerValve類所表示的閥:用來將client的IP 地址輸出到控制檯上;
2)注意其invoke方法:它先調用方法參數 valveContext的 invokeNext方法來調用管道中的下一個閥。而後,它會把幾行字符串output到 console;
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;
  }
}
【5.6】HeaderLoggerValve類
1)HeaderLoggerValve類做用:會把請求頭信息output到 console;
2)注意其invoke方法:它先調用方法參數 valveContext的 invokeNext方法來調用管道中的下一個閥。
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;
  }
}

 

【5.7】Bootstrap1
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();
    }
  }
}

 

Attention)我這裏總結了該測試用例的調用流程圖(共三張)
 
 
【5.8】運行應用程序
1)運行參數
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
------------------------------------

 

2)運行結果

 
【6】Context應用程序
0)intro to Context app:本app 展現瞭如何使用一個包含了兩個Wrapper實例的Context實例來構建web app, 這兩個Wrapper 實例包裝了兩個servlet類,當應用程序有多個 Wrapper實例時,須要使用一個 映射器。映射器是組件,幫助servlet容器(Context實例)選擇一個子容器來處理某個指定的請求;
1)雖然有些應用程序只須要一個servlet,但大部分web app 是須要多個servlet合做的。這些應用程序中,須要的servlet容器是Context,不是Wrapper;
2)本應用程序的映射器:是SimpleContextMapper類的實例,該類實現類Mapper接口,servlet容器可使用多個 映射器來支持不一樣的協議。
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); // 返回要處理某個特定請求的子容器的實例;
}

 

3)SimpleContext類:是Context容器的一個實例,它使用了SimpleContextMapper 類的實例做爲其映射器,將SimpleContextValve 的實例做爲基礎閥;
4)Context容器中額外添加了兩個閥: ClinetIPLoggerValve 和 HeaderLoggerValve,幷包含兩個 Wrapper 實例做爲其子容器,兩者都是 SimpleWrapper 實例;這兩個Wrapper實例使用 SimpleWrapperValve 實例做爲其基礎閥,再也不添加其餘閥;
5)剩下的內容包括:
step1)容器包含一個管道,容器的invoke方法會調用管道的invoke方法;
step2)管道的invoke方法會調用全部添加到其容器中的閥,而後再調用其基礎閥的invoke方法;
step3)在Wrapper實例中, 基礎閥負責載入相關聯的servlet類,並對請求進行響應;
step4)在包含子容器的 Context實例中, 基礎閥使用映射器來查找一個子容器,該子容器負責處理接收到的請求。若找到了相應的子容器,則調用其invoke方法,轉到step1繼續執行;
6)下面對上述的steps 作 detailed intro
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);
  }

 

Attention)
A1)Wrapper實例的管道會調用 SimpleWrapperValve類的 invoke方法,它會分配servlet實例,並調用其 service方法;
A2)Wrapper實例中:並無與載入器相關聯,可是Context 實例關聯了類載入器,所以,SimpleWrapper類的 getLoader() 方法會返回父容器的載入器;
 
【6.1】SimpleContextValve類
1)該類是 SimleContext的基礎閥,最重要的方法是invoke方法;
【6.2】SimpleContextMapper類
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)map方法須要兩個參數:一個request對象和一個布爾變量。
2)在本app中, 忽略了第2個參數。map() 方法:會從request對象中解析出請求的上下文路徑,並調用 Conetext 實例的findServletMapping() 方法 來獲取一個與該路徑相關聯的名稱,若是找到了這個名稱,則它調用 Context實例的findChild方法獲取一個 Wrapper 實例;
【6.3】SimpleContext類
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實例;
【6.4】BootStrap2
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();
    }

 

【6.5】運行應用程序
1)運行參數
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
------------------------------------
2)運行結果
 
相關文章
相關標籤/搜索