HttpServer1类
这个应用程序里边的HttpServer1类类似于第1章里边的简单服务器应用程序的HttpServer类。不过,在这个应用程序里边HttpServer1类可以同时提供静态资源和servlet。要请求一个静态资源,你可以在你的浏览器地址栏或者网址框里边敲入一个URL:http://machineName:port/staticResource就像是在第1章提到的,你可以请求一个静态资源。
为了请求一个servlet,你可以使用下面的URL:
http://machineName:port/servlet/servletClass因此,假如你在本地请求一个名为PrimitiveServlet的servlet,你在浏览器的地址栏或者网址框中敲入:
http://localhost:8080/servlet/PrimitiveServletservlet容器可以就提供PrimitiveServlet了。不过,假如你调用其他servlet,如ModernServlet,servlet容器将会抛出一个异常。在以下各章中,你将会建立可以处理这两个情况的程序。
HttpServer1类显示在Listing 2.2中。
Listing 2.2: HttpServer1类的await方法
package ex02.pyrmont;类的await方法等待HTTP请求直到一个shutdown命令给发出,让你想起第1章的await方法。Listing 2.2的await方法和第1章的区别是,在Listing 2.2里边,请求可以分发给一个StaticResourceProcessor或者一个ServletProcessor。假如URI包括字符串/servlet/的话,请求将会转发到后面去。
import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
public class HttpServer1 {
/** 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.
*/
// shutdown command
private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
// the shutdown command received
private boolean shutdown = false;
public static void main(String[] args) {
HttpServer1 server = new HttpServer1();
server.await();
}
public void await() {
ServerSocket serverSocket = null;
int port = 8080;
try {
serverSocket = new ServerSocket(port, 1,
InetAddress.getByName("127.0.0.1"));
}
catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
// Loop waiting for a request
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/")) {
ServletProcessor1 processor = new ServletProcessor1();
processor.process(request, response);
}
else {
StaticResoureProcessor 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);
}
}
}
}
不然的话,请求将会传递给StaticResourceProcessor实例 instance. 请注意,这部分在Listing 2.2中灰暗显示类的await方法等待HTTP请求直到一个shutdown命令给发出,让你想起第1章的await方法。Listing 2.2的await方法和第1章的区别是,在Listing 2.2里边,请求可以分发给一个StaticResourceProcessor或者一个ServletProcessor。假如URI包括字符串/servlet/的话,请求将会转发到后面去。
不然的话,请求将会传递给StaticResourceProcessor实例 instance. 请注意,这部分在Listing 2.2中灰暗显示。
Request类
servlet的service方法从servlet容器中接收一个javax.servlet.ServletRequest实例和一个javax.servlet.ServletResponse实例。这就是说对于每一个HTTP请求,servlet容器必须构造一个ServletRequest对象和一个ServletResponse对象并把它们传递给正在服务的servlet的service方法。ex02.pyrmont.Request类代表一个request对象并被传递给servlet的service方法。就本身而言,它必须实现javax.servlet.ServletRequest接口。这个类必须提供这个接口所有方法的实现。不过,我们想要让它非常简单并且仅仅提供实现其中一些方法,我们在以下各章中再实现全部的方法。要编译Request类,你需要把这些方法的实现留空。假如你看过Listing 2.3中的Request类,你将会看到那些需要返回一个对象的方法返回了null
Listing 2.3: Request类
package ex02.pyrmont;另外,Request类仍然有在第1章中讨论的parse和getUri方法。
import java.io.InputStream;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
public class Request implements ServletRequest {
private InputStream input;
private String uri;
public Request(InputStream input){
this.input = input;
}
public String getUri() {
return uri;
}
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;
}
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.print(request.toString());
uri = parseUri(request.toString());
}
/* implementation of ServletRequest */
public Object getAttribute(String attribute) {
return null;
}
public Enumeration getAttributeNames() {
return null;
}
public String getRealPath(String path) {
return null;
}
public RequestDispatcher getRequestDispatcher(String path) {
return null;
}
public boolean isSecure() {
return false;
}
public String getCharacterEncoding() {
return null;
}
public int getContentLength() {
return 0;
}
public String getContentType() {
return null;
}
public ServletInputStream getInputStream() throws IOException {
return null;
}
public Locale getLocale() {
return null;
}
public Enumeration getLocales() {
return null;
}
public String getParameter(String name) {
return null;
}
public Map getParameterMap() {
return null;
}
public Enumeration getParameterNames() {
return null;
}
public String[] getParameterValues(String parameter) {
return null;
}
public String getProtocol() {
return null;
}
public BufferedReader getReader() throws IOException {
return null;
}
public String getRemoteAddr() {
return null;
}
public String getRemoteHost() {
return null;
}
public String getScheme() {
return null;
}
public String getServerName() {
return null;
}
public int getServerPort() {
return 0;
}
public void removeAttribute(String attribute) { }
public void setAttribute(String key, Object value) { }
public void setCharacterEncoding(String encoding)
throws UnsupportedEncodingException { }
}
Response类
在Listing 2.4列出的ex02.pyrmont.Response类,实现了javax.servlet.ServletResponse。就本身而言,这个类必须提供接口里边的所有方法的实现。类似于Request类,我们把除了getWriter之外的所有方法的实现留空。Listing 2.4: Response类
package ex02.pyrmont;在getWriter方法中,PrintWriter类的构造方法的第二个参数是一个布尔值表明是否允许自动刷新。传递true作为第二个参数将会使任何println方法的调用都会刷新输出(output)。不过,print方法不会刷新输出。
import java.io.OutputStream;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.File;
import java.io.PrintWriter;
import java.util.Locale;
import javax.servlet.ServletResponse;
import javax.servlet.ServletOutputStream;
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 {
/* request.getUri has been replaced by request.getRequestURI */
File file = new File(Constants.WEB_ROOT, request.getUri());
fis = new FileInputstream(file);
/*
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
*/
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\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: 23\r\n" +
"\r\n" +
"<h1>File Not Found</h1>";
output.write(errorMessage.getBytes());
}
finally {
if (fis!=null)
fis.close();
}
}
/** implementation of ServletResponse */
public void flushBuffer() throws IOException ( }
public int getBufferSize() {
return 0;
}
public String getCharacterEncoding() {
return null;
}
public Locale getLocale() {
return null;
}
public ServletOutputStream getOutputStream() throws IOException {
return null;
}
public PrintWriter getWriter() throws IOException {
// autoflush is true, println() will flush,
// but print() will not.
writer = new PrintWriter(output, true);
return writer;
}
public boolean isCommitted() {
return false;
}
public void reset() { }
public void resetBuffer() { }
public void setBufferSize(int size) { }
public void setContentLength(int length) { }
public void setContentType(String type) { }
public void setLocale(Locale locale) { }
}
因此,任何print方法的调用都会发生在servlet的service方法的最后一行,输出将不会被发送到浏览器。这个缺点将会在下一个应用程序中修复。
Response类还拥有在第1章中谈到的sendStaticResource方法。
StaticResourceProcessor类
ex02.pyrmont.StaticResourceProcessor类用来提供静态资源请求。唯一的方法是process方法。Listing 2.5给出了StaticResourceProcessor类。Listing 2.5: StaticResourceProcessor类
package ex02.pyrmont;process方法接收两个参数:一个ex02.pyrmont.Request实例和一个ex02.pyrmont.Response实例。这个方法只是简单的呼叫Response对象的sendStaticResource方法。
import java.io.IOException;
public class StaticResourceProcessor {
public void process(Request request, Response response) {
try {
response.sendStaticResource();
}
catch (IOException e) {
e.printStackTrace();
}
}
}