一、这一章从头构建一个简单的Servlet容器,可以处理Servlet和静态资源(如html文件/图片等)。

要处理Servlet,必须遵循javax.servlet.Servlet规范,而处理静态资源同第一章。

关键是模仿tomcat的结构,来合理组织代码。

首先,servlet规范规定javax.servlet.Servlet接口有5个方法,签名如下:

public void init(ServletConfig config) throws ServletException 
public void service(ServletRequest request, ServletResponse response) throws ServletException, java.io.IOException
public void destroy()
public ServletConfig getServletConfig()
public java.lang.String getServletInfo()

其中,init,service,destroy为servlet生命周期方法。

这一章有两个示例Application。Application1和Application2。

二、Application1-UML图:

HttpServer1中有await方法,等待HTTP请求,对于每个请求创建Request和Response对象,并且分派到ServletProcess1或者StaticRequestProcessor(根据请求url)。

HttpServer1:

public class HttpServer1 {

	private boolean shutdown = false;

	public static void main(String[] args) {
new HttpServer1().await();
} public void await() {
ServerSocket serverSocket = null;
int port = 8080;
try {
serverSocket = new ServerSocket(port, 1, InetAddress
.getByName("localhost"));
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
while (!shutdown) {
Socket socket = null;
InputStream inputStream = null;
OutputStream outputStream = null;
try {
socket = serverSocket.accept();
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
//create request object
Request request = new Request(inputStream);
request.parse();
//create response object
Response response = new Response(outputStream);
response.setRequest(request);
//check if this is a request for a servlet or for a static resource
if(request.getUri().startsWith("/servlet/")) {
ServletProcessor1 servletProcessor1 = new ServletProcessor1();
servletProcessor1.process(request, response);
} else {
StaticResourceProcessor staticResourceProcessor = new StaticResourceProcessor();
staticResourceProcessor.process(request, response);
}
//close the socket
socket.close();
// check if the url is a shutdown command
shutdown = request.getUri().equals("SHUTDOWN");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

HttpServer1主要是创建ServerSocket,绑定在8080端口,等待socket请求,并根据uri判断是静态资源请求还是请求servlet。

Request:

package ex02.pyrmont;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map; import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest; public class Request implements ServletRequest { private InputStream inputStream;
private String uri; public Request(InputStream inputStream) {
this.inputStream = inputStream;
} public void parse() {
// read a set of characters from the socket
StringBuffer request = new StringBuffer(2048);
byte[] buffer = new byte[2048];
int len = 0;
try {
len = inputStream.read(buffer);
} catch (IOException e) {
e.printStackTrace();
len = -1;
}
for(int j=0; j<len; j++) {
request.append((char)buffer[j]);
}
System.out.println(request.toString());
uri = praseUri(request.toString());
} private String praseUri(String string) {
int index1, index2;
index1 = string.indexOf(" ");
if(index1 != -1) {
index2 = string.indexOf(" ", index1 + 1);
if(index2 > index1) {
return string.substring(index1 + 1, index2);
}
}
return null;
} public String getUri() {
return uri;
} public Object getAttribute(String arg0) {
// TODO Auto-generated method stub
return null;
} public Enumeration getAttributeNames() {
// TODO Auto-generated method stub
return null;
} public String getCharacterEncoding() {
// TODO Auto-generated method stub
return null;
} public int getContentLength() {
// TODO Auto-generated method stub
return 0;
} public String getContentType() {
// TODO Auto-generated method stub
return null;
} public ServletInputStream getInputStream() throws IOException {
// TODO Auto-generated method stub
return null;
} public Locale getLocale() {
// TODO Auto-generated method stub
return null;
} public Enumeration getLocales() {
// TODO Auto-generated method stub
return null;
} public String getParameter(String arg0) {
// TODO Auto-generated method stub
return null;
} public Map getParameterMap() {
// TODO Auto-generated method stub
return null;
} public Enumeration getParameterNames() {
// TODO Auto-generated method stub
return null;
} public String[] getParameterValues(String arg0) {
// TODO Auto-generated method stub
return null;
} public String getProtocol() {
// TODO Auto-generated method stub
return null;
} public BufferedReader getReader() throws IOException {
// TODO Auto-generated method stub
return null;
} public String getRealPath(String arg0) {
// TODO Auto-generated method stub
return null;
} public String getRemoteAddr() {
// TODO Auto-generated method stub
return null;
} public String getRemoteHost() {
// TODO Auto-generated method stub
return null;
} public RequestDispatcher getRequestDispatcher(String arg0) {
// TODO Auto-generated method stub
return null;
} public String getScheme() {
// TODO Auto-generated method stub
return null;
} public String getServerName() {
// TODO Auto-generated method stub
return null;
} public int getServerPort() {
// TODO Auto-generated method stub
return 0;
} public boolean isSecure() {
// TODO Auto-generated method stub
return false;
} public void removeAttribute(String arg0) {
// TODO Auto-generated method stub } public void setAttribute(String arg0, Object arg1) {
// TODO Auto-generated method stub } public void setCharacterEncoding(String arg0)
throws UnsupportedEncodingException {
// TODO Auto-generated method stub } }

Request包含Socket的输入流,解析出来的请求uri,在控制台打印出请求的HTTP协议。如下:

GET /servlet/PrimitiveServlet HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: localhost:8080
Connection: Keep-Alive

Response:

package ex02.pyrmont;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Locale; import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse; public class Response implements ServletResponse { private OutputStream outputStream;
private PrintWriter printWriter = null;
private Request request; public Response(OutputStream outputStream) {
this.outputStream = outputStream;
} /**
* this method is used to server static pages
*/
public void sendStaticResource() {
byte[] buff = new byte[1024];
FileInputStream fis = null;
File file = new File(Constants.WEB_ROOT, request.getUri());
try {
fis = new FileInputStream(file);
int ch = fis.read(buff, 0, 1024);
while(ch != -1) {
outputStream.write(buff, 0, ch);
ch = fis.read(buff, 0, 1024);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} public void setRequest(Request request) {
this.request = request;
} public void flushBuffer() throws IOException {
// TODO Auto-generated method stub } public int getBufferSize() {
// TODO Auto-generated method stub
return 0;
} public String getCharacterEncoding() {
// TODO Auto-generated method stub
return null;
} public Locale getLocale() {
// TODO Auto-generated method stub
return null;
} public ServletOutputStream getOutputStream() throws IOException {
// TODO Auto-generated method stub
return null;
} public PrintWriter getWriter() throws IOException {
printWriter = new PrintWriter(outputStream, true);
return printWriter;
} public boolean isCommitted() {
// TODO Auto-generated method stub
return false;
} public void reset() {
// TODO Auto-generated method stub } public void resetBuffer() {
// TODO Auto-generated method stub } public void setBufferSize(int arg0) {
// TODO Auto-generated method stub } public void setContentLength(int arg0) {
// TODO Auto-generated method stub } public void setContentType(String arg0) {
// TODO Auto-generated method stub } public void setLocale(Locale arg0) {
// TODO Auto-generated method stub } }

Response包含socket的输出流,并实现了ServletReponse接口的getWriter方法,包含方法sendStaticResource发送静态资源到客户端。

StaticResourceProcessor类:

package ex02.pyrmont;

public class StaticResourceProcessor {

    public void process(Request request, Response response) {
response.sendStaticResource();
}
}

StaticResourceProcessor包含process方法,通过传入的request和response对象,调用response的sendStaticResource。

ServeltProcess1类:

package ex02.pyrmont;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler; import javax.servlet.Servlet;
import javax.servlet.ServletException; public class ServletProcessor1 {
public void process(Request request, Response response) {
String uri = request.getUri();
String servletName = uri.substring(uri.lastIndexOf("/") + 1);
URLClassLoader loader = null; // create a URL ClassLoader
URL[] urls = new URL[1];
URLStreamHandler urlStreamHandler = null;
File classPath = new File(Constants.WEB_ROOT);
String spec;
try {
spec = new URL("file", null, classPath.getCanonicalPath() + File.separator).toString();
urls[0] = new URL(null, spec, urlStreamHandler);
loader = new URLClassLoader(urls);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//new instance
Class myClass = null;
try {
myClass = loader.loadClass(servletName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} //invoke the service method
Servlet servlet = null;
try {
servlet = (Servlet)myClass.newInstance();
servlet.service(request, response);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

该类主要实现的工作是加载public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot"目录下的servlet class,并调用该servlet的service方法。

这里测试的是PrimitiveServlet.class,代码如下:

import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter; public class PrimitiveServlet implements Servlet { public void init(ServletConfig config) throws ServletException {
System.out.println("init");
} public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
System.out.println("from service");
PrintWriter out = response.getWriter();
out.println("Hello. Roses are red.");
out.print("Violets are blue.");
} public void destroy() {
System.out.println("destroy");
} public String getServletInfo() {
return null;
}
public ServletConfig getServletConfig() {
return null;
} }

三、Application2

在Application1中有个潜在的问题,ServletProcessor1传入Request对象和Response对象到调用的Servlet的service方法中,如果编程者知道实现逻辑,那么在service方法,它可以通过把ServletRequest向下转型到Request对象,把ServletResponse向下转型到Response对象,这样就可以调用Request对象和Response对象中的public方法,如parse方法。

解决的办法是用Facade类。UML图如下:

在Application2,我们添加两个facade类:RequestFacade和ReponseFacade。

RequestFacade类:

package ex02.pyrmont;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map; import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest; public class RequestFacade implements ServletRequest {
private ServletRequest request; public RequestFacade(Request request) {
this.request = request;
} /** implementaion of ServletRequest **/
public Object getAttribute(String name) {
return this.request.getAttribute(name);
} public Enumeration getAttributeNames() {
return this.request.getAttributeNames();
} public String getCharacterEncoding() {
return this.request.getCharacterEncoding();
} public int getContentLength() {
return this.request.getContentLength();
} public String getContentType() {
return this.request.getContentType();
} public ServletInputStream getInputStream() throws IOException {
return this.request.getInputStream();
} public Locale getLocale() {
return this.request.getLocale();
} public Enumeration getLocales() {
return this.request.getLocales();
} public String getParameter(String name) {
return this.request.getParameter(name);
} public Map getParameterMap() {
return this.request.getParameterMap();
} public Enumeration getParameterNames() {
return this.request.getParameterNames();
} public String[] getParameterValues(String name) {
return this.request.getParameterValues(name);
} public String getProtocol() {
return this.request.getProtocol();
} public BufferedReader getReader() throws IOException {
return this.request.getReader();
} public String getRealPath(String path) {
return this.request.getRealPath(path);
} public String getRemoteAddr() {
return this.request.getRemoteAddr();
} public String getRemoteHost() {
return this.request.getRemoteHost();
} public RequestDispatcher getRequestDispatcher(String path) {
return this.request.getRequestDispatcher(path);
} public String getScheme() {
return this.request.getScheme();
} public String getServerName() {
return this.request.getServerName();
} public int getServerPort() {
return this.request.getServerPort();
} public boolean isSecure() {
return this.request.isSecure();
} public void removeAttribute(String name) {
this.request.removeAttribute(name);
} public void setAttribute(String name, Object o) {
this.request.setAttribute(name, o);
} public void setCharacterEncoding(String env)
throws UnsupportedEncodingException {
this.request.setCharacterEncoding(env);
} }

该类包含Request对象,但是过滤了Request的parse等方法。

ResponseFacade类:

package ex02.pyrmont;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Locale; import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse; public class ResponseFacade implements ServletResponse { private ServletResponse response; public ResponseFacade(Response response) {
this.response = response;
} /** implementaion of ServletResponse **/
public void flushBuffer() throws IOException {
this.response.flushBuffer();
} public int getBufferSize() {
return this.response.getBufferSize();
} public String getCharacterEncoding() {
return this.response.getCharacterEncoding();
} public Locale getLocale() {
return this.response.getLocale();
} public ServletOutputStream getOutputStream() throws IOException {
return this.response.getOutputStream();
} public PrintWriter getWriter() throws IOException {
return this.response.getWriter();
} public boolean isCommitted() {
return this.response.isCommitted();
} public void reset() {
this.response.reset();
} public void resetBuffer() {
this.response.resetBuffer();
} public void setBufferSize(int size) {
this.response.setBufferSize(size);
} public void setContentLength(int len) {
this.response.setContentLength(len);
} public void setContentType(String type) {
this.response.setContentType(type);
} public void setLocale(Locale loc) {
this.response.setLocale(loc);
} }

该类包含Response对象,但是过滤了Reponse的sendStaticResponse等方法。

修改后的ServletProcessor类如下:

package ex02.pyrmont;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler; import javax.servlet.Servlet;
import javax.servlet.ServletException; public class ServletProcessor2 {
public void process(Request request, Response response) {
String uri = request.getUri();
String servletName = uri.substring(uri.lastIndexOf("/") + 1);
URLClassLoader loader = null; // create a URL ClassLoader
URL[] urls = new URL[1];
URLStreamHandler urlStreamHandler = null;
File classPath = new File(Constants.WEB_ROOT);
String spec;
try {
spec = new URL("file", null, classPath.getCanonicalPath() + File.separator).toString();
urls[0] = new URL(null, spec, urlStreamHandler);
loader = new URLClassLoader(urls);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//new instance
Class myClass = null;
try {
myClass = loader.loadClass(servletName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} //invoke the service method
Servlet servlet = null;
try {
servlet = (Servlet)myClass.newInstance();
// use facade class
RequestFacade requestFacade = new RequestFacade(request);
ResponseFacade responseFacade = new ResponseFacade(response);
servlet.service(requestFacade, responseFacade);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

Chapter 2: A Simple Servlet Container的更多相关文章

  1. What is the difference Apache (Http Server) and Tomcat (Servlet Container)

    The Apache Project The Apache Project is a collaborative software development effort. Its goal is to ...

  2. Web container==Servlet container

    Web container From Wikipedia, the free encyclopedia   (Redirected from Servlet container)     Web co ...

  3. 1.端口被占用问题:Embedded servlet container failed to start. Port 8097 was already in use.

    1.端口被占用问题:Embedded servlet container failed to start. Port 8097 was already in use.netstat -anonetst ...

  4. SpringBoot项目 org.springframework.boot.context.embedded.EmbeddedServletContainerException: Unable to start embedded Jetty servlet container报错

    SpringBoot项目启动报错 ERROR 2172 --- [ main] o.s.boot.SpringApplication : Application startup failed org. ...

  5. what is Servlet Container[转载]

    1 在这个博客中,我将描述一下web服务器.Servlet容器的基本概念,以及Servlet容器和jvm之间的关系.我想要证明的是Servlet容器不过就是一个java程序. 2 什么是web服务器 ...

  6. Chapter 1: A Simple Web Server

    这算是一篇读书笔记,留着以后复习看看. Web Server又称为Http Server,因为它使用HTTP协议和客户端(一般是各种各样的浏览器)进行通信. 什么是HTTP协议呢? HTTP协议是基于 ...

  7. servlet container:tomcat jetty and undertow

    1 spring boot内嵌容器支持tomcat.jetty和undertow 但是undertow性能最好,详见: https://examples.javacodegeeks.com/enter ...

  8. JSF 2.0 hello world example

    In this tutorial, we will show you how to develop a JavaServer Faces (JSF) 2.0 hello world example, ...

  9. What is the difference between J2EE and Spring

    来自于:https://www.quora.com/What-is-the-difference-between-J2EE-and-Spring Lot of people specially tho ...

随机推荐

  1. sass less

    CSS 预处理器技术已经非常的成熟,而且也涌现出了越来越多的 CSS 的预处理器框架.本文向你介绍使用最为普遍的三款 CSS 预处理器框架,分别是 Sass.Less CSS.Stylus. 首先我们 ...

  2. 《JavaScript权威指南》读书笔记(三)

    日期:2015-12-05 浏览器location和history: replace不会显示历史,location会: history对象脚本不能真正访问,但支持三种方法:back().foward( ...

  3. JavaScript中的test()方法

    定义和用法 test() 方法用于检测一个字符串是否匹配某个模式. 语法 RegExpObject.test(string) 参数 描述 string 必需.要检测的字符串. 返回值 如果字符串 st ...

  4. 452. Minimum Number of Arrows to Burst Balloons——排序+贪心算法

    There are a number of spherical balloons spread in two-dimensional space. For each balloon, provided ...

  5. ajax异步加载小例子....(通俗易懂)

    web.html(用户端): <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...

  6. 7款适用老旧设备并对初学者非常友好的轻量级Linux发行版

    我们由从 7 到 1 的顺序向大家介绍. 7. Linux Lite 正如其名,Linux Lite 是 Linux 发行版的一个轻量级版本,用户并不需要强大的硬件就可以将它跑起来,而且其使用非常简单 ...

  7. 六种流行的语言---C、C++、python、Java、php、C#比较[转]

    语言大餐 回归正题,本文是六种语言连接mysql数据库的代码展示,在LZ尝试的过程中,无论是语言环境搭建.mysql依赖库的导入还是代码的风格,各种语言都各有千秋.接下来,我们就让这些语言一一登场吧. ...

  8. 关于php用simplexml_load_string解析xml出现乱码的小结

    最近在做项目时需要通过xml接口读取合作伙伴数据到数据库,在xml解析环节出现有些特殊中文字符乱码的现象.后采取下面的办法终于解决. 1.curl 抓取过来的字符是unicode编码,需要先转换为ut ...

  9. Centos 6.2 安装mysql5.5

    1.  安装mysql 相关依赖库(没有的话就安装,有就不用安装了) 通过 rpm -qa | grep name 的方式验证以下软件包是否已全部安装. gcc* gcc-c++* autoconf* ...

  10. jdk和tomcat环境部署

    部署前需要准备的东西: 1.确定操作系统(32位或64位) 2.准备对应的jdk和tomcat软件 3.准备一份环境变量配置说明,省的到时候忘记了 步骤: 1.安装JDK 安装好JDK后,再配置JDK ...