码迷,mamicode.com
首页 > 其他好文 > 详细

How Tomcat Works学习笔记

时间:2014-11-30 16:40:14      阅读:105      评论:0      收藏:0      [点我收藏+]

标签:style   blog   http   io   ar   os   使用   sp   java   

0、servlet容器是如何工作的

      servlet容器是一个复杂的系统,但是,它有3个基本任务,对每个请求,servlet容器会为其完成以下三个操作:
      1:创建一个request对象,用可能会在Servlet中使用到的信息填充该request对象,如参数、头、cookie、查询字符串、URI等。request对象是javax.servlrt.ServletRequest接口或javax.servlet.http.ServletRequest接口的一个实例。

      2:创建一个response对象,用来向Web客户端发送响应,response对象是javax.servlrt.ServletResponse接口或javax.servlet.http.ServletResponse接口的一个实例。

      3:调用Servlet的service方法,将request对象和response对象作为参数传入,Servlet从request对象中读取信息,并通过response对象发送响应信息。

1、一个简单的Web容器

      首先创建一个HttpServer对象,这个对象最重要的方法是await方法,该方法会创建一个ServerSocket对象,然后调用该对象的accept()方法等待客户端连接,如果有客户端连接,则相应创建Request和Response对象,并且通过response的sendStaticResource()方法向客户端发送响应。

public class HttpServer {
	public static final String WEB_ROOT = System.getProperty("user.dir")
			+ File.separator + "webroot";

	private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";

	private boolean shutdown = false;

	public static void main(String[] args) {
		HttpServer server = new HttpServer();
		server.await();
	}

	public void await() {
		ServerSocket serverSocket = null;
		int port = 8080;
		try {
			serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}

		while (!shutdown) {
			Socket socket = null;
			InputStream input = null;
			OutputStream output = null;
			try {
				socket = serverSocket.accept();
				input = socket.getInputStream();
				output = socket.getOutputStream();

				Request request = new Request(input);

				Response response = new Response(output);
				response.setRequest(request);
				response.sendStaticResource();

				socket.close();

				shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
			} catch (Exception e) {
				e.printStackTrace();
				continue;
			}
		}
	}
}

      Request对象如下,parse()方法用于得到客户端的输入并打印输出,parseUri()方法用于得到客户端的请求路径:

public class Request {
	private InputStream input;
	private String uri;

	public Request(InputStream input) {
		this.input = input;
	}

	public void parse() {
		StringBuffer request = new StringBuffer(2048);
		int i;
		byte[] buffer = new byte[2048];
		try {
			i = input.read(buffer);
		} catch (IOException e) {
			e.printStackTrace();
			i = -1;
		}
		for (int j = 0; j < i; j++) {
			request.append((char) buffer[j]);
		}
		System.out.print(request.toString());
		uri = parseUri(request.toString());
	}

	private String parseUri(String requestString) {
		int index1, index2;
		index1 = requestString.indexOf(‘ ‘);
		if (index1 != -1) {
			index2 = requestString.indexOf(‘ ‘, index1 + 1);
			if (index2 > index1)
				return requestString.substring(index1 + 1, index2);
		}
		return null;
	}

	public String getUri() {
		return uri;
	}
}

  Response对象最重要的方法是sendStaticResource()方法,该方法通过调用Request对象的getUri()方法得到客户端请求文件路径,然后读取文件并发送给用户:

public class Response {
	private static final int BUFFER_SIZE = 1024;
	Request request;
	OutputStream output;

	public Response(OutputStream output) {
		this.output = output;
	}

	public void setRequest(Request request) {
		this.request = request;
	}

	public void sendStaticResource() throws IOException {
		byte[] bytes = new byte[BUFFER_SIZE];
		FileInputStream fis = null;
		try {
			File file = new File(HttpServer.WEB_ROOT, request.getUri());
			if (file.exists()) {
				fis = new FileInputStream(file);
				int ch = fis.read(bytes, 0, BUFFER_SIZE);
				while (ch != -1) {
					output.write(bytes, 0, ch);
					ch = fis.read(bytes, 0, BUFFER_SIZE);
				}
			} else {
				String errorMessage = "HTTP/1.1 404 File Not Found\r\n"
						+ "Content-Type: text/html\r\n"
						+ "Content-Length: 23\r\n" + "\r\n"
						+ "<h1>File Not Found</h1>";
				output.write(errorMessage.getBytes());
			}
		} catch (Exception e) {
			System.out.println(e.toString());
		} finally {
			if (fis != null)
				fis.close();
		}
	}
}

  启动HttpServer,然后在浏览器输入localhost:8080/index.html即可看到一个静态网页输出。

2、一个简单的servlet容器

      《How Tomcat Works》第二章,太长了,不写了。

3、连接器

      本章的应用程序包含三个模块:连接器模块、启动模块和核心模块。

      启动模块就只有一个类Bootstrap,负责启动应用程序。

      核心模块有两个类,servletProcessor类和StaticResourceProcessor类,前者处理Servlet请求,后者处理静态资源请求。

      连接器模块是这章的重点,包含以下5个类型:

      1:连接器及其支持类(HttpConnector和HttpProcessor);

      2:表示HTTP请求的类(HttpRequest)及其支持类;

      3:表示HTTP响应的类(HttpResponse)及其支持类;

      4:外观类(HttpRequestFacade和HTTPResponseFacade);

      5:常量类。

       Bootstrap类是整个应用程序的启动类,该类定义如下:

public final class Bootstrap {
  public static void main(String[] args) {
    HttpConnector connector = new HttpConnector();
    connector.start();
  }
}

  Bootstrap类通过实例化HttpConnector类并调用其start()方法启动应用程序,HttpConector类的定义如下:

public class HttpConnector implements Runnable {
  boolean stopped;
  private String scheme = "http";

  public String getScheme() {
    return scheme;
  }

  public void run() {
    ServerSocket serverSocket = null;
    int port = 8080;
    try {
      serverSocket =  new ServerSocket(port, 1,InetAddress.getByName("127.0.0.1"));
    }
    catch (IOException e) {
      e.printStackTrace();
      System.exit(1);
    }
    while (!stopped) {
      Socket socket = null;
      try {
        socket = serverSocket.accept();
      }
      catch (Exception e) {
        continue;
      }
      HttpProcessor processor = new HttpProcessor(this);
      processor.process(socket);
    }
  }

  public void start() {
    Thread thread = new Thread(this);
    thread.start();
  }
}

  HttpConector主要做以下事情:等待HTTP请求,为每个请求创建一个HttpProcessor实例,调用HttpProcessor对象的process()方法。HttpProcessor的定义如下:

public class HttpProcessor {
  public HttpProcessor(HttpConnector connector) {
    this.connector = connector;
  }

  private HttpConnector connector = null;
  private HttpRequest request;
  private HttpRequestLine requestLine = new HttpRequestLine();
  private HttpResponse response;
  protected String method = null;
  protected String queryString = null;

  protected StringManager sm =
    StringManager.getManager("ex03.pyrmont.connector.http");

  public void process(Socket socket) {
    SocketInputStream input = null;
    OutputStream output = null;
    try {
      input = new SocketInputStream(socket.getInputStream(), 2048);
      output = socket.getOutputStream();

      request = new HttpRequest(input);
      response = new HttpResponse(output);
      response.setRequest(request);

      response.setHeader("Server", "Pyrmont Servlet Container");

      parseRequest(input, output);
      parseHeaders(input);

      if (request.getRequestURI().startsWith("/servlet/")) {
        ServletProcessor processor = new ServletProcessor();
        processor.process(request, response);
      }
      else {
        StaticResourceProcessor processor = new StaticResourceProcessor();
        processor.process(request, response);
      }

      socket.close();

    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }

  private void parseHeaders(SocketInputStream input)
    throws IOException, ServletException {
    while (true) {
      HttpHeader header = new HttpHeader();;

      input.readHeader(header);
      if (header.nameEnd == 0) {
        if (header.valueEnd == 0) {
          return;
        }
        else {
          throw new ServletException
            (sm.getString("httpProcessor.parseHeaders.colon"));
        }
      }

      String name = new String(header.name, 0, header.nameEnd);
      String value = new String(header.value, 0, header.valueEnd);
      request.addHeader(name, value);
      if (name.equals("cookie")) {
        Cookie cookies[] = RequestUtil.parseCookieHeader(value);
        for (int i = 0; i < cookies.length; i++) {
          if (cookies[i].getName().equals("jsessionid")) {
            if (!request.isRequestedSessionIdFromCookie()) {
              request.setRequestedSessionId(cookies[i].getValue());
              request.setRequestedSessionCookie(true);
              request.setRequestedSessionURL(false);
            }
          }
          request.addCookie(cookies[i]);
        }
      }
      else if (name.equals("content-length")) {
        int n = -1;
        try {
          n = Integer.parseInt(value);
        }
        catch (Exception e) {
          throw new ServletException(sm.getString("httpProcessor.parseHeaders.contentLength"));
        }
        request.setContentLength(n);
      }
      else if (name.equals("content-type")) {
        request.setContentType(value);
      }
    } //end while
  }


  private void parseRequest(SocketInputStream input, OutputStream output)
    throws IOException, ServletException {

    input.readRequestLine(requestLine);
    String method =
      new String(requestLine.method, 0, requestLine.methodEnd);
    String uri = null;
    String protocol = new String(requestLine.protocol, 0, requestLine.protocolEnd);

    if (method.length() < 1) {
      throw new ServletException("Missing HTTP request method");
    }
    else if (requestLine.uriEnd < 1) {
      throw new ServletException("Missing HTTP request URI");
    }

    int question = requestLine.indexOf("?");
    if (question >= 0) {
      request.setQueryString(new String(requestLine.uri, question + 1,
        requestLine.uriEnd - question - 1));
      uri = new String(requestLine.uri, 0, question);
    }
    else {
      request.setQueryString(null);
      uri = new String(requestLine.uri, 0, requestLine.uriEnd);
    }

    if (!uri.startsWith("/")) {
      int pos = uri.indexOf("://");
      if (pos != -1) {
        pos = uri.indexOf(‘/‘, pos + 3);
        if (pos == -1) {
          uri = "";
        }
        else {
          uri = uri.substring(pos);
        }
      }
    }

    String match = ";jsessionid=";
    int semicolon = uri.indexOf(match);
    if (semicolon >= 0) {
      String rest = uri.substring(semicolon + match.length());
      int semicolon2 = rest.indexOf(‘;‘);
      if (semicolon2 >= 0) {
        request.setRequestedSessionId(rest.substring(0, semicolon2));
        rest = rest.substring(semicolon2);
      }
      else {
        request.setRequestedSessionId(rest);
        rest = "";
      }
      request.setRequestedSessionURL(true);
      uri = uri.substring(0, semicolon) + rest;
    }
    else {
      request.setRequestedSessionId(null);
      request.setRequestedSessionURL(false);
    }

    String normalizedUri = normalize(uri);

    ((HttpRequest) request).setMethod(method);
    request.setProtocol(protocol);
    if (normalizedUri != null) {
      ((HttpRequest) request).setRequestURI(normalizedUri);
    }
    else {
      ((HttpRequest) request).setRequestURI(uri);
    }

    if (normalizedUri == null) {
      throw new ServletException("Invalid URI: " + uri + "‘");
    }
  }

  protected String normalize(String path) {
    if (path == null)
      return null;
    String normalized = path;

    if (normalized.startsWith("/%7E") || normalized.startsWith("/%7e"))
      normalized = "/~" + normalized.substring(4);

    if ((normalized.indexOf("%25") >= 0)
      || (normalized.indexOf("%2F") >= 0)
      || (normalized.indexOf("%2E") >= 0)
      || (normalized.indexOf("%5C") >= 0)
      || (normalized.indexOf("%2f") >= 0)
      || (normalized.indexOf("%2e") >= 0)
      || (normalized.indexOf("%5c") >= 0)) {
      return null;
    }

    if (normalized.equals("/."))
      return "/";

    if (normalized.indexOf(‘\\‘) >= 0)
      normalized = normalized.replace(‘\\‘, ‘/‘);
    if (!normalized.startsWith("/"))
      normalized = "/" + normalized;

    while (true) {
      int index = normalized.indexOf("//");
      if (index < 0)
        break;
      normalized = normalized.substring(0, index) +
        normalized.substring(index + 1);
    }

    while (true) {
      int index = normalized.indexOf("/./");
      if (index < 0)
        break;
      normalized = normalized.substring(0, index) +
        normalized.substring(index + 2);
    }

    while (true) {
      int index = normalized.indexOf("/../");
      if (index < 0)
        break;
      if (index == 0)
        return (null);  // Trying to go outside our context
      int index2 = normalized.lastIndexOf(‘/‘, index - 1);
      normalized = normalized.substring(0, index2) +
        normalized.substring(index + 3);
    }

    if (normalized.indexOf("/...") >= 0)
      return (null);

    return (normalized);
  }
}

  对于HttpProcessor对象,我们只需要重点关注process()方法即可,其他方法都是是有方法,供process()方法调用,该类的process()方法完成了以下4个操作:创建一个HttpRequest对象,创建一个HttpResponse对象,调用parseRequest()方法和parseHeader()方法填充HttpRequest对象,将HttpRequest对象和HttpResponse对象传递给ServletProcessor对象或StaticResourceProcessor对象的process()方法。

      接下来就是讲解Request和Response的实现了,但是代码太长,不写了。其实Request和Response对象的实现就是对HTTP协议的实现,此处略。接下来我们查看ServletProcessor的实现:

public class ServletProcessor {
  public void process(HttpRequest request, HttpResponse response) {
    String uri = request.getRequestURI();
    String servletName = uri.substring(uri.lastIndexOf("/") + 1);
    URLClassLoader loader = null;
    try {
      URL[] urls = new URL[1];
      URLStreamHandler streamHandler = null;
      File classPath = new File(Constants.WEB_ROOT);
      String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
      urls[0] = new URL(null, repository, streamHandler);
      loader = new URLClassLoader(urls);
    }
    catch (IOException e) {
      System.out.println(e.toString() );
    }
    Class myClass = null;
    try {
      myClass = loader.loadClass(servletName);
    }
    catch (ClassNotFoundException e) {
      System.out.println(e.toString());
    }

    Servlet servlet = null;
    try {
      servlet = (Servlet) myClass.newInstance();
      HttpRequestFacade requestFacade = new HttpRequestFacade(request);
      HttpResponseFacade responseFacade = new HttpResponseFacade(response);
      servlet.service(requestFacade, responseFacade);
      ((HttpResponse) response).finishResponse();
    }
    catch (Exception e) {
      System.out.println(e.toString());
    }
    catch (Throwable e) {
      System.out.println(e.toString());
    }
  }
}

  此处,使用类加载器加载了一个类,然后调用newInstance()方法进行初始化类,初始化后调用该类的service()方法。再查看StaticResourceProcessor的实现,该类直接调用Response的sendStaticResource()方法:

public class StaticResourceProcessor {
  public void process(HttpRequest request, HttpResponse response) {
    try {
      response.sendStaticResource();
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }
}

  /* This method is used to serve a static page */
  public void sendStaticResource() throws IOException {
    byte[] bytes = new byte[BUFFER_SIZE];
    FileInputStream fis = null;
    try {
      File file = new File(Constants.WEB_ROOT, request.getRequestURI());
      fis = new FileInputStream(file);

      int ch = fis.read(bytes, 0, BUFFER_SIZE);
      while (ch!=-1) {
        output.write(bytes, 0, ch);
        ch = fis.read(bytes, 0, BUFFER_SIZE);
      }
    }
    catch (FileNotFoundException e) {
      String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
        "Content-Type: text/html\r\n" +
        "Content-Length: 23\r\n" +
        "\r\n" +
        "<h1>File Not Found</h1>";
      output.write(errorMessage.getBytes());
    }
    finally {
      if (fis!=null)
        fis.close();
    }
  }

4、Tomcat的默认连接器

      上一节讲的连接器已经可以工作,但是那个只是一个学习工具,本节将介绍并直接使用Tomcat 4的默认连接器。本节的应用程序将直接使用默认连接器,该应用程序包含两个类:SimpleContainer和Bootstrap类。SimpleContainer类实现了org.apache.catalina.Container接口,这样它就可以与默认连接器关联:

public class SimpleContainer implements Container {
  public static final String WEB_ROOT =
    System.getProperty("user.dir") + File.separator  + "webroot";

  public SimpleContainer() {
  }

  public String getInfo() {
    return null;
  }

  public Loader getLoader() {
    return null;
  }

  public void setLoader(Loader loader) {
  }

  public Logger getLogger() {
    return null;
  }

  public void setLogger(Logger logger) {
  }

  public Manager getManager() {
    return null;
  }

  public void setManager(Manager manager) {
  }

  public Cluster getCluster() {
    return null;
  }

  public void setCluster(Cluster cluster) {
  }

  public String getName() {
    return null;
  }

  public void setName(String name) {
  }

  public Container getParent() {
    return null;
  }

  public void setParent(Container container) {
  }

  public ClassLoader getParentClassLoader() {
    return null;
  }

  public void setParentClassLoader(ClassLoader parent) {
  }

  public Realm getRealm() {
    return null;
  }

  public void setRealm(Realm realm) {
  }

  public DirContext getResources() {
    return null;
  }

  public void setResources(DirContext resources) {
  }

  public void addChild(Container child) {
  }

  public void addContainerListener(ContainerListener listener) {
  }

  public void addMapper(Mapper mapper) {
  }

  public void addPropertyChangeListener(PropertyChangeListener listener) {
  }

  public Container findChild(String name) {
    return null;
  }

  public Container[] findChildren() {
    return null;
  }

  public ContainerListener[] findContainerListeners() {
    return null;
  }

  public Mapper findMapper(String protocol) {
    return null;
  }

  public Mapper[] findMappers() {
    return null;
  }

  public void invoke(Request request, Response response)
    throws IOException, ServletException {
    String servletName = ( (HttpServletRequest) request).getRequestURI();
    servletName = servletName.substring(servletName.lastIndexOf("/") + 1);
    URLClassLoader loader = null;
    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);
      loader = new URLClassLoader(urls);
    }
    catch (IOException e) {
      System.out.println(e.toString() );
    }
    Class myClass = null;
    try {
      myClass = loader.loadClass(servletName);
    }
    catch (ClassNotFoundException e) {
      System.out.println(e.toString());
    }

    Servlet servlet = null;

    try {
      servlet = (Servlet) myClass.newInstance();
      servlet.service((HttpServletRequest) request, (HttpServletResponse) response);
    }
    catch (Exception e) {
      System.out.println(e.toString());
    }
    catch (Throwable e) {
      System.out.println(e.toString());
    }
  }

  public Container map(Request request, boolean update) {
    return null;
  }

  public void removeChild(Container child) {
  }

  public void removeContainerListener(ContainerListener listener) {
  }

  public void removeMapper(Mapper mapper) {
  }

  public void removePropertyChangeListener(PropertyChangeListener listener) {
  }
}

  可以看到,这个类只是Container接口的简单实现,所以里面很多方法都未实现,只实现了一个invoke()方法。Bootstrap类的实现如下:

public final class Bootstrap {
  public static void main(String[] args) {
    HttpConnector connector = new HttpConnector();
    SimpleContainer container = new SimpleContainer();
    connector.setContainer(container);
    try {
      connector.initialize();
      connector.start();

      // make the application wait until we press any key.
      System.in.read();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
}

  注意,现在代码中去除了处理静态资源的代码,通过链接http://localhost:8080/servlet/ModernServlet可以查看Servlet内容。

5、Servlet容器

      在Tomcat中共有4种容器,分别是Engine,Host,Context和Wrapper。

管道任务

      Tomcat4中管道任务相关的类有以下几个:Pipeline、Value、ValueContext、Contained。管道任务类似于servlet中的过滤器,Pipeline是过滤链,Value类似过滤器,可以通过编辑server.xml动态添加阀(Value),当一个阀(Value)调用完后会调用下一个阀(Value),基础阀总是最后一个调用。一个Servlet容器可以有一条管道(Pipeline),当调用容器的invoke方法后,容器会调用管道的invoke()方法,管道再调用阀的invoke()方法。同时Tomcat还引入例如ValueContext接口用于控制阀的遍历。

Context应用程序

      一个Context容器代表一个应用程序,本节的例子将介绍一个包含两个Wrapper实例的Context实例。这一节最精髓的就是Bootstrap类,该类的实现如下:

public final class Bootstrap2 {
  public static void main(String[] args) {
    HttpConnector connector = new HttpConnector();
    Wrapper wrapper1 = new SimpleWrapper();
    wrapper1.setName("Primitive");
    wrapper1.setServletClass("PrimitiveServlet");
    Wrapper wrapper2 = new SimpleWrapper();
    wrapper2.setName("Modern");
    wrapper2.setServletClass("ModernServlet");

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

    Mapper mapper = new SimpleContextMapper();
    mapper.setProtocol("http");
    context.addMapper(mapper);
    Loader loader = new SimpleLoader();
    context.setLoader(loader);
    context.addServletMapping("/Primitive", "Primitive");
    context.addServletMapping("/Modern", "Modern");
    connector.setContainer(context);
    try {
      connector.initialize();
      connector.start();

      System.in.read();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
}

  首先创建了两个Wrapper对象,每个Wrapper对象有name和servletClass属性,然后把两个wrapper对象加入Context容器,为Context容器增加管道,接着创建映射器,说明每个请求对应的Servlet class。

      还有需要注意的是,SimpleContext类和SimpleWrapper类的构造函数,在这两个构造函数中指定了基础阀:

  public SimpleContext() {
    pipeline.setBasic(new SimpleContextValve());
  }
  public SimpleWrapper() {
    pipeline.setBasic(new SimpleWrapperValve());
  }

6、生命周期

      Catalina在设计上允许一个组件包含其他组件,例如servlet容器可以包含载入器、管理器等,父组件负责负责启动/关闭它的子组件。Lifecycle接口如下:

public interface Lifecycle {
    public static final String START_EVENT = "start";
    public static final String BEFORE_START_EVENT = "before_start";
    public static final String AFTER_START_EVENT = "after_start";
    public static final String STOP_EVENT = "stop";
    public static final String BEFORE_STOP_EVENT = "before_stop";
    public static final String AFTER_STOP_EVENT = "after_stop";

    public void addLifecycleListener(LifecycleListener listener);
    public LifecycleListener[] findLifecycleListeners();
    public void removeLifecycleListener(LifecycleListener listener);
    public void start() throws LifecycleException;
    public void stop() throws LifecycleException;
}

  本章的SimpleContext类与上一节的SimpleContext类十分类似,区别在于本章的SimpleContext实现了Lifecycle接口:

public class SimpleContext implements Context, Pipeline, Lifecycle {
  public SimpleContext() {
    pipeline.setBasic(new SimpleContextValve());
  }

  protected LifecycleSupport lifecycle = new LifecycleSupport(this);

  public void addLifecycleListener(LifecycleListener listener) {
    lifecycle.addLifecycleListener(listener);
  }

  public LifecycleListener[] findLifecycleListeners() {
    return null;
  }

  public void removeLifecycleListener(LifecycleListener listener) {
    lifecycle.removeLifecycleListener(listener);
  }

  public synchronized void start() throws LifecycleException {
    if (started)
      throw new LifecycleException("SimpleContext has already started");

    lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
    started = true;
    try {
      if ((loader != null) && (loader instanceof Lifecycle))
        ((Lifecycle) loader).start();

      Container children[] = findChildren();
      for (int i = 0; i < children.length; i++) {
        if (children[i] instanceof Lifecycle)
          ((Lifecycle) children[i]).start();
      }

      if (pipeline instanceof Lifecycle)
        ((Lifecycle) pipeline).start();
      lifecycle.fireLifecycleEvent(START_EVENT, null);
    }
    catch (Exception e) {
      e.printStackTrace();
    }

    lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
  }

  public void stop() throws LifecycleException {
    if (!started)
      throw new LifecycleException("SimpleContext has not been started");

    lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
    lifecycle.fireLifecycleEvent(STOP_EVENT, null);
    started = false;
    try {
      if (pipeline instanceof Lifecycle) {
        ((Lifecycle) pipeline).stop();
      }

      Container children[] = findChildren();
      for (int i = 0; i < children.length; i++) {
        if (children[i] instanceof Lifecycle)
          ((Lifecycle) children[i]).stop();
      }
      if ((loader != null) && (loader instanceof Lifecycle)) {
        ((Lifecycle) loader).stop();
      }
    }
    catch (Exception e) {
      e.printStackTrace();
    }
    lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
  }

}

  可以看到SimpleContext在start()/stop()时候也会把他所有的子组件全部启动/关闭。Bootstrap类如下:

public final class Bootstrap {
  public static void main(String[] args) {
    Connector connector = new HttpConnector();
    Wrapper wrapper1 = new SimpleWrapper();
    wrapper1.setName("Primitive");
    wrapper1.setServletClass("PrimitiveServlet");
    Wrapper wrapper2 = new SimpleWrapper();
    wrapper2.setName("Modern");
    wrapper2.setServletClass("ModernServlet");

    Context context = new SimpleContext();
    context.addChild(wrapper1);
    context.addChild(wrapper2);

    Mapper mapper = new SimpleContextMapper();
    mapper.setProtocol("http");
    LifecycleListener listener = new SimpleContextLifecycleListener();
    ((Lifecycle) context).addLifecycleListener(listener);
    context.addMapper(mapper);
    Loader loader = new SimpleLoader();
    context.setLoader(loader);
    context.addServletMapping("/Primitive", "Primitive");
    context.addServletMapping("/Modern", "Modern");
    connector.setContainer(context);
    try {
      connector.initialize();
      ((Lifecycle) connector).start();
      ((Lifecycle) context).start();

      // make the application wait until we press a key.
      System.in.read();
      ((Lifecycle) context).stop();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
}

  在Bootstrap类中,我们调用context的start()方法启动应用,同时我们也在SimpleContext类上增加了一个监听器。

 7、日志记录器

      日志记录器是用来记录消息的组件,本节应用程序与上节相比,多了以下内容:

    // ------ add logger --------
    System.setProperty("catalina.base", System.getProperty("user.dir"));
    FileLogger logger = new FileLogger();
    logger.setPrefix("FileLog_");
    logger.setSuffix(".txt");
    logger.setTimestamp(true);
    logger.setDirectory("webroot");
    context.setLogger(logger);
    //---------------------------

8、载入器

      对于类加载器,每个Context都有一个类加载器,出于安全考虑,我们不能直接使用系统类加载器,而应该实现自己的类加载器,只允许servlet加载WEB-INF/classes目录下的类。这一章的应用程序直接使用StandardContext类和WebappLoader类,可以看到,这两个Catalina类的使用方法与我们自己写的类使用方法差别并不大:

public final class Bootstrap {
  public static void main(String[] args) {
    System.setProperty("catalina.base", System.getProperty("user.dir"));
    Connector connector = new HttpConnector();
    Wrapper wrapper1 = new SimpleWrapper();
    wrapper1.setName("Primitive");
    wrapper1.setServletClass("PrimitiveServlet");
    Wrapper wrapper2 = new SimpleWrapper();
    wrapper2.setName("Modern");
    wrapper2.setServletClass("ModernServlet");

    Context context = new StandardContext();
    // StandardContext‘s start method adds a default mapper
    context.setPath("/myApp");
    context.setDocBase("myApp");

    context.addChild(wrapper1);
    context.addChild(wrapper2);
    
    context.addServletMapping("/Primitive", "Primitive");
    context.addServletMapping("/Modern", "Modern");

    LifecycleListener listener = new SimpleContextConfig();
    ((Lifecycle) context).addLifecycleListener(listener);

    Loader loader = new WebappLoader();
    context.setLoader(loader);

    connector.setContainer(context);

    try {
      connector.initialize();
      ((Lifecycle) connector).start();
      ((Lifecycle) context).start();
      // now we want to know some details about WebappLoader
      WebappClassLoader classLoader = (WebappClassLoader) loader.getClassLoader();
      System.out.println("Resources‘ docBase: " + ((ProxyDirContext)classLoader.getResources()).getDocBase());
      String[] repositories = classLoader.findRepositories();
      for (int i=0; i<repositories.length; i++) {
        System.out.println("  repository: " + repositories[i]);
      }

      // make the application wait until we press a key.
      System.in.read();
      ((Lifecycle) context).stop();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
}

9、Session管理

      Catalina通过一个称为Session管理器的组件来管理建立的Session对象,该组件由org.apache.catalina.Manager接口表示,Session管理器需要与一个Context容器相关联,且必须与一个Context容器关联,在该组件内部会有一个hashmap用于存储session:

HashMap sessions = new HashMap();
sessions.put(session.getId(), session);

      这一节的Bootstrap类中增加了Session管理器:

    // add a Manager
    Manager manager = new StandardManager();
    context.setManager(manager);

  同时,为了支持request.getSession操作,需要修改SimpleWrapperValue基础阀,在request中设置Context属性:

    //-- new addition -----------------------------------
    Context context = (Context) wrapper.getParent();
    request.setContext(context);

10、安全性

      这一章讲解的功能从没用过,略。

11、StandardWrapper

      讲解StandardWrapper类的一些细节,具体内容还是看书吧。

12、StandardContext

      讲解StandardContext类的细节,不存在代码变更。

13、Host和Engine

      如果需要在Tomcat上部署多个Context容器,那就需要使用Host容器。Engine表示整个servlet引擎,一个Engine可以有多个Host子容器。下面是一个使用Engine作为顶层容器的简单例子:

public final class Bootstrap2 {
  public static void main(String[] args) {
    System.setProperty("catalina.base", System.getProperty("user.dir"));
    Connector connector = new HttpConnector();

    Wrapper wrapper1 = new StandardWrapper();
    wrapper1.setName("Primitive");
    wrapper1.setServletClass("PrimitiveServlet");
    Wrapper wrapper2 = new StandardWrapper();
    wrapper2.setName("Modern");
    wrapper2.setServletClass("ModernServlet");

    Context context = new StandardContext();
    context.setPath("/app1");
    context.setDocBase("app1");

    context.addChild(wrapper1);
    context.addChild(wrapper2);

    LifecycleListener listener = new SimpleContextConfig();
    ((Lifecycle) context).addLifecycleListener(listener);

    Host host = new StandardHost();
    host.addChild(context);
    host.setName("localhost");
    host.setAppBase("webapps");

    Loader loader = new WebappLoader();
    context.setLoader(loader);
    context.addServletMapping("/Primitive", "Primitive");
    context.addServletMapping("/Modern", "Modern");

    Engine engine = new StandardEngine();
    engine.addChild(host);
    engine.setDefaultHost("localhost");

    connector.setContainer(engine);
    try {
      connector.initialize();
      ((Lifecycle) connector).start();
      ((Lifecycle) engine).start();
      System.in.read();
      ((Lifecycle) engine).stop();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
}

14、服务器组件和服务组件

      服务器组件是非常有用的,因为它使用了一种优雅的方法来启动/关闭整个系统,不需要再对连接器和容器分别启动/关闭。

15、Digester类

      Digester是Apache的开源项目,使用Digester可以把XML文档中的元素转换成Java对象,这样就可以基于XML文件进行对象配置了。

16、关闭钩子

      JVM在关闭之前会启动所有已注册的关闭钩子,利用这种机制,我们可以确保Tomcat完成所有善后清理工作。

17、启动Tomcat

      本文主要讲解启动Tomcat的shell脚本内容。

18、部署器

      讲解Host是如何不是Context的,必须清楚的是,真正在Tomcat中,我们不是像上面一样通过硬编码部署进去的。

19、Manager应用程序的servlet类

      本章主要讲解如何使用Tomcat自带的Manager应用程序管理已经部署的Web应用程序。

20、基于JMX的管理  

      本文介绍如何使用JMX管理Tomcat。        

 

How Tomcat Works学习笔记

标签:style   blog   http   io   ar   os   使用   sp   java   

原文地址:http://www.cnblogs.com/timlearn/p/4132783.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!