• tomcat(5)servlet容器


    【0】README
    0.0)本文部分文字描写叙述转自:“深入剖析tomcat”,旨在学习 tomcat(5)servlet容器 的基础知识。
    0.1)intro to servlet容器:servlet容器是用来处理请求servlet资源,并为web客户端填充response 对象的模块;
    0.2)补充:吃个饭过来。真心没想到这么多人围观,再次对文章又一次排版(并非博主我不给力,是CSDN编辑器时不时吊链子啊,oh),并加入了測试用例的程序流程图(共3张)[1604111947]。
    0.3)通知:本文因为CSDN编辑器掉链子使得博文排版乱八七糟。故晚辈我又又一次进行了排版,请大家移步到 tomcat(5)servlet容器(lastest version),本文明天也会删除[1604112328]。

    【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方法的伪代码:

    import java.io.IOException;
    import java.io.PrintWriter;
    import java.io.OutputStreamWriter;
    import java.io.UnsupportedEncodingException;
    import java.lang.reflect.Method;
    import java.text.MessageFormat;
    import java.util.Enumeration;
    import java.util.Locale;
    import java.util.ResourceBundle;


    import java.io.IOException;
    import java.io.PrintWriter;
    import java.io.OutputStreamWriter;
    import java.io.UnsupportedEncodingException;
    import java.lang.reflect.Method;
    import java.text.MessageFormat;
    import java.util.Enumeration;
    import java.util.Locale;
    import java.util.ResourceBundle;
    // 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容器。并初始化并启动连接器; 
    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:ench-clustercloud-data-preprocessHowTomcatWorkssrc>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;E:ench-clustercloud-data-preprocessHowTomcatWorkswebroot 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:ench-clustercloud-data-preprocessHowTomcatWorkssrc>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;E:ench-clustercloud-data-preprocessHowTomcatWorkswebroot  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)运行结果
     

  • 相关阅读:
    Docker安装Zookeeper并进行操作
    JVM 完整深入解析
    synchronized关键字加到static静态方法和非static静态方法区别
    submit与execute区别
    ThreadPoolTaskExecutor和ThreadPoolExecutor区别
    Redis占用内存大小
    Java中CycliBarriar和CountdownLatch区别
    Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition
    文本格式
    JavaScript事件
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/8820489.html
Copyright © 2020-2023  润新知