Chapter8 Loader

来源:互联网 发布:淘宝商品id批量导出 编辑:程序博客网 时间:2024/05/22 01:30

 

Chapter 8: Loader 

Overview 

You have seen a simple loader implementation in the previous chapters, which was 

used for loading servlet classes. This chapter explains the standard web application 

loader, or loader for short, in Catalina. A servlet container needs a customized 

loader and cannot simply use the system's class loader because it should not trust 

the servlets it is running. If it were to load all servlets and other classes needed by 

the servlets using the system's class loader, as we did in the previous chapters, then 

a servlet would be able to access any class and library included in the CLASSPATH 

environment variable of the running Java Virtual Machine (JVM), This would be a 

breach of security. A servlet is only allowed to load classes in the WEB-INF/classes 

directory and its subdirectories and from the libraries deployed into the 

WEB-INF/lib directory. That's why a servlet container requires a loader of its own. 

Each web application (context) in a servlet container has its own loader. A loader 

employs a class loader that applies certain rules to loading classes. In Catalina, a 

loader is represented by the org.apache.catalina.Loader interface. 

Another reason why Tomcat needs its own loader is to support automatic reloading 

whenever a class in the WEB-INF/classes or WEB-INF/lib directories has been 

modified. The class loader in the Tomcat loader implementation uses a separate 

thread that keeps checking the time stamps of the servlet and supporting class files. 

To support automatic reloading, a class loader must implement the 

org.apache.catalina.loader.Reloader interface. 

The first section of this chapter briefly reviews the class loading mechanism in Java. 

Next, it covers the Loader interface, which all loaders must implement, followed by 

the Reloader interface. After a look at the implementation of the loader and class 

loader, the chapter presents an application that demonstrates how to use a Tomcat's 

loader. 

Two terms are used extensively in this chapter: repository and resources. A 

repository is a place that will be searched by the class loader. The term resources 

refers to a DirContext object in a class loader whose document base points to the 

context's document base. 

Java Class Loader 

Every time you create an instance of a Java class, the class must first be loaded into 

memory. The JVM uses a class loader to load classes. The class loader normally 

searches some core Java libraries and all directories included in the CLASSPATH environment variable. If it does not find the required class, it throws a 

java.lang.ClassNotFoundException. 

Starting from J2SE 1.2, the JVM employs three class loaders: bootstrap class loader, 

extension class loader, and system class loader. Each of the three class loaders has a 

parent-child relationship with each other, in which the bootstrap class loader sits at 

the top of the hierarchy and the system class loader at the bottom. 

The bootstrap class loader is used to bootstrap the JVM. It starts working whenever 

you call the java.exe program. As such, it must be implemented using the native code 

because it is used to load the classes required for the JVM to function. Also, it is 

responsible for loading all the core Java classes, such as those in java.lang and 

java.io packages. The bootstrap class loader searches the core libraries such as rt.jar, 

i18n.jar, etc. Which libraries are searched depends on the version of the JVM and 

the operating system. 

The extension class loader is responsible for loading classes in a standard extension 

directory. This is to make the programmer's life easier because they can just copy 

JAR files into this extension directory and the jar files will be searched automatically. 

The extension library differs from one vendor to another. Sun's JVM's standard 

extension directory is /jdk/jre/lib/ext. 

The system class loader is the default class loader and searches the directories and 

JAR files specified in the CLASSPATH environment variable. 

So, which class loader does the JVM use? The answer lies in the delegation model, 

which is there for security reasons. Every time a class needs to be loaded, the 

system class loader is first called. However, it does not load the class right away. 

Instead, it delegates the task to its parent, the extension class loader. The extension 

class loader also delegates it to its parent, the bootstrap class loader. Therefore, the 

bootstrap class loader is always given the first chance to load a class. If the bootstrap 

class loader can't find the class needed, the extension class loader will try to load the 

class. If the extension class loader also fails, the system class loader will perform the 

task. If the system class loader can't find the class, a 

java.lang.ClassNotFoundException is thrown. Why the round trip? 

The delegation model is very important for security. As you know, you can use the 

security manager to restrict access to a certain directory. Now, someone with 

malicious intents can write a class called java.lang.Object that can be used to access 

any directory in the hard disk. Because the JVM trusts the java.lang.Object class, it 

will not watch its activity in this regard. As a result, if the custom java.lang.Object 

was allowed to be loaded, the security manager would be easily paralyzed. 

Fortunately, this will not happen because of the delegation model. Here is how it 

works. When the custom java.lang.Object class is called somewhere in the program, the 

system class loader delegates the request to the extension class loader, which 

delegates to the bootstrap class loader. The bootstrap class loader searches its core 

libraries, and finds the standard java.lang.Object and instantiates it. As a result, the 

custom java.lang.Object will never be loaded. 

The great thing about the class loading mechanism in Java is that you can write your 

own class loader by extending the abstract java.lang.ClassLoader class. The reasons 

why Tomcat needs a custom class loader include the following: 

  To specify certain rules in loading classes. 

  To cache the previously loaded classes. 

  To pre-load classes so they are ready to use. 

The Loader Interface 

There are rules in loading servlet and other classes in a web application. For example, 

a servlet in an application can use classes deployed to the WEB-INF/classes 

directory and any sub-directory under it. However, servlets do not have access to 

other classes, even though those classes are included in the CLASSPATH of the 

JVM running Tomcat. Also, a servlet can only access libraries deployed under the 

WEB-INF/lib directory and not other directories. 

A Tomcat loader represents a web application loader rather than a class loader. A 

loader must implement the org.apache.catalina.Loader interface. The loader 

implementation uses a custom class loader represented by the 

org.apache.catalina.loader.WebappClassLoader class. You can obtain the 

ClassLoader inside a web loader using the Loader interface's getClassLoader 

method. 

Among others, the Loader interface defines methods to work with a collection of 

repositories. The WEB-INF/classes and WEB-INF/lib of a web application are 

directories to be added as repositories. The Loader interface's addRepository 

method is used to add a repository and its findRepositories method returns an array 

of all repositories. 

A Tomcat loader implementation is usually associated with a context, and the 

getContainer and setContainer methods of the Loader interface are used for building 

this association. A loader can also supports reloading, if one or more classes in a 

context have been modified. This way, a servlet programmer can recompile a servlet 

or a supporting class and the new class will be reloaded without restarting Tomcat. 

For the reloading purpose, the Loader interface has the modified method. In a loader 

implementation, the modified method must return true if one or more classes in its 

repositories have been modified, and therefore reloading is required. A loader does not do the reloading itself, however. Instead, it calls the Context interface's reload 

method. Two other methods, setReloadable and getReloadable, are used to 

determine if reloading is enabled in the Loader. By default, in the standard 

implementation of Context (the org.apache.catalina.core.StandardContext class, 

which is discussed in Chapter 12), reloading is not enabled. Therefore, to enable 

reloading of a context, you need to add a Context element for that context in your 

server.xml file, such as the following: 

<Context path="/myApp" docBase="myApp" debug="0" reloadable="true"/> 

Also, a Loader implementation can be told whether or not to delegate to a parent 

class loader. For this purpose, the Loader interface provides the getDelegate and 

setDelegate methods. 

The Loader interface is given in Listing 8.1. 

Listing 8.1: The Loader interface   

 

 

 

 Catalina provides the org.apache.catalina.loader.WebappLoader as an 

implementation of the Loader interface. For its class loader, the WebappLoader 

object contains an instance of the org.apache.catalina.loader.WebappClassLoader 

class, which extends the java.net.URLClassLoader class. 

  Note   Whenever the container associated with a loader needs a servlet class, i.e. 

when its invoke method is called, the container first calls the loader's 

getClassLoader method to obtain the class loader. The container then calls the 

loadClass method of the class loader to load the servlet class. More details on 

this can be found in Chapter 11, "StandardWrapper". 

The class diagram for the Loader interface and its implementation is given in Figure 

8.1. 

 

 

 

 

The most important method of the Reloader interface is modified, which returns true 

if one of the servlet or supporting classes in a web application has been modified. The addRepository method is used to add a repository and the findRepositories method 

returns a String array of all repositories in the class loader implementing Reloader. 

 

The WebappLoader Class 

The org.apache.catalina.loader.WebappLoader class is the implementation of the 

Loader interface and represents a web application loader responsible for loading 

classes for a web application. WebappLoader creates an instance of the 

org.apache.catalina.loader.WebappClassLoader class as its class loader. Like other 

Catalina components, WebappLoader implements org.apache.catalina.Lifecycle and 

it is started and stopped by the associated container. The WebappLoader class also 

implements the java.lang.Runnable interface so that it can dedicate a thread for 

repeatedly calling the modified method of its class loader. If the modified method 

returns true, the WebappLoader instance notifies its associated container (in this 

case a context). The class reloading itself is performed by the Context, not by the 

WebappLoader. How the Context does this is discussed in Chapter 12, 

"StandardContext". 

Important tasks are performed when the WebappLoader class's start method is 

called: 

  Creating a class loader 

  Setting repositories 

  Setting the class path 

  Setting permissions 

  Starting a new thread for auto-reload. 

Each of these tasks is discussed in the following sub-sections. 

Creating A Class Loader 

For loading classes, a WebappLoader instance employs an internal class loader. You 

may recall from the discussion of the Loader interface that this interface provides the 

getClassLoader method but there is no setClassLoader. Therefore, you cannot 

instantiate a class loader and pass it to the WebappLoader. Does it mean that the 

WebappLoader does not have the flexibility to work with a non-default class loader? 

The answer is no. The WebappLoader provides the getLoaderClass and 

setLoaderClass methods to obtain and change the value of its private variable 

loaderClass. This variable is a String representing the name of the class of the class 

loader. By default, the value of loaderClass is org.apache.catalina.loader.WebappClassLoader. If you wish, you can create your 

own class loader that extends WebappClassLoader and call the setLoaderClass to 

force your WebappLoader to use your custom class loader. Otherwise, when it is 

started, the WebappLoader will create an instance of WebappClassLoader by calling 

its private createClassLoader method. This method is given in Listing 8.3. 

Listing 8.3: The createClassLoader method   

 

 

 

 

It is possible to use a different class loader other than an instance of 

WebappClassLoader. Note, however, that the createClassLoader method returns a 

WebappClassLoader. Therefore, if your custom class loader does not extend 

WebappClassLoader, this method will throw an exception. 

Setting Repositories 

The WebappLoader class's start method calls the setRepositories method to add 

repositories to its class loader. The WEB-INF/classes directory is passed to the 

class loader's addRepository method and the WEB-INF/lib directory is passed to the class loader's setJarPath method. This way, the class loader will be able to load 

classes in the WEB-INF/classes directory and from any library deployed to the 

WEB-INF/lib directory. 

Setting the Class Path 

This task is performed by the start method by calling the setClassPath method. The 

setClassPath method assigns to an attribute in the servlet context a string 

containing the class path information for the Jasper JSP compiler. It will not be 

discussed here. 

Setting Permissions 

If the security manager is used when running Tomcat, the setPermissions method 

adds the permissions to the class loader to access necessary directories, such as 

WEB-INF/classes and WEB-INF/lib. If no security manager is used, this method 

returns right away. 

Starting a New Thread for Auto-Reload 

WebappLoader supports auto-reload. If a class in the WEB-INF/classes or 

WEB-INF/lib directories is re-compiled, the class must be reloaded automatically 

without restarting Tomcat. For this purpose, WebappLoader has a thread that 

continuously checks the date stamp of each resource every x seconds. The x here is 

defined by the value of the checkInterval variable. By default its value is 15, meaning 

that a check for auto-reload is performed every 15 seconds. The getCheckInterval 

and setCheckInterval methods are used to access this variable. 

In Tomcat 4 WebappLoader implements the java.lang.Runnable interface to support 

auto-reload. The implementation of the run method in WebappLoader is given in 

Listing 8.3. 

Listing 8.3: The run method   

 

 

 

 

  Note   In Tomcat 5 the task of checking for modified classes is performed by the 

backgroundProcess method of the org.apache.catalina.core.StandardContext 

object. This method is called periodically by a dedicated thread in the 

org.apache.catalina.core.ContainerBase class, the parent class of 

StandardContext. Check the ContainerBase class's 

ContainerBackgroundProcessor inner class that implements Runnable. 

At its core, the run method in Listing 8.3 contains a while loop that will run until the 

started variable (used to indicate that the WebappLoader has been started) is set to 

false. The while loop does the following: 

  Sleep for the period specified by the checkInterval variable. 

  Check if any class it loaded has been modified by calling the modified method 

of the WebappLoader instance's class loader. If not, continue. 

  If a class has been modified, call the notifyContext private method to ask the 

Context associated with this WebappLoader to reload. 

The notifyContext method is given in Listing 8.4. 

Listing 8.4: The notifyContext method   

 

 

 

 

The notifyContext method does not call the Context interface's reload method 

directly. Instead, it instantiates the inner class WebappContextNotifier and passes 

the thread object and calls its start method. This way, the execution of reload will be 

performed by a different thread. The WebappContextNotifier class is given in Listing 

8.5. 

Listing 8.5: The WebappContextNotifier inner class   

 

 

When an instance of WebappContextNotifier is passed to a Thread and the Thread 

object's start method is invoked, the run method of the WebappContextNotifier 

instance will be executed. In turn, the run method calls the reload method of the 

Context interface. You can see how the reload method is implemented in the 

org.apache.catalina.core.StandardContext class in Chapter 12. 

 

The WebappClassLoader Class 

The org.apache.catalina.loader.WebappClassLoader class represents the class 

loader responsible for loading the classes used in a web application. 

WebappClassLoader extends the java.net.URLClassLoader class, the class we used 

for loading Java classes in the applications in the previous chapters. 

WebappClassLoader was designed for optimization and security in mind. For 

example, it caches the previously loaded classes to enhance performance. It also 

caches the names of classes it has failed to find, so that the next time the same 

classes are requested to be loaded, the class loader can throw the 

ClassNotFoundException without first trying to find them. WebappClassLoader 

searches for classes in the list of repositories as well as the specified JAR files. With regard to security, the WebappClassLoader will not allow certain classes to be 

loaded. These classes are stored in a String array triggers and currently has one 

member: 

 

Also, you are not allowed to load classes belonging to these packages and 

subpackages under them, without first delegating to the system class loader: 

 

Let's now look at how this class performs caching and class loading in the following 

sub-sections. 

Caching 

For better performance, classes that are loaded are cached so that the next time a 

class is required, it can be taken from the cache. Caching can be done locally, 

meaning that the cache is managed by the WebappClassLoader instance. In addition, 

the java.lang.ClassLoader maintains a Vector of previously loaded classes to prevent 

those classes from being garbage-collected. In this case, caching is managed by the 

super class. 

Each class (either deployed as a class file under WEB-INF/classes or from a JAR file) 

that may be loaded by WebappClassLoader is referred to as a resource. A resource 

is represented by the org.apache.catalina.loader.ResourceEntry class. A 

ResourceEntry instance holds the byte array representation of the class, the last 

modified date, the Manifest (if the resource is from a JAR file), etc. 

The ResourceEntry class is given in Listing 8.6. 

Listing 8.6: The ResourceEntry class.   

 

 

 

 

All cached resources are stored in a HashMap called resourceEntries. The keys are 

the resource names. All resources that were not found are stored in another 

HashMap called notFoundResources. 

Loading Classes 

When loading a class, the WebappClassLoader class applies these rules: 

  All previously loaded classes are cached, so first check the local cache. 

  If not found in the local cache, check in the cache, i.e. by calling the 

findLoadedClass of the java.lang.ClassLoader class. 

  If not found in both caches, use the system's class loader to prevent the web 

application from overriding J2EE class. 

  If SecurityManager is used, check if the class is allowed to be loaded. If the 

class is not allowed, throw a ClassNotFoundException. 

  If the delegate flag is on or if the class to be loaded belongs to the package 

name in the package trigger, use the parent class loader to load the class. If 

the parent class loader is null, use the system class loader. 

  Load the class from the current repositories. 

  If the class is not found in the current repositories, and if the delegate flag is 

not on, use the parent class loader. If the parent class loader is null, use the 

system class loader. 

  If the class is still not found, throw a ClassNotFoundException. The Application 

The application accompanying this chapter demonstrates how to use a 

WebappLoader instance associated with a context. The standard implementation of 

Context is the org.apache.catalina.core.StandardContext class, therefore this 

application instantiates the StandardContext class. However, the discussion of 

StandardContext itself will be deferred until Chapter 12. You do not need to 

understand the details of this class at this stage. All you need to know about 

StandardContext is that it works with a listener that listens to events it fires, such as 

the START_EVENT and STOP_EVENT. The listener must implement the 

org.apache.catalina.lifecycle.LifecycleListener interface and call the 

StandardContext class's setConfigured method. For this application, the listener is 

represented by the ex08.pyrmont.core.SimpleContextConfig class, which is given in 

Listing 8.6. 

Listing 8.6: The SimpleContextConfig class   

 

 

 

 

All you need to do is instantiate both StandardContext and SimpleContextConfig and 

then register the latter with StandardContext by calling the addLifecycleListener 

method of the org.apache.catalina.Lifecycle interface. This interface was discussed 

in detail in Chapter 6, "Lifecycles". 

In addition, the application retains the following classes from the previous chapter: 

SimplePipeline, SimpleWrapper, and SimpleWrapperValve. 

The application can be tested using both PrimitiveServlet and ModernServlet, but 

this time the use of StandardContext dictates the servlets be stored under the WEB-INF/classes of an application directory. The application directory is called 

myApp and should have been created when you deployed the downloadable ZIP file 

the first time. To tell the StandardContext instance where to find the application 

directory, you set a system property called catalina.base with the value of the 

user.dir property, as follows. 

System.setProperty("catalina.base", System.getProperty("user.dir")); 

 

In fact, that is the first line in the main method of the Bootstrap class. Afterwards, 

the main method instantiates the default connector. 

    

It then creates two wrappers for the two servlets and initialize them, just like the 

application in the previous chapter. 

   

It then creates an instance of StandardContext and set the path as well as the 

document base of the context. 

  

This is equivalent to having the following element in the server.xml file. 

<Context path="/myApp" docBase="myApp"/> 

Then, the two wrappers are added to the context, and it adds the mapping for both 

so that the context can locate the wrappers. 

   

The next step is to instantiate a listener and register it with the context.

     

Next, it instantiates the WebappLoader and associates it with the context. 

    Loader loader = new WebappLoader(); 

    context.setLoader(loader); 

Afterwards, the context is associated with the default connector, and the 

connector's initialize and start methods are called, followed by the context's start 

method. This puts the servlet container into service. 

    connector.setContainer(context); 

    try { 

      connector.initialize(); 

      ((Lifecycle) connector).start(); 

 

      ((Lifecycle) context).start(); 

The next lines simply display the value of the resources' docBase and all the 

repositories in the class loader. 

      // now we want to know some details about WebappLoader 

     WebappClassLoader classLoader = (WebappClassLoader) 

        loader.getClassLoader(); 

      System.out.println("Resources' docBase: " + 

       ((ProxyDirContext)classLoader.getResources ()).getDocBase()); 

      String[] repositories = classLoader.findRepositories(); 

      for (int i=0; i<repositorles.length; i++) { 

        System.out.println("  repository: " + repositories[i]); 

      } 

These lines will make docBase and the list of repositories displayed when you run the 

application. 

Resources' docBase: C:/HowTomcatWorks/myApp 

   repository: /WEB-INF/classes/ 

The value of docBase may be different on your machine, depending where you install 

the application. 

Finally, the application waits until the user presses Enter on the console to stop the 

application. 

      // make the application wait until we press a key.       System.in.read(); 

      ((Lifecycle) context).stop(); 

Running the Application 

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

java -classpath ./lib/servlet.jar;./lib/commons-collections.jar;./ 

ex08.pyrmont.startup.Bootstrap 

In Linux, you use a colon to separate two libraries. 

java -classpath ./lib/servlet.jar:./lib/commons-collections.jar:./ 

ex08.pyrmont.startup.Bootstrap 

To invoke PrimitiveServlet servlet, you use the following URL in your browser. 

http://localhost:8080/Primitive   

To invoke ModernServlet, you use the following URL. 

http://localhost:8080/Modern   

Summary 

The web application loader, or simply a loader, is a most important component in 

Catalina. A loader is responsible for loading classes and therefore employs an 

internal class loader. This internal class loader is a customized class that is used by 

Tomcat to apply certain rules to the class loading within an application context. Also, 

the customized class loader supports caching and can check whether or not one or 

more classes have been modified.