Class Loading in the Servlet Container

 < Free Open Study > 

We have a choice of how we package and deploy the classes that comprise our web applications. We could put everything in a single WAR. Alternatively, we could decide that some classes or groups of classes are reusable between applications, so it makes more sense to create a separate JAR that can be deployed alongside any web application that requires it. We could even have some classes (such as JDBC drivers or XML parsers) that we want to make available to all applications. Then, when we want to upgrade the classes they only need to be replaced once.

Understanding exactly where to deploy classes is an often-misunderstood topic that even experienced developers run into trouble with. There are three important considerations to make:

  • Accessibility

    Can an application and the container find the required classes?

  • Reusability

    Should we deploy the same set of classes separately for each application that uses them?

  • Separation

    What conflicts might occur when several applications use the static values of common classes?

Deployment Organization

There are five locations where we could conceivably deploy classes within Tomcat:

  • The %CATALINA_HOME%/common/lib directory

  • The %CATALINA_HOME%/lib directory

  • The %CATALINA_HOME%/server/lib directory

  • The WEB-INF/classes directory of a web application

  • The WEB-INF/lib directory of a web application

In general, application-independent classes should be packaged in a JAR and placed in the /lib or /common/lib directory of Tomcat. Application classes should be placed (within their package structure) in the WEB-INF/classes directory of the application context, or in a JAR within the WEB-INF/lib directory.

It would be useful if the container could discover the dependencies of a deployed web application so it could ensure that those dependencies are satisfied prior to attempting to run the application. The Servlet 2.3 specification suggests a mechanism for this based on specifying dependencies to common JARs in the MANIFEST.MF file of a WAR. However, at the time of writing, Tomcat does not yet support this feature.

Locating Classes and Other Resources

To investigate the problems associated with class loading it would be useful to have a simple way of finding out the location from which the servlet container will load any given class; and in the case of a class deployed in multiple locations, to tell us which version of the class will take precedence.

The following servlet will allow us to do just that. It could be deployed as part of any web application to aid in debugging:

     package classloading;     import*;     import*;     import javax.servlet.*;     import javax.servlet.http.*;     public class ResourceLocatorServlet extends HttpServlet { 

The doGet() method takes a resource name (which in most cases will be the name of a class) as a parameter:

      public void doGet (HttpServletRequest req, HttpServletResponse res)        throws ServletException, IOException { 

We try to find the resource using the current classloader:

         String resource = req.getParameter("resource");         URL resourceURL = null;         String resourceURLString = null; 

The next clause is key. It asks the servlet to get hold of the resource (the supplied class name) using its classloader (or another classloader in the chain). We receive a URL that indicates where the given class would be loaded from if it were to be instantiated within our application:

         if (resource != null) {           resourceURL = this.getClass().getResource(resource);   }         if (resourceURL != null) {           resourceURLString = resourceURL.toString();         }         PrintWriter writer = res.getWriter(); 

We're using the getResource() method of Class. javax.servlet.ServletContext also has a getResource() method that does not use classloaders.

Finally, we communicate to the user the location of the requested resource:

         writer.println("<html><head></head>");         writer.println("<body style=\"font-family:verdana;font-size:8pt\">");         writer.println("<h3>Resource Locator</h3>");         writer.println("<p>" + resource + "</p><p>Found at:</p>");         writer.println(resourceURLString);         writer.println("</body></html>");         writer.close();     } } 

We can also use the getResource() method to get hold of other resources, such as image or sound files from within a JAR. If you've ever written an application that loads graphics or sounds from files and you wonder why it no longer works when you package everything in a JAR file, try using getResource() to get hold of those files.

We'll deploy ResourceLocatorServlet in a web application called ClassLoading. Then we can use the servlet to find any given class. For example, we can find the location of javax.servlet.http.HttpServlet by navigating to http://localhost:8080/ClassLoading/servlet/classloading.ResourceLocatorServlet?resource=/javax/servlet/http/HttpServlet.class (notice how we must replace the package separator periods with forward slashes):

click to expand

The resulting information is very useful. It tells us the preferred location from which the class will be loaded (if the same class is deployed in more than one place).

Class Loading Precedence in Tomcat 4

We're going to use ResourceLocator to discover the class loading precedence in Tomcat 4. We want to discover what version of a class will be loaded if that class can be found in more than one location. That is, in what order do the different classloaders used by Tomcat check the different locations?

We going to need a target class for the container to locate and load (TargetClass is probably the simplest class in the book):

     package classloading;     public class TargetClass {     } 

We'll put TargetClass and a JAR (targetclass.jar) containing TargetClass into the following directories of Tomcat:

     webapps/ClassLoading/WEB-INF/classes/classloading/TargetClass.class     webapps/ClassLoading/WEB-INF/lib/targetclass.jar     server/lib\targetclass.jar     common\lib\targetclass.jar     lib/targetclass.jar 

To test the class loading precedence we'll run through a series of steps using ResourceLocatorServlet. Each time we find out from where the class would be loaded, we'll delete the class from that location before repeating the process.

Run ResourceLocatorServlet, specifying TargetClass.class as the resource to look for by navigating to http://localhost:8080/ClassLoading/servlet/classloading.ResourceLocatorServlet?resource=/classloading/TargetClass.class:

click to expand

The result tells us that the preferred location for loading classes is from the WEB-INF/classes directory of the web application. This is consistent with the Servlet 2.3 specification, which states:

start sidebar

"The web application classloader must load classes from the WEB-INF/classes directory first, ..."

end sidebar

Delete TargetClass from WEB-INF/classes and enter the same URL again. You'll find that the location corresponds with the library JAR provided in the WEB-INF/lib directory of our application.

Tomcat keeps a copy of web applications in %CATALINA_HOME%/work. You may need to delete the contents of this folder and restart Tomcat for the example to behave as expected.

This too is consistent with the Servlet 2.3 specification, which states:

start sidebar

"The web application classloader must load classes from the WEB-INF/classes directory first, and then from library JARs in the WEB-INF/lib directory."

end sidebar

Delete targetclass.jar from WEB-INF/lib. TargetClass is no longer deployed anywhere within the application context. Locate the class once more. This time it will be found in the application-independent Tomcat directory, %CATALINA_HOME%/lib. Delete the JAR from this location and relocate the class. This time it will be found in %CATALINA_HOME%/common/lib. When the JAR is removed from this location the result of ResourceLocatorServlet will be:

click to expand

This means that %CATALINA_HOME%/server/lib is never searched. It is accessed only by Tomcat.

We've established the class loading order of precedence for Tomcat as:

  • Classes deployed in the WEB-INF/classes directory of a web application

  • Classes in application-specific archives (in WEB-INF/lib)

  • Classes deployed in Tomcat's /lib directory

  • Classes deployed in Tomcat's /common/lib directory

start sidebar

The order of precedence we've established is for Tomcat 4. If you're using a different servlet container the precedence could be different. It's well worth running through these steps in your deployment environment to determine the exact order.

end sidebar

These results mean that if we try to modify our web application by creating a new version of a class, we will only see the change if we deploy the new version in the location of highest precedence. For example, if we deploy a new version of a class in an application archive, we must remove any previous version of the class from the WEB-INF/classes directory.

 < Free Open Study > 

Professional Java Servlets 2.3
Professional Java Servlets 2.3
ISBN: 186100561X
EAN: 2147483647
Year: 2006
Pages: 130 © 2008-2017.
If you may any questions please contact us: