zoukankan      html  css  js  c++  java
  • Tomcat源码学习(13)How Tomcat works(转)

    第3章:连接器

    概要

        在介绍中提到,Catalina中有两个主要的模块:连接器和容器。本章中你将会写一个可以创建更好的请求和响应对象的连接器,用来改进第2章中的程序。一个符合Servlet 2.3和2.4规范的连接器必须创建javax.servlet.http.HttpServletRequest和javax.servlet.http.HttpServletResponse,并传递给被调用的servlet的service方法。在第2章 中,servlet容器只可以运行实现了javax.servlet.Servlet的servlet,并传递 javax.servlet.ServletRequest和javax.servlet.ServletResponse实例给service方法。因为连接器并不知道servlet的类型(例如它是否实现了javax.servlet.Servlet,继承了javax.servlet.GenericServlet,或者继承了javax.servlet.http.HttpServlet),所以连接器必须始终提供HttpServletRequest和HttpServletResponse的实例。
        在本章的应用程序中,连接器解析HTTP请求头部并让servlet可以获得头部, cookies, 参数名/值等等。你将会完善第2章中Response类的getWriter方法,让它能够正确运行。由于这些改进,你将会从 PrimitiveServlet中获取一个完整的响应,并能够运行更加复杂的ModernServlet。
        本章你建立的连接器是将在第4章详细讨论的Tomcat4的默认连接器的一个简化版本。Tomcat的默认连接器在Tomcat4中是不推荐使用的,但它仍然可以作为一个非常棒的学习工具。在这章的剩余部分,"connector"指的是内置在我们应用程序的模块。
        注意:和上一章的应用程序不同的是,本章的应用程序中,连接器和容器是分离的。
        本章的应用程序可以在包ex03.pyrmont和它的子包中找到。组成连接器的这些类是包
    ex03.pyrmont.connector 和ex03.pyrmont.connector.http的一部分。在本章的开头,每个附带的程序都有个bootstrap类用来启动应用程序。不过,在这个阶段,尚未有一个机制来停止这个应用程序。一旦运行,你必须通过关闭控制台(Windows)或者杀死进程(UNIX/Linux)的方法来鲁 莽的关闭应用程序。
        在我们解释该应用程序之前,让我们先来说说包org.apache.catalina.util里边的StringManager类。这个类用来处理这个程序中不同模块和Catalina自身的错误信息的国际化。之后会讨论附带的应用程序。

    StringManager类

        一个像Tomcat这样的大型应用需要仔细的处理错误信息。在Tomcat中,错误信息对于系统管理员和servlet程序员都是有用的。例 如,Tomcat记录错误信息,让系统管理员可以定位发生的任何异常。对servlet程序员来说,Tomcat会在抛出的任何一个 javax.servlet.ServletException中发送一个错误信息,这样程序员可以知道他/她的servlet究竟发送什么错误了。
        Tomcat所采用的方法是在一个属性文件里边存储错误信息,这样,可以容易的修改这些信息。不过,Tomcat中有数以百计的类。把所有类使用的错误信 息存储到一个大的属性文件里边将会容易产生维护的噩梦。为了避免这一情况,Tomcat为每个包都分配一个属性文件。例如,在包 org.apache.catalina.connector里边的属性文件包含了该包所有的类抛出的所有错误信息。每个属性文件都会被一个 org.apache.catalina.util.StringManager类的实例所处理。当Tomcat运行时,将会有许多 StringManager实例,每个实例会读取包对应的一个属性文件。此外,由于Tomcat的受欢迎程度,提供多种语言的错误信息也是有意义的。目前,有三种语言是被支持的。英语的错误信息属性文件名为LocalStrings.properties。另外两个是西班牙语和日语,分别放在 LocalStrings_es.properties和LocalStrings_ja.properties里边。
        当包里边的一个类需要查找放在该包属性文件的一个错误信息时,它首先会获得一个StringManager实例。不过,相同包里边的许多类可能也需要 StringManager,为每个对象创建一个StringManager实例是一种资源浪费。因此,StringManager类被设计成一个StringManager实例可以被包里边的所有类共享。假如你熟悉设计模式,你将会正确的猜到StringManager是一个单例 (singleton)类。仅有的一个构造方法是私有的,所有你不能在类的外部使用new关键字来实例化。你通过传递一个包名来调用它的公共静态方法 getManager来获得一个实例。每个实例存储在一个以包名为键(key)的Hashtable中。
    private static Hashtable managers = new Hashtable();
    public synchronized static StringManager
    getManager(String packageName) {
         StringManager mgr = (StringManager)managers.get(packageName);
         if (mgr == null) {
             mgr = new StringManager(packageName);
             managers.put(packageName, mgr);
         }
         return mgr;
    }
        注意:一篇关于单例模式的题为"The Singleton Pattern"的文章可以在附带的ZIP文件中找到。
        例如,要在包ex03.pyrmont.connector.http的一个类中使用StringManager,可以传递包名给StringManager类的getManager方法:
    StringManager sm =
         StringManager.getManager("ex03.pyrmont.connector.http");
        在包ex03.pyrmont.connector.http中,你会找到三个属性文件:LocalStrings.properties, LocalStrings_es.properties和LocalStrings_ja.properties。StringManager实例是根据运行程序的服务器的区域设置来决定使用哪个文件的。假如你打开LocalStrings.properties,非注释的第一行是这样的:
    httpConnector.alreadyInitialized=HTTP connector has already been initialized
        要获得一个错误信息,可以使用StringManager类的getString,并传递一个错误代号。这是其中一个重载方法:
    public String getString(String key)
        通过传递httpConnector.alreadyInitialized作为getString的参数,将会返回"HTTP connector has already been initialized"。

    应用程序

        从本章开始,每章附带的应用程序都会分成模块。这章的应用程序由三个模块组成:connector,
    startup和core。
        startup模块只有一个类,Bootstrap,用来启动应用的。connector模块的类可以分为五组:
    • 连接器和它的支撑类(HttpConnector和HttpProcessor)。
    • 指代HTTP请求的类(HttpRequest)和它的辅助类。
    • 指代HTTP响应的类(HttpResponse)和它的辅助类。
    • Facade类(HttpRequestFacade和HttpResponseFacade)。
    • Constant类
        core模块由两个类组成:ServletProcessor和StaticResourceProcessor。
        Figure 3.1显示了这个应用的类的UML图。为了让图更具可读性,HttpRequest和HttpResponse相关的类给省略了。你可以在我们讨论Request和Response对象的时候分别找到UML图。
                 Figure 3.1: 应用程序的UML图
        和Figure 2.1的UML图相比,第2章中的HttpServer类被分离为两个类:HttpConnector和HttpProcessor,Request被 HttpRequest所取代,而Response被HttpResponse所取代。同样,本章的应用使用了更多的类。
        第2章中的HttpServer类的职责是等待HTTP请求并创建请求和响应对象。在本章的应用中,等待HTTP请求的工作交给HttpConnector实例,而创建请求和响应对象的工作交给了HttpProcessor实例。
        本章中,HTTP请求对象由实现了javax.servlet.http.HttpServletRequest的HttpRequest类来代表。一个 HttpRequest对象将会给转换为一个HttpServletRequest实例并传递给被调用的servlet的service方法。因此,每个 HttpRequest实例必须适当增加字段,以便servlet可以使用它们。值需要赋给HttpRequest对象,包括URI,查询字符串,参数,cookies和其他的头部等等。因为连接器并不知道被调用的servlet需要哪个值,所以连接器必须从HTTP请求中解析所有可获得的值。不过,解析一个HTTP请求牵涉昂贵的字符串和其他操作,假如只是解析servlet需要的值的话,连接器就能节省许多CPU周期。例如,假如servlet不 解析任何一个请求参数(例如不调用javax.servlet.http.HttpServletRequest的getParameter, getParameterMap,getParameterNames或者getParameterValues方法),连接器就不需要从查询字符串或者 HTTP请求内容中解析这些参数。Tomcat的默认连接器(和本章应用程序的连接器)试图不解析参数直到servlet真正需要它的时候,通过这样来获得更高效率。
        Tomcat的默认连接器和我们的连接器使用SocketInputStream类来从套接字的InputStream中读取字节流。一个 SocketInputStream实例对从套接字的getInputStream方法中返回的java.io.InputStream实例进行包装。 SocketInputStream类提供了两个重要的方法:readRequestLine和readHeader。readRequestLine返回一个HTTP请求的第一行。例如,这行包括了URI,方法和HTTP版本。因为从套接字的输入流中处理字节流意味着只读取一次,从第一个字节到最后一个字节(并且不回退),因此readHeader被调用之前,readRequestLine必须只被调用一次。readHeader每次被调用来获得一个头部的名/值对,并且应该被重复的调用知道所有的头部被读取到。readRequestLine的返回值是一个HttpRequestLine的实例,而 readHeader的返回值是一个HttpHeader对象。我们将在下节中讨论类HttpRequestLine和HttpHeader。
        HttpProcessor对象创建了HttpRequest的实例,因此必须在它们当中增加字段。HttpProcessor类使用它的parse方法 来解析一个HTTP请求中的请求行和头部。解析出来并把值赋给HttpProcessor对象的这些字段。不过,parse方法并不解析请求内容或者请求 字符串里边的参数。这个任务留给了HttpRequest对象它们。只是当servlet需要一个参数时,查询字符串或者请求内容才会被解析。
        另一个跟上一个应用程序比较的改进是用来启动应用程序的bootstrap类ex03.pyrmont.startup.Bootstrap的出现。
        我们将会在下面的子节里边详细说明该应用程序:
    • 启动应用程序
    • 连接器
    • 创建一个HttpRequest对象
    • 创建一个HttpResponse对象
    • 静态资源处理器和servlet处理器
    • 运行应用程序

    启动应用程序

        你可以从ex03.pyrmont.startup.Bootstrap类来启动应用程序。这个类在Listing 3.1中给出。
             Listing 3.1: Bootstrap类
    package ex03.pyrmont.startup;
    import ex03.pyrmont.connector.http.HttpConnector;
    public final class Bootstrap {
         public static void main(String[] args) {
             HttpConnector connector = new HttpConnector();
             connector.start();
         }
    }
        Bootstrap类中的main方法实例化HttpConnector类并调用它的start方法。HttpConnector类在Listing 3.2给出。
             Listing 3.2: HttpConnector类的start方法
    package ex03.pyrmont.connector.http;
    import java.io.IOException;
    import java.net.InetAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    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) {
                 // Accept the next incoming connection from the server socket
                 Socket socket = null;
                 try {
                     socket = serverSocket.accept();
                }
                 catch (Exception e) {
                     continue;
                 }
                 // Hand this socket off to an HttpProcessor
                 HttpProcessor processor = new HttpProcessor(this);
                 processor.process(socket);
             }
         }
         public void start() {
             Thread thread = new Thread(this);
             thread.start ();
         }
    }
  • 相关阅读:
    PAT (Advanced Level) 1114. Family Property (25)
    PAT (Advanced Level) 1113. Integer Set Partition (25)
    PAT (Advanced Level) 1112. Stucked Keyboard (20)
    PAT (Advanced Level) 1111. Online Map (30)
    PAT (Advanced Level) 1110. Complete Binary Tree (25)
    PAT (Advanced Level) 1109. Group Photo (25)
    PAT (Advanced Level) 1108. Finding Average (20)
    PAT (Advanced Level) 1107. Social Clusters (30)
    PAT (Advanced Level) 1106. Lowest Price in Supply Chain (25)
    PAT (Advanced Level) 1105. Spiral Matrix (25)
  • 原文地址:https://www.cnblogs.com/macula7/p/1960624.html
Copyright © 2011-2022 走看看