A Simple Servlet Container in Java

Yesterday I started to refresh my Java skills. Basically I will follow the same way I refreshed my Perl skills. In the past 1 month I refreshed my Perl skills by designing a tiny pure perl web container. You might check it out from http://luohuahuang.com/2013/08/12/camel-a-pure-perl-web-server/

In the following section I will introduce HTTP again but from prospect of Java language.

Internet is running upon a ‘stateless’ protocol ‘HTTP’. http://tools.ietf.org/pdf/rfc2616.pdf defines Hypertext Transfer Protocol — HTTP/1.1. Basically for a HTTP connection, it has,

HTTP Request

POST /firstapp/test.pl HTTP/1.1
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: en-us,en;q=0.5
Host: localhost:8080
Referer: http://localhost:8080/firstapp/test.pl
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:7.0.1) Gecko/20100101 Firefox/7.0.1
Content-Length: 792

It contains three parts:

URI: Uniform Resource Identifier.

Request Header

Message Body.

HTTP Response

HTTP/1.1 200 OK

Server: …

Let’s see how to get started.

Firstly, we need to new a java.net.ServerSocket. You can request it to listen in localhost:8080

try {
serverSocket = new ServerSocket(PORT, 1, InetAddress.getByName(IP_ADDR));
catch (IOException e) {
e.printStackTrace();
System.out.println("Could not listen on port: " + PORT);
System.exit(-1);
}

Secondly, ASA the connection is established, it will retrieve a socket handler for you and you can use that socket handler to retrieve InputStream and also prepare your OutputStream.

try {
input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
output = socket.getOutputStream();

Thirdly, create two classes by implementing interfaces javax.servlet.ServletRequest and javax.servlet.ServletResponse respectively. Here you need to extract the URI from HTTP Request so that you can know what it wants. Basically you can category requests into two types: Static Request (.html) and Servlet. The difference between these two types is: for static request, you can just simply open that requested .html file and write it back to the outputstream; for Servlet request, you need to load the Servlet Class into the classloader, run its service() method and then retrieve back the result.

if (request.getUri().startsWith("/servlet/")) {
ServletProcessor processor = new ServletProcessor();
processor.process(request, response);
} else {
StaticResourceProcessor processor = new StaticResourceProcessor();
processor.process(request, response);
}
// …
// load the servlet into classloader. run it and retrieve the result.
public void process(HttpRequest request, HttpResponse response) {
String uri = request.getUri();
String servletName = uri.substring(uri.lastIndexOf("/") + 1);
URLClassLoader loader = null;
try {
// create a URLClassLoader
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File(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() + "servlet" + File.separator;
// the code for forming the URL is taken from the addRepository
// method in
// org.apache.catalina.loader.StandardClassLoader class.
urls[0] = new URL(null, repository, streamHandler);
loader = new URLClassLoader(urls);
} catch (IOException e) {
System.out.println(e.toString());
}
@SuppressWarnings("rawtypes")
Class myClass = null;
try {
myClass = loader.loadClass(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());
}

About a Servlet Lifecycle,

Below is a sample servlet,

import java.io.*;
import javax.servlet.*;
import java.io.PrintWriter;

//javac -cp /home/luhuang/workspace/Duke/lib/servlet-api-2.2.jar HelloWorld.java
public class HelloWorld implements Servlet {
	public void init(ServletConfig config) throws ServletException {
	}

	public void service(ServletRequest request, ServletResponse response)
			throws ServletException, IOException {
		PrintWriter out = response.getWriter();
		out.println("<html>");
		out.println("<body>");
		out.println("<h1>Hello Servlet - Greeting from Duke</h1>");
		out.println("</body>");
		out.println("</html>");
	}

	public void destroy() {
	}

	public String getServletInfo() {
		return null;
	}

	public ServletConfig getServletConfig() {
		return null;
	}

}
You just need to implement its service() method and no need to bother about other methods.
Here is the snap. 🙂
servlet1