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.