How Tomcat works学习笔记<二>

  

一个简单的Servlet 容器

该章中介绍了怎么编写自己的容器,实现了两个Servlet容器,第一个容器设计的尽可能简单,第二个容器相对复杂一些,这两个容器都支持处理静态资源和Servlet,测试PrimitiveServlet被放在工程的webroot目录下。

2.1     Javax.servlet.Servlet接口

Servlet编程都必须通过javax.servlet和javax.servlet.http包,其中最重要的是javax.servlet.Servlet接口,所有的Servlet都必须实现这个接口。Servlet接口有下面五个方法:

         public void destroy()

    public void init(ServletConfig config) throws ServletException

    public ServletConfig getServletConfig()

        public String getServletInfo()

         public void service(ServletRequest request, ServletResponse response)throws ServletException, IOException

在Servlet容器在实例化servlet时调用init方法,当客服端发送一个请求的时候,Servlet容器会调用servlet的service方法,并传送ServletRequest和ServletResponse对象,ServletRequest包含客户端的HTTP请求信息,ServletResponse封装了servlet的响应,当Servlet容器从服务中移除servlet的时候调用destroy方法。

一个完整的容器处理HTTP请求的过程:

1.  当第一次调用servlet的时候,加载servlet类,并调用servlet初始化方法;

2.       对于每个请求,构造一个ServletRequest实例和一个ServletResponse实例

3.       调用servlet的service方法,并传入请求和响应对象

4.       当servlet关闭(shut down)的时候,调用其destroy对象并卸载(unload)servlet类

 

2.2     应用一

在第一个servlet容器中没用正式容器那么复杂,不会去servlet的调用init和destory方法,主要做下面的几件事情:

1.       监听客户端的请求;

2.       构造ServletRequest和ServletResponse对象;

3.       如果是请求静态资源,则调用静态资源处理器的process方法,并传入ServletRequest和ServletResponse对象;

4.       如果是请求servlet,则加载servlet类调用其的service方法,并传入ServletRequest和ServletResponse对象。

在这里有一个问题,即每次请求的时候都会重新加载servlet,在正式容器中是不被允许的,肯定会影响性能。

这个应用包括以下六个类:

         HttpServer1

         Request

         Response

         StaticResourceProcessor

         ServletProcessor1

         Constants

类似于第一章,应用的入口也在HttpServer1,main方法创建HttpServer1实例并调用其await方法

2.2.1  HttpServer1类

HttpServer1与前一章的HttpServer相类似,仅添加了处理servlet的逻辑。

具体类见:

package com.ex02.pyfrmont;

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;

 

/**

 * 服务器实现类,await方法,该方法监听制定端口的Http请求,

 * 处理以后返回给客户端,直到收到shutdown命令

 * @author peng_yf

 * @version 1.0,2011-08-20

 * @since 1.0

 */

public class HttpServer1 {

 

    /**

     * WEB_ROOT is the directory where our HTML and other files reside.

     * For this packge,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;

   

    /**

     * @param args

     */

    public static void main(String[] args) {

       HttpServer1 server = new HttpServer1();

       server.await();

    }

 

    private 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 begings with "/servlet/"

              if(request.getUri().startsWith("/servlet")){

                  ServletProcessor1 processor = new ServletProcessor1();

                  processor.process(request,response);

              }else{

                  StaticResourceProcessor 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();

              continue;

           }

       }

    }

}

2.2.2  Request类

Request代表客户端的请求,必须实现javax.servlet.ServletRequest接口,除了第一章Request中的方法,其它方法都是空实现:

package com.ex02.pyfrmont;

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;

/**

 * 代表一个HTTP请求,用Socket中与客户端通信的输入流来构造,可

 * 以通过其中的read方法读取HTTP格式数据

 * @author Peng_yf

 * @version 1.0,2011-08-20

 * @since 1.0

 */

public class Request implements ServletRequest{

    private InputStream input;

    private String uri;

    public Request(InputStream input) {

       this.input = input;

    }

    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.println(request.toString());

       uri = parseUri(request.toString());

    }

    private String parseUri(String requestString){

       int index1,index2;

       index1 = requestString.indexOf(' ');

       while(index1 != -1){

           index2 = requestString.indexOf(' ', index1+1);

           if(index1 < index2){

              return requestString.substring(index1+1,index2);

           }

       }

       return null;

    }

    public String getUri(){

       return this.uri;

    }

    @Override

    public Object getAttribute(String arg0) {

       return null;

    }

    @Override

    public Enumeration getAttributeNames() {

       return null;

    }

    @Override

    public String getCharacterEncoding() {

       return null;

    }

    @Override

    public int getContentLength() {

       return 0;

    }

    @Override

    public String getContentType() {

       return null;

    }

    @Override

    public ServletInputStream getInputStream() throws IOException {

       return null;

    }

    @Override

    public String getLocalAddr() {

       return null;

    }

    @Override

    public String getLocalName() {

       return null;

    } 

    @Override

    public int getLocalPort() {

       return 0;

    }

    @Override

    public Locale getLocale() {

       return null;

    }

    @Override

    public Enumeration getLocales() {

       return null;

    }

    @Override

    public String getParameter(String arg0) {

       return null;

    }

    @Override

    public Map getParameterMap() {

       return null;

    }

    @Override

    public Enumeration getParameterNames() {

       return null;

    }

    @Override

    public String[] getParameterValues(String arg0) {

       return null;

    }

    @Override

    public String getProtocol() {

       return null;

    }

    @Override

    public BufferedReader getReader() throws IOException {

       return null;

    }

    @Override

    public String getRealPath(String arg0) {

       return null;

    }

    @Override

    public String getRemoteAddr() {

       return null;

    }

    @Override

    public String getRemoteHost() {

       return null;

    }

    @Override

    public int getRemotePort() {

       return 0;

    }

    @Override

    public RequestDispatcher getRequestDispatcher(String arg0) {

       return null;

    }

    @Override

    public String getScheme() {

       return null;

    }

    @Override

    public String getServerName() {

       return null;

    }

    @Override

    public int getServerPort() {

       return 0;

    }

    @Override

    public boolean isSecure() {

       return false;

    }

    @Override

    public void removeAttribute(String arg0) {

    }

    @Override

    public void setAttribute(String arg0, Object arg1) {

    }

    @Override

    public void setCharacterEncoding(String arg0)

           throws UnsupportedEncodingException {

    }

}

2.2.3  Response类

Response代表客户端的请求,必须实现javax.servlet.ServletResponse接口,除了上一章Response中的方法,其它均为空实现:

package com.ex02.pyfrmont;

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;

/**

 * HTTP Response = Status-Line

 * *((general-header|response-header|entity-header)CRLF)

 * CRLF

 * [message body]

 * Status-Line=HTTP-Version SP Status-Code SP Response-Phrase CRLF

 * @author Peng_yf

 * @version 1.0, 2011-08-20

 * @since 1.0

 */

public class Response implements ServletResponse{

    private static final int BUFFER_SIZE = 1024;

    OutputStream output;

    Request request;

    PrintWriter writer;

    public Response(OutputStream output) {

       this.output = output;

    }

    public void setRequest(Request request) {

       this.request = request;

    }

    public void sendStaticResource() {

       byte[] bytes = new byte[BUFFER_SIZE];

       FileInputStream fis = null;

       try {

           File file = new File(Constants.WEB_ROOT,request.getUri());

           if(file.exists()){

              fis = new FileInputStream(file);

              int ch = fis.read(bytes,0,BUFFER_SIZE);

              if(ch != -1){

                  output.write(bytes, 0, ch);

                  ch = fis.read(bytes,0,BUFFER_SIZE);

              }

           }else{

              //file not found

              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());   

           }

       } catch (Exception e) {

           //thrown if cannot instantiate a File object.

           System.out.println(e.getMessage());

           e.printStackTrace();

       }finally{

           if(fis != null){

              try {

                  fis.close();

              } catch (IOException e) {

                  e.printStackTrace();

              }

           }

       }

    }

    public PrintWriter getWriter() throws IOException{

       //autoflush is true, println() will flush

       //but print() will not.

       writer = new PrintWriter(output,true);

       return writer;

    }

    @Override

    public void flushBuffer() throws IOException {

    }

    @Override

    public int getBufferSize() {

       return 0;

    }

    @Override

    public String getCharacterEncoding() {

       return null;

    }

    @Override

    public String getContentType() {

       return null;

    }

    @Override

    public Locale getLocale() {

       return null;

    }

    @Override

    public ServletOutputStream getOutputStream() throws IOException {

       return null;

    }

    @Override

    public boolean isCommitted() {

       return false;

    }

    @Override

    public void reset() {

    }

    @Override

    public void resetBuffer() {

    }

    @Override

    public void setBufferSize(int arg0) {

    }

    @Override

    public void setCharacterEncoding(String arg0) {

    }

    @Override

    public void setContentLength(int arg0) {

    }

    @Override

    public void setContentType(String arg0) {

    }

    @Override

    public void setLocale(Locale arg0) {

    }

}

2.2.4  StaticResourceProcessor类

StaticResourceProcessor类是静态资源处理类,当请求的是静态资源的时候调用该类的process方法:

package com.ex02.pyfrmont;

 

/**

 * 静态资源处理器

 * @author Peng_yf

 */

public class StaticResourceProcessor {

    /**

     * 静态资源处理

     * @param request

     * @param response

     */

    public void process(Request request, Response response) {

       response.sendStaticResource();

    }

}

2.2.5  ServletProcessor1类

ServletProcessor1类servlet请求处理器,当请求的是servlet类的时候方法该类的process方法,加载servlet类,并调用其中的service方法:

package com.ex02.pyfrmont;

import java.io.File;

import java.io.IOException;

import java.net.URL;

import java.net.URLClassLoader;

import java.net.URLStreamHandler;

import javax.servlet.Servlet;

import javax.servlet.ServletException;

 

/**

 * servlet处理器

 * @author Peng_yf

 */

public class ServletProcessor1 {

 

    /**

     * 用来处理servlet请求

     * @param request

     * @param response

     */

    public void process(Request request, Response response) {

       String uri = request.getUri();

       String servletName = uri.substring(uri.lastIndexOf("/")+1);

       URLClassLoader loader = null;

      

       try{

           URL[] urls = new URL[1];

           URLStreamHandler streamHandler = null;

           File classPath = new File(Constants.WEB_ROOT);

           //the forming of repository take from the createClassLoader methed in

           //org.apache.catalina.startup.ClassLoaderFactory

           String repository =

              (new URL("file",null,classPath.getCanonicalPath() + File.separator)).toString();

           //the code for forming the URL is taken from the addRepository methed in

           //org.apache.catalina.startup.StandardClassLoader.

           urls[0] = new URL(null,repository,streamHandler);

           loader = new URLClassLoader(urls);

       }catch (IOException e) {

           System.out.println(e.getMessage());

           e.printStackTrace();

       }

      

       Class myClass = null;

       try {

           myClass = loader.loadClass(servletName);

       } catch (ClassNotFoundException e) {

           e.printStackTrace();

       }

       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) {

           e.printStackTrace();

       }

    }

}

在servlet 容器中,用到了repository模型,类加载器查找servlet类的路径叫储存库(repository),这里就只有一个地址。

 

2.2.6  运行应用

要运行应用,需要导入servlet-api.jar(servlet2.4以后)或servlet.jar(servlet2.3),同时在需要把PrimitiveServlet.java编译后放在webroot下,在页面浏览器中输入

        http://localhost:8080/servlet/PrimitiveServlet

将会在页面输出:

         Hello.Roses are red. Violets are blue.

要运行同时还需要添加常量类:

package com.ex02.pyfrmont;

import java.io.File;

/**

 * 常量类

 * @author Peng_yf

 */

public class Constants {

    /**

     * webroot目录

     */

    public static final String WEB_ROOT =

       System.getProperty("user.dir") + File.separator + "webroot";

}

2.3     应用二

在应用一中ServletProcessor1中的parse方法,是直接把HttpServer1传过来的Request和Response直接传递给servlet的service方法,这样可能会有一个安全隐患,在servlet类中知道具体事例的程序员可以直接把ServletRequest和ServletResponse事例向下转型为Request和Response,这用就有可以调用它们的parse和sendStaticResource方法,对于这个问题,可以使用门面模式来解决,分别为Request和Response提供一个实现于ServletRequest和ServletResponse门面类RequestFacade和ResponseFacade:

RequestFacade类:

package com.ex02.pyfrmont;

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;

/**

 * request门面类

 * @author Peng_yf

 * @version 1.0, 2011-08-21

 * @since 1.0

 */

public class RequestFacade implements ServletRequest {

         private Request request = null;

         public RequestFacade(Request request){

                   this.request = request;

         }

         @Override

         public Object getAttribute(String arg0) {

                   return request.getAttribute(arg0);

         }

         @Override

         public Enumeration getAttributeNames() {

                   return request.getAttributeNames();

         }

         @Override

         public String getCharacterEncoding() {

                   return request.getCharacterEncoding();

         }

         @Override

         public int getContentLength() {

                   return request.getContentLength();

         }

         @Override

         public String getContentType() {

                   return request.getContentType();

         }

         @Override

         public ServletInputStream getInputStream() throws IOException {

                   return request.getInputStream();

         }

         @Override

         public String getLocalAddr() {

                   return request.getLocalAddr();

         }

         @Override

         public String getLocalName() {

                   return request.getLocalName();

         }

         @Override

         public int getLocalPort() {

                   return request.getLocalPort();

         }

         @Override

         public Locale getLocale() {

                   return request.getLocale();

         }

         @Override

         public Enumeration getLocales() {

                   return request.getLocales();

         }

         @Override

         public String getParameter(String arg0) {

                   return request.getParameter(arg0);

         }

         @Override

         public Map getParameterMap() {

                   return request.getParameterMap();

         }

         @Override

         public Enumeration getParameterNames() {

                   return request.getParameterNames();

         }

         @Override

         public String[] getParameterValues(String arg0) {

                   return request.getParameterValues(arg0);

         }

         @Override

         public String getProtocol() {

                   return request.getProtocol();

         }

         @Override

         public BufferedReader getReader() throws IOException {

                   return request.getReader();

         }

         @Override

         public String getRealPath(String arg0) {

                   return request.getRealPath(arg0);

         }

         @Override

         public String getRemoteAddr() {

                   return request.getRemoteAddr();

         }

         @Override

         public String getRemoteHost() {

                   return request.getRemoteHost();

         }

         @Override

         public int getRemotePort() {

                   return request.getRemotePort();

         }

         @Override

         public RequestDispatcher getRequestDispatcher(String arg0) {

                   return request.getRequestDispatcher(arg0);

         }

         @Override

         public String getScheme() {

                   return request.getScheme();

         }

         @Override

         public String getServerName() {

                   return request.getServerName();

         }

         @Override

         public int getServerPort() {

                   return request.getServerPort();

         }

         @Override

         public boolean isSecure() {

                   return request.isSecure();

         }

         @Override

         public void removeAttribute(String arg0) {

                   request.removeAttribute(arg0);

         }

         @Override

         public void setAttribute(String arg0, Object arg1) {

                   request.setAttribute(arg0, arg1);

         }

         @Override

         public void setCharacterEncoding(String arg0)

                            throws UnsupportedEncodingException {

                   request.setCharacterEncoding(arg0);

         }

}

 

ResponseFacade类:

package com.ex02.pyfrmont;

import java.io.IOException;

import java.io.PrintWriter;

import java.util.Locale;

import javax.servlet.ServletOutputStream;

import javax.servlet.ServletResponse;

/**

 * Response门面类

 * @author Peng_yf

 * @version 1.0, 2011-08-21

 */

public class ResponseFacade implements ServletResponse {

         private Response response = null;   

         public ResponseFacade(Response response){

                   this.response = response;

         }

         @Override

         public void flushBuffer() throws IOException {

                   response.flushBuffer();

         }

         @Override

         public int getBufferSize() {

                   return response.getBufferSize();

         }

         @Override

         public String getCharacterEncoding() {

                   return response.getCharacterEncoding();

         }

         @Override

         public String getContentType() {

                   return response.getContentType();

         }

         @Override

         public Locale getLocale() {

                   return response.getLocale();

         }

         @Override

         public ServletOutputStream getOutputStream() throws IOException {

                   return response.getOutputStream();

         }

         @Override

         public PrintWriter getWriter() throws IOException {

                   return response.getWriter();

         }

         @Override

         public boolean isCommitted() {

                   return response.isCommitted();

         }

         @Override

         public void reset() {

                   response.reset();

         }

         @Override

         public void resetBuffer() {

                   response.resetBuffer();

         }

         @Override

         public void setBufferSize(int arg0) {

                   response.setBufferSize(arg0);

         }

         @Override

         public void setCharacterEncoding(String arg0) {

                   response.setCharacterEncoding(arg0);

         }

         @Override

         public void setContentLength(int arg0) {

                   response.setContentLength(arg0);

         }

         @Override

         public void setContentType(String arg0) {

                   response.setContentType(arg0);

         }

         @Override

         public void setLocale(Locale local) {

                   response.setLocale(local);

         }

}

 

修改应用一中的servlet处理器,在调用servlet的service方法时构造门面类传入:

package com.ex02.pyfrmont;

import java.io.File;

import java.io.IOException;

import java.net.URL;

import java.net.URLClassLoader;

import java.net.URLStreamHandler;

import javax.servlet.Servlet;

import javax.servlet.ServletException;

/**

 * servlet处理器

 * @author Peng_yf

 * @version 1.0, 2011-08-21

 * @since 1.0

 */

public class ServletProcessor2 {

    /**

     * 用来处理servlet请求

     * @param request

     * @param response

     */

    public void process(Request request, Response response) {

       String uri = request.getUri();

       String servletName = uri.substring(uri.lastIndexOf("/")+1);

       URLClassLoader loader = null

       try{

           URL[] urls = new URL[1];

           URLStreamHandler streamHandler = null;

           File classPath = new File(Constants.WEB_ROOT);

           //the forming of repository take from the createClassLoader methed in

           //org.apache.catalina.startup.ClassLoaderFactory

           String repository =

              (new URL("file",null,classPath.getCanonicalPath() + File.separator)).toString();

           //the code for forming the URL is taken from the addRepository methed in

           //org.apache.catalina.startup.StandardClassLoader.

           urls[0] = new URL(null,repository,streamHandler);

           loader = new URLClassLoader(urls);

       }catch (IOException e) {

           System.out.println(e.getMessage());

           e.printStackTrace();

       }

       Class myClass = null;

       try {

           myClass = loader.loadClass(servletName);

       } catch (ClassNotFoundException e) {

           e.printStackTrace();

       }

       Servlet servlet = null;

       RequestFacade requestFacade = new RequestFacade(request);

       ResponseFacade responseFacade = new ResponseFacade(response);

       try {

           servlet = (Servlet)myClass.newInstance();

           servlet.service(requestFacade, responseFacade);

       } catch (InstantiationException e) {

           e.printStackTrace();

       } catch (IllegalAccessException e) {

           e.printStackTrace();

       } catch (ServletException e) {

           e.printStackTrace();

       } catch (IOException e) {

           e.printStackTrace();

       }

    }

}

HttpServer2与HttpServer基本相同,在servlet请求时调用ServletProcessor2处理器:

if(request.getUri().startsWith("/servlet")){

    ServletProcessor2 processor = new ServletProcessor2();

    processor.process(request,response);

}

运行与测试方法与应用相同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值