zoukankan      html  css  js  c++  java
  • Chapter 2: A Simple Servlet Container

    一、这一章从头构建一个简单的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
            
        }
    
    }
    View Code

    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
    View Code

    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();
        }
    }
    View Code

    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();
            }
        }
    }
    View Code

    该类主要实现的工作是加载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;
      }
    
    }
    View Code

    三、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);
        }
    
    }
    View Code

    该类包含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);
        }
    
    }
    View Code

    该类包含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();
            }
        }
    }
    View Code
  • 相关阅读:
    LeetCode数学系列(1)——第172解题思路
    python的匿名函数lambda解释及用法
    LeetCode位操作系列(2)——位运算的常用技巧:lowbit运算,包含lowbit公式、讲解、231题运用
    【零散】jupyter notebook快捷键 mac版
    【油猴插件】分享推荐
    【Mac】 Chromedriver 存放路径
    【全网首发】微信公众号常见垃圾文章广告软文关键词整理
    Mac Chrome浏览器取消自动升级(最新版)
    requests与selenium之前cookies传递
    [转]scrapy中的request.meta
  • 原文地址:https://www.cnblogs.com/wenwujuncheng/p/3523092.html
Copyright © 2011-2022 走看看