How Tomcat works学习笔记<三>

来源:互联网 发布:linux设置权限 编辑:程序博客网 时间:2024/04/27 18:32
 

3、连接器:Connector

            连接器Connector和容器Container是Catalina最主要的两大模块,在这一章中通过为应用添加Connector来增强应用,将会在Connector中为Servlet来创建更好的请求Request和响应Response对象。在Servlet2.3和2.4的规范中一个Connector必须能够创建javax.servlet.http.HttpServletRequest和javax.servlet.http.HttpServletResponse对象传递给所调用servlet的service方法。在前一章中我们在servlet容器仅仅能够调用实现了javax.servlet.Servlet的servlet,并传送javax.servlet.ServletRequest和javax.servlet.ServletResponse实例给servlet的service方法,因为连接器并不知道servlet的具体类型(实现javax.servlet.Servlet或者继承javax.servlet.http.HttpServlet或者继承javax.servlet.GenericServlet),所以通常是提供javax.servlet.http.HttpServletRequest和javax.servlet.http.HttpServletResponse实例。

           在这里应用中,我们的Connector中会去解析HTTP请求的头信息来确保servlet获得headers、cookies及parameter的键值对,从PrimitiveServlet中获得更加完善的响应,并能够运行ModernServlet。

           这里使用的Connector是从Tomcat4中的默认连接器中简化而来,虽然从Tomcat5以后,默认的Connector已经废弃,不建议使用了,但是作为它仍然不失为一个很好的教学案例。从这一章开始,将会为每一个应用添加一个引导程序bootstrap,但暂时还不提供停止程序。

           在Tomcat中用org.apache.catalina.util.StringManager来处理每个包中的错误信息的国际化,首先来看看这个类:

3.1          StringManager类

         在Tomcat中记录日志是通过StringManager类来实现的,StringManager被设计成多例模式,通过StringManager.getManager(String pakegeName)的方式为每一个包实例化一个StringManager对象,同时StringManger被设计成能够支持错误信息国际化,目前支持英语、西班牙语和日语,分别对用每个包下的LocalString.properties、LocalString_es.properties和LocalString_ja.properties,StringManager的多例实现如下:

private static Hashtable<String, StringManager> managers =

     new Hashtable<String, StringManager>();

public synchronized static StringManager getManager(

String packageName){

       StringManager mgr = managers.get(packageName);

       if(mgr == null){

           mgr = new StringManager(packageName);

           managers.put(packageName, mgr);

       }  

       return mgr;

    }

如需要在com.ex03.pyf.connector.http下使用,只需要传入包名即可,如:

    StringManager mgr =

       StringManager.getManager(“com.ex03.pyf.connector.http”);

3.2          应用

         从这一章开始运用模块化思想进行设计,主要为三大模块:startup、core、connector。Startup模块目前只有一个Bootstrap类,core包括ServletProcessor类和StaticResourceProcessor两个类,connector被分成五个部分:

         Connector及其相关支持类(HttpConnector和HttpProcessor)

         HTTP请求及其相关支持类(HttpRequest)

         HTTP响应及其相关支持类(HttpResponse)

         门面类(HttpRequestFacade和HttpResponseFacade)

         常量类(Constant)

        

见下面类图:

 

3.2.1              启动应用

         为应用提供一个引导程序Bootstrap类,在该类中提供应用的入口main方法,实例化连接器HttpConnector类,并调用其的start方法:

package com.ex03.pyf.startup;

import com.ex03.pyf.connector.http.HttpConnector;

/**

 * 应用启动类

 * @author Peng_yf

 * @version 1.0, 2011-08-22

 * @since 1.0

 */

public class Bootstrap {

  /**

   * 启动应用

   * @param args

   */

  public static void main(String[] args) {

     HttpConnector connector = new HttpConnector();

     connector.start();

  }

}

3.2.2              连接器

         com.ex03.pyf.connector.http.HttpConnector代表一个连接器,它实现Runnable接口,提供一个start方法,在其中会启动一个线程创建一个ServerSocket监听来自客户端的请求,一旦接收到请求,它将会调用HttpProcessor的processor方法进行处理:

package com.ex03.pyf.connector.http;

import java.io.IOException;

import java.net.InetAddress;

import java.net.ServerSocket;

import java.net.Socket;

import java.net.UnknownHostException;

/**

 * 连接器

 * @author Peng_yf

 * @version 1.0, 2011-08-22

 * @since 1.0

 */

public class HttpConnector implements Runnable{

  boolean stopped = false;

  /**

   * 启动连接器

   */

  public void start() {

     Thread thread = new Thread(this);

     thread.start();

  }

  @Override

  public void run() {

     ServerSocket serverSocket = null;

     int port = 8080;

     try {

         serverSocket = new ServerSocket(port,1,InetAddress.getByName("127.0.0.1"));

     } catch (UnknownHostException e) {

         e.printStackTrace();

     } catch (IOException e) {

         e.printStackTrace();

         System.exit(1);

     }

     while(!stopped){

         //Accept the next incoming connection from server socket

         Socket socket = null;

         try {

            socket = serverSocket.accept();

         } catch (IOException e) {  

            e.printStackTrace();

            continue;

         }

         //Hand this socket off to HttpProcessor;

         HttpProcessor processor = new HttpProcessor(this);

         processor.process(socket);

     }

  }

}

          对于每一个请求Request,HttpProcessor的process方法收到一个Socket,对于每一个HTTP request,都需要做下面的事情:

1.       创建HttpRequest对象;

2.       创建HttpResponse对象;

3.       解析HTTP请求的第一行,及其头信息构造HttpRequest;

4.       把HttpRequest和HttpResponse对象传给ServletProcessor或StaticResourceProcessor的process方法

3.2.3              创建HttpRequest对象

         HttpRequest类实现了javax.servlet.http.HttpServletRequest,相应的门面类HttpRequestFacadele,具体关系见类图关系:

创建一个HttpRequest包括下列步骤:

读取Socket的输入流(InputStream)

解析RequestLine

解析RequestHeader

解析Cookies

获取参数Parameters

 

3.2.4              创建HttpResponse对象

         HttpResponse实现了javax.servlet.http.HttpServletResponse,相应的门面类HttpResponseFacade,具体关系见类图关系:

        

3.2.5              运行应用

        要运行应用,需要导入servlet-api.jar(servlet2.4以后)或servlet.jar(servlet2.3),同时在需要把PrimitiveServlet.java编译后放在webroot下,在页面浏览器中输入

                       http://localhost:8080/servlet/PrimitiveServlet

        将会在页面输出:

                       Hello.Roses are red. Violets are blue.