`
hyw520110
  • 浏览: 220869 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

[How Tomcat Works]第3章 连接器(二)

    博客分类:
  • java
阅读更多

译者 jarfield  

博客 http://jarfield.iteye.com

获取参数

    除非servlet 调用javax.servlet.http.HttpServletRequestgetParametergetParameterMapgetParameterNamesgetParameterValues 方法来读取请求参数,否则,我们都不需要解析query stringHTTP 请求体(request body )。 因此,HttpRequest 类实现的这四个方法,都是先调用parseParameter 方法。

    请求参数只需要被解析一次,而且也许只能被解析一次,因为如果请求参数是在请求体中,参数解析会导致SocketInputStream 到达字节流的末尾。HttpRequest 类使用boolean 成员parsed 来表示请求参数是否已经被解析。

    如果是GET 请求,那么所有的请求参数都在query sting 中。如果是POST 请求,在请求体中也可以找到一些参数。所有参数以名/值对的形式存储在一个HashMap 中。Servlet 程序员能够以两种形式获取参数:Map (调用HttpServletRequestgetParameterMap 方法)和名/值对。但是,这里有个“圈套”。Servlet 程序员不允许修改参数的值。因此,使用了一个特殊的HashMaporg.apache.catalina.util.ParameterMap

    ParameterMap 类继承了java.util.HashMap ,并使用了一个boolean 成员locked 。只有locked 的值为false 时,名/值对才能被添加、修改和删除。否则,将会抛出一个IllegalStateException 异常。ParameterMap 的代码见Listing 3.6 。它重载了添加、修改和删除元素的方法。这些方法只能在locked 值为false 才能被调用。

Listing 3.6: The org.apache.Catalina.util.ParameterMap class.   

Java代码 复制代码
  1. package org.apache.catalina.util;   
  2. import java.util.HashMap;   
  3. import java.util.Map;   
  4.     
  5. public final class ParameterMap extends HashMap {   
  6.    public ParameterMap() {   
  7.      super ();   
  8.    }   
  9.    public ParameterMap(int initialCapacity) {   
  10.      super(initialCapacity);   
  11.    }   
  12.    public ParameterMap(int initialCapacity, float loadFactor) {   
  13.      super(initialCapacity, loadFactor);   
  14.    }   
  15.    public ParameterMap(Map map) {   
  16.      super(map);   
  17.    }   
  18.    private boolean locked = false;   
  19.    public boolean isLocked() {   
  20.      return (this.locked);   
  21.     
  22.    }   
  23.    public void setLocked(boolean locked) {   
  24.      this.locked = locked;   
  25.    }   
  26.    private static final StringManager sm =   
  27.      StringManager.getManager("org.apache.catalina.util");   
  28.    public void clear() {   
  29.      if (locked)   
  30.        throw new IllegalStateException(sm.getString("parameterMap.locked"));   
  31.      super.clear();   
  32.    }   
  33.    public Object put(Object key, Object value) {   
  34.      if (locked)   
  35.        throw new IllegalStateException   
  36.          (sm.getString("parameterMap.locked"));   
  37.      return (super.put(key, value));   
  38.    }   
  39.    public void putAll(Map map) {   
  40.      if (locked)   
  41.        throw new IllegalStateException   
  42.          (sm.getString("parameterMap.locked"));   
  43.      super.putAll(map);   
  44.    }   
  45.     
  46.    public Object remove(Object key) {   
  47.      if (locked)   
  48.        throw new IllegalStateException   
  49.          (sm.getString("parameterMap.locked"));   
  50.      return (super.remove(key));   
  51.    }   
  52. }   
package org.apache.catalina.util;
import java.util.HashMap;
import java.util.Map;
 
public final class ParameterMap extends HashMap {
   public ParameterMap() {
     super ();
   }
   public ParameterMap(int initialCapacity) {
     super(initialCapacity);
   }
   public ParameterMap(int initialCapacity, float loadFactor) {
     super(initialCapacity, loadFactor);
   }
   public ParameterMap(Map map) {
     super(map);
   }
   private boolean locked = false;
   public boolean isLocked() {
     return (this.locked);
 
   }
   public void setLocked(boolean locked) {
     this.locked = locked;
   }
   private static final StringManager sm =
     StringManager.getManager("org.apache.catalina.util");
   public void clear() {
     if (locked)
       throw new IllegalStateException(sm.getString("parameterMap.locked"));
     super.clear();
   }
   public Object put(Object key, Object value) {
     if (locked)
       throw new IllegalStateException
         (sm.getString("parameterMap.locked"));
     return (super.put(key, value));
   }
   public void putAll(Map map) {
     if (locked)
       throw new IllegalStateException
         (sm.getString("parameterMap.locked"));
     super.putAll(map);
   }
 
   public Object remove(Object key) {
     if (locked)
       throw new IllegalStateException
         (sm.getString("parameterMap.locked"));
     return (super.remove(key));
   }
} 

 

    现在,让我们看看parseParameters 方法是如何工作的。

    由于请求参数既可以存在于query string 中,也可以存在于 HTTP请求体中,parseParameters 方法必须都检查query string 和请求体。一旦解析完成,就可以在parameters 成员变量中找到这些参数。因此该方法首先检查成员变量parsed 的值,如果之前已经解析过,parsed 的值就是true

Java代码 复制代码
  1. if (parsed)   
  2.   return;  
    if (parsed)
      return;


    然后,parseParameters 方法声明创建一个名为resultsParameterMap 变量,将它指向parameters 。如果parametersnull ,就创建一个新的ParameterMap 对象。

Java代码 复制代码
  1. ParameterMap results = parameters;   
  2. if (results == null)   
  3.   results = new ParameterMap();   
    ParameterMap results = parameters;
    if (results == null)
      results = new ParameterMap(); 

 

    接着,parseParameters 方法打开ParameterMap 的锁,以便修改它。

Java代码 复制代码
  1. results.setLocked(false);   
    results.setLocked(false); 

 

    下一步,parseParameters 方法检查编码,如果编码为空则赋予默认编码。

Java代码 复制代码
  1. String encoding = getCharacterEncoding();   
  2. if (encoding == null)   
  3.   encoding = "ISO-8859-1";   
    String encoding = getCharacterEncoding();
    if (encoding == null)
      encoding = "ISO-8859-1"; 


    接着,parseParameters 方法尝试解析query string 。参数解析是由org.apache.Catalina.util.RequestUtil 类的parseParameter 方法完成的。

Java代码 复制代码
  1. // Parse any parameters specified in the query string   
  2. String queryString = getQueryString();   
  3. try {   
  4.   RequestUtil.parseParameters(results, queryString, encoding);   
  5. }   
  6. catch (UnsupportedEncodingException e) {   
  7.   ;   
  8. }   
    // Parse any parameters specified in the query string
    String queryString = getQueryString();
    try {
      RequestUtil.parseParameters(results, queryString, encoding);
    }
    catch (UnsupportedEncodingException e) {
      ;
    } 

 

    下一步,parseParameters 方法尝试看看HTTP 请求体中是否包含参数。如果用户使用POST 方法发送请求,content length 大于0 ,并且content typeapplication/x-www-form-urlencoded ,那么请求体中就会包含参数。解析请求体的代码如下。

Java代码 复制代码
  1. // Parse any parameters specified in the input stream   
  2. String contentType = getContentType();   
  3. if (contentType == null)   
  4.   contentType = "";   
  5. int semicolon = contentType.indexOf(';');   
  6. if (semicolon >= 0) {   
  7.   contentType = contentType.substring (0, semicolon).trim();   
  8. }   
  9. else {   
  10.   contentType = contentType.trim();   
  11. }   
  12. if ("POST".equals(getMethod()) && (getContentLength() > 0)   
  13.   && "application/x-www-form-urlencoded".equals(contentType)) {   
  14.   try {   
  15.     int max = getContentLength();   
  16.     int len = 0;           
  17.     byte buf[] = new byte[getContentLength()];   
  18.     ServletInputStream is = getInputStream();   
  19.     while (len < max) {   
  20.       int next = is.read(buf, len, max - len);   
  21.       if (next < 0 ) {   
  22.         break;   
  23.       }   
  24.       len += next;   
  25.     }   
  26.     is.close();   
  27.     if (len < max) {   
  28.       throw new RuntimeException("Content length mismatch");   
  29.     }   
  30.     RequestUtil.parseParameters(results, buf, encoding);   
  31.   }   
  32.   catch (UnsupportedEncodingException ue) {   
  33.     ;   
  34.   }   
  35.   catch (IOException e) {   
  36.     throw new RuntimeException("Content read fail");   
  37.   }   
  38. }   
    // 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");
      }
    } 


    最后,parseParameters 方法锁住ParameterMap ,设置parsed 成员的值为true ,并将results 赋值给parameters

Java代码 复制代码
  1. // Store the final results   
  2. results.setLocked(true);   
  3. parsed = true;   
  4. parameters = results;  
    // Store the final results
    results.setLocked(true);
    parsed = true;
    parameters = results;

创建HttpResponse 对象

   HttpResponse 类实现了javax.servlet.http.HttpServletResponse 接口,跟随该类的还有一个门面类HttpResponseFacadeFigure 3.3 展现了HttpResponse 和相关类的类图。

   
    在第2 章,我们使用的HttpResponse 类仅包括部分功能。例如,它的getWriter 方法返回了一个java.io.PrintWriter 对象,该对象的print 方法并不会自动flush 。本章的应用将修复这个问题。为了理解如何修复的,你需要知道Writer 是什么。

    在servlet 内部,你使用PrintWriter 来写入字符。你可以使用任何你希望的编码,但是字符总是作为字节流发送给浏览器。因此,第2 章中ex02.pyrmont.HttpResponsegetWriter 方法的实现也就不奇怪了:

Java代码 复制代码
  1. public PrintWriter getWriter() {   
  2.   // if autoflush is true, println() will flush,   
  3.   // but print() will not.   
  4.   // the output argument is an OutputStream   
  5.   writer = new PrintWriter(output, true);   
  6.   return writer;   
  7. }   
   public PrintWriter getWriter() {
     // if autoflush is true, println() will flush,
     // but print() will not.
     // the output argument is an OutputStream
     writer = new PrintWriter(output, true);
     return writer;
   } 


    看,我们是如何创建PrintWriter 对象的?传递一个java.io.OutputStream 实例作为参数。任何内容传递给PrintWriterprintprintln 方法,都会被转成字节流,通过底层OutputStream 对象发送出去。

    本章我们使用ex03.pyrmont.connector.ResponseStream 类的实例作为PrintWriterOutputStream 。注意,ResponseStream 类直接继承了java.io.OutputStream 类。

    本章我们还使用继承了PrintWriterex03.pyrmont.connector.ResponseWriter 类。ResponseWriter 类重写了所有的printprintln 方法,对这些方法的调用都会自动将输出flush 到底层的OutputStream 。因此,我们使用以ResponseStream 对象作为底层的ResponseWriter 实例。

    我们本可以将一个ResponseStream 对象作为参数,实例化ResponseWriter 类。然而,我们使用java.io.OutputStreamWriter 对象桥接了ResponseWriter 对象和ResponseStream 对象。

    有了OutputStreamWriter ,被写入的字符自动按照指定的字符集被编码成字节。这里使用的字符集,或者通过名称来指定,或者显式指定Charset 对象,或者使用平台默认的字符集。对write 方法的每次调用,都会对给定的字符进行编码转换。在写入底层输入流之前,转换后的字节先被累积在缓冲区中。缓冲区的大小可以指定,但对于大多数场景来说,默认大小已经足够了。

    因此,getWriter 方法就按下面的代码来实现:

Java代码 复制代码
  1. public PrintWriter getWriter() throws IOException {   
  2.   ResponseStream newStream = new ResponseStream(this);   
  3.   newStream.setCommit(false);   
  4.   OutputStreamWriter osr =   
  5.     new OutputStreamWriter(newStream, getCharacterEncoding());   
  6.   writer = new ResponseWriter(osr);   
  7.   return writer;   
  8. }   
   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;
   } 

静态资源处理器和Servlet 处理器

    ServletProcessor 类和第2 章的ex02.pyrmont.ServletProcessor 类是类似的。它们都只有一个方法:process 。然而,ex03.pyrmont.connector.ServletProcessorprocess 方法接受一个HttpRequest 对象和一个HttpResponse 对象,而不是Request 对象和Response 对象。下面是本章应用的process 方法原型:

Java代码 复制代码
  1. public void process(HttpRequest request, HttpResponse response) {   
public void process(HttpRequest request, HttpResponse response) { 


    另外,process 方法使用HttpRequestFacadeHttpResponseFacade ,作为请求和响应的门面类。而且在调用了servletservice 方法后,还调用了HttpResponse 类的finishResponse 方法。

Java代码 复制代码
  1. servlet = (Servlet) myClass.newInstance();   
  2. HttpRequestFacade requestPacade = new HttpRequestFacade(request);   
  3. HttpResponseFacade responseFacade = new  
  4.   HttpResponseFacade(response);   
  5. servlet.service(requestFacade, responseFacade);   
  6. ((HttpResponse) response).finishResponse();  
      servlet = (Servlet) myClass.newInstance();
      HttpRequestFacade requestPacade = new HttpRequestFacade(request);
      HttpResponseFacade responseFacade = new
        HttpResponseFacade(response);
      servlet.service(requestFacade, responseFacade);
      ((HttpResponse) response).finishResponse();


    StaticResourceProcessor 类和ex02.pyrmont.StaticResourceProcessor 基本相同。

运行应用程序

    要在Windows 运行该应用,在工作目录运行以下命令:

Shell代码 复制代码
  1. java -classpath ./lib/servlet.jar;./ ex03.pyrmont.startup.Bootstrap  
java -classpath ./lib/servlet.jar;./ ex03.pyrmont.startup.Bootstrap


    在Linux 上,需要使用冒号来分隔两个库。

Shell代码 复制代码
  1. java -classpath ./lib/servlet.jar:./ ex03.pyrmont.startup.Bootstrap   
java -classpath ./lib/servlet.jar:./ ex03.pyrmont.startup.Bootstrap 

 

    要显示index.html ,使用下面的URL

Html代码 复制代码
  1. http://localhost:808O/index.html  
http://localhost:808O/index.html

 

    要调用PrimitiveServlet,直接在浏览器中访问下面的URL:

Html代码 复制代码
  1. http://localhost:8080/servlet/PrimitiveServlet     
http://localhost:8080/servlet/PrimitiveServlet   


    你会在浏览器中看到下面的内容:

Html代码 复制代码
  1. Hello. Roses are red.   
  2. Violets are blue.   
Hello. Roses are red.
Violets are blue. 

 
    提示:第2章中运行 PrimitiveServlet 时,并没有输出第二行。

    你也可以调用第2 章中没有的ModernServletURL 是:

Html代码 复制代码
  1. http://localhost:8080/servlet/ModernServlet     
http://localhost:8080/servlet/ModernServlet   


    提示: ModernServlet 的代码在工作目录的 webroot 目录下。

    你可以在URL后面拼上一个query stirng ,来测试servletFigure 3.4 显示了,以下面URL 运行ModernServlet 的输出结果:

Html代码 复制代码
  1. http://localhost:8080/servlet/ModernServlet?userName=tarzan&password=pwd      
http://localhost:8080/servlet/ModernServlet?userName=tarzan&password=pwd    

 


Figure 3.4 : 运行ModernServlet   

总结

    本章你已经学习到了连接器 是如何工作的。本章构建的连接器 ,是Tomcat 4 默认连接器 的一个简化版本。正如你知道的,这个默认连接器 因为低效而被不推荐使用(deprecated )。例如,所有的HTTP 请求headers都被解析,即使servlet 从来没有使用它们。结果,默认连接器 运行速度缓慢,并被更快的Coyote 代替。Coyote 的代码可以从Apache基金会网站下载到。无论如何,默认连接器都是一个很好的学习工具,我们将在第4 章详细讨论它。

分享到:
评论

相关推荐

    How Tomcat Works 中文版.pdf

    《How Tomcat Works》中文版一书详细剖析了Tomcat服务器的内部工作机制。该书基于Tomcat 4.1.12和5.0.18两个版本,深入讲解了其servlet容器的架构和运作原理,尤其是代号为Catalina的核心组件。 Tomcat是一个开源的...

    译How Tomcat Works(第二章)

    《译How Tomcat Works(第二章)》这篇文章主要讲解了Apache Tomcat服务器的工作原理,它是一个开源的Java Servlet容器,广泛用于部署Web应用程序。在这一章中,我们将深入探讨Tomcat如何处理HTTP请求,以及其内部架构...

    How Tomcat Works 中文版+例程源码

    《How Tomcat Works》是一本深入探讨Apache Tomcat工作原理的书籍,中文版的提供使得国内开发者能够更方便地理解这一流行的开源Java Servlet容器。这本书不仅涵盖了Tomcat的基础知识,还详细解析了其内部机制,对于...

    How Tomcat Works【英文PDF+中文HTML+源码】.zip

    1. **架构概述**:Tomcat的架构基于服务器-客户端模型,主要由Catalina(Servlet容器)、 Coyote(HTTP/HTTPS连接器)和Jasper(JSP引擎)三个主要组件构成。Catalina处理Servlet,Coyote处理网络通信,Jasper编译和...

    HowTomcatWorks(书和源码)

    《How Tomcat Works》是一本深入解析Apache Tomcat工作原理的书籍,同时也包含了源码,为读者提供了理论与实践相结合的深入学习体验。Tomcat是一款广泛使用的开源Java Servlet容器,它是Apache软件基金会 Jakarta...

    How Tomcat Works 中文版

    《How Tomcat Works中文版》这本书是一本深入探讨Apache Tomcat服务器工作原理的专著。Apache Tomcat服务器,或简称为Tomcat,是世界上广泛使用的Java Servlet容器和JavaServer Pages(JSP)引擎,负责处理基于Java...

    How Tomcat Works中文

    ### How Tomcat Works中文版深度解析 #### 一、引言与概述 《How Tomcat Works》是一本针对Apache Tomcat服务器内部工作机制进行深入剖析的专业书籍。本书详细介绍了Tomcat 4.1.12和5.0.18两个版本的内部结构与...

    How Tomcat Works 英文书及源码

    《How Tomcat Works》这本书是理解Apache Tomcat服务器工作原理的宝贵资源,它全面深入地讲解了这个流行的Java Servlet和JavaServer Pages(JSP)容器的内部机制。书中的20个章节涵盖了从基础概念到高级特性的广泛...

    How Tomcat works(PDF)

    《How Tomcat Works》这本书深入浅出地介绍了Apache Tomcat这款广泛应用的Java Servlet容器的工作原理。Tomcat作为开源软件,是许多Web应用的基础,尤其在轻量级开发和测试环境中非常常见。以下是对Tomcat核心知识点...

    How Tomcat Works 读书笔记(第三章)

    《How Tomcat Works》这本书是理解Apache Tomcat服务器工作原理的重要资源,第三章主要探讨了Tomcat的架构和核心组件。以下是对这部分内容的详细解读: Tomcat作为一款开源的Java Servlet容器,其核心功能是解析...

    HowTomcatWorks 中文版+源码.rar

    《HowTomcatWorks》是一本深入解析Apache Tomcat工作原理的书籍,中文版的发布使得更多的中国开发者能够理解和掌握这款广泛应用的开源Java Servlet容器的工作机制。Tomcat是Apache软件基金会Jakarta项目的一部分,它...

    how tomcat works

    《how tomcat works》是一本深入探讨Apache Tomcat内部工作原理的专业书籍。Apache Tomcat是一个开源的Java Servlet容器,它实现了Java Servlet和JavaServer Pages技术规范,提供了Java Web服务器的功能。对于Java ...

    how tomcat works中英文版

    《How Tomcat Works》是一本深入探讨Apache Tomcat工作原理的书籍,包含了中英文两个版本。这本书对于理解Java Servlet和JavaServer Pages(JSP)容器的运作方式具有极高的价值,特别是对于那些想要深入理解Web应用...

    How Tomcat Works 中文版/英文版 + 源码

    《How Tomcat Works》是一本深入解析Apache Tomcat服务器内部工作原理的重要参考资料,它提供了对Tomcat架构的全面理解,包括其设计、配置和优化。这本书的中文版和英文版都为读者提供了便利,无论你是母语为中文...

Global site tag (gtag.js) - Google Analytics