Tomcat源码学习(10)How Tomcat works(转)

HttpServer1类

    这个应用程序里边的HttpServer1类类似于第1章里边的简单服务器应用程序的HttpServer类。不过,在这个应用程序里边HttpServer1类可以同时提供静态资源和servlet。要请求一个静态资源,你可以在你的浏览器地址栏或者网址框里边敲入一个URL:
http://machineName:port/staticResource
    就像是在第1章提到的,你可以请求一个静态资源。
    为了请求一个servlet,你可以使用下面的URL:
http://machineName:port/servlet/servletClass
    因此,假如你在本地请求一个名为PrimitiveServlet的servlet,你在浏览器的地址栏或者网址框中敲入:
http://localhost:8080/servlet/PrimitiveServlet
    servlet容器可以就提供PrimitiveServlet了。不过,假如你调用其他servlet,如ModernServlet,servlet容器将会抛出一个异常。在以下各章中,你将会建立可以处理这两个情况的程序。
    HttpServer1类显示在Listing 2.2中。
         Listing 2.2: HttpServer1类的await方法
package ex02.pyrmont;
import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
public class HttpServer1 {
     /** WEB_ROOT is the directory where our HTML and other files reside.
     * For this package, WEB_ROOT is the "webroot" directory under the
     * working directory.
     * The working directory is the location in the file system
     * from where the java command was invoked.
     */
     // shutdown command
     private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
     // the shutdown command received
     private boolean shutdown = false;
     public static void main(String[] args) {
         HttpServer1 server = new HttpServer1();
         server.await();
     }
     public void await() {
         ServerSocket serverSocket = null;
         int port = 8080;
         try {
             serverSocket = new ServerSocket(port, 1,
             InetAddress.getByName("127.0.0.1"));
         }
         catch (IOException e) {
             e.printStackTrace();
             System.exit(1);
         }
         // Loop waiting for a request
         while (!shutdown) {
             Socket socket = null;
             InputStream input = null;
             OutputStream output = null;
             try {
                 socket = serverSocket.accept();
                 input = socket.getInputstream();
                 output = socket.getOutputStream();
                 // create Request object and parse
                 Request request = new Request(input);
                 request.parse();
                 // create Response object
                 Response response = new Response(output);
                 response.setRequest(request);
                 // check if this is a request for a servlet or
                 // a static resource
                 // a request for a servlet begins with "/servlet/"
                 if (request.getUri().startsWith("/servlet/")) {
                     ServletProcessor1 processor = new ServletProcessor1();
                     processor.process(request, response);
                 }
                 else {
                     StaticResoureProcessor processor =
                     new StaticResourceProcessor();
                     processor.process(request, response);
                 }
                 // Close the socket
                 socket.close();
                 //check if the previous URI is a shutdown command
                 shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
             }
             catch (Exception e) {
                 e.printStackTrace();
                 System.exit(1);
             }
         }
     }
}
    类的await方法等待HTTP请求直到一个shutdown命令给发出,让你想起第1章的await方法。Listing 2.2的await方法和第1章的区别是,在Listing 2.2里边,请求可以分发给一个StaticResourceProcessor或者一个ServletProcessor。假如URI包括字符串/servlet/的话,请求将会转发到后面去。
    不然的话,请求将会传递给StaticResourceProcessor实例 instance. 请注意,这部分在Listing 2.2中灰暗显示类的await方法等待HTTP请求直到一个shutdown命令给发出,让你想起第1章的await方法。Listing 2.2的await方法和第1章的区别是,在Listing 2.2里边,请求可以分发给一个StaticResourceProcessor或者一个ServletProcessor。假如URI包括字符串/servlet/的话,请求将会转发到后面去。
    不然的话,请求将会传递给StaticResourceProcessor实例 instance. 请注意,这部分在Listing 2.2中灰暗显示。

Request类

    servlet的service方法从servlet容器中接收一个javax.servlet.ServletRequest实例和一个javax.servlet.ServletResponse实例。这就是说对于每一个HTTP请求,servlet容器必须构造一个ServletRequest对象和一个ServletResponse对象并把它们传递给正在服务的servlet的service方法。
    ex02.pyrmont.Request类代表一个request对象并被传递给servlet的service方法。就本身而言,它必须实现javax.servlet.ServletRequest接口。这个类必须提供这个接口所有方法的实现。不过,我们想要让它非常简单并且仅仅提供实现其中一些方法,我们在以下各章中再实现全部的方法。要编译Request类,你需要把这些方法的实现留空。假如你看过Listing 2.3中的Request类,你将会看到那些需要返回一个对象的方法返回了null
         Listing 2.3: Request类
package ex02.pyrmont;
import java.io.InputStream;
import java.io.IOException;
import java.io.BufferedReader;
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 input;
     private String uri;
     public Request(InputStream input){
         this.input = input;
     }
     public String getUri() {
         return uri;
     }
     private String parseUri(String requestString) {
         int index1, index2;
         index1 = requestString.indexOf(' ');
         if (index1 != -1) {
             index2 = requestString.indexOf(' ', index1 + 1);
             if (index2 > index1)
                 return requestString.substring(index1 + 1, index2);
         }
         return null;
     }
     public void parse() {
         // Read a set of characters from the socket
         StringBuffer request = new StringBuffer(2048);
         int i;
         byte[] buffer = new byte[2048];
         try {
             i = input.read(buffer);
        }
         catch (IOException e) {
             e.printStackTrace();
             i = -1;
         }
         for (int j=0; j<i; j++) {
             request.append((char) buffer(j));
         }
         System.out.print(request.toString());
         uri = parseUri(request.toString());
     }
     /* implementation of ServletRequest */
     public Object getAttribute(String attribute) {
         return null;
     }
     public Enumeration getAttributeNames() {
         return null;
     }
     public String getRealPath(String path) {
         return null;
     }
     public RequestDispatcher getRequestDispatcher(String path) {
         return null;
     }
     public boolean isSecure() {
         return false;
     }
     public String getCharacterEncoding() {
         return null;
     }
     public int getContentLength() {
         return 0;
     }
     public String getContentType() {
         return null;
     }
     public ServletInputStream getInputStream() throws IOException {
         return null;
     }
     public Locale getLocale() {
         return null;
     }
     public Enumeration getLocales() {
         return null;
     }
     public String getParameter(String name) {
         return null;
     }
     public Map getParameterMap() {
         return null;
     }
     public Enumeration getParameterNames() {
         return null;
     }
     public String[] getParameterValues(String parameter) {
         return null;
     }
     public String getProtocol() {
         return null;
     }
     public BufferedReader getReader() throws IOException {
         return null;
     }
     public String getRemoteAddr() {
         return null;
     }
     public String getRemoteHost() {
         return null;
     }
     public String getScheme() {
         return null;
     }
     public String getServerName() {
         return null;
     }
     public int getServerPort() {
         return 0;
     }
     public void removeAttribute(String attribute) { }
     public void setAttribute(String key, Object value) { }
     public void setCharacterEncoding(String encoding)
         throws UnsupportedEncodingException { }
}
    另外,Request类仍然有在第1章中讨论的parse和getUri方法。

Response类

    在Listing 2.4列出的ex02.pyrmont.Response类,实现了javax.servlet.ServletResponse。就本身而言,这个类必须提供接口里边的所有方法的实现。类似于Request类,我们把除了getWriter之外的所有方法的实现留空。
         Listing 2.4: Response类
package ex02.pyrmont;
import java.io.OutputStream;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.File;
import java.io.PrintWriter;
import java.util.Locale;
import javax.servlet.ServletResponse;
import javax.servlet.ServletOutputStream;
public class Response implements ServletResponse {
     private static final int BUFFER_SIZE = 1024;
     Request request;
     OutputStream output;
     PrintWriter writer;
     public Response(OutputStream output) {
         this.output = output;
     }
     public void setRequest(Request request) {
         this.request = request;
     }
     /* This method is used to serve static pages */
     public void sendStaticResource() throws IOException {
         byte[] bytes = new byte[BUFFER_SIZE];
         FileInputstream fis = null;
         try {
             /* request.getUri has been replaced by request.getRequestURI */
             File file = new File(Constants.WEB_ROOT, request.getUri());
             fis = new FileInputstream(file);
             /*
             HTTP Response = Status-Line
             *(( general-header | response-header | entity-header ) CRLF)
             CRLF
             [ message-body ]
             Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
             */
             int ch = fis.read(bytes, 0, BUFFER_SIZE);
             while (ch!=-1) {
                 output.write(bytes, 0, ch);
                 ch = fis.read(bytes, 0, BUFFER_SIZE);
             }
         }
         catch (FileNotFoundException e) {
             String errorMessage = "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>";
             output.write(errorMessage.getBytes());
         }
         finally {
             if (fis!=null)
             fis.close();
         }
     }
     /** implementation of ServletResponse */
     public void flushBuffer() throws IOException ( }
     public int getBufferSize() {
         return 0;
     }
     public String getCharacterEncoding() {
         return null;
     }
     public Locale getLocale() {
         return null;
     }
     public ServletOutputStream getOutputStream() throws IOException {
         return null;
     }
     public PrintWriter getWriter() throws IOException {
         // autoflush is true, println() will flush,
         // but print() will not.
         writer = new PrintWriter(output, true);
         return writer;
     }
     public boolean isCommitted() {
         return false;
     }
     public void reset() { }
     public void resetBuffer() { }
     public void setBufferSize(int size) { }
     public void setContentLength(int length) { }
     public void setContentType(String type) { }
     public void setLocale(Locale locale) { }
}
    在getWriter方法中,PrintWriter类的构造方法的第二个参数是一个布尔值表明是否允许自动刷新。传递true作为第二个参数将会使任何println方法的调用都会刷新输出(output)。不过,print方法不会刷新输出。
    因此,任何print方法的调用都会发生在servlet的service方法的最后一行,输出将不会被发送到浏览器。这个缺点将会在下一个应用程序中修复。
    Response类还拥有在第1章中谈到的sendStaticResource方法。

StaticResourceProcessor类

    ex02.pyrmont.StaticResourceProcessor类用来提供静态资源请求。唯一的方法是process方法。Listing 2.5给出了StaticResourceProcessor类。
         Listing 2.5: StaticResourceProcessor类
package ex02.pyrmont;
import java.io.IOException;
public class StaticResourceProcessor {
     public void process(Request request, Response response) {
     try {
         response.sendStaticResource();
     }
     catch (IOException e) {
         e.printStackTrace();
     }
     }
}
    process方法接收两个参数:一个ex02.pyrmont.Request实例和一个ex02.pyrmont.Response实例。这个方法只是简单的呼叫Response对象的sendStaticResource方法。
原文地址:https://www.cnblogs.com/macula7/p/1960628.html