How Tomcat Works(四)

32308 ワード

サーブレットコンテナには、コネクタ(connector)とコンテナ(container)の2つの主要なモジュールがあり、ここでは、requestオブジェクトとresponseオブジェクトをより優雅に作成するためのコネクタを作成します.サーブレット2.3と2.4を互換するために、コネクタはjavaxを作成する.servlet.http.HttpServeretRequestオブジェクトとjavax.servlet.http.HttpServletResponseオブジェクト(servletオブジェクトタイプはjavax.servlet.Servletインタフェースを実装するかjavax.servlet.GenericServletクラスまたはjavax.servlet.http.HttpServletクラスから継承することができます).
本明細書のアプリケーションでは、コネクタはhttpリクエストヘッダ、cookies、リクエストパラメータなどを解析します.同時にResponseクラスのgetWriter()メソッドを修正し,よりよく動作させた.
まず、servletコンテナでエラーメッセージの国際化をどのように実現するかについて説明します.ここでは主にStringManagerクラスが実現する機能です.
Tomcatは、エラーメッセージをpropertiesファイルに格納し、読み取りと編集が容易です.しかしtomcatのクラスが特に多いため、すべてのクラスで使用されるエラーメッセージを同じpropertiesファイルに格納するとメンテナンスが困難になります.したがってtomcatの処理はpropertiesを異なるパッケージに分割することであり、各propertiesファイルはStringManagerクラスのインスタンスで処理され、tomcatの実行時にStringManagerクラスの複数のインスタンスが生成される.同じパッケージ内のクラスはStringManagerクラスのインスタンスを共有します(ここでは単一のモードを採用します).これらの異なるパッケージに使用されるStringManagerクラスのインスタンスは、1つのhashtableコンテナに格納され、パッケージ名をkeyとしてStringManagerクラスのインスタンスが格納されます.
public class StringManager {

    /**
     * The ResourceBundle for this StringManager.
     */
    
    private ResourceBundle bundle;

    /**
     * Creates a new StringManager for a given package. This is a
     * private method and all access to it is arbitrated by the
     * static getManager method call so that only one StringManager
     * per package will be created.
     *
     * @param packageName Name of package to create StringManager for.
     */

    private StringManager(String packageName) {
    String bundleName = packageName + ".LocalStrings";
    bundle = ResourceBundle.getBundle(bundleName);
    }

    /**
     * Get a string from the underlying resource bundle.
     *
     * @param key 
     */
    
    public String getString(String key) {
        if (key == null) {
            String msg = "key is null";

            throw new NullPointerException(msg);
        }

        String str = null;

        try {
        str = bundle.getString(key);
        } catch (MissingResourceException mre) {
            str = "Cannot find message associated with key '" + key + "'";
        }

        return str;
    }
   // --------------------------------------------------------------
    // STATIC SUPPORT METHODS
    // --------------------------------------------------------------

    private static Hashtable managers = new Hashtable();

    /**
     * Get the StringManager for a particular package. If a manager for
     * a package already exists, it will be reused, else a new
     * StringManager will be created and returned.
     *
     * @param packageName
     */

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

次に、コネクタがどのように実装されているかを分析します.
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();
  }
}

HttpConnectorコネクタの実装から,クライアント要求を傍受し,クライアント要求を監視する際に取得したsocketオブジェクトをHttpProcessorオブジェクトに渡すprocess()メソッド処理を担当することが分かる.
次に、HttpProcessorクラスの実装を分析します.
/* this class used to be called HttpServer */
public class HttpProcessor {

  public HttpProcessor(HttpConnector connector) {
    this.connector = connector;
  }
  /**
   * The HttpConnector with which this processor is associated.
   */
  private HttpConnector connector = null;
  private HttpRequest request;
private HttpResponse response; public void process(Socket socket) { SocketInputStream input = null; OutputStream output = null; try {
// SocketInputStream input
= new SocketInputStream(socket.getInputStream(), 2048); output = socket.getOutputStream(); // create HttpRequest object and parse request = new HttpRequest(input); // create HttpResponse object response = new HttpResponse(output); response.setRequest(request); response.setHeader("Server", "Pyrmont Servlet Container"); // parseRequest(input, output);
// parseHeaders(input);
//check if this is a request for a servlet or a static resource //a request for a servlet begins with "/servlet/" if (request.getRequestURI().startsWith("/servlet/")) { ServletProcessor processor = new ServletProcessor(); processor.process(request, response); } else { StaticResourceProcessor processor = new StaticResourceProcessor(); processor.process(request, response); } // Close the socket socket.close(); // no shutdown for this application } catch (Exception e) { e.printStackTrace(); } }

上記の方法では、socketオブジェクトの入力ストリームと出力ストリームを取得してRequestオブジェクトとResponseオブジェクトをそれぞれ作成し、httpリクエスト行とリクエストヘッダ(Requestオブジェクトに埋め込まれた対応するプロパティ)を解析し、プロセッサに配布します.
次のRequestオブジェクトはjavaxを実現する.servlet.http.HttpServiceletRequestインタフェースは、主に関連要求パラメータを設定する方法と関連要求パラメータを取得する方法を提供します.
httpリクエストヘッダ、cookies、リクエストパラメータなどの情報は、それぞれ次のメンバー変数に格納されます.
protected ArrayList cookies = new ArrayList();
protected HashMap headers = new HashMap();
protected ParameterMap parameters = null;
なお、protected void parseParameters()メソッドは、ParameterMap parametersメンバー変数を初期化するために使用される1回のみ実行されます.
/**
   * Parse the parameters of this request, if it has not already occurred.
   * If parameters are present in both the query string and the request
   * content, they are merged.
   */
  protected void parseParameters() {
    if (parsed)
      return;
    ParameterMap results = parameters;
    if (results == null)
      results = new ParameterMap();
    results.setLocked(false);
    String encoding = getCharacterEncoding();
    if (encoding == null)
      encoding = "ISO-8859-1";

    // Parse any parameters specified in the query string
    String queryString = getQueryString();
    try {
      RequestUtil.parseParameters(results, queryString, encoding);
    }
    catch (UnsupportedEncodingException e) {
      ;
    }

    // Parse any parameters specified in the input stream
    String contentType = getContentType();
    if (contentType == null)
      contentType = "";
    int semicolon = contentType.indexOf(';');
    if (semicolon >= 0) {
      contentType = contentType.substring(0, semicolon).trim();
    }
    else {
      contentType = contentType.trim();
    }
    if ("POST".equals(getMethod()) && (getContentLength() > 0)
      && "application/x-www-form-urlencoded".equals(contentType)) {
      try {
        int max = getContentLength();
        int len = 0;
        byte buf[] = new byte[getContentLength()];
        ServletInputStream is = getInputStream();
        while (len < max) {
          int next = is.read(buf, len, max - len);
          if (next < 0 ) {
            break;
          }
          len += next;
        }
        is.close();
        if (len < max) {
          throw new RuntimeException("Content length mismatch");
        }
        RequestUtil.parseParameters(results, buf, encoding);
      }
      catch (UnsupportedEncodingException ue) {
        ;
      }
      catch (IOException e) {
        throw new RuntimeException("Content read fail");
      }
    }

    // Store the final results
    results.setLocked(true);
    parsed = true;
    parameters = results;
  }

なぜ同期ロックキーを取らないのか分かりません.ParameterMap parametersメンバー変数を初期化する回数には無理がないのでしょうか?
上記のメソッドのParameterMapオブジェクトは、HashMapから継承され、boolean locked=falseメンバー変数を使用して書き込みロックを設定します(これも問題あります).
public final class ParameterMap extends HashMap {


    // ----------------------------------------------------------- Constructors


    /**
     * Construct a new, empty map with the default initial capacity and
     * load factor.
     */
    public ParameterMap() {

        super();

    }


    /**
     * Construct a new, empty map with the specified initial capacity and
     * default load factor.
     *
     * @param initialCapacity The initial capacity of this map
     */
    public ParameterMap(int initialCapacity) {

        super(initialCapacity);

    }


    /**
     * Construct a new, empty map with the specified initial capacity and
     * load factor.
     *
     * @param initialCapacity The initial capacity of this map
     * @param loadFactor The load factor of this map
     */
    public ParameterMap(int initialCapacity, float loadFactor) {

        super(initialCapacity, loadFactor);

    }


    /**
     * Construct a new map with the same mappings as the given map.
     *
     * @param map Map whose contents are dupliated in the new map
     */
    public ParameterMap(Map map) {

        super(map);

    }


    // ------------------------------------------------------------- Properties


    /**
     * The current lock state of this parameter map.
     */
    private boolean locked = false;


    /**
     * Return the locked state of this parameter map.
     */
    public boolean isLocked() {

        return (this.locked);

    }


    /**
     * Set the locked state of this parameter map.
     *
     * @param locked The new locked state
     */
    public void setLocked(boolean locked) {

        this.locked = locked;

    }


    /**
     * The string manager for this package.
     */
    private static final StringManager sm =
        StringManager.getManager("org.apache.catalina.util");


    // --------------------------------------------------------- Public Methods



    /**
     * Remove all mappings from this map.
     *
     * @exception IllegalStateException if this map is currently locked
     */
    public void clear() {

        if (locked)
            throw new IllegalStateException
                (sm.getString("parameterMap.locked"));
        super.clear();

    }


    /**
     * Associate the specified value with the specified key in this map.  If
     * the map previously contained a mapping for this key, the old value is
     * replaced.
     *
     * @param key Key with which the specified value is to be associated
     * @param value Value to be associated with the specified key
     *
     * @return The previous value associated with the specified key, or
     *  <code>null</code> if there was no mapping for key
     *
     * @exception IllegalStateException if this map is currently locked
     */
    public Object put(Object key, Object value) {

        if (locked)
            throw new IllegalStateException
                (sm.getString("parameterMap.locked"));
        return (super.put(key, value));

    }


    /**
     * Copy all of the mappings from the specified map to this one.  These
     * mappings replace any mappings that this map had for any of the keys
     * currently in the specified Map.
     *
     * @param map Mappings to be stored into this map
     *
     * @exception IllegalStateException if this map is currently locked
     */
    public void putAll(Map map) {

        if (locked)
            throw new IllegalStateException
                (sm.getString("parameterMap.locked"));
        super.putAll(map);

    }


    /**
     * Remove the mapping for this key from the map if present.
     *
     * @param key Key whose mapping is to be removed from the map
     *
     * @return The previous value associated with the specified key, or
     *  <code>null</code> if there was no mapping for that key
     *
     * @exception IllegalStateException if this map is currently locked
     */
    public Object remove(Object key) {

        if (locked)
            throw new IllegalStateException
                (sm.getString("parameterMap.locked"));
        return (super.remove(key));

    }


}

同様にResponseオブジェクトはjavaxを実現する.servlet.http.HttpServeretResponseインタフェース
ここでは,前述のgetWriter()メソッド出力文字をクライアントに書き換えた.
public PrintWriter getWriter() throws IOException {
    ResponseStream newStream = new ResponseStream(this);
    newStream.setCommit(false);
    OutputStreamWriter osr =
      new OutputStreamWriter(newStream, getCharacterEncoding());
    writer = new ResponseWriter(osr);
    return writer;
  }

--------------------------------------------------------------------------- 
本シリーズHow Tomcat Works系本人オリジナル
転載は出典のブログ園のハリネズミのおとなしいことを明記してください
本人メールアドレス:chenying 998179#163.com(#を@)に変更
このリンクhttp://www.cnblogs.com/chenying99/p/3231639.html