How Servlet Containers Work
by Budi Kurniawan05/14/2003
Editor's Note: This article and the previous one in this series, "How Web Servers Work," are excerpts from the book How Tomcat Works, a tutorial on the internal workings of Tomcat. If you have not done so, please read the previous article first; it gives you some useful background information. In this article, you'll learn how to build two servlet containers. The applications accompanying this article can be downloaded. If you are interested, other parts of the book are available for download for a limited period of time from the author's web site.
This article explains how a simple servlet container works. There are two servlet container applications presented; the first one is made as simple as possible and the second is a refinement of the first. The sole reason I do not try to make the first container perfect is to keep it simple. More sophisticated servlet containers, including Tomcat 4 and 5, are discussed in other chapters of How Tomcat Works.
Both servlet containers can process simple servlets, as well as static
resources. You can use PrimitiveServlet, located in the
webroot/ directory, to test this container. More complex servlets
are beyond the capability of these containers, but you can learn how to build
more sophisticated servlet containers in the How Tomcat Works book.
The classes for both applications are part of the ex02.pyrmont
package. To understand how the applications work, you need to be familiar with
the javax.servlet.Servlet interface. To refresh your memory, the
first section of this article discusses this interface. After that, you'll
learn what a servlet container has to do to serve a servlet.
The javax.servlet.Servlet Interface
Servlet programming is made possible through the classes and interfaces of
two packages: javax.servlet and javax.servlet.http.
Of these classes and interfaces, the javax.servlet.Servlet
interface is the most important interface. All servlets must implement this
interface or extend a class that does.
The Servlet interface has five methods, whose signatures are as
follows:
public void init(ServletConfig config) throws ServletExceptionpublic void service(ServletRequest request, ServletResponse response) throws ServletException, java.io.IOExceptionpublic void destroy()public ServletConfig getServletConfig()public java.lang.String getServletInfo()
|
Related Reading
Java Web Services in a Nutshell |
The init, service, and destroy
methods are the servlet's lifecycle methods. The init method is
called once by the servlet container after the servlet class has been
instantiated to indicate to the servlet that it being placed into service. The
init method must complete successfully before the servlet can
receive any requests. A servlet programmer can override this method to write
initialization code that needs to run only once, such as loading a database
driver, initializing values, and so on. In other cases, this method is normally
left blank.
The service method is then called by the servlet container to
allow the servlet to respond to a request. The servlet container passes a
javax.servlet.ServletRequest object and a
javax.servlet.ServletResponse object. The
ServletRequest object contains the client's HTTP request
information and the ServletResponse encapsulates the servlet's
response. These two objects enable you to write custom code that determines how
the servlet services the client request.
The servlet container calls the destroy method before removing
a servlet instance from service. This normally happens when the servlet
container is shut down or when the servlet container needs some free memory.
This method is called only after all threads within the servlet's
service method have exited or after a timeout period has passed.
After the servlet container calls destroy, it will not call the
service method again on this servlet. The destroy
method gives the servlet an opportunity to clean up any resources that are
being held (for example, memory, file handles, and threads) and make sure that
any persistent state is synchronized with the servlet's current state in
memory.
Listing 2.1 contains the code for a servlet named
PrimitiveServlet, a very simple servlet that you can use to test
the servlet container applications in this article. The
PrimitiveServlet class implements
javax.servlet.Servlet (as all servlets must) and provides
implementations for all five servlet methods. What it does is very simple: each
time any of the init, service, or destroy methods is called, the servlet writes
the method's name to the console. The code in the service method also obtains
the java.io.PrintWriter object from the ServletResponse object and sends
strings to the browser.
Listing 2.1. PrimitiveServlet.java
import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
public class PrimitiveServlet implements Servlet {
public void init(ServletConfig config) throws ServletException {
System.out.println("init");
}
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
System.out.println("from service");
PrintWriter out = response.getWriter();
out.println("Hello. Roses are red.");
out.print("Violets are blue.");
}
public void destroy() {
System.out.println("destroy");
}
public String getServletInfo() {
return null;
}
public ServletConfig getServletConfig() {
return null;
}
}
Application 1
Now, let's look at servlet programming from a servlet container's perspective. In a nutshell, a fully functional servlet container does the following for each HTTP request for a servlet:
- When the servlet is called for the first time, load the servlet class and
call its
initmethod (once only). - For each request, construct an instance of
javax.servlet.ServletRequestand an instance ofjavax.servlet.ServletResponse. - Invoke the servlet's
servicemethod, passing theServletRequestandServletResponseobjects. - When the servlet class is shut down, call the servlet's
destroymethod and unload the servlet class.
What happens in a servlet container is much more complex than that. However,
this simple servlet container is not fully functional. Therefore, it can only
run very simple servlets and does not call the servlet's init and
destroy methods. Instead, it does the following:
- Wait for HTTP request.
- Construct a
ServletRequestobject and aServletResponseobject. - If the request is for a static resource, invoke the
processmethod of theStaticResourceProcessorinstance, passing theServletRequestandServletResponseobjects. - If the request is for a servlet, load the servlet class and invoke its
servicemethod, passing theServletRequestandServletResponseobjects. Note that in this servlet container, the servlet class is loaded every time the servlet is requested.
In the first application, the servlet container consists of six classes:
HttpServer1RequestResponseStaticResourceProcessorServletProcessor1Constants
Just like the application in the previous article, the entry point of this
application (the static main method) is in the
HttpServer class. This method creates an instance of
HttpServer and calls its await method. As the name
implies, this method waits for HTTP requests, creates a Request
object and a Response object, and dispatches either to a
StaticResourceProcessor instance or a
ServletProcessor instance, depending on whether the request is for
a static resource or a servlet.
The Constants class contains the static final
WEB_ROOT that is referenced from other classes.
WEB_ROOT indicates the location of PrimitiveServlet
and the static resources this container can serve.
The HttpServer1 instance keeps waiting for HTTP requests until
it receives a shutdown command. Issue a shutdown command the same way as you
did it in the previous article.
Each of the classes in the application is discussed in the following sections.