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容器,并初始化并启动连接器;

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

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

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)运行结果

 

时间: 2024-08-24 12:11:18

tomcat(5)servlet容器的相关文章

JSP学习 —— 开篇:JSP,servlet容器,Tomcat,servlet容器之间的关系

JSP(JAVA SERVER PAGE)的缩写,其本身就是servlet的简化,是一种动态网页标准,其特点是在HTML代码中嵌入JAVA代码,JSP标签或用户标签来生成网页.至于它为什么会出现,主要原因在于早期的servlet技术在编写代码时经常通篇性的写一大堆HTML标签,静态文本及文本格式等表现逻辑,其开发效率非常之低下:为了解决这种情况,便随之出现了JSP,其静态部分(包括表现逻辑,如图片,文字等等)全用HTML语言来编写,只有需要动态生成的逻辑才由嵌入的JAVA代码来实现. 说到最后,

深入剖析tomcat之servlet容器

其实我们开发中经常用tomcat应用服务器,tomcat就一个servlet容器,能够运行基于serlvlet的应用程序并响应相应的http请求,开发时间长了,还是想想具体知道它是怎么运行的,尤其是servlet容器的机理,所以有幸拜读了外国人的<深入剖析tomcat>,感觉挺不错的,可以在此点击免费下载电子书,建议大家有时间读读,在读的过程中边读边翻阅着tomcat的源码,更有助于你理解它的各个机制,此处有tomcat 7的源码,点击可免费下载. 本人目前时间和组织语言能力及功力有限,记录纸

Web服务器(Apache)与Servlet容器(Tomcat)

之前一直比较迷惑Apache与Tomcat的关系,通过查询资料,有所了解,现记录于此. Apache与Tomcat 两者定位:Apache是HTTP Web服务器,Tomcat是Web容器. 有一个非常形象的比喻:Apache是一辆车,可以装载静态的物件(HTML静态网页等):但不能装动态的水(JSP.CGI等),要装水就需要桶(容器),当然桶也可以不放在车上而单独存放,则该容器即为Tomcat. 两者的主要区别: Apache是世界上最流行的Web服务器(其次是微软的IIS),可以处理浏览器的

tomcat和servlet的关系

一.什么是servlet? 处理请求和发送响应的过程是由一种叫做Servlet的程序来完成的,并且Servlet是为了解决实现动态页面而衍生的东西.理解这个的前提是了解一些http协议的东西,并且知道B/S模式(浏览器/服务器). 二.tomcat和servlet的关系 Tomcat 是Web应用服务器,是一个Servlet/JSP容器. Tomcat 作为Servlet容器,负责处理客户请求,把请求传送给Servlet,并将Servlet的响应传送回给客户. 而Servlet是一种运行在支持J

SpringBoot-配置嵌入式Servlet容器(十三)

SpringBoot默认使用Tomcat作为嵌入式的Servlet容器: 如何定制和修改Servlet容器的相关配置 1.修改和server有关的配置(ServerProperties[也是EmbeddedServletContainerCustomizer]): server.port=8081 server.context-path=/crud server.tomcat.uri-encoding=UTF-8 //通用的Servlet容器设置 server.xxx //Tomcat的设置 s

SpringBoot(七) -- 嵌入式Servlet容器

一.嵌入式Servlet容器 在传统的开发中,我们在完成开发后需要将项目打成war包,在外部配置好TomCat容器,而这个TomCat就是Servlet容器.在使用SpringBoot开发时,我们无需再外部配置Servlet容器,使用的是嵌入式的Servlet容器(TomCat).如果我们使用嵌入式的Servlet容器,存在以下问题: 1.如果我们是在外部安装了TomCat,如果我们想要进行自定义的配置优化,可以在其conf文件夹下修改配置文件来实现.在使用内置Servlet容器时,我们可以使用

how tomcat works 读书笔记(二)----------一个简单的servlet容器

app1 (建议读者在看本章之前,先看how tomcat works 读书笔记(一)----------一个简单的web服务器 http://blog.csdn.net/dlf123321/article/details/39378157) 回顾我们上一章,我们开发了一个最最简单的web服务器,它可以使用户访问服务器内的静态资源.当然这是远远不够的,在这一节里,我们就试着让服务器在能相应静态资源的基础上继续支持servlet. servlet接口 javax.servlet.Servlet接口

Spring整合quartz关闭,关闭Tomcat Servlet容器时内存泄漏

出错信息 22-Sep-2017 06:19:51.064 WARNING [main] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [license] appears to have started a thread named [org.springframework.scheduling.quartz.SchedulerFactoryBean#0_Wo

how tomcat works 五 servlet容器 上

servlet容器是用来处理请求servlet资源,并为Web客户端填充response对象的模块.在上一篇文章(也就是书的第四章)我们设计了SimpleContainer类,让他实现Container接口,也基本完成了容器的作用.但是我们得知道在实际的tomcat中有4类容器: Engine: 表示整个Catalina servlet引擎; Host: 包含一个或多个Context容器的虚拟主机; Context:表示一个Web应用程序,一个Context可以包含多个Wrapper Wrapp