How Tomcat Works 读书笔记(一)
一直以来觉得容器都是一个很神秘的东西,一般开发的时候不会关心容器到底是怎么实现的。现在看了《How Tomcat Works》这本书后大概知道容器的实现,写出来自己巩固下
java 基础里面有章是关于Socket的,这个对象是通过http协议来发送消息的。tomcat既然是通过JAVA来实现他的底层也是通过Socket来实现。
首先实现一个Servlet容器。它的功能是可以加载serlvet对象以及访问静态文件。
1、Server端
import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.InetAddress;import java.net.ServerSocket;import java.net.Socket;public class MockServer {public static final String = System.getProperty("user.dir") + File.separator + "webroot";private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";private boolean shutdown = false;public static void main(final String[] args) throws Exception {MockServer server = new MockServer();server.await();}public void await() {ServerSocket socketServer = null;int port = 8080;try {socketServer = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));} catch (IOException e) {e.printStackTrace();}while (!shutdown) {Socket socket = null;InputStream in = null;OutputStream out = null;try {socket = socketServer.accept();in = socket.getInputStream();out = socket.getOutputStream();Request request = new Request(in);request.parse();Response response = new Response(out);response.setRequest(request);socket.close();if (request.getUri().startsWith("/servlet")) {MockServletPorcessor servlet = new MockServletPorcessor();servlet.process(request, response);} else if (request.getUri().equals(SHUTDOWN_COMMAND)) {shutdown = true;} else {StaticResource sr = new StaticResource();sr.process(request, response);}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}
代码很简单前面就是启动一个server并且接受socket 后面解释下request.getUri()这个方法后面会说,就是截取"GET /XXXX HTTP/1.1"HTTP协议的字符串,如果我们的请求是http://127.0.0.1:8080/SHUTDOWN 截取后的结果就是SHUTDOWN。
2、Request对象
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 final InputStream in;private String uri;public Request(final InputStream in) {this.in = in;}public void parse() {StringBuffer sb = new StringBuffer(2048);int i = 0;byte[] buffer = new byte[2048];try {i = in.read(buffer);} catch (IOException e) {e.printStackTrace();}for (int j = 0; j < i; j++) {sb.append((char) buffer[j]);}System.out.println(String.valueOf(sb));uri = parseUri(String.valueOf(sb));}// "GET /SHUTDOWN HTTP/1.1"private String parseUri(final String request) {int index1, index2;index1 = request.indexOf(' ');if (index1 != -1) {index2 = request.indexOf(' ', index1 + 1);if (index2 > index1) {return request.substring(index1 + 1, index2);}}return null;}public String getUri() {return uri;}@Overridepublic Object getAttribute(final String arg0) {// TODO Auto-generated method stubreturn null;}@Overridepublic Enumeration getAttributeNames() {// TODO Auto-generated method stubreturn null;}@Overridepublic String getCharacterEncoding() {// TODO Auto-generated method stubreturn null;}@Overridepublic int getContentLength() {// TODO Auto-generated method stubreturn 0;}@Overridepublic String getContentType() {// TODO Auto-generated method stubreturn null;}@Overridepublic ServletInputStream getInputStream() throws IOException {// TODO Auto-generated method stubreturn null;}@Overridepublic String getLocalAddr() {// TODO Auto-generated method stubreturn null;}@Overridepublic String getLocalName() {// TODO Auto-generated method stubreturn null;}@Overridepublic int getLocalPort() {// TODO Auto-generated method stubreturn 0;}@Overridepublic Locale getLocale() {// TODO Auto-generated method stubreturn null;}@Overridepublic Enumeration getLocales() {// TODO Auto-generated method stubreturn null;}@Overridepublic String getParameter(final String arg0) {// TODO Auto-generated method stubreturn null;}@Overridepublic Map getParameterMap() {// TODO Auto-generated method stubreturn null;}@Overridepublic Enumeration getParameterNames() {// TODO Auto-generated method stubreturn null;}@Overridepublic String[] getParameterValues(final String arg0) {// TODO Auto-generated method stubreturn null;}@Overridepublic String getProtocol() {// TODO Auto-generated method stubreturn null;}@Overridepublic BufferedReader getReader() throws IOException {// TODO Auto-generated method stubreturn null;}@Overridepublic String getRealPath(final String arg0) {// TODO Auto-generated method stubreturn null;}@Overridepublic String getRemoteAddr() {// TODO Auto-generated method stubreturn null;}@Overridepublic String getRemoteHost() {// TODO Auto-generated method stubreturn null;}@Overridepublic int getRemotePort() {// TODO Auto-generated method stubreturn 0;}@Overridepublic RequestDispatcher getRequestDispatcher(final String arg0) {// TODO Auto-generated method stubreturn null;}@Overridepublic String getScheme() {// TODO Auto-generated method stubreturn null;}@Overridepublic String getServerName() {// TODO Auto-generated method stubreturn null;}@Overridepublic int getServerPort() {// TODO Auto-generated method stubreturn 0;}@Overridepublic boolean isSecure() {// TODO Auto-generated method stubreturn false;}@Overridepublic void removeAttribute(final String arg0) {// TODO Auto-generated method stub}@Overridepublic void setAttribute(final String arg0, final Object arg1) {// TODO Auto-generated method stub}@Overridepublic void setCharacterEncoding(final String arg0) throws UnsupportedEncodingException {// TODO Auto-generated method stub}}
自己定义的request对象 这个对象由于是基因servlet实现所以必须实现ServletRequest的方法。具体的servlet方法现在都没实现,暂时只实现一个截取uri的方法。这个方法的作用前面已经说明。
3、Response方法
import java.io.File;import java.io.FileInputStream;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 static final int BUFFER_SIZE = 1024;private Request request;private final OutputStream out;private PrintWriter writer;public Response(final OutputStream out) {this.out = out;}public void setRequest(final Request request) {this.request = request;}public void sendStaticResource() {byte[] bytes = new byte[BUFFER_SIZE];FileInputStream fis = null;try {File file = new File(MockServer.WEB_ROOT, request.getUri());if (file.exists()) {fis = new FileInputStream(file);int ch = fis.read(bytes, 0, BUFFER_SIZE);while (ch != -1) {out.write(bytes, 0, ch);ch = fis.read(bytes, 0, BUFFER_SIZE);}} else {String message = "Http/1.1 404 File Not Found\r\n" + "Content-Type: text/html\r\n"+ "Content-Length: 23\r\n" + "\r\n" + "<h1>File Not Found</h1>";out.write(message.getBytes());}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {if (null != fis) {try {fis.close();} catch (IOException e) {e.printStackTrace();}}}}@Overridepublic void flushBuffer() throws IOException {// TODO Auto-generated method stub}@Overridepublic int getBufferSize() {// TODO Auto-generated method stubreturn 0;}@Overridepublic String getCharacterEncoding() {// TODO Auto-generated method stubreturn null;}@Overridepublic String getContentType() {// TODO Auto-generated method stubreturn null;}@Overridepublic Locale getLocale() {// TODO Auto-generated method stubreturn null;}@Overridepublic ServletOutputStream getOutputStream() throws IOException {// TODO Auto-generated method stubreturn null;}@Overridepublic PrintWriter getWriter() throws IOException {writer = new PrintWriter(out, true);return writer;}@Overridepublic boolean isCommitted() {// TODO Auto-generated method stubreturn false;}@Overridepublic void reset() {// TODO Auto-generated method stub}@Overridepublic void resetBuffer() {// TODO Auto-generated method stub}@Overridepublic void setBufferSize(final int arg0) {// TODO Auto-generated method stub}@Overridepublic void setCharacterEncoding(final String arg0) {// TODO Auto-generated method stub}@Overridepublic void setContentLength(final int arg0) {// TODO Auto-generated method stub}@Overridepublic void setContentType(final String arg0) {// TODO Auto-generated method stub}@Overridepublic void setLocale(final Locale arg0) {// TODO Auto-generated method stub}}
也是实现servlet的接口,同时实现一个sendStaticResource()这个方法就是访问静态文件,如果不能访问就返回404错误。
4、MockServletPorcessor
这个方法就是对servlet的处理类,如果请求的后缀有/servlet那么代码就会进入这个类
import java.io.File;import java.net.URL;import java.net.URLClassLoader;import java.net.URLStreamHandler;import javax.servlet.Servlet;public class MockServletPorcessor {public void process(final Request request, final Response response) {String uri = request.getUri();String servletName = uri.substring(uri.lastIndexOf("/") + 1);URLClassLoader loader = null;try {URL[] urls = new URL[1];URLStreamHandler handler = null;File = new File("bin/com/xxx/server");String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString();System.out.print(repository);urls[0] = new URL(null, repository, handler);loader = new URLClassLoader(urls);Class myClass = loader.loadClass(servletName);Servlet servlet = (Servlet) myClass.newInstance();servlet.service(request, response);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}// TODO Auto-generated method stub}}
这个类的基本功能有3个
(1)加载servlet类库
classpath的路径是servlet的路径(bin/com/xxx/server)
(2)加载servlet类
(3)实现service方法
5、StaticResource静态访问类
public class StaticResource {public void process(final Request request, final Response response) {response.sendStaticResource();}}
只是简单的调用response类。
6、TestServlet
package com.xxx.server;import java.io.IOException;import javax.servlet.Servlet;import javax.servlet.ServletConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;public class TestServlet implements Servlet {@Overridepublic void destroy() {// TODO Auto-generated method stub}@Overridepublic ServletConfig getServletConfig() {// TODO Auto-generated method stubreturn null;}@Overridepublic String getServletInfo() {// TODO Auto-generated method stubreturn null;}@Overridepublic void init(final ServletConfig arg0) throws ServletException {// TODO Auto-generated method stub}@Overridepublic void service(final ServletRequest request, final ServletResponse response) throws ServletException,IOException {System.out.println("In Service");}}
一个简单的serlvet 这里保留了包的名称因为以后会说到
启动server容器 然后我们可以通过页面上
http://127.0.0.1:8080/servlet/com.xxx.server.TestServlet 这里必须使用全限定名才行
来运行servlet
通过http://127.0.0.1:8080/index.html来访问资源(当然前提是WEB_ROOT下面有这个资源)
通过http://127.0.0.1:8080/SHUTDOWN来关闭容器