FileDocCategorySizeDatePackage
StandardClassLoader.javaAPI DocGlassfish v2 API34406Fri May 04 22:32:10 BST 2007org.apache.catalina.loader

StandardClassLoader

public class StandardClassLoader extends URLClassLoader implements Reloader
Subclass implementation of java.net.URLClassLoader that knows how to load classes from disk directories, as well as local and remote JAR files. It also implements the Reloader interface, to provide automatic reloading support to the associated loader.

In all cases, URLs must conform to the contract specified by URLClassLoader - any URL that ends with a "/" character is assumed to represent a directory; all other URLs are assumed to be the address of a JAR file.

IMPLEMENTATION NOTE - Local repositories are searched in the order they are added via the initial constructor and/or any subsequent calls to addRepository().

IMPLEMENTATION NOTE - At present, there are no dependencies from this class to any other Catalina class, so that it could be used independently.

author
Craig R. McClanahan
author
Remy Maucherat
version
$Revision: 1.4 $ $Date: 2007/05/05 05:32:09 $

Fields Summary
private static com.sun.org.apache.commons.logging.Log
log
protected int
debug
The debugging detail level of this component.
protected boolean
delegate
Should this class loader delegate to the parent class loader before searching its own repositories (i.e. the usual Java2 delegation model)? If set to false, this class loader will search its own repositories first, and delegate to the parent only if the class or resource is not found locally.
protected String[]
repositories
The list of local repositories, in the order they should be searched for locally loaded classes or resources.
private ArrayList
permissionList
A list of read File and Jndi Permission's required if this loader is for a web application context.
private HashMap
loaderPC
The PermissionCollection for each CodeSource for a web application context.
private SecurityManager
securityManager
Instance of the SecurityManager installed.
private boolean
policy_refresh
Flag that the security policy has been refreshed from file.
private ClassLoader
parent
The parent class loader.
private ClassLoader
system
The system class loader.
protected URLStreamHandlerFactory
factory
URL stream handler for additional protocols.
Constructors Summary
public StandardClassLoader()
Construct a new ClassLoader with no defined repositories and no parent ClassLoader.


    // ----------------------------------------------------------- Constructors


                     
      

        super(new URL[0]);
        this.parent = getParent();
        this.system = getSystemClassLoader();
        securityManager = System.getSecurityManager();

    
public StandardClassLoader(URLStreamHandlerFactory factory)
Construct a new ClassLoader with no defined repositories and no parent ClassLoader, but with a stream handler factory.

param
factory the URLStreamHandlerFactory to use when creating URLs


        super(new URL[0], null, factory);
        this.factory = factory;

    
public StandardClassLoader(ClassLoader parent)
Construct a new ClassLoader with no defined repositories and the specified parent ClassLoader.

param
parent The parent ClassLoader


        super((new URL[0]), parent);
        this.parent = parent;
        this.system = getSystemClassLoader();
        securityManager = System.getSecurityManager();

    
public StandardClassLoader(ClassLoader parent, URLStreamHandlerFactory factory)
Construct a new ClassLoader with no defined repositories and the specified parent ClassLoader.

param
parent The parent ClassLoader
param
factory the URLStreamHandlerFactory to use when creating URLs


        super((new URL[0]), parent, factory);
        this.factory = factory;

    
public StandardClassLoader(String[] repositories)
Construct a new ClassLoader with the specified repositories and no parent ClassLoader.

param
repositories The initial set of repositories


        super(convert(repositories));
        this.parent = getParent();
        this.system = getSystemClassLoader();
        securityManager = System.getSecurityManager();
        if (repositories != null) {
            for (int i = 0; i < repositories.length; i++)
                addRepositoryInternal(repositories[i]);
        }

    
public StandardClassLoader(String[] repositories, ClassLoader parent)
Construct a new ClassLoader with the specified repositories and parent ClassLoader.

param
repositories The initial set of repositories
param
parent The parent ClassLoader


        super(convert(repositories), parent);
        this.parent = parent;
        this.system = getSystemClassLoader();
        securityManager = System.getSecurityManager();
        if (repositories != null) {
            for (int i = 0; i < repositories.length; i++)
                addRepositoryInternal(repositories[i]);
        }

    
public StandardClassLoader(URL[] repositories, ClassLoader parent)
Construct a new ClassLoader with the specified repositories and parent ClassLoader.

param
repositories The initial set of repositories
param
parent The parent ClassLoader


        super(repositories, parent);
        this.parent = parent;
        this.system = getSystemClassLoader();
        securityManager = System.getSecurityManager();
        if (repositories != null) {
            for (int i = 0; i < repositories.length; i++)
                addRepositoryInternal(repositories[i].toString());
        }

    
Methods Summary
public voidaddRepository(java.lang.String repository)
Add a new repository to the set of places this ClassLoader can look for classes to be loaded.

param
repository Name of a source of classes to be loaded, such as a directory pathname, a JAR file pathname, or a ZIP file pathname
exception
IllegalArgumentException if the specified repository is invalid or does not exist


        if (debug >= 1)
            log("addRepository(" + repository + ")");

        // Add this repository to our underlying class loader
        try {
            URLStreamHandler streamHandler = null;
            String protocol = parseProtocol(repository);
            if (factory != null)
                streamHandler = factory.createURLStreamHandler(protocol);
            URL url = new URL(null, repository, streamHandler);
            super.addURL(url);
        } catch (MalformedURLException e) {
            IllegalArgumentException iae = new IllegalArgumentException
                ("Invalid repository: " + repository);
            iae.initCause(e);
            throw iae;
        }

        // Add this repository to our internal list
        addRepositoryInternal(repository);

    
protected voidaddRepositoryInternal(java.lang.String repository)
Add a repository to our internal array only.

param
repository The new repository
exception
IllegalArgumentException if the manifest of a JAR file cannot be processed correctly


        URLStreamHandler streamHandler = null;
        String protocol = parseProtocol(repository);
        if (factory != null)
            streamHandler = factory.createURLStreamHandler(protocol);

        // Validate the manifest of a JAR file repository
        if (!repository.endsWith(File.separator) &&
            !repository.endsWith("/")) {
            JarFile jarFile = null;
            try {
                Manifest manifest = null;
                if (repository.startsWith("jar:")) {
                    URL url = new URL(null, repository, streamHandler);
                    JarURLConnection conn =
                        (JarURLConnection) url.openConnection();
                    conn.setAllowUserInteraction(false);
                    conn.setDoInput(true);
                    conn.setDoOutput(false);
                    conn.connect();
                    jarFile = conn.getJarFile();
                } else if (repository.startsWith("file://")) {
                    jarFile = new JarFile(repository.substring(7));
                } else if (repository.startsWith("file:")) {
                    jarFile = new JarFile(repository.substring(5));
                } else if (repository.endsWith(".jar")) {
                    URL url = new URL(null, repository, streamHandler);
                    URLConnection conn = url.openConnection();
                    JarInputStream jis =
                        new JarInputStream(conn.getInputStream());
                    manifest = jis.getManifest();
                } else {
                    throw new IllegalArgumentException
                        ("addRepositoryInternal:  Invalid URL '" +
                         repository + "'");
                }
            } catch (Throwable t) {
                IllegalArgumentException iae = new IllegalArgumentException
                    ("addRepositoryInternal");
                iae.initCause(t);
                throw iae;
            } finally {
                if (jarFile != null) {
                    try {
                        jarFile.close();
                    } catch (Throwable t) {}
                }
            }
        }

        // Add this repository to our internal list
        synchronized (repositories) {
            String results[] = new String[repositories.length + 1];
            System.arraycopy(repositories, 0, results, 0, repositories.length);
            results[repositories.length] = repository;
            repositories = results;
        }

    
protected static java.net.URL[]convert(java.lang.String[] input)
Convert an array of String to an array of URL and return it.

param
input The array of String to be converted

        return convert(input, null);
    
protected static java.net.URL[]convert(java.lang.String[] input, java.net.URLStreamHandlerFactory factory)
Convert an array of String to an array of URL and return it.

param
input The array of String to be converted
param
factory Handler factory to use to generate the URLs


        URLStreamHandler streamHandler = null;

        URL url[] = new URL[input.length];
        for (int i = 0; i < url.length; i++) {
            try {
                String protocol = parseProtocol(input[i]);
                if (factory != null)
                    streamHandler = factory.createURLStreamHandler(protocol);
                else
                    streamHandler = null;
                url[i] = new URL(null, input[i], streamHandler);
            } catch (MalformedURLException e) {
                url[i] = null;
            }
        }
        return (url);

    
public java.lang.ClassfindClass(java.lang.String name)
Find the specified class in our local repositories, if possible. If not found, throw ClassNotFoundException.

param
name Name of the class to be loaded
exception
ClassNotFoundException if the class was not found


        if (debug >= 3)
            log("    findClass(" + name + ")");

        // (1) Permission to define this class when using a SecurityManager
        if (securityManager != null) {
            int i = name.lastIndexOf('.");
            if (i >= 0) {
                try {
                    if (debug >= 4)
                        log("      securityManager.checkPackageDefinition");
                    securityManager.checkPackageDefinition(name.substring(0,i));
                } catch (Exception se) {
                    if (debug >= 4)
                        log("      -->Exception-->ClassNotFoundException", se);
                    throw new ClassNotFoundException(name, se);
                }
            }
        }

        // Ask our superclass to locate this class, if possible
        // (throws ClassNotFoundException if it is not found)
        Class clazz = null;
        try {
            if (debug >= 4)
                log("      super.findClass(" + name + ")");
            try {
                synchronized (this) {
                    clazz = findLoadedClass(name);
                    if (clazz != null)
                        return clazz;
                    clazz = super.findClass(name);
                }
            } catch(AccessControlException ace) {
                throw new ClassNotFoundException(name, ace);
            } catch (RuntimeException e) {
                if (debug >= 4)
                    log("      -->RuntimeException Rethrown", e);
                throw e;
            }
            if (clazz == null) {
                if (debug >= 3)
                    log("    --> Returning ClassNotFoundException");
                throw new ClassNotFoundException(name);
            }
        } catch (ClassNotFoundException e) {
            if (debug >= 3)
                log("    --> Passing on ClassNotFoundException", e);
            throw e;
        }

        // Return the class we have located
        if (debug >= 4)
            log("      Returning class " + clazz);
        if ((debug >= 4) && (clazz != null))
            log("      Loaded by " + clazz.getClassLoader());
        return (clazz);

    
protected java.io.InputStreamfindLoadedResource(java.lang.String name)
Finds the resource with the given name if it has previously been loaded and cached by this class loader, and return an input stream to the resource data. If this resource has not been cached, return null.

param
name Name of the resource to return


        return (null);  // FIXME - findLoadedResource()

    
public java.lang.String[]findRepositories()
Return a String array of the current repositories for this class loader. If there are no repositories, a zero-length array is returned. For security reason, returns a clone of the Array (since String are immutable).


        return ((String[])repositories.clone());

    
public java.net.URLfindResource(java.lang.String name)
Find the specified resource in our local repository, and return a URL refering to it, or null if this resource cannot be found.

param
name Name of the resource to be found


        if (debug >= 3)
            log("    findResource(" + name + ")");

        URL url = super.findResource(name);
        if (debug >= 3) {
            if (url != null)
                log("    --> Returning '" + url.toString() + "'");
            else
                log("    --> Resource not found, returning null");
        }
        return (url);

    
public java.util.EnumerationfindResources(java.lang.String name)
Return an enumeration of URLs representing all of the resources with the given name. If no resources with this name are found, return an empty enumeration.

param
name Name of the resources to be found
exception
IOException if an input/output error occurs


        if (debug >= 3)
            log("    findResources(" + name + ")");
        return (super.findResources(name));

    
public intgetDebug()
Return the debugging detail level for this component.



    // ------------------------------------------------------------- Properties


                 
       

        return (this.debug);

    
public booleangetDelegate()
Return the "delegate first" flag for this class loader.


        return (this.delegate);

    
protected final java.security.PermissionCollectiongetPermissions(java.security.CodeSource codeSource)
Get the Permissions for a CodeSource. If this instance of StandardClassLoader is for a web application context, add read FilePermissions for the base directory (if unpacked), the context URL, and jar file resources.

param
CodeSource where the code was loaded from
return
PermissionCollection for CodeSource

        if (!policy_refresh) {
            // Refresh the security policies
            Policy policy = Policy.getPolicy();
            policy.refresh();
            policy_refresh = true;
        }
        String codeUrl = codeSource.getLocation().toString();
        PermissionCollection pc;
        if ((pc = (PermissionCollection)loaderPC.get(codeUrl)) == null) {
            pc = super.getPermissions(codeSource);
            if (pc != null) {
                Iterator perms = permissionList.iterator();
                while (perms.hasNext()) {
                    Permission p = (Permission)perms.next();
                    pc.add(p);
                }
                loaderPC.put(codeUrl,pc);
            }
        }
        return (pc);

    
public java.net.URLgetResource(java.lang.String name)
Find the resource with the given name. A resource is some data (images, audio, text, etc.) that can be accessed by class code in a way that is independent of the location of the code. The name of a resource is a "/"-separated path name that identifies the resource. If the resource cannot be found, return null.

This method searches according to the following algorithm, returning as soon as it finds the appropriate URL. If the resource cannot be found, returns null.

  • If the delegate property is set to true, call the getResource() method of the parent class loader, if any.
  • Call findResource() to find this resource in our locally defined repositories.
  • Call the getResource() method of the parent class loader, if any.

param
name Name of the resource to return a URL for


        if (debug >= 2)
            log("getResource(" + name + ")");
        URL url = null;

        // (1) Delegate to parent if requested
        if (delegate) {
            if (debug >= 3)
                log("  Delegating to parent classloader");
            ClassLoader loader = parent;
            if (loader == null)
                loader = system;
            url = loader.getResource(name);
            if (url != null) {
                if (debug >= 2)
                    log("  --> Returning '" + url.toString() + "'");
                return (url);
            }
        }

        // (2) Search local repositories
        if (debug >= 3)
            log("  Searching local repositories");
        url = findResource(name);
        if (url != null) {
            if (debug >= 2)
                log("  --> Returning '" + url.toString() + "'");
            return (url);
        }

        // (3) Delegate to parent unconditionally if not already attempted
        if( !delegate ) {
            ClassLoader loader = parent;
            if (loader == null)
                loader = system;
            url = loader.getResource(name);
            if (url != null) {
                if (debug >= 2)
                    log("  --> Returning '" + url.toString() + "'");
                return (url);
            }
        }

        // (4) Resource was not found
        if (debug >= 2)
            log("  --> Resource not found, returning null");
        return (null);

    
public java.io.InputStreamgetResourceAsStream(java.lang.String name)
Find the resource with the given name, and return an input stream that can be used for reading it. The search order is as described for getResource(), after checking to see if the resource data has been previously cached. If the resource cannot be found, return null.

param
name Name of the resource to return an input stream for


        if (debug >= 2)
            log("getResourceAsStream(" + name + ")");
        InputStream stream = null;

        // (0) Check for a cached copy of this resource
        stream = findLoadedResource(name);
        if (stream != null) {
            if (debug >= 2)
                log("  --> Returning stream from cache");
            return (stream);
        }

        // (1) Delegate to parent if requested
        if (delegate) {
            if (debug >= 3)
                log("  Delegating to parent classloader");
            ClassLoader loader = parent;
            if (loader == null)
                loader = system;
            stream = loader.getResourceAsStream(name);
            if (stream != null) {
                // FIXME - cache???
                if (debug >= 2)
                    log("  --> Returning stream from parent");
                return (stream);
            }
        }

        // (2) Search local repositories
        if (debug >= 3)
            log("  Searching local repositories");
        URL url = findResource(name);
        if (url != null) {
            // FIXME - cache???
            if (debug >= 2)
                log("  --> Returning stream from local");
            try {
               return (url.openStream());
            } catch (IOException e) {
               log("url.openStream(" + url.toString() + ")", e);
               return (null);
            }
        }

        // (3) Delegate to parent unconditionally
        if (!delegate) {
            if (debug >= 3)
                log("  Delegating to parent classloader");
            ClassLoader loader = parent;
            if (loader == null)
                loader = system;
            stream = loader.getResourceAsStream(name);
            if (stream != null) {
                // FIXME - cache???
                if (debug >= 2)
                    log("  --> Returning stream from parent");
                return (stream);
            }
        }

        // (4) Resource was not found
        if (debug >= 2)
            log("  --> Resource not found, returning null");
        return (null);

    
public java.lang.ClassloadClass(java.lang.String name)
Load the class with the specified name. This method searches for classes in the same manner as loadClass(String, boolean) with false as the second argument.

param
name Name of the class to be loaded
exception
ClassNotFoundException if the class was not found


        return (loadClass(name, false));

    
public java.lang.ClassloadClass(java.lang.String name, boolean resolve)
Load the class with the specified name, searching using the following algorithm until it finds and returns the class. If the class cannot be found, returns ClassNotFoundException.
  • Call findLoadedClass(String) to check if the class has already been loaded. If it has, the same Class object is returned.
  • If the delegate property is set to true, call the loadClass() method of the parent class loader, if any.
  • Call findClass() to find this class in our locally defined repositories.
  • Call the loadClass() method of our parent class loader, if any.
If the class was found using the above steps, and the resolve flag is true, this method will then call resolveClass(Class) on the resulting Class object.

param
name Name of the class to be loaded
param
resolve If true then resolve the class
exception
ClassNotFoundException if the class was not found


        if (debug >= 2)
            log("loadClass(" + name + ", " + resolve + ")");
        Class clazz = null;

        // (0) Check our previously loaded class cache
        clazz = findLoadedClass(name);
        if (clazz != null) {
            if (debug >= 3)
                log("  Returning class from cache");
            if (resolve)
                resolveClass(clazz);
            return (clazz);
        }

        // If a system class, use system class loader
        if( name.startsWith("java.") ) {
            ClassLoader loader = system;
            clazz = loader.loadClass(name);
            if (clazz != null) {
                if (resolve)
                    resolveClass(clazz);
                return (clazz);
            }
            throw new ClassNotFoundException(name);
        }

        // (.5) Permission to access this class when using a SecurityManager
        if (securityManager != null) {
            int i = name.lastIndexOf('.");
            if (i >= 0) {
                try {
                    securityManager.checkPackageAccess(name.substring(0,i));
                } catch (SecurityException se) {
                    String error = "Security Violation, attempt to use " +
                        "Restricted Class: " + name;
                    log(error);
                    throw new ClassNotFoundException(error, se);
                }
            }
        }

        // (1) Delegate to our parent if requested
        if (delegate) {
            if (debug >= 3)
                log("  Delegating to parent classloader");
            ClassLoader loader = parent;
            if (loader == null)
                loader = system;
            try {
                clazz = loader.loadClass(name);
                if (clazz != null) {
                    if (debug >= 3)
                        log("  Loading class from parent");
                    if (resolve)
                        resolveClass(clazz);
                    return (clazz);
                }
            } catch (ClassNotFoundException e) {
                ;
            }
        }

        // (2) Search local repositories
        if (debug >= 3)
            log("  Searching local repositories");
        try {
            clazz = findClass(name);
            if (clazz != null) {
                if (debug >= 3)
                    log("  Loading class from local repository");
                if (resolve)
                    resolveClass(clazz);
                return (clazz);
            }
        } catch (ClassNotFoundException e) {
            ;
        }

        // (3) Delegate to parent unconditionally
        if (!delegate) {
            if (debug >= 3)
                log("  Delegating to parent classloader");
            ClassLoader loader = parent;
            if (loader == null)
                loader = system;
            try {
                clazz = loader.loadClass(name);
                if (clazz != null) {
                    if (debug >= 3)
                        log("  Loading class from parent");
                    if (resolve)
                        resolveClass(clazz);
                    return (clazz);
                }
            } catch (ClassNotFoundException e) {
                ;
            }
        }

        // This class was not found
        throw new ClassNotFoundException(name);

    
private voidlog(java.lang.String message)
Log a debugging output message.

param
message Message to be logged


        if (log.isDebugEnabled())
            log.debug("StandardClassLoader: " + message);

    
private voidlog(java.lang.String message, java.lang.Throwable throwable)
Log a debugging output message with an exception.

param
message Message to be logged
param
throwable Exception to be logged


        if (log.isDebugEnabled())
            log.debug("StandardClassLoader: " + message, throwable);

    
public booleanmodified()
This class loader doesn't check for reloading.


        return (false);

    
protected static java.lang.StringparseProtocol(java.lang.String spec)
Parse URL protocol.

return
String protocol

        if (spec == null)
            return "";
        int pos = spec.indexOf(':");
        if (pos <= 0)
            return "";
        return spec.substring(0, pos).trim();
    
public voidsetDebug(int debug)
Set the debugging detail level for this component.

param
debug The new debugging detail level


        this.debug = debug;

    
public voidsetDelegate(boolean delegate)
Set the "delegate first" flag for this class loader.

param
delegate The new "delegate first" flag


        this.delegate = delegate;

    
protected voidsetPermissions(java.lang.String path)
If there is a Java SecurityManager create a read FilePermission or JndiPermission for the file directory path.

param
path file directory path

        if( securityManager != null ) {
            if( path.startsWith("jndi:") || path.startsWith("jar:jndi:") ) {
                permissionList.add(new JndiPermission(path + "*"));
            } else {
                permissionList.add(new FilePermission(path + "-","read"));
            }
        }
    
protected voidsetPermissions(java.net.URL url)
If there is a Java SecurityManager add a read FilePermission or JndiPermission for URL.

param
url URL for a file or directory on local system

        setPermissions(url.toString());
    
public java.lang.StringtoString()
Render a String representation of this object.


        StringBuffer sb = new StringBuffer("StandardClassLoader\r\n");
        sb.append("  delegate: ");
        sb.append(delegate);
        sb.append("\r\n");
        sb.append("  repositories:\r\n");
        for (int i = 0; i < repositories.length; i++) {
            sb.append("    ");
            sb.append(repositories[i]);
            sb.append("\r\n");
        }
        if (this.parent != null) {
            sb.append("----------> Parent Classloader:\r\n");
            sb.append(this.parent.toString());
            sb.append("\r\n");
        }
        return (sb.toString());