Tomcat剖析(三):连接器(2)

第一部分:概述

这一节基于《深度剖析Tomcat》第三章:连接器 总结而成,作为上一节的补充,补充上一节未涉及的当我们获取参数时才解析参数这个功能。同时插入Tomcat错误处理的方法

最好先到我的github上下载本书相关的代码,同时去上网下载这本书。

核心类:

  • HttpRequest.java:主要是它里面的相关获取参数的方法
  • ParameterMap.java:存放参数列表
  • StringManager.java:获取错误信息时使用的类

第二部分:代码讲解

HttpRequest.java

功能:动态获取参数

因为只有我们发出请求且从未解析过时才会去读取参数,所以ex03.pyrmont.connector.http.HttpRequest中的getParameter,getParameterMap, getParameterNames 或者 getParameterValues 四个读取参数的方法开头都调用了 parseParameter 方法。如果已经解析过了(参数在请求内容里边被找到的话,),参数解析将会使得 SocketInputStream 到达字节流的尾部。类 HttpRequest 使用一个布尔变量 parsed 来指示是否已经解析过了。

下面先看看parseParameter方法及其里面的注释

protected void parseParameters() {
if (parsed) //如果已经解析过了,直接返回
return;
ParameterMap results = parameters;
if (results == null)//ParameterMap本文下面讲解
results = new ParameterMap();
results.setLocked(false); //打开 parameterMap的锁以便写值。
String encoding = getCharacterEncoding();
if (encoding == null)//检查字符编码,并在字符编码为 null的时候赋予默认字符编码。
encoding = "ISO-8859-1"; //getQueryString方法在上一节解析了请求头时,如果URL如果有查询参数有设置。
//尝试解析查询字符串。解析参数是使用org.apache.Catalina.util.RequestUtil的 parseParameters方法来处理的。
//如果queryString为空(URL中没有参数),下面parseParameters方法的的解析直接返回
String queryString = getQueryString();
try {
RequestUtil.parseParameters(results, queryString, encoding);
} catch (UnsupportedEncodingException e) {
;
} //获取内容类型
String contentType = getContentType();
if (contentType == null)
contentType = "";
int semicolon = contentType.indexOf(';');
if (semicolon >= 0) {
contentType = contentType.substring(0, semicolon).trim();
} else {
contentType = contentType.trim();
}
//请求方式是POST(内容长度大于零)且内容类型是 application/x-www-form-urlencoded
//同样用parseParameters解析POST中的内容
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");
}
} //锁定 ParameterMap表示不可修改参数
//设置 parsed为 true表示已经解析过了,
results.setLocked(true);
parsed = true;
parameters = results;
}

获取的参数可以在查询字符串或者请求内容里边找到。假如用户使用 GET 方法来请求 servlet 的话,所有的参数将在查询字符串里边出现。假如使用 POST 方法的话,你可以在请求内容中找到一些。这就是为什么会处理两次解析的原因。

ParameterMap.java

功能:参数所有的名/值对存储在HashMap里边。

Servlet 程序员可以以 Map 的形式获得参数(通过调用 HttpServletRequest 的 getParameterMap 方法)和参数名/值,但不允许修改参数值。因此将使用一个特殊的HashMap---org.apache.catalina.util.ParameterMap

ParameterMap 继承了 java.util.HashMap,所以许多方法都是用super关键字直接调用HashMap中的方法

那又是如何保证参数不被修改呢?

  • Tomcat在ParameterMap中加入布尔变量 locked 当 locked 是false 的时候,名/值对仅仅可以添加,更新或者移除。否则,lock为true时,异常 IllegalStateException 会抛出,结合parseParameters方法可以更加清晰了解。所以在put时做了些许的修改(对错误处理的类StringManager上一节说过了。)

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

    因为Tomcat4时还没有泛型,所以没有使用泛型,同时也没有继承效率更高的LinkedHashMap

    Tomcat7中是这样的(一小片段)

    public final class ParameterMap<K,V> extends LinkedHashMap<K,V> {
    
         public V put(K key, V value) {
    
            if (locked)
    throw new IllegalStateException
    (sm.getString("parameterMap.locked"));
    return (super.put(key, value)); }
    }

StringManager.java

功能:获取错误信息

这个类在整个Catalina项目中有着比较重要的作用,对于本节的应用,可以在ex03.pyrmont.connector.http.HttpProcessor中找到这个类的实例。

一个像 Tomcat 这样的大型应用需要仔细的处理错误信息。在 Tomcat 中,错误信息对于系统管理员和 servlet 程序员都是有用的。例如,Tomcat 记录错误信息,让系统管理员可以定位发生的任何异常。对 servlet 程 序 员 来 说 , Tomcat 会在抛出的任何一个javax.servlet.ServletException 中发送一个错误信息,这样程序员可以知道他 servlet究竟发送什么错误了。Tomcat 所采用的方法是在一个属性文件里边存储错误信息,这样,可以容易的修改这些信息。

看看StringManager类,下面是核心代码。 代码中的注释有助于理解。

package org.apache.catalina.util;

import java.util.Hashtable;
import java.util.MissingResourceException;
import java.util.ResourceBundle; public class StringManager { private ResourceBundle bundle; //设为private,单例模式的特点,要获取对象,可以通过getManager(String)方法
//就是在这里获取了实例对应包下的bundle对象。
//格式如org.apache.catalina.util.LocalStrigns.properties
private StringManager(String packageName) {
String bundleName = packageName + ".LocalStrings";
bundle = ResourceBundle.getBundle(bundleName);
} //StringManager类实例的包下properties文件中等号左边的值作为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;
} //保存不同包下的StringManager对象
private static Hashtable managers = new Hashtable(); //单例模式用这个方法获取不同包的StringManager对象,
//因为可能同时被多个类使用产生错误,所以方法需要设置为同步
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;
}
}

StringManager详细说明:

  • Tomcat有数以百计的类,如果把所有的类的错误信息都保存在某个大的属性文件中,将导致很严重的维护问题。为了避免这一情况,Tomcat 为每个包都分配一个属性文件。例如,在包org.apache.catalina.connector 里边的属性文件包含了该包所有的类抛出的所有错误信息。每个属性文件都会被一个 org.apache.catalina.util.StringManager 类的实例所处理。相同包里边的许多类可能也需要 StringManager,为每个对象创建一个 StringManager 实例是一种资源浪费。StringManager类采用了单例模式,同一包下使用同一StringManager对象即可,放入StringManager类的managers实例中,不同包才使用不同的对象。

  • 当Tomcat 运行时,将会有许多 StringManager 实例,每个实例会读取这个实例对应包下的一个properties属性文件。此外,由于 Tomcat 的受欢迎程度,提供多种语言的错误信息也是有意义的。目前,有三种语言是被支持的。英语的错误信息属性文件名为 LocalStrings.properties。另外两个是西班牙语和日语,分别放在 LocalStringses.properties 和 LocalStringsja.properties里边。

  • 没有展示的方法都是getString的重载方法,可以在org.apache.catalina.util.Manager中找到这个类查看具体实现。

ex03.pyrmont.connector.http.HttpProcessor.java中有这样几段

protected StringManager sm = StringManager
.getManager("ex03.pyrmont.connector.http"); throw new ServletException(
sm.getString("httpProcessor.parseHeaders.colon"));

ex03.pyrmont.connector.http.LocalStrings.properties中对应的有

httpProcessor.parseHeaders.colon=Invalid HTTP header format

说了那么多,大家应该可以理解了吧。

最后简单说说ResourceBundle国际化的使用:

比如有如下目录结构

package org.bundle;

import java.util.Locale;
import java.util.ResourceBundle; public class TestResourceBundle { public static void main(String[] args) { Locale locale1 = new Locale("zh", "CN");
ResourceBundle resb1 = ResourceBundle.getBundle("org.bundle.myres", locale1);
System.out.println(resb1.getString("login")); Locale locale3 = new Locale("en", "US");
ResourceBundle resb3 = ResourceBundle.getBundle("org.bundle.myres", locale3);
System.out.println(resb3.getString("login")); ResourceBundle resb2 = ResourceBundle.getBundle("org.bundle.myres");//按
System.out.println(resb2.getString("login"));
}
}

myres_en_US.properties和myres.properties内容

login=login

myres_zh_CN.properties内容:后面表示“请登录"中文的UTF-8编码

login=\u8BF7\u767B\u5F55

读取的文件命名有规范: 自定义名_语言代码_国别代码.properties

对于ResourceBundle而言,需要加上完整包名,getBundle第一个参数就是完整包名+自定义名 ,而语言代码和国别代码来自Locale中。

输出结果

请登录
login
请登录

可以看到,如果没有指定Locale,使用的是系统默认的区域和语言。

第三部分:小结

首先我们先讲解了Tomcat如何动态获取参数且只解析一次,即parsed变量和HashMap维护

然后了解了如何防止修改参数值,即扩展HashMap并通过locked变量

最后我们了解了Tomcat获取错误信息的方法

如果我们以后在一个很大型的项目中有许多的类需要处理和管理大量信息时(不仅仅国际化和错误处理) ,你能联想到Tomcat是如何管理错误信息的?我们可以通过包内单例模式,包之间实例保存在Map中。 这样既实现了有效的管理,又节省了内存消耗。所以说学习Tomcat中最重要的是学习思想

关于Tomcat4中连接器就简单说到这,下一节中将讲解Tomcat4默认的连接器,是难点。

如果觉得写得不错的话就推荐一下。

相应代码可以在我的github上找到下载,拷贝到eclipse,然后打开对应包的代码即可。

如发现编译错误,可能是由于jdk不同版本对编译的要求不同导致的,可以不管,供学习研究使用。

Tomcat剖析(三):连接器(2)的更多相关文章

  1. Tomcat剖析(三):连接器(1)

    Tomcat剖析(三):连接器(1) 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三):连接器(1) 4 ...

  2. Tomcat剖析(四):Tomcat默认连接器(2)

    Tomcat剖析(四):Tomcat默认连接器(2) 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三): ...

  3. Tomcat剖析(四):Tomcat默认连接器(1)

    Tomcat剖析(四):Tomcat默认连接器(1) 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三): ...

  4. Tomcat剖析(二):一个简单的Servlet服务器

    Tomcat剖析(二):一个简单的Servlet服务器 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三) ...

  5. Tomcat剖析(一):一个简单的Web服务器

    Tomcat剖析(一):一个简单的Web服务器 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三):连接器 ...

  6. Tomcat剖析(五):Tomcat 容器

    Tomcat剖析(五):Tomcat 容器 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三):连接器(1 ...

  7. javaweb学习总结十八(软件密码学、配置tomcat的https连接器以及tomcat管理平台)

    一:软件密码学 1:对称加密 对称加密是最快速.最简单的一种加密方式,加密(encryption)与解密(decryption)用的是同样的密钥(secret key).对称加密有很多种算法,由于它效 ...

  8. Spring Boot 揭秘与实战(五) 服务器篇 - 内嵌的服务器 Tomcat剖析

    文章目录 1. 内嵌的 Tomcat,一个Jar包运行 2. 如何定制内嵌 Tomcat3. War 包部署的使用细节 2.1. 设置内嵌Tomcat的端口 2.2. 设置内嵌Tomcat的最大线程数 ...

  9. [转]Apache HTTP Server 与 Tomcat 的三种连接方式介绍

    首先我们先介绍一下为什么要让 Apache 与 Tomcat 之间进行连接.事实上 Tomcat 本身已经提供了 HTTP 服务,该服务默认的端口是 8080,装好 tomcat 后通过 8080 端 ...

随机推荐

  1. C++ Primer 学习笔记_33_STL实践与分析(7) --容器适配器

    STL实践与分析 --容器适配器 引: 除了顺序容器.标准库还提供了三种顺序容器适配器:queue,priority_queue和stack.适配器是标准库中的概念.包含容器适配器,迭代器适配器和函数 ...

  2. HPUX在oracle10g安装和卸载缩写

    创作品,出自 "深蓝的blog" 博客,欢迎转载,转载时请务必注明出处.否则追究版权法律责任. 深蓝的blog:http://blog.csdn.net/huangyanlong/ ...

  3. ASP.NET2.0组件控件开发视频 初体验

    原文:ASP.NET2.0组件控件开发视频 初体验 ASP.NET2.0组件控件开发视频 初体验 录了视频,质量不是很好,大家体验下.我会重新录制的 如果不清楚,可以看看http://v.youku. ...

  4. 又一次认识HTML,CSS,Javascript 之node-webkit 初探

    今天我们来系统的.全面的 了解一下前端的一些技术,将有助于我们写出 更优秀的 产品 出来. 什么是HTML? HTML 是用来描写叙述网页的一种语言. HTML 包括一些根节点.子节点,文本节点.属性 ...

  5. 开销是有益的:AppCan 至HTML5移动创新和创业精神和健康

      2014年移动创业更趋向理性,消费级App市场接近饱和,BAT等巨头的竞争更加激烈,市场版图及格局基本定型.而企业级移动应用却迎来爆发增长,替代进入红海的消费级App市场,企业级定制APP开发成为 ...

  6. Oracle listener lsnrctl

    lsnrctl(Listener Control)是一家SQL*Net具,用于控制数据库listener,此工具提供了控制命令listener开端.停止,查看listener状态,更改listener ...

  7. Attribute(两)——定义自己的特色+Asp.net MVC中间filter详细解释

    部分博客是预先定义的有关特性的一些基本特征,同时还Attribute这一概念的一个宏观上的认识,在上篇博客结尾介绍了有关为自己定义特性服务的AttributeUsage,这篇博客主要是通过filter ...

  8. Struts2 拦截器—拦截action

    对于拦截器的基本使用这里我就懒得打字了,我这里就讲下如何用 Struts2 拦截器 拦截action.这是我个人的想法,如果有什么不对的,或者你们有什么更好的方法.请多多留言! 拦截器的默认拦截的方法 ...

  9. 左右margin top问题百分比值

    不想说今天心情有多差! 9点多開始发现一个"bug",需求是依据屏幕高度动态调整某个div的位置.代码大概是这种. <div style="margin-top: ...

  10. Ubuntu14.04设备JDK

    1.设备JDK 打开命令直插式工具.输入以下三个命令: sudo add-apt-repository ppa:webupd8team/java sudo apt-get update sudo ap ...