How Tomcat Works 2: A Simple Servlet Server

1 javax.servlet.Servlet 接口

(1)接口包含五个方法:

Public void init(ServletCongig config)

Public void service(ServletRequest request, ServletResponse response)

Public void destory()

Public void ServletConfig getServletConfig()

Public String getServletInfo()

(2)方法解析:

    Init(),service(),destory()是servlet的生命周期方法。

    init(): Servlet Container在实例化servlet类时,会调用init(), 仅调用一次,可以用加载data driver, 初始化值等功能override这个方法。

    service(): 每次有request for servlet都会调用service()方法,传入request 信息,处理数据,封装response信息。

    destory(): 当从servlet container移除serlvet实例时,调用destroy()。通常SHUTDOWN或者servlet container 需要更多内存时,会调用该方法。

  (3)实现类实例:

package com.cisco.tomcat.simpleServletServer;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.*;

public class PrimitiveServlet implements Servlet{

	@Override
	public void init(ServletConfig config) throws ServletException {
		System.out.println("init");
		
	}

	@Override
	public ServletConfig getServletConfig() {		
		return null;
	}

	@Override
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		System.out.println("from service");
		PrintWriter out = res.getWriter();
		out.println("Hello. Rose is red");
		out.println("Violets are blue");		
	}

	@Override
	public String getServletInfo() {
		return null;
	}

	@Override
	public void destroy() {
		System.out.println("destroy");		
	}

}

 

 

2 功能简单的一个Servlet Container应用

(1)功能健全的servlet container应具备四个功能:

I. 当第一次调用servlet时,应加载这个servlet class并调用init()方法

II. 为每次的request, 创建ServletRequest和ServletResponse的实例

III. 调用servlet的service()方法,并传递ServletRequest对象和ServletResponse对象

IIII. 当sevlet类shutdown, 调用servlet的destory()方法,卸载该servlet类

(2)功能简化的这个ServletContainer主要做几个功能:

I 等待HTTP requests

II 创建ServletRequest和ServletResponse对象

III 如果请求静态资源,调用StaticResourceProcessor实例的process()方法,并传递ServletRequest和ServletResponse对象

|||| 如果请求一个servlet,加载serlvet 类,调用service()方法,并传递ServletRequest和ServletResponse对象(注:这种简化版每次都会加载serlvet类)

(3)这个ServletContainer

    主要包含6个类:    HTTPServer/Request/Response/StaticResourceProcessor/ServletProcessor/Constants

     UML图如下:

 

    URL: http://127.0.0.1:8080/servlet/servletClass

(4)具体实现:

HttpServer代码(较上节server增加了静态动态资源判断,转向到staticResourceProcessor或者servletProcessor处理):

package com.cisco.tomcat.simpleServletServer;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;


public class HttpServer {
	private static final String SHUTDOWN_COMMOND="/SHUTDOWN";
	private static final int PORT = 8080;
	private static final String HOST = "127.0.0.1";
	
	private boolean shutdown = false;
	
	public static void main(String[] args) {
		System.out.println("Start the Servlet Container......");
		HttpServer server = new HttpServer();
		server.await();
	}
	
	private void await() {
		ServerSocket serverSocket = null;
		try {
			serverSocket = new ServerSocket(PORT, 1, InetAddress.getByName(HOST));			
		} 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 request = new Request(input);
				request.parse();
				
				// 创建Response对象
				Response response = new Response(output);
				response.setRequest(request);
				
				// 判断是静态资源or Servlet,这里使用请求路径
				if(request.getUri().startsWith("/servlet/")) {
					ServletProcessor processor = new ServletProcessor();
					processor.process(request, response);					
				}else {
					StaticResourceProcessor processor = new StaticResourceProcessor();
					processor.process(request, response);
				}
				
				// 关闭socket
				socket.close();
				
				// 检查是否是关闭命令
				shutdown = SHUTDOWN_COMMOND.equals(request.getUri());
			} catch (IOException e) {
				e.printStackTrace();
				System.exit(1);
			}
			
		}

		
	}
}

 

 

Constants代码:

package com.cisco.tomcat;

import java.io.File;

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

 

 

 

Request代码:

package com.cisco.tomcat.simpleServletServer;

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.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;
	}
	
	public void parse() {
		StringBuffer request = new StringBuffer();
		int i;
		byte[] bytes = new byte[2048];
		try {
			i = input.read(bytes, 0, 2048);
		}catch (IOException e) {
			e.printStackTrace();
			i = -1;
		}
		for(int j=0; j<i; j++) {
			request.append((char)bytes[j]);
		}
		System.out.println(request.toString());
		uri = parseUri(request.toString());		
	}
	
	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;
	}
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return super.toString();
	}

	@Override
	public Object getAttribute(String name) {
		// 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 void setCharacterEncoding(String env) throws UnsupportedEncodingException {
		// TODO Auto-generated method stub

	}

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

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

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

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

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

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

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

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

	@Override
	public String getProtocol() {
		// 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 BufferedReader getReader() throws IOException {
		// 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 void setAttribute(String name, Object o) {
		// TODO Auto-generated method stub

	}

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

	}

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

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

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

	@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() throws IllegalStateException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse)
			throws IllegalStateException {
		// 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代码(静态资源的process方法放在了这里,同时提供一个PrintWriter,供Servlet内使用response对象来打印到socket管道中,response.getWriter().println()):

package com.cisco.tomcat.simpleServletServer;

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;

import com.cisco.tomcat.Constants;

public class Response implements ServletResponse {

	private static final int BUFFER_SIZE = 1024;
	private OutputStream output;
	private Request request;
	private PrintWriter writer;
	
	public void sendStaticResource() throws IOException{
		byte[] bytes = new byte[BUFFER_SIZE];
		FileInputStream fis = null;
		try {
			File file = new File(Constants.WEB_ROOT, request.getUri());
			fis = new FileInputStream(file);
			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\n"
					+ "Content-Type:text/html\n"
					+ "Content-Length:23\n"
					+ "\n"
					+ "<h1>File Not Found</h1>";
			output.write(errorMessage.getBytes());
		}finally {
			if(fis != null) {
				fis.close();
			}
		}
	}
	
	public void setRequest(Request request) {
		this.request = request;
	}
	
	public Response(OutputStream output) {
		this.output = output;
	}
	
	@Override
	public PrintWriter getWriter() throws IOException {
		writer = new PrintWriter(output, true);
		return writer;
	}
	
	@Override
	public String getCharacterEncoding() {
		// TODO Auto-generated method stub
		return null;
	}

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

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

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

	}

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

	}

	@Override
	public void setContentLengthLong(long length) {
		// TODO Auto-generated method stub

	}

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

	}

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

	}

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

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

	}

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

	}

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

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

	}

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

	}

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

}

 

 

ServletProcessor代码:

package com.cisco.tomcat.simpleServletServer;

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 com.cisco.tomcat.Constants;

public class ServletProcessor {
	public void process(Request request, Response response) {
		String uri = request.getUri();
		String servletName = "com.cisco.tomcat.simpleServletServer." + uri.substring(uri.lastIndexOf("/")+1);
		
		// 实现URL class loader
		URLClassLoader loader = null;
		try {
			URL[] urls = new URL[1];
			URLStreamHandler streamHandler = null;
			File classPath = new File(Constants.WEB_SERVLET_ROOT);
			
			// 从org.apache.catalina.loader.StandardClassLoader/startup.ClassLoaderFactory中获取该code
			String repository = (new URL("file", null, classPath.getCanonicalPath()+File.separator)).toString();
			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 (Exception e) {
			System.out.println(e.toString());
		}
		
		// 反射机制生成servlet实例,并调用service()方法
		Servlet servlet = null;
		try {
			servlet = (Servlet)myClass.newInstance();
			servlet.service(request, response);
		}catch (Exception e) {
			System.out.println(e.toString());
		}
	}
}

 

 

StaticResourceProcessor代码:

package com.cisco.tomcat.simpleServletServer;

import java.io.IOException;

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

 

 

Servlet Container需要调用的exampleServlet.class,如果文件为

C:/example/target/classes/com/cisco/tomcat/ExampleServlet.class. 那么URLClassloader的 URL地址要配置成c:/example/target/classes/。而文件的名称也要携带包名 也就是“com.cisco.tomcat.”+"ExampleServlet", 否则URLClassLoader会报找不到class的错误信息【NoClassDefFoundError: wrong name】

 

 

3. 优化的Servlet Container

    上述servletProcessor中,使用servlet.service(request, response)时, servlet对象此时能够调用request和response的所有public方法,如getUri(),parse(), sendStaticResource()等,但实际上这可能是不安全的。所以tomcat引用了Façade Pattern(门面模式),来解决这个问题。

    门面模式的主要作用:将子系统的各个调用封装在一个子系统对外统一接口,屏蔽内部接口实现细节

 

这里主要增加两个类:RequestFacade和ResponseFacade:

RequestFacade代码(构造函数复制私有的request对象,隐藏request的对公函数):

public class RequestFacade implements ServletRequest{

	private ServletRequest request = null;
	
	public RequestFacade(ServletRequest request) {
		this.request = request;
	}
	@Override
	public Object getAttribute(String name) {
		return request.getAttribute(name);
	}

	@Override
	public Enumeration<String> getAttributeNames() {
		return request.getAttributeNames();
	}

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

ResponseFacade代码:

public class ResponseFacade implements ServletResponse{

	private ServletResponse response;
	
	public ResponseFacade(ServletResponse response) {
		this.response = response; 
	}
	
	@Override
	public String getCharacterEncoding() {
		return response.getCharacterEncoding();
	}

	@Override
	public String getContentType() {
		return response.getContentType();
	}

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

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值