27 June 2008

ClassLoaders and Web Applications

This post has changed to correct some errors

In the previous posting I described the class loader heirarchy, and mentioned that you can use standard Java API classes and methods to create a branching tree structure of loaders. I also touched on the fact that Java EE application servers use this to ensure that each web application gets its own private load path for classes.

However, the loading rules as I described them don't quite apply to web applications, so this article is intended to cover the differences.

I'm describing the way things work in Tomcat; other servers should be doing something similar.

Tomcat Loader Heirarchy

As I mentioned in the previous post, ordinary Java applications have three loaders at run time: the Boot loader, which is usually native, platform-specific code built into the JVM and searches the standard class libraries; the Extension class loader, which searches JARs in the $JAVA_HOME/jre/lib/ext directory; and the System class loader, which searches directories and JARs specified in the CLASSPATH. The Extension loader is the parent of the System loader, and the Boot loader is (in practice) the parent of the Extension loader. Slight correction: some JVMs combine the Boot and Extension loaders into one.

Tomcat adds two more levels to this heirarchy for web applications: at the bottom, a WebappClassLoader (one for each deployed web application) with the application's WEB-INF/classes and all the archive files in the WEB-INF/lib directory set as the search path; and above that a StandardClassLoader (one only for the whole server), with its search path set as Tomcat's lib directory and all the archive files in it.

Tomcat's system class loader is the parent of this StandardClassLoader and has its search path set to include Tomcat's bootstrap.jar file and little else.

The StandardClassLoader class is a subclass of URLClassLoader but doesn't add or override anything from that class - it's functionally identical.

The WebappClassLoader class, on the other hand, subclasses URLClassLoader but reimplements most if not all of the methods. Here's why...

Servlet API loader rules

The Servlet API specification is the root of things here; it says that the loader search algorithm for web applications should ensure that classes and JARs packaged in the WAR should be searched before any of the servlet container's library JAR files, but should not allow the application to override any of the standard Java classes.

Tomcat's WebappClassLoader achieves the first part of this (searching the WAR contents first) by not delegating searches to its parent until after it has already searched its own repositories - the opposite of the usual procedure. (I'm not clear on how it ensures that standard classes don't get overridden - the code in that area is tricky to follow.) Major correction required here: In fact the WebAppClassLoader delegates directly to the System class loader first, then searches its own repositories, then finally delegates to its parent. This ensures that JRE classes can't be overridden (they get searched first) and that repositories are searched before Tomcat's lib contents.

That covers the important differences that apply to web applications. There is one other small aspect that I'd like to mention.

Getting Resources

The Servlet API specification also mandates that web applications must be able to locate their own resources using ClassLoader.getResource(). The heirarchy as described achieves this. However the specification says nothing about the static ClassLoader.getSystemResource() method and in fact this method is of little use in web applications because the System class loader doesn't know anything about the web application's resources (as you can see from where it is in the heirarchy as described).

Labels: ,


Post a Comment

Subscribe to Post Comments [Atom]

<< Home