Tomcat剖析(三):连接器(2)
Tomcat剖析(三):连接器(2)
- 1. Tomcat剖析(一):一个简单的Web服务器
- 2. Tomcat剖析(二):一个简单的Servlet服务器
- 3. Tomcat剖析(三):连接器(1)
- 4. Tomcat剖析(三):连接器(2)
- 5. Tomcat剖析(四):Tomcat默认连接器(1)
- 6. Tomcat剖析(四):Tomcat默认连接器(2)
- 7. Tomcat剖析(五):容器
第一部分:概述
这一节基于《深度剖析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)的更多相关文章
- Tomcat剖析(三):连接器(1)
Tomcat剖析(三):连接器(1) 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三):连接器(1) 4 ...
- Tomcat剖析(四):Tomcat默认连接器(2)
Tomcat剖析(四):Tomcat默认连接器(2) 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三): ...
- Tomcat剖析(四):Tomcat默认连接器(1)
Tomcat剖析(四):Tomcat默认连接器(1) 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三): ...
- Tomcat剖析(二):一个简单的Servlet服务器
Tomcat剖析(二):一个简单的Servlet服务器 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三) ...
- Tomcat剖析(一):一个简单的Web服务器
Tomcat剖析(一):一个简单的Web服务器 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三):连接器 ...
- Tomcat剖析(五):Tomcat 容器
Tomcat剖析(五):Tomcat 容器 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三):连接器(1 ...
- javaweb学习总结十八(软件密码学、配置tomcat的https连接器以及tomcat管理平台)
一:软件密码学 1:对称加密 对称加密是最快速.最简单的一种加密方式,加密(encryption)与解密(decryption)用的是同样的密钥(secret key).对称加密有很多种算法,由于它效 ...
- Spring Boot 揭秘与实战(五) 服务器篇 - 内嵌的服务器 Tomcat剖析
文章目录 1. 内嵌的 Tomcat,一个Jar包运行 2. 如何定制内嵌 Tomcat3. War 包部署的使用细节 2.1. 设置内嵌Tomcat的端口 2.2. 设置内嵌Tomcat的最大线程数 ...
- [转]Apache HTTP Server 与 Tomcat 的三种连接方式介绍
首先我们先介绍一下为什么要让 Apache 与 Tomcat 之间进行连接.事实上 Tomcat 本身已经提供了 HTTP 服务,该服务默认的端口是 8080,装好 tomcat 后通过 8080 端 ...
随机推荐
- 技术七Gitservergitolite要构建和操作方便
最近,人懒,为了让自己的变化,所以,我决定花时间学习一些新的技术,.对于这些新技术,现在的需求不是很高.只需要在它的入口. 由于本人仅仅是花三四天整出来的东西,所以不洗勿喷,另外难免会有错误,如有还请 ...
- 九度OJ 1177 查找 (模拟)
题目1177:查找 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:5659 解决:1667 题目描写叙述: 读入一组字符串(待操作的),再读入一个int n记录记下来有几条命令,总共同拥有 ...
- 十一:Java之GUI图形Awt和Swing
一. AWT和 Swing AWT 和 Swing 是 Java 设计 GUI用户界面的基础.与 AWT 的重量级组件不同,Swing 中大部分是轻量级组件.正是这个原因,Swing 差点儿无所不能, ...
- TCP/IP具体解释--nagle算法和TCP_NODELAY
在client一直给server发送小数据的时候,接受到一个回应会在非常长的时间以后,可是将多个小数据写操作合并成一个写操作,问题就没了. 这个事件的缘由可能是TCP_NODELAY的原因 如今大概明 ...
- 网络基础知识系列:阐述VLAN和Trunk
网络性能是影响的效率的重要因素. 大的广播域分割方法,旨在提高网络性能.一个接口上,可是,路由器的LAN接口数量有限,它的主要功能是在网络间数据传输,而不是对终端设备提供网络接入. 訪问LAN的功能还 ...
- mac eclipse svn subeclipse: Failed to load JavaHL Library.
Failed to load JavaHL Library. These are the errors that were encountered: no libsvnjavahl-1 in java ...
- POJ 3076 Sudoku DLX精确覆盖
DLX精确覆盖模具称号..... Sudoku Time Limit: 10000MS Memory Limit: 65536K Total Submissions: 4416 Accepte ...
- 从电商秒杀与抢购谈Web系统大规模并发
从电商秒杀与抢购谈Web系统大规模并发 http://www.iamlintao.com/4242.html 一.大规模并发带来的挑战 在过去的工作中,我曾经面对过5w每秒的高并发秒杀功能,在这个过程 ...
- Kienct与Arduino学习笔记(2) 深度图像与现实世界的深度图的坐标
转载请注明出处:http://blog.csdn.net/lxk7280 首先,要接触一下KinectOrbit这个摄像机库,这篇文章中有这个库的下载网址和简单的介绍:http://blog.csdn ...
- Android - 除首次使用状态(SharedPreferences)
除首次使用状态(SharedPreferences) 本文地址: http://blog.csdn.net/caroline_wendy 用户首次登陆时, 可能须要用户教育, 解说界面操作, 可是不应 ...