将http服务器升级为servlet容器

上一篇博文讲了如何编写一个简单的http服务器,但只能请求静态的资源,因此,在这一篇博文中,将简单的http服务器升级为servlet容器,

既可处理静态资源,也能请求简单的servlet。

现在呢,从servlet容器的角度审视servlet程序的开发,简单来说,对一个servlet的每个http请求,一个功能齐全的servlet容器有以下几件事要做:

*当第一次调用某个servlet时,要载入该servlet类,并调用,其init()方法。

*针对每个request请求,创建一个javax.servlet.ServletRequest实例和一个javax.servlet.ServletResponse实例。

*调用该servlet的service方法,将ServletRequest对象和ServletResponse对象作为参数传入。

*当关闭该servlet类,调用其destroy()方法,并卸载该servlet。

当然呢,本篇博文写的servlet容器并没有这么齐全的功能,只能完成其中的部分功能。

这个servlet容器是在http服务器的基础上完成,因此只是增加了几个类,并对其中的功能进行了增强。其中,该servlet容器包括以下几个类。

*HttpServer

*Request

*Response

*StaticResourceProcessor

*ServletProcessor

上面五个类的源码为:

HttpServer:

package cn.com.servletServer;

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

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 invoke.
	 */
	public static final String WEB_ROOT=System.getProperty("user.dir")+File.separator+"webroot";
	
	private static final String SHUTDOWN_COMMAND="/SHUTDOWN";
	
	private boolean shutdown=false;
	
	public static void main(String[] args) {
		HttpServer server=new HttpServer();
		server.await();
	}
	
	public void await(){
		ServerSocket serverSocket=null;
		int port=8080;
		try {
			serverSocket=new ServerSocket(port,1,InetAddress.getByName("127.0.0.1"));
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(0);
		}
		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/")){
					ServletProcessor processor=new ServletProcessor();
					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);
			}
		}
	}
}
这个HttpServer类与上一个HttpServer类相似,但是该应用程序中的HttpServer类既可对静态资源请求,也可以对于servlet资源请求。

这个类中的await()方法会一直等待HTTP请求,直到接收到一条关闭命令,并且这个方法可以将http请求分发给StaticResourceProcessor对象

或ServletProcesor对象来处理,当url包含字符串"/servlet/"时,会把请求转发给ServletProcessor对象处理,否则的话,把http请求传递给

StaticResourceProcessor对象处理。

Request:

package cn.com.servletServer;

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 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 (Exception 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());
	}
	
	public 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 String getUri(){
		return this.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 long getContentLengthLong() {
		// 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() throws IllegalStateException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public AsyncContext startAsync(ServletRequest arg0, ServletResponse arg1)
			throws IllegalStateException {
		// TODO Auto-generated method stub
		return null;
	}
}
Request类要处理servlet,因此实现了javax.Servlet.ServletRequest接口中声明的所有方法,但在此只需处理简单的servlet,因此,只实现了

少量的方法,大部分留空,留待以后再来实现。

Response:

package cn.com.servletServer;

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 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;
	}
	
	public void sendStaticResource()throws IOException{
		byte[] bytes=new byte[BUFFER_SIZE];
		FileInputStream fis=null;
		try {
			File file=new File(HttpServer.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) {
			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 {
		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 setContentLengthLong(long 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
		
	}
}
Response类实现了javax.servlet.ServletRequest接口,该类提供了ServletResponse接口中声明的所有方法的实现,与Request类类似,除了

getWriter()方法以外,大部分方法的实现都留空。

在getWriter()方法中,PrintWriter类的构造函数第二个参数是一个布尔值,表示是否启用autoFlush。对第二个参数传入的true表示对println()方法的任何调用

都会刷新输出,但是调用print()方法时不会刷新输出,因此,在这里留下了一个小bug,留待以后再来解决。

StaticResourceProcessor:

package cn.com.servletServer;

public class StaticResourceProcessor {
	
	public void process(Request request,Response response){
		try {
			response.sendStaticResource();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
该类只有一个方法,即process()方法,用途也只有一个,即用于处理静态资源。

ServletProcessor:

package cn.com.servletServer;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;

import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * 该类用于处理对servlet资源的请求
 * @author Administrator
 *
 */
public class ServletProcessor {
	
	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 streamHandle=null;
			File classPath=new File(HttpServer.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
			urls[0]=new URL(null,repository,streamHandle);
			loader=new URLClassLoader(urls);
		} catch (Exception e) {
			System.out.println(e.toString());
		}
		Class myClass=null;
		try {
			myClass=loader.loadClass("cn.com.client."+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());
		}
	}
}
该类也比较简单,只有一个方法:process()方法,用于处理对servlet资源的请求,

但是,这个类有一定问题,原本servlet资源也是只需放入webroot即可,但是在这里它无法请求webroot中的servlet资源,只能把servlet放入了

src的一个包中才能请求到资源,因此有大神知道请指点我一下。

用于测试的servlet比较简单,源码如下:

package cn.com.client;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;


public class PrimitiveServlet implements Servlet {

	@Override
	public void destroy() {
		
	}

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

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

	@Override
	public void init(ServletConfig arg0) throws ServletException {
		
	}

	@Override
	public void service(ServletRequest request, ServletResponse response)
			throws ServletException, IOException {
		System.out.print("from service");
		PrintWriter out=response.getWriter();
		out.println("Hello. Roses are red.");
		out.print("Violets are blue.");
	}
	
}
现在,我们进行测试。

技术分享

页面

技术分享

如此,一个简单的servlet容器就完成了,但有着诸多的问题,留待以后逐一解决。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。