how tomcat works 简单的server

  让我们从一个servlet容器的角度来研究一下servlet编程。总的来说,一个全功能的servlet容器会为servlet的每个HTTP请求做下面一些工作:
 当第一次调用servlet的时候,加载该servlet类并调用servlet的init方法(仅仅一次)。
 对每次请求,构造一个javax.servlet.ServletRequest实例和一个javax.servlet.ServletResponse实例。
 调用servlet的service方法,同时传递ServletRequest和ServletResponse对象。
 当servlet类被关闭的时候,调用servlet的destroy方法并卸载servlet类。
  本章的第一个servlet容器不是全功能的。因此,她不能运行什么除了非常简单的servlet,而且也不调用servlet的init方法和destroy方法。相反它做了下面的事情:
 等待HTTP请求。
 构造一个ServletRequest对象和一个ServletResponse对象。
 假如该请求需要一个静态资源的话,调用StaticResourceProcessor实例的process方法,同时传递ServletRequest和ServletResponse对象。
 假如该请求需要一个servlet的话,加载servlet类并调用servlet的service方法,同时传递ServletRequest和ServletResponse对象。
  注意:在这个servlet容器中,每一次servlet被请求的时候,servlet类都会被加载。 第一个应用程序由6个类组成:
 HttpServer1
 Request
 Response
 StaticResourceProcessor
 ServletProcessor1
 Constants

  

  HttpServer1类:

  

View Code
 1 package ex02.pyrmont;
 2 
 3 import java.net.Socket;
 4 import java.net.ServerSocket;
 5 import java.net.InetAddress;
 6 import java.io.InputStream;
 7 import java.io.OutputStream;
 8 import java.io.IOException;
 9 
10 public class HttpServer1 {
11 
12   /** WEB_ROOT is the directory where our HTML and other files reside.
13    *  For this package, WEB_ROOT is the "webroot" directory under the working
14    *  directory.
15    *  The working directory is the location in the file system
16    *  from where the java command was invoked.
17    */
18   // shutdown command
19   private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
20 
21   // the shutdown command received
22   private boolean shutdown = false;
23 
24   public static void main(String[] args) {
25     HttpServer1 server = new HttpServer1();
26     server.await();
27   }
28 
29   public void await() {
30     ServerSocket serverSocket = null;
31     int port = 8080;
32     try {
33       serverSocket =  new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
34     }
35     catch (IOException e) {
36       e.printStackTrace();
37       System.exit(1);
38     }
39 
40     // Loop waiting for a request
41     while (!shutdown) {
42       Socket socket = null;
43       InputStream input = null;
44       OutputStream output = null;
45       try {
46         socket = serverSocket.accept();
47         input = socket.getInputStream();
48         output = socket.getOutputStream();
49 
50         // create Request object and parse
51         Request request = new Request(input);
52         request.parse();
53 
54         // create Response object
55         Response response = new Response(output);
56         response.setRequest(request);
57 
58         // check if this is a request for a servlet or a static resource
59         // a request for a servlet begins with "/servlet/"
60         if (request.getUri().startsWith("/servlet/")) {
61           ServletProcessor1 processor = new ServletProcessor1();
62           processor.process(request, response);
63         }
64         else {
65           StaticResourceProcessor processor = new StaticResourceProcessor();
66           processor.process(request, response);
67         }
68 
69         // Close the socket
70         socket.close();
71         //check if the previous URI is a shutdown command
72         shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
73       }
74       catch (Exception e) {
75         e.printStackTrace();
76         System.exit(1);
77       }
78     }
79   }
80 }

  Request类:

  

View Code
  1 package ex02.pyrmont;
  2 
  3 import java.io.InputStream;
  4 import java.io.IOException;
  5 import java.io.BufferedReader;
  6 import java.io.UnsupportedEncodingException;
  7 import java.util.Enumeration;
  8 import java.util.Locale;
  9 import java.util.Map;
 10 import javax.servlet.RequestDispatcher;
 11 import javax.servlet.ServletInputStream;
 12 import javax.servlet.ServletRequest;
 13 
 14 
 15 public class Request implements ServletRequest {
 16 
 17   private InputStream input;
 18   private String uri;
 19 
 20   public Request(InputStream input) {
 21     this.input = input;
 22   }
 23 
 24   public String getUri() {
 25     return uri;
 26   }
 27 
 28   private String parseUri(String requestString) {
 29     int index1, index2;
 30     index1 = requestString.indexOf(' ');
 31     if (index1 != -1) {
 32       index2 = requestString.indexOf(' ', index1 + 1);
 33       if (index2 > index1)
 34         return requestString.substring(index1 + 1, index2);
 35     }
 36     return null;
 37   }
 38 
 39   public void parse() {
 40     // Read a set of characters from the socket
 41     StringBuffer request = new StringBuffer(2048);
 42     int i;
 43     byte[] buffer = new byte[2048];
 44     try {
 45       i = input.read(buffer);
 46     }
 47     catch (IOException e) {
 48       e.printStackTrace();
 49       i = -1;
 50     }
 51     for (int j=0; j<i; j++) {
 52       request.append((char) buffer[j]);
 53     }
 54     System.out.print(request.toString());
 55     uri = parseUri(request.toString());
 56   }
 57 
 58   /* implementation of the ServletRequest*/
 59   public Object getAttribute(String attribute) {
 60     return null;
 61   }
 62 
 63   public Enumeration getAttributeNames() {
 64     return null;
 65   }
 66 
 67   public String getRealPath(String path) {
 68     return null;
 69   }
 70 
 71   public RequestDispatcher getRequestDispatcher(String path) {
 72     return null;
 73   }
 74 
 75   public boolean isSecure() {
 76     return false;
 77   }
 78 
 79   public String getCharacterEncoding() {
 80     return null;
 81   }
 82 
 83   public int getContentLength() {
 84     return 0;
 85   }
 86 
 87   public String getContentType() {
 88     return null;
 89   }
 90 
 91   public ServletInputStream getInputStream() throws IOException {
 92     return null;
 93   }
 94 
 95   public Locale getLocale() {
 96     return null;
 97   }
 98 
 99   public Enumeration getLocales() {
100     return null;
101   }
102 
103   public String getParameter(String name) {
104     return null;
105   }
106 
107   public Map getParameterMap() {
108     return null;
109   }
110 
111   public Enumeration getParameterNames() {
112     return null;
113   }
114 
115   public String[] getParameterValues(String parameter) {
116     return null;
117   }
118 
119   public String getProtocol() {
120     return null;
121   }
122 
123   public BufferedReader getReader() throws IOException {
124     return null;
125   }
126 
127   public String getRemoteAddr() {
128     return null;
129   }
130 
131   public String getRemoteHost() {
132     return null;
133   }
134 
135   public String getScheme() {
136    return null;
137   }
138 
139   public String getServerName() {
140     return null;
141   }
142 
143   public int getServerPort() {
144     return 0;
145   }
146 
147   public void removeAttribute(String attribute) {
148   }
149 
150   public void setAttribute(String key, Object value) {
151   }
152 
153   public void setCharacterEncoding(String encoding)
154     throws UnsupportedEncodingException {
155   }
156 
157 }

  Response类:

View Code
  1 package ex02.pyrmont;
  2 
  3 import java.io.OutputStream;
  4 import java.io.IOException;
  5 import java.io.FileInputStream;
  6 import java.io.FileNotFoundException;
  7 import java.io.File;
  8 import java.io.PrintWriter;
  9 import java.util.Locale;
 10 import javax.servlet.ServletResponse;
 11 import javax.servlet.ServletOutputStream;
 12 
 13 public class Response implements ServletResponse {
 14 
 15   private static final int BUFFER_SIZE = 1024;
 16   Request request;
 17   OutputStream output;
 18   PrintWriter writer;
 19 
 20   public Response(OutputStream output) {
 21     this.output = output;
 22   }
 23 
 24   public void setRequest(Request request) {
 25     this.request = request;
 26   }
 27 
 28   /* This method is used to serve a static page */
 29   public void sendStaticResource() throws IOException {
 30     byte[] bytes = new byte[BUFFER_SIZE];
 31     FileInputStream fis = null;
 32     try {
 33       /* request.getUri has been replaced by request.getRequestURI */
 34       File file = new File(Constants.WEB_ROOT, request.getUri());
 35       fis = new FileInputStream(file);
 36       /*
 37          HTTP Response = Status-Line
 38            *(( general-header | response-header | entity-header ) CRLF)
 39            CRLF
 40            [ message-body ]
 41          Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
 42       */
 43       int ch = fis.read(bytes, 0, BUFFER_SIZE);
 44       while (ch!=-1) {
 45         output.write(bytes, 0, ch);
 46         ch = fis.read(bytes, 0, BUFFER_SIZE);
 47       }
 48     }
 49     catch (FileNotFoundException e) {
 50       String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
 51         "Content-Type: text/html\r\n" +
 52         "Content-Length: 23\r\n" +
 53         "\r\n" +
 54         "<h1>File Not Found</h1>";
 55       output.write(errorMessage.getBytes());
 56     }
 57     finally {
 58       if (fis!=null)
 59         fis.close();
 60     }
 61   }
 62 
 63 
 64   /** implementation of ServletResponse  */
 65   public void flushBuffer() throws IOException {
 66   }
 67 
 68   public int getBufferSize() {
 69     return 0;
 70   }
 71 
 72   public String getCharacterEncoding() {
 73     return null;
 74   }
 75 
 76   public Locale getLocale() {
 77     return null;
 78   }
 79 
 80   public ServletOutputStream getOutputStream() throws IOException {
 81     return null;
 82   }
 83 
 84   public PrintWriter getWriter() throws IOException {
 85     // autoflush is true, println() will flush,
 86     // but print() will not.
 87     writer = new PrintWriter(output, true);
 88     return writer;
 89   }
 90 
 91   public boolean isCommitted() {
 92     return false;
 93   }
 94 
 95   public void reset() {
 96   }
 97 
 98   public void resetBuffer() {
 99   }
100 
101   public void setBufferSize(int size) {
102   }
103 
104   public void setContentLength(int length) {
105   }
106 
107   public void setContentType(String type) {
108   }
109 
110   public void setLocale(Locale locale) {
111   }
112 }

  在getWriter方法中,PrintWriter类的构造方法的第二个参数是一个布尔值表明是否允许自动刷新。传递true作为第二个参数将会使任何println方法的调用都会刷新输出(output)。不过,print方法不会刷新输出。 因此,任何print方法的调用都会发生在servlet的service方法的最后一行,输出将不会被发送到浏览器。这个缺点将会在下一个应用程序中修复。

  StaticResourceProcessor类:

  

 1 package ex02.pyrmont;
 2 
 3 import java.io.IOException;
 4 
 5 public class StaticResourceProcessor {
 6 
 7   public void process(Request request, Response response) {
 8     try {
 9       response.sendStaticResource();
10     }
11     catch (IOException e) {
12       e.printStackTrace();
13     }
14   }
15 }

  ServletProcessor1类:

View Code
 1 package ex02.pyrmont;
 2 
 3 import java.net.URL;
 4 import java.net.URLClassLoader;
 5 import java.net.URLStreamHandler;
 6 import java.io.File;
 7 import java.io.IOException;
 8 import javax.servlet.Servlet;
 9 import javax.servlet.ServletRequest;
10 import javax.servlet.ServletResponse;
11 
12 public class ServletProcessor1 {
13 
14   public void process(Request request, Response response) {
15 
16     String uri = request.getUri();
17     String servletName = uri.substring(uri.lastIndexOf("/") + 1);
18     URLClassLoader loader = null;
19 
20     try {
21       // create a URLClassLoader
22       URL[] urls = new URL[1];
23       URLStreamHandler streamHandler = null;
24       File classPath = new File(Constants.WEB_ROOT);
25       // the forming of repository is taken from the createClassLoader method in
26       // org.apache.catalina.startup.ClassLoaderFactory
27       String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
28       // the code for forming the URL is taken from the addRepository method in
29       // org.apache.catalina.loader.StandardClassLoader class.
30       urls[0] = new URL(null, repository, streamHandler);
31       loader = new URLClassLoader(urls);
32     }
33     catch (IOException e) {
34       System.out.println(e.toString() );
35     }
36     Class myClass = null;
37     try {
38       myClass = loader.loadClass(servletName);
39     }
40     catch (ClassNotFoundException e) {
41       System.out.println(e.toString());
42     }
43 
44     Servlet servlet = null;
45 
46     try {
47       servlet = (Servlet) myClass.newInstance();
48       servlet.service((ServletRequest) request, (ServletResponse) response);
49     }
50     catch (Exception e) {
51       System.out.println(e.toString());
52     }
53     catch (Throwable e) {
54       System.out.println(e.toString());
55     }
56 
57   }
58 }

  这会危害安全性。知道这个servlet容器的内部运作的Servlet程序员可以分别把ServletRequest和ServletResponse实例向下转换为ex02.pyrmont.Request和ex02.pyrmont.Response,并调用他们的公共方法。拥有一个Request实例,它们就可以调用parse方法。拥有一个Response实例,就可以调用sendStaticResource方法。 你不可以把parse和sendStaticResource方法设置为私有的,因为它们将会被其他的类调用。不过,这两个方法是在个servlet内部是不可见的。其中一个解决办法就是让Request和Response类拥有默认访问修饰,所以它们不能在ex02.pyrmont包的外部使用。不过,这里有一个更优雅的解决办法:通过使用facade类。请看Figure 2.2中的UML图。

  在这第二个应用程序中,我们增加了两个façade类: RequestFacade和ResponseFacade。RequestFacade实现了ServletRequest接口并通过在构造方法中传递一个引用了ServletRequest对象的Request实例作为参数来实例化。ServletRequest接口中每个方法的实现都调用了Request对象的相应方法。然而ServletRequest对象本身是私有的,并不能在类的外部访问。我们构造了一个RequestFacade对象并把它传递给service方法,而不是向下转换Request对象为ServletRequest对象并传递给service方法。Servlet程序员仍然可以向下转换ServletRequest实例为RequestFacade,不过它们只可以访问ServletRequest接口里边的公共方法。现在parseUri方法就是安全的了。

  请注意RequestFacade的构造方法。它接受一个Request对象并马上赋值给私有的servletRequest对象。还请注意,RequestFacade类的每个方法调用ServletRequest对象的相应的方法。

  请注意RequestFacade的构造方法。它接受一个Request对象并马上赋值给私有的servletRequest对象。还请注意,RequestFacade类的每个方法调用ServletRequest对象的相应的方法。 这同样使用于ResponseFacade类。 这里是应用程序2中使用的类:

 HttpServer2
 Request
 Response
 StaticResourceProcessor
 ServletProcessor2
 Constants
HttpServer2类类似于HttpServer1,除了它在await方法中使用ServletProcessor2而不是ServletProcessor1:

  

1 if (request.getUri().startWith("/servlet/")) { 
2     servletProcessor2 processor = new ServletProcessor2();       processor.process(request, response); 
3 } else {
4  ... 
5 }

ServletProcessor2类类似于ServletProcessor1,除了process方法中的以下部分:

1 Servlet servlet = null; 
2 RequestFacade requestFacade = new RequestFacade(request);
3  ResponseFacade responseFacade = new ResponseFacade(response);
4  try {
5  servlet = (Servlet) myClass.newInstance(); servlet.service((ServletRequest) requestFacade,(ServletResponse)responseFacade); }

Listing 2.7 显示了一个不完整的RequestFacade类

Listing 2.7: RequestFacade类

View Code
  1 package ex02.pyrmont;
  2 
  3 import java.io.IOException;
  4 import java.io.BufferedReader;
  5 import java.io.UnsupportedEncodingException;
  6 import java.util.Enumeration;
  7 import java.util.Locale;
  8 import java.util.Map;
  9 import javax.servlet.RequestDispatcher;
 10 import javax.servlet.ServletInputStream;
 11 import javax.servlet.ServletRequest;
 12 
 13 public class RequestFacade implements ServletRequest {
 14 
 15   private ServletRequest request = null;
 16 
 17   public RequestFacade(Request request) {
 18     this.request = request;
 19   }
 20 
 21   /* implementation of the ServletRequest*/
 22   public Object getAttribute(String attribute) {
 23     return request.getAttribute(attribute);
 24   }
 25 
 26   public Enumeration getAttributeNames() {
 27     return request.getAttributeNames();
 28   }
 29 
 30   public String getRealPath(String path) {
 31     return request.getRealPath(path);
 32   }
 33 
 34   public RequestDispatcher getRequestDispatcher(String path) {
 35     return request.getRequestDispatcher(path);
 36   }
 37 
 38   public boolean isSecure() {
 39     return request.isSecure();
 40   }
 41 
 42   public String getCharacterEncoding() {
 43     return request.getCharacterEncoding();
 44   }
 45 
 46   public int getContentLength() {
 47     return request.getContentLength();
 48   }
 49 
 50   public String getContentType() {
 51     return request.getContentType();
 52   }
 53 
 54   public ServletInputStream getInputStream() throws IOException {
 55     return request.getInputStream();
 56   }
 57 
 58   public Locale getLocale() {
 59     return request.getLocale();
 60   }
 61 
 62   public Enumeration getLocales() {
 63     return request.getLocales();
 64   }
 65 
 66   public String getParameter(String name) {
 67     return request.getParameter(name);
 68   }
 69 
 70   public Map getParameterMap() {
 71     return request.getParameterMap();
 72   }
 73 
 74   public Enumeration getParameterNames() {
 75     return request.getParameterNames();
 76   }
 77 
 78   public String[] getParameterValues(String parameter) {
 79     return request.getParameterValues(parameter);
 80   }
 81 
 82   public String getProtocol() {
 83     return request.getProtocol();
 84   }
 85 
 86   public BufferedReader getReader() throws IOException {
 87     return request.getReader();
 88   }
 89 
 90   public String getRemoteAddr() {
 91     return request.getRemoteAddr();
 92   }
 93 
 94   public String getRemoteHost() {
 95     return request.getRemoteHost();
 96   }
 97 
 98   public String getScheme() {
 99    return request.getScheme();
100   }
101 
102   public String getServerName() {
103     return request.getServerName();
104   }
105 
106   public int getServerPort() {
107     return request.getServerPort();
108   }
109 
110   public void removeAttribute(String attribute) {
111     request.removeAttribute(attribute);
112   }
113 
114   public void setAttribute(String key, Object value) {
115     request.setAttribute(key, value);
116   }
117 
118   public void setCharacterEncoding(String encoding)
119     throws UnsupportedEncodingException {
120     request.setCharacterEncoding(encoding);
121   }
122 
123 }
posted @ 2012-10-29 16:19  寒剑飘香  阅读(190)  评论(0编辑  收藏  举报