上一篇博文讲了如何编写一个简单的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容器就完成了,但有着诸多的问题,留待以后逐一解决。