学习tomcat源码(2) 实现servlet容器功能

javax.servlet.Servlet接口:

Servlet编程是通过javax.servlet和javax.servlet.http这两个包的类和接口来实现的。其中一个至关重要的就是javax.servlet.Servlet接口了。所有的servlet必须实现实现或者继承实现该接口的类。 Servlet接口有五个方法,其用法如下。
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()

在Servlet的五个方法中,init,service和destroy是servlet的生命周期方法。在servlet类已经初始化之后,init方法将会被servlet容器所调用。servlet容器只调用一次,以此表明servlet已经被加载进服务中。init方法必须在servlet可以接受任何请求之前成功运行完毕。一个servlet程序员可以通过覆盖这个方法来写那些仅仅只要运行一次的初始化代码,例如加载数据库驱动,值初始化等等。在其他情况下,这个方法通常是留空的。 servlet容器为servlet请求调用它的service方法。servlet容器传递一个javax.servlet.ServletRequest对象和javax.servlet.ServletResponse对象。ServletRequest对象包括客户端的HTTP请求信息,而ServletResponse对象封装servlet的响应。在servlet的生命周期中,service方法将会给调用多次。 当从服务中移除一个servlet实例的时候,servlet容器调用destroy方法。这通常发生在servlet容器正在被关闭或者servlet容器需要一些空闲内存的时候。仅仅在所有servlet线程的service方法已经退出或者超时淘汰的时候,这个方法才被调用。在servlet容器已经调用完destroy方法之后,在同一个servlet里边将不会再调用service方法。destroy方法提供了一个机会来清理任何已经被占用的资源,例如内存,文件句柄和线程,并确保任何持久化状态和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对象。


实例:

该程序由6个类组成:
 HttpServer1
 Request
 Response
 StaticResourceProcessor
 ServletProcessor1
 Constants

HttpServer1代码:

package ex01;
import java.net.*;
import java.io.*;
public class HttpServer {

	/** 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. */
	
	
	//shutdowm command
	private static final String SHUTDOWN_COMMAND="/SHUTDOWN";
	// the shutdown command received
	private boolean shutdown=false;
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		HttpServer server=new HttpServer();
		server.await();
	}
	
	public void await(){
		ServerSocket serverSocket=null;
		int port=8081;
		try{
			serverSocket=new ServerSocket(port,1,
					InetAddress.getByName("127.0.0.1"));
			
		}catch(IOException e){
			e.printStackTrace();
			System.exit(1);
		}
		while(!shutdown){
			Socket socket=null;
			InputStream input=null;
			OutputStream output=null;
			try{
				socket=serverSocket.accept();
				input=socket.getInputStream();
				output=socket.getOutputStream();
				Request request=new Request(input);
				request.parse();
				Response response=new Response(output);
				response.setRequest(request);
				//check if this a request for a servlet or
				//a static resource
				//a request for a servlet begins whith"/servlet"
				if(request.getUri().startsWith("/servlet")){
					ServletProcessor1 processor=new ServletProcessor1();
					processor.process(request,response);
				}
				else{
					StaticResourceProcessor processor=new StaticResourceProcessor();
					processor.process(request,response);
				}
				//response.sendStaticResource();
				socket.close();
				shutdown=request.getUri().equals(SHUTDOWN_COMMAND);
				
			}catch(Exception e){
				e.printStackTrace();
				continue;
			}
		}
	}

}

Request代码:

package ex01;
import java.io.*;
import java.util.*;

import javax.servlet.*;
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(Constants.WEB_ROOT);
		this.uri=parseUri(request.toString());
		System.out.println("uri:"+uri);
	}
	
	public String parseUri(String requestString){
		int index1=0,index2=0;
		index1=requestString.indexOf(' ');
		if(index1!=-1){
			index2=requestString.indexOf(' ', index1+1);
			if(index2>index1){
				return requestString.substring(index1+1, index2);
			}
		}
		return null;
	}
	
	public String getUri(){
		return uri;
	}

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

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

	@Override
	public Enumeration<String> getAttributeNames() {
		// TODO Auto-generated method stub
		return null;
	}

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

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

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

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

	@Override
	public ServletInputStream getInputStream() throws IOException {
		// TODO Auto-generated method stub
		return null;
	}

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

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

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

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

	@Override
	public Enumeration<Locale> getLocales() {
		// TODO Auto-generated method stub
		return null;
	}

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

	@Override
	public Map<String, String[]> getParameterMap() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Enumeration<String> getParameterNames() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String[] getParameterValues(String arg0) {
		// TODO Auto-generated method stub
		return null;
	}

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

	@Override
	public BufferedReader getReader() throws IOException {
		// TODO Auto-generated method stub
		return null;
	}

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

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

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

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

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

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

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

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

	@Override
	public ServletContext getServletContext() {
		// 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 boolean isSecure() {
		// TODO Auto-generated method stub
		return false;
	}

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

	@Override
	public void setAttribute(String arg0, Object arg1) {
		// TODO Auto-generated method stub
		
	}

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

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

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

Respons代码:

package ex01;
import java.io.*;
import java.util.*;

import javax.servlet.*;
/* *
 * 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 
  */
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{
			File file=new File(Constants.WEB_ROOT,request.getUri());
			if(file.exists()){
				fis=new FileInputStream(file);
				int ch=fis.read(bytes,0,BUFFER_SIZE);
				while(ch!=-1){
					output.write(bytes, 0, BUFFER_SIZE);
					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){
			e.printStackTrace();
			System.out.println(e.toString() );
		}finally{
			if(fis!=null){
				fis.close();
			}
		}
	}

	@Override
	public void flushBuffer() throws IOException {
		// TODO Auto-generated method stub
		
	}

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

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

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

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

	@Override
	public ServletOutputStream getOutputStream() throws IOException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public PrintWriter getWriter() throws IOException {
		// TODO Auto-generated method stub
		// autoflush is true, println() will flush,
		// but print() will not.
		writer=new PrintWriter(output,true);
		return writer;
	}

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

	@Override
	public void reset() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void resetBuffer() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void setBufferSize(int arg0) {
		// TODO Auto-generated method stub
		
	}

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

	@Override
	public void setContentLength(int arg0) {
		// TODO Auto-generated method stub
		
	}

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

	@Override
	public void setLocale(Locale arg0) {
		// TODO Auto-generated method stub
		
	}
}

ServletProcessor1代码:
package ex01;
import java.net.*;
import java.io.*;
import javax.servlet.*;

public class ServletProcessor1 {
	public void process(Request request,Response response){
		String uri=request.getUri();
		String servletName=uri.substring(uri.lastIndexOf("/")+1);
		System.out.println("servletName:"+servletName);
		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
			//creaerClassLoadrer 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
			System.out.println("repository:"+repository);
			urls[0]=new URL(null,repository,streamHandler);
			loader=new URLClassLoader(urls);
			
		}catch(Exception e){
			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((ServletRequest) request,(ServletResponse)response);
		}catch(Exception e2){
			e2.printStackTrace();
		}catch(Throwable e3){
			e3.printStackTrace();
		}
	}
}

StaticResourceProcessor代码:
package ex01;
import java.io.*;

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

Constance代码:
package ex01;

import java.io.File;

public class Constants {
	public static final String WEB_ROOT=System.getProperty("user.dir")+File.separator+"webroot";
}

在原来的基础上实现了加载servlet的功能,由ServletProcessor1实现。

但是该应用程序有一个严重的问题。在ServletProcessor1类的process方法,向上转换ex02.pyrmont.Request实例为javax.servlet.ServletRequest,并作为第一个参数传递给servlet的service方法。也向下转换ex02.pyrmont.Response实例为javax.servlet.ServletResponse,并作为第二个参数传递给servlet的service方法。
try { servlet = (Servlet) myClass.newInstance(); servlet.service((ServletRequest) request,(ServletResponse) response); } 这会危害安全性。知道这个servlet容器的内部运作的Servlet程序员可以分别把ServletRequest和ServletResponse实例向下转换为ex02.pyrmont.Request和ex02.pyrmont.Response,并调用他们的公共方法。拥有一个Request实例,它们就可以调用parse方法。拥有一个Response实例,就可以调用sendStaticResource方法。

但不可以把parse和sendStaticResource方法设置为私有的,因为它们将会被其他的类调用。不过,这两个方法是在个servlet内部是不可见的。其中一个解决办法就是让Request和Response类拥有默认访问修饰,所以它们不能在ex02.pyrmont包的外部使用。

至于第二个方法代码的实现,明天再写,洗洗睡了。



你可能感兴趣的:(学习tomcat源码(2) 实现servlet容器功能)