将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容器就完成了,但有着诸多的问题,留待以后逐一解决。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。