The Artima Developer Community
Sponsored Link

Chapter 8 of Inside the Java Virtual Machine
The Linking Model
by Bill Venners

<<  Page 16 of 20  >>

Advertisement

Using a 1.1 User-Defined Class Loader

Prior to 1.2, the loadClass() method of java.lang.ClassLoader was abstract. To create your own user-defined class loader, you subclassed ClassLoader and implemented loadClass(). In 1.2, a concrete implementation of loadClass() was included in ClassLoader. This concrete loadClass() supports the parent-delegation model introduced in 1.2, and in general makes it easier and less error prone to create a user-defined class loader. To create a user-defined class loader in 1.2, you can subclass ClassLoader< and, rather than override loadClass(), you can override findClass() -- a method with a much simpler contract than loadClass(). This approach to creating a user- defined class loader will be described later in this chapter.

To give you some historical perspective of how class loaders changed between 1.1 and 1.2, consider this implementation of GreeterClassLoader, written for 1.1 and included in the first edition of this book:

// On CD-ROM in file
// linking/ex6/COM/artima/greeter/GreeterClassLoader.java
package COM.artima.greeter;

import java.io.*;
import java.util.Hashtable;

public class GreeterClassLoader extends ClassLoader {

    // basePath gives the path to which this class
    // loader appends "/.class" to get the
    // full path name of the class file to load
    private String basePath;

    public GreeterClassLoader(String basePath) {

        this.basePath = basePath;
    }

    public synchronized Class loadClass(String className,
        boolean resolveIt) throws ClassNotFoundException {

        Class result;
        byte classData[];

        // Check the loaded class cache
        result = findLoadedClass(className);
        if (result != null) {
            // Return a cached class
            return result;
        }

        // Check with the primordial class loader
        try {
            result = super.findSystemClass(className);
            // Return a system class
            return result;
        }
        catch (ClassNotFoundException e) {
        }

        // Don't attempt to load a system file except through
        // the primordial class loader
        if (className.startsWith("java.")) {
            throw new ClassNotFoundException();
        }

        // Try to load it from the basePath directory.
        classData = getTypeFromBasePath(className);
        if (classData == null) {
            System.out.println("GCL - Can't load class: "
                + className);
            throw new ClassNotFoundException();
        }

        // Parse it
        result = defineClass(className, classData, 0,
            classData.length);
        if (result == null) {
            System.out.println("GCL - Class format error: "
                + className);
            throw new ClassFormatError();
        }

        if (resolveIt) {
            resolveClass(result);
        }

        // Return class from basePath directory
        return result;
    }

    private byte[] getTypeFromBasePath(String typeName) {

        FileInputStream fis;
        String fileName = basePath + File.separatorChar
            + typeName.replace('.', File.separatorChar)
            + ".class";

        try {
            fis = new FileInputStream(fileName);
        }
        catch (FileNotFoundException e) {
            return null;
        }

        BufferedInputStream bis = new BufferedInputStream(fis);

        ByteArrayOutputStream out = new ByteArrayOutputStream();

        try {
            int c = bis.read();
            while (c != -1) {
                out.write(c);
                c = bis.read();
            }
        }
        catch (IOException e) {
            return null;
        }

        return out.toByteArray();
    }
}

The 1.1 GreeterClassLoader declares one instance variable, basePath. This variable, a String, is used to store the directory path (passed to GreetingClassLoader's constructor) in which the loadClass() method should look for the class file of the type it has been requested to load.

The loadClass() method begins by checking to see if the requested type has already been loaded by this class loader. It does this by invoking findLoadedClass(), an instance method in ClassLoader, passing in the fully qualified name of the requested type as a parameter. If this class loader has already been marked as an initiating class loader of a type with the requested fully qualified name, findLoadedClass() will return the Class instance representing the type:

// Check the loaded class cache
result = findLoadedClass(className);
if (result != null) {
    // Return a cached class
    return result;
}

As mentioned earlier in this chapter, the virtual machine maintains a list of type names that have already been requested of each class loader. These lists, which include all the types for which each class loader has been marked as an initiating loader, represent the sets of unique names that currently populate each class loader's namespace. When loading classes in Step 1a of the process of resolving CONSTANT_Class_info entries (described earlier in this chapter), the virtual machine always checks its internal list before automatically invoking loadClass(). As a result, the virtual machine will never automatically invoke loadClass() on a user-defined class loader with the name of a type already loaded by that user-defined class loader. Nevertheless, the GreeterClassLoader invokes findLoadedClass()<<> to check the requested class against the list of the names of the types it has already loaded. Why? Because even though the virtual machine will never ask a user-defined class loader to load the same type twice, the application just might.

As an example, imagine the Greet application were invoked with this command line:

java Greet greeters Hello Hello Hello Hello Hello
Given this command line, the Greet application would invoke loadClass() with the name Hello five times on the same GreeterClassLoader object. The first time, the GreeterClassLoader would load the class. The next four times, however, the GreeterClassLoader would simply get the Class instance for Hello by calling findLoadedClass() and return that. It would only load class Hello once.

If the loadClass() method determines that the requested type has not been loaded into its name space, it next passes the name of the requested type to findSystemClass():

// Check with the primordial class loader
try {
    result = super.findSystemClass(className);
    // Return a system class
    return result;
}
catch (ClassNotFoundException e) {
}

When the findSystemClass() method is invoked in a 1.1 virtual machine, the primordial class loader attempts to load the type. In 1.2, the system class loader attempts to load the type. If the load is successful, findSystemClass() returns the Class instance representing the type, and loadClass() returns that same Class instance.

If the primordial (in 1.1) or system (in 1.2 ) class loader is unable to load the type, findSystemClass() throws ClassNotFoundError. In this case, the loadClass() method next checks to make sure the requested class is not part of the java package:

// Don't attempt to load a system file except through
// the primordial class loader
if (className.startsWith("java.")) {
    throw new ClassNotFoundException();
}

This check prevents members of the standard java packages (java.lang, java.io, etc.) from being loaded by anything but the bootstrap class loader. As mentioned in Chapter 3, "Security," two types that declare themselves to be part of the same named package are only granted access to each other's package-visible members if they belong to the same runtime package (if they were loaded by the same class loader). But the notion of a "runtime package" and its affect on accessibility was first introduced in the second edition of the Java virtual machine specification. Thus, early versions of class loaders had to explicitly prevent user-defined class loaders from attempting to load types that declare themselves to be part of the Java API (or any other "restricted" packages) but that couldn't be loaded by the bootstrap class loader.

If the type name doesn't begin with "java.", the loadClass() method next invokes getTypeFromBasePath(), which attempts to import the binary data in the user- defined class loader's custom way:

// Try to load it from the basePath directory.
classData = getTypeFromBasePath(className);
if (classData == null) {
    throw new ClassNotFoundException();
}

The getTypeFromBasePath() method looks for a file with the type name plus a ".class" extension in the base directory passed to the GreeterClassLoader's constructor. If the getTypeFromBasePath() method is unable to find the file, it returns a null result and the loadClass() method throws ClassNotFoundException. Otherwise, loadClass() invokes defineClass(), passing the byte array returned by getTypeFromBasePath():

// Parse it
result = defineClass(className, classData, 0,
    classData.length);
if (result == null) {
    System.out.println("GCL - Class format error: "
        + className);
    throw new ClassFormatError();
}

The defineClass() method completes the loading process: it parses the binary data into internal data structures and creates a Class instance. The defineClass() method does not link and initialize the type. (As mentioned earlier in this chapter, the defineClass() method also makes sure all the type's supertypes are loaded. It does this by invoking loadClass() on this user-defined class loader for each direct superclass and superinterface, and recursively applies the resolution process on all supertypes in the hierarchy.)

If defineClass() is successful, the loadClass() method checks to see if resolve were set to true. If so, it invokes resolveClass(), passing the Class instance returned by defineClass(). The resolveClass() method links the class. , it Finally, loadClass() returns the newly created Class instance:

if (resolveIt) {
    resolveClass(result);
}

// Return class from basePath directory
return result;

<<  Page 16 of 20  >>


Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use