ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

How Servlet Containers Work
Pages: 1, 2, 3, 4

The ServletProcessor1 Class

The ServletProcessor1 class processes HTTP requests for servlets. It is surprisingly simple, consisting only of the process method. This method accepts two arguments: an instance of javax.servlet.ServletRequest and an instance of javax.servlet.ServletResponse. The process method also constructs a java.net.URLClassLoader object and uses it to load the servlet class file. Upon obtaining a Class object from the class loader, the process method creates an instance of the servlet and calls its service method.



The process method is given in Listing 2.4.

Listing 2.4. The ServletProcessor1 class' process method

public void process(Request request, Response response) {
    String uri            = request.getUri();
    String servletName    = uri.substring(uri.lastIndexOf("/") + 1);
    URLClassLoader loader = null;

    try {
        // create a URLClassLoader
        URLStreamHandler streamHandler = null;

        URL[] urls        = new URL[1];
        File classPath    = new File(Constants.WEB_ROOT);
        String repository = (new URL("file", null, 
            classPath.getCanonicalPath() + File.separator)).toString() ;
        urls[0]           = new URL(null, repository, streamHandler);
        loader            = new URLClassLoader(urls);
    }
    catch (IOException e) {
        System.out.println(e.toString() );
    }

    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());
    }
}

The process method accepts two arguments: an instance of ServletRequest and an instance of ServletResponse. From the ServletRequest, it obtains the URI by calling the getRequestUri method:

String uri = request.getUri();

Remember that the URI is in the following format:

/servlet/servletName

where servletName is the name of the servlet class.

To load the servlet class, we need to know the servlet name from the URI, which we do using the next line of the process method:

String servletName = uri.substring(uri.lastIndexOf("/") + 1);

Next, the process method loads the servlet. To do this, you need to create a class loader and tell this class loader the class' location. This servlet container directs the class loader to look in the directory pointed by Constants.WEB_ROOT. WEB_ROOT points to the webroot/ directory under the working directory.

To load a servlet, use the java.net.URLClassLoader class, which is an indirect child class of java.lang.ClassLoader. Once you have an instance of the URLClassLoader class, use its loadClass method to load a servlet class. Instantiating the URLClassLoader class is straightforward. This class has three constructors, the simplest one being:

public URLClassLoader(URL[] urls);

where urls is an array of java.net.URL objects pointing to the locations on which searches will be conducted when loading a class. Any URL that ends with a / is assumed to refer to a directory. Otherwise, the URL is assumed to refer to a .jar file, which will be downloaded and opened as needed.

In a servlet container, the location where a class loader can find servlet classes is called a repository.

In our application, there is only one location in which the class loader must look — the webroot/ directory under the working directory. Therefore, we start by creating an array of a single URL. The URL class provides several constructors, so there are many ways to construct a URL object. For this application, I used the same constructor used in another class in Tomcat. The constructor has the following signature:

public URL(URL context, String spec, URLStreamHandler hander)
    throws MalformedURLException

You can use this constructor by passing a specification for the second argument and null for both the first and the third arguments. However, there is another constructor that accepts three arguments:

public URL(String protocol, String host, String file)
    throws MalformedURLException

Thus, the compiler will not know which constructor you mean if you write the following:

new URL(null, aString, null);

You can get around this by telling the compiler the type of the third argument, like this:

URLStreamHandler streamHandler = null;
new URL(null, aString, streamHandler);

For the second argument, pass the String containing the repository (the directory where servlet classes can be found). Create it with this code:

String repository = (new URL("file", null,
    classPath.getCanonicalPath() + File.separator)).toString();

Combining everything, here is the part of the process method that constructs the right URLClassLoader instance:

// create a URLClassLoader
URLStreamHandler streamHandler = null;

URL[] urls        = new URL[1];
File classPath    = new File(Constants.WEB_ROOT);
String repository = (new URL("file", null, 
    classPath.getCanonicalPath() + File.separator)).toString() ;
urls[0]           = new URL(null, repository, streamHandler);
loader            = new URLClassLoader(urls);

The code that forms a repository comes from the createClassLoader method in org.apache.catalina.startup.ClassLoaderFactory, and the code for forming the URL is taken from the addRepository method in the org.apache.catalina.loader.StandardClassLoader class. However, you don't have to worry about these classes at this stage.

Having a class loader, you can load a servlet class using the loadClass method:

Class myClass = null;
try {
    myClass = loader.loadClass(servletName);
}
catch (ClassNotFoundException e) {
    System.out.println(e.toString());
}

Next, the process method creates an instance of the servlet class loaded, downcasts it to javax.servlet.Servlet, and invokes the servlet's service method:

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());
}

Compiling and Running the Application

To compile the application, type the following from the working directory:

javac -d . -classpath ./lib/servlet.jar src/ex02/pyrmont/*.java

To run the application on Windows, type the following command from the working directory:

java -classpath ./lib/servlet.jar;./ ex02.pyrmont.HttpServer1

in Linux, use a colon to separate between libraries.

java -classpath ./lib/servlet.jar:./ ex02.pyrmont.HttpServer1

To test the application, type the following in the URL or address box of your browser:

http://localhost:8080/index.html

or

http://localhost:8080/servlet/PrimitiveServlet

You will see the following text in your browser:

Hello. Roses are red.

Note that you can't see the second string (Violets are blue) because only the first string is flushed to the browser. The applications accompanying the later chapters of the How Tomcat Works book show you how to fix this problem.

Pages: 1, 2, 3, 4

Next Pagearrow