《深入剖析Tomcat》阅读(二)

   Tomcat是基于Sun公司标准的开源Servlet容器。

   Servlet是什么?

    Servlet(Server Applet),全称Java Servlet,未有中文译文。是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。

Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。

最早支持Servlet标准的是JavaSoft的Java Web Server。此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。

Servlet编程需要使用到javax.servlet 和 javax.servlet.http两个包下面的类和接口,在所有的类和接口中,javax.servlet.servlet 接口最为重要。所有的servlet程序都必须实现该接口或者继承实现了该接口的类。

Servlet只是一种标准,按照这种标准既可以实现HTTP请求也可以实现其他的协议,用于执行Servlet的启动和管理程序称为Servlet容器,Tomcat便是一个很经常使用的Servlet容器。

上面一小节介绍了一个简单的HTTP的web服务器,是单线程的只能处理单个请求,请求处理完毕之后就重新开始准备接受连接的web服务器。

没有用到Servlet标准,只是简单的定义了Request和Response,简单解析了url以及将静态的文本文件通过输出流输出,并且我们发现response并没有http报文头,浏览器应该是针对这种情况做了特殊处理,比如直接打印流中的内容。

------------------------------------------------------------------------------------

一个功能齐全的Servlet容器有以下几件事情

1)第一次调用某个Servlet时,要载入该Servlet,并调用其init()方法,并且仅此一次

 

------------------------------------------------------------

下面举一个很简单的Servlet容器的例子

 

 

Constants.java

import java.io.File;
//定义了访问静态资源的位置
public class Constants
{
	public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";
}

Constants类定义了静态finla WEB_ROOT,WEB_ROOT指定了该Servlet容器中使用PrimitiveServlet类和静态资源的位置。

HttpServer1一直等待HTTP请求,并且是单线程处理HTTP请求,直到等待到shut命令

与一种不同的是,此时将会在HttpServer1当中解析url,通过url判断寻找的是Servlet对象还是静态资源,然后分发到不同的处理过程。

都分别架构Request对象和Response对象传入

ServletProcessor1 和
StaticResourceProcessor的process方法当中,在方法当中回调Request和Response的相关方法

 

HttpServer1.java

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
				{
					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();
				System.exit(1);
			}
		}
	}
}

Request.java

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.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

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 the 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
	{
	}

	@Override
	public int getRemotePort()
	{
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public String getLocalName()
	{
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getLocalAddr()
	{
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int getLocalPort()
	{
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public ServletContext getServletContext()
	{
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public AsyncContext startAsync()
	{
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse)
	{
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean isAsyncStarted()
	{
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean isAsyncSupported()
	{
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public AsyncContext getAsyncContext()
	{
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public DispatcherType getDispatcherType()
	{
		// TODO Auto-generated method stub
		return null;
	}

}

Response.java

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 a static page */
	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)
	{
	}

	@Override
	public String getContentType()
	{
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setCharacterEncoding(String charset)
	{
		// TODO Auto-generated method stub
		
	}
}

ServletProcessor1.java

import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;
import java.io.File;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class ServletProcessor1 {

  public void process(Request request, Response response) {

    String uri = request.getUri();
    String servletName = uri.substring(uri.lastIndexOf("/") + 1);
    URLClassLoader loader = null;

    try {
      // create a URLClassLoader
      URL[] urls = new URL[1];
      URLStreamHandler streamHandler = null;
      File classPath = new File(Constants.WEB_ROOT);
      // the forming of repository is taken from the createClassLoader method 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 method in
      // org.apache.catalina.loader.StandardClassLoader class.
      urls[0] = new URL(null, repository, streamHandler);
      loader = new URLClassLoader(urls);
    }
    catch (IOException e) {
      System.out.println(e.toString() );
    }
    Class myClass = null;
    try {
      myClass = loader.loadClass(servletName);
    }
    catch (ClassNotFoundException e) {
      System.out.println(e.toString());
    }

    Servlet servlet = null;

    try {
      servlet = (Servlet) myClass.newInstance();
      servlet.service((ServletRequest) request, (ServletResponse) response);
    }
    catch (Exception e) {
      System.out.println(e.toString());
    }
    catch (Throwable e) {
      System.out.println(e.toString());
    }

  }
}

  

import java.io.IOException;

public class StaticResourceProcessor {

  public void process(Request request, Response response) {
    try {
      response.sendStaticResource();
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }
}

  在静态资源的访问过程中,静态资源的的outpu过程放在了Response当中,其实是不合理的,应该放在Processor当中

 

 

---------------------------------------------------

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;
}
Request类的parseURI的过程
利用了HTTP请求报文中的GET uri HTTP/1.1的规范找到第一行中间的uri字段

该资源是host后面的部分
比如www.baidu.com/guowuxin
则该字段便是/guowuxin
  

 

----------------------------------------------------------------------------------------------------------

下面讲一个这个程序的缺点
1:编程的OOP可能被破坏,封装性,传送给Servlet的ServletRequest对象本身应该
只能使用ServletRequest对象的相关方法,但是由于本身是子类Request对象,
所以如果认为进行强转,也是可以使用子类的方法的,破坏了封装性质,
不过这个问题可能出现在很多情景下,并不是一定要处理的额。

使用外观模式将这个类包起来就可以解决这个问题,
包接口的方法很类似于适配器模式,但是二者是不同的,
适配器模式是由于参数个数的不同或者方法名的不同而进行适配的,
这里是为了进一步的封装子类的方法。

 

package ex02.pyrmont;

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 RequestFacade implements ServletRequest {

  private ServletRequest request = null;

  public RequestFacade(Request request) {
    this.request = request;
  }

  /* implementation of the ServletRequest*/
  public Object getAttribute(String attribute) {
    return request.getAttribute(attribute);
  }

  public Enumeration getAttributeNames() {
    return request.getAttributeNames();
  }

  public String getRealPath(String path) {
    return request.getRealPath(path);
  }

  public RequestDispatcher getRequestDispatcher(String path) {
    return request.getRequestDispatcher(path);
  }

  public boolean isSecure() {
    return request.isSecure();
  }

  public String getCharacterEncoding() {
    return request.getCharacterEncoding();
  }

  public int getContentLength() {
    return request.getContentLength();
  }

  public String getContentType() {
    return request.getContentType();
  }

  public ServletInputStream getInputStream() throws IOException {
    return request.getInputStream();
  }

  public Locale getLocale() {
    return request.getLocale();
  }

  public Enumeration getLocales() {
    return request.getLocales();
  }

  public String getParameter(String name) {
    return request.getParameter(name);
  }

  public Map getParameterMap() {
    return request.getParameterMap();
  }

  public Enumeration getParameterNames() {
    return request.getParameterNames();
  }

  public String[] getParameterValues(String parameter) {
    return request.getParameterValues(parameter);
  }

  public String getProtocol() {
    return request.getProtocol();
  }

  public BufferedReader getReader() throws IOException {
    return request.getReader();
  }

  public String getRemoteAddr() {
    return request.getRemoteAddr();
  }

  public String getRemoteHost() {
    return request.getRemoteHost();
  }

  public String getScheme() {
   return request.getScheme();
  }

  public String getServerName() {
    return request.getServerName();
  }

  public int getServerPort() {
    return request.getServerPort();
  }

  public void removeAttribute(String attribute) {
    request.removeAttribute(attribute);
  }

  public void setAttribute(String key, Object value) {
    request.setAttribute(key, value);
  }

  public void setCharacterEncoding(String encoding)
    throws UnsupportedEncodingException {
    request.setCharacterEncoding(encoding);
  }

}

 

 

package ex02.pyrmont;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Locale;
import javax.servlet.ServletResponse;
import javax.servlet.ServletOutputStream;

public class ResponseFacade implements ServletResponse {

  private ServletResponse response;
  public ResponseFacade(Response response) {
    this.response = response;
  }

  public void flushBuffer() throws IOException {
    response.flushBuffer();
  }

  public int getBufferSize() {
    return response.getBufferSize();
  }

  public String getCharacterEncoding() {
    return response.getCharacterEncoding();
  }

  public Locale getLocale() {
    return response.getLocale();
  }

  public ServletOutputStream getOutputStream() throws IOException {
    return response.getOutputStream();
  }

  public PrintWriter getWriter() throws IOException {
    return response.getWriter();
  }

  public boolean isCommitted() {
    return response.isCommitted();
  }

  public void reset() {
    response.reset();
  }

  public void resetBuffer() {
    response.resetBuffer();
  }

  public void setBufferSize(int size) {
    response.setBufferSize(size);
  }

  public void setContentLength(int length) {
    response.setContentLength(length);
  }

  public void setContentType(String type) {
    response.setContentType(type);
  }

  public void setLocale(Locale locale) {
    response.setLocale(locale);
  }

}

 

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

  // shutdown command
  private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";

  // the shutdown command received
  private boolean shutdown = false;

  public static void main(String[] args) {
    HttpServer2 server = new HttpServer2();
    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/")) {
          ServletProcessor2 processor = new ServletProcessor2();
          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();
        System.exit(1);
      }
    }
  }
}

  

 

package ex02.pyrmont;

import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;
import java.io.File;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class ServletProcessor2 {

  public void process(Request request, Response response) {

    String uri = request.getUri();
    String servletName = uri.substring(uri.lastIndexOf("/") + 1);
    URLClassLoader loader = null;

    try {
      // create a URLClassLoader
      URL[] urls = new URL[1];
      URLStreamHandler streamHandler = null;
      File classPath = new File(Constants.WEB_ROOT);
      // the forming of repository is taken from the createClassLoader method 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 method in
      // org.apache.catalina.loader.StandardClassLoader class.
      urls[0] = new URL(null, repository, streamHandler);
      loader = new URLClassLoader(urls);
    }
    catch (IOException e) {
      System.out.println(e.toString() );
    }
    Class myClass = null;
    try {
      myClass = loader.loadClass(servletName);
    }
    catch (ClassNotFoundException e) {
      System.out.println(e.toString());
    }

    Servlet servlet = null;
    RequestFacade requestFacade = new RequestFacade(request);
    ResponseFacade responseFacade = new ResponseFacade(response);
    try {
      servlet = (Servlet) myClass.newInstance();
      servlet.service((ServletRequest) requestFacade, (ServletResponse) responseFacade);
    }
    catch (Exception e) {
      System.out.println(e.toString());
    }
    catch (Throwable e) {
      System.out.println(e.toString());
    }

  }
}

  

posted @ 2015-10-22 00:05  无心流泪  阅读(373)  评论(0编辑  收藏  举报