FileDocCategorySizeDatePackage
WebappLoader.javaAPI DocGlassfish v2 API42013Wed Jul 18 08:31:58 BST 2007org.apache.catalina.loader

WebappLoader

public class WebappLoader extends Object implements PropertyChangeListener, org.apache.catalina.Lifecycle, org.apache.catalina.Loader, MBeanRegistration
Classloader implementation which is specialized for handling web applications in the most efficient way, while being Catalina aware (all accesses to resources are made through the DirContext interface). This class loader supports detection of modified Java classes, which can be used to implement auto-reload support.

This class loader is configured by adding the pathnames of directories, JAR files, and ZIP files with the addRepository() method, prior to calling start(). When a new class is required, these repositories will be consulted first to locate the class. If it is not present, the system class loader will be used instead.

author
Craig R. McClanahan
author
Remy Maucherat
version
$Revision: 1.11 $ $Date: 2007/07/18 15:31:57 $

Fields Summary
private static boolean
first
First load of the class.
private WebappClassLoader
classLoader
The class loader being managed by this Loader component.
private org.apache.catalina.Container
container
The Container with which this Loader has been associated.
private int
debug
The debugging detail level for this component.
protected org.apache.catalina.DefaultContext
defaultContext
The DefaultContext with which this Loader is associated.
private boolean
delegate
The "follow standard delegation model" flag that will be used to configure our ClassLoader.
private static final String
info
The descriptive information about this Loader implementation.
protected org.apache.catalina.util.LifecycleSupport
lifecycle
The lifecycle event support for this component.
private String
loaderClass
The Java class name of the ClassLoader implementation to be used. This class should extend WebappClassLoader, otherwise, a different loader implementation must be used.
private ClassLoader
parentClassLoader
The parent class loader of the class loader we will create.
private boolean
reloadable
The reloadable flag for this Loader.
private String[]
repositories
The set of repositories associated with this class loader.
protected static final org.apache.catalina.util.StringManager
sm
The string manager for this package.
private boolean
started
Has this component been started?
protected PropertyChangeSupport
support
The property change support for this component.
private String
classpath
Classpath set in the loader.
private ArrayList
loaderRepositories
Repositories that are set in the loader, for JMX.
private ArrayList
overridablePackages
List of packages that may always be overridden, regardless of whether they belong to a protected namespace (i.e., a namespace that may never be overridden by a webapp)
private boolean
ignoreHiddenJarFiles
private boolean
useMyFaces
private boolean
initialized
private static com.sun.org.apache.commons.logging.Log
log
private ObjectName
oname
private MBeanServer
mserver
private String
domain
private ObjectName
controller
Constructors Summary
public WebappLoader()
Construct a new WebappLoader with no defined parent class loader (so that the actual parent will be the system class loader).


        this(null);

    
public WebappLoader(ClassLoader parent)
Construct a new WebappLoader with the specified class loader to be defined as the parent of the ClassLoader we ultimately create.

param
parent The parent class loader

        super();
        this.parentClassLoader = parent;
    
Methods Summary
public voidaddLifecycleListener(org.apache.catalina.LifecycleListener listener)
Add a lifecycle event listener to this component.

param
listener The listener to add


        lifecycle.addLifecycleListener(listener);

    
public voidaddOverridablePackage(java.lang.String packageName)
Adds the given package name to the list of packages that may always be overriden, regardless of whether they belong to a protected namespace

       if ( overridablePackages == null){
           overridablePackages = new ArrayList();
       }
        
       overridablePackages.add( packageName ); 
    
public voidaddPropertyChangeListener(java.beans.PropertyChangeListener listener)
Add a property change listener to this component.

param
listener The listener to add


        support.addPropertyChangeListener(listener);

    
public voidaddRepository(java.lang.String repository)
Add a new repository to the set of repositories for this class loader.

param
repository Repository to be added


        if (log.isTraceEnabled())
            log.trace(sm.getString("webappLoader.addRepository", repository));

        for (int i = 0; i < repositories.length; i++) {
            if (repository.equals(repositories[i]))
                return;
        }
        String results[] = new String[repositories.length + 1];
        for (int i = 0; i < repositories.length; i++)
            results[i] = repositories[i];
        results[repositories.length] = repository;
        repositories = results;

        if (started && (classLoader != null)) {
            classLoader.addRepository(repository);
            if( loaderRepositories != null ) loaderRepositories.add(repository);
            setClassPath();
        }

    
public voidcloseJARs(boolean force)
Used to periodically signal to the classloader to release JAR resources.

        if (classLoader !=null){
            classLoader.closeJARs(force);
        }
    
private booleancopy(java.io.InputStream is, java.io.OutputStream os)
Copy a file to the specified temp directory. This is required only because Jasper depends on it.


        try {
            byte[] buf = new byte[4096];
            while (true) {
                int len = is.read(buf);
                if (len < 0)
                    break;
                os.write(buf, 0, len);
            }
            is.close();
            os.close();
        } catch (IOException e) {
            return false;
        }

        return true;

    
private booleancopyDir(javax.naming.directory.DirContext srcDir, java.io.File destDir)
Copy directory.


        try {

            NamingEnumeration enumeration = srcDir.list("");
            while (enumeration.hasMoreElements()) {
                NameClassPair ncPair =
                    (NameClassPair) enumeration.nextElement();
                String name = ncPair.getName();
                Object object = srcDir.lookup(name);
                File currentFile = new File(destDir, name);
                if (object instanceof Resource) {
                    InputStream is = ((Resource) object).streamContent();
                    OutputStream os = new FileOutputStream(currentFile);
                    if (!copy(is, os))
                        return false;
                } else if (object instanceof InputStream) {
                    OutputStream os = new FileOutputStream(currentFile);
                    if (!copy((InputStream) object, os))
                        return false;
                } else if (object instanceof DirContext) {
                    currentFile.mkdir();
                    copyDir((DirContext) object, currentFile);
                }
            }

        } catch (NamingException e) {
            return false;
        } catch (IOException e) {
            return false;
        }

        return true;

    
private WebappClassLoadercreateClassLoader()
Create associated classLoader.


        Class clazz = Class.forName(loaderClass);
        WebappClassLoader classLoader = null;

        if (parentClassLoader == null) {
            parentClassLoader = Thread.currentThread().getContextClassLoader();
        }
        Class[] argTypes = { ClassLoader.class };
        Object[] args = { parentClassLoader };
        Constructor constr = clazz.getConstructor(argTypes);
        classLoader = (WebappClassLoader) constr.newInstance(args);

        classLoader.setUseMyFaces(useMyFaces);

        return classLoader;

    
public voiddestroy()

        if( controller==oname ) {
            // Self-registration, undo it
            Registry.getRegistry().unregisterComponent(oname);
            oname = null;
        }
        initialized = false;

    
public org.apache.catalina.LifecycleListener[]findLifecycleListeners()
Get the lifecycle listeners associated with this lifecycle. If this Lifecycle has no listeners registered, a zero-length array is returned.


        return lifecycle.findLifecycleListeners();

    
public java.lang.String[]findRepositories()
Return the set of repositories defined for this class loader. If none are defined, 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.lang.ClassLoadergetClassLoader()
Return the Java class loader to be used by this Container.


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


                    
       

        return ((ClassLoader) classLoader);

    
public java.lang.StringgetClasspath()
Classpath, as set in org.apache.catalina.jsp_classpath context property

return
The classpath

        return classpath;
    
private java.lang.StringgetClasspath(java.lang.ClassLoader loader)

        try {
            Method m=loader.getClass().getMethod("getClasspath", new Class[] {});
            if( log.isTraceEnabled())
                log.trace("getClasspath " + m );
            if( m==null ) return null;
            Object o=m.invoke( loader, new Object[] {} );
            if( log.isTraceEnabled() )
                log.trace("gotClasspath " + o);
            if( o instanceof String )
                return (String)o;
            return null;
        } catch( Exception ex ) {
            if (log.isTraceEnabled())
                log.trace("getClasspath ", ex);
        }
        return null;
    
public org.apache.catalina.ContainergetContainer()
Return the Container with which this Logger has been associated.


        return (container);

    
public javax.management.ObjectNamegetController()

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


        return (this.debug);

    
public org.apache.catalina.DefaultContextgetDefaultContext()
Return the DefaultContext with which this Loader is associated. 4985680 What is that ???


        return (this.defaultContext);

    
public booleangetDelegate()
Return the "follow standard delegation model" flag used to configure our ClassLoader.


        return (this.delegate);

    
public java.lang.StringgetInfo()
Return descriptive information about this Loader implementation and the corresponding version number, in the format <description>/<version>.


        return (info);

    
public java.lang.StringgetLoaderClass()
Return the ClassLoader class name.


        return (this.loaderClass);

    
public java.lang.String[]getLoaderRepositories()

        if( loaderRepositories==null ) return  null;
        String res[]=new String[ loaderRepositories.size()];
        loaderRepositories.toArray(res);
        return res;
    
public java.lang.StringgetLoaderRepositoriesString()

        String repositories[]=getLoaderRepositories();
        StringBuffer sb=new StringBuffer();
        for( int i=0; i<repositories.length ; i++ ) {
            sb.append( repositories[i]).append(":");
        }
        return sb.toString();
    
public booleangetReloadable()
Return the reloadable flag for this Loader.


        return (this.reloadable);

    
public java.lang.String[]getRepositories()

        return ((String[])repositories.clone());
    
public java.lang.StringgetRepositoriesString()
Extra repositories for this loader

        StringBuffer sb=new StringBuffer();
        for( int i=0; i<repositories.length ; i++ ) {
            sb.append( repositories[i]).append(":");
        }
        return sb.toString();
    
public voidinit()


       
        initialized=true;

        if( oname==null ) {
            // not registered yet - standalone or API
            if( container instanceof StandardContext) {
                // Register ourself. The container must be a webapp
                try {
                    StandardContext ctx=(StandardContext)container;
                    Engine eng=(Engine)ctx.getParent().getParent();
                    String path = ctx.getEncodedPath();
                    if (path.equals("")) {
                        path = "/";
                    }   
                    oname=new ObjectName(ctx.getEngineName() + ":type=Loader,path=" +
                                path + ",host=" + ctx.getParent().getName());
                    Registry.getRegistry().registerComponent(this, oname, null);
                    controller=oname;
                } catch (Exception e) {
                    log.error("Error registering loader", e );
                }
            }
        }

        if( container == null ) {
            // JMX created the loader
            // TODO

        }
    
private voidlog(java.lang.String message)
Log a message on the Logger associated with our Container (if any)

param
message Message to be logged


        Logger logger = null;
        if (container != null)
            logger = container.getLogger();
        if (logger != null)
            logger.log("WebappLoader[" + container.getName() + "]: "
                       + message);
        else {
            String containerName = null;
            if (container != null)
                containerName = container.getName();
            System.out.println("WebappLoader[" + containerName
                               + "]: " + message);
        }

    
private voidlog(java.lang.String message, java.lang.Throwable throwable)
Log a message on the Logger associated with our Container (if any)

param
message Message to be logged
param
throwable Associated exception


        Logger logger = null;
        if (container != null)
            logger = container.getLogger();
        if (logger != null) {
            logger.log("WebappLoader[" + container.getName() + "] "
                       + message, throwable);
        } else {
            String containerName = null;
            if (container != null)
                containerName = container.getName();
            System.out.println("WebappLoader[" + containerName
                               + "]: " + message);
            System.out.println("" + throwable);
            throwable.printStackTrace(System.out);
        }

    
public booleanmodified()
Has the internal repository associated with this Loader been modified, such that the loaded classes should be reloaded?


        return (classLoader.modified());

    
public voidpostDeregister()

    
public voidpostRegister(java.lang.Boolean registrationDone)

    
public voidpreDeregister()

    
public javax.management.ObjectNamepreRegister(javax.management.MBeanServer server, javax.management.ObjectName name)


       
                                      
        oname=name;
        mserver=server;
        domain=name.getDomain();

        return name;
    
public voidpropertyChange(java.beans.PropertyChangeEvent event)
Process property change events from our associated Context.

param
event The property change event that has occurred


        // Validate the source of this event
        if (!(event.getSource() instanceof Context))
            return;
        Context context = (Context) event.getSource();

        // Process a relevant property change
        if (event.getPropertyName().equals("reloadable")) {
            try {
                setReloadable
                    ( ((Boolean) event.getNewValue()).booleanValue() );
            } catch (NumberFormatException e) {
                log.error(sm.getString("webappLoader.reloadable",
                                 event.getNewValue().toString()));
            }
        }

    
public voidremoveLifecycleListener(org.apache.catalina.LifecycleListener listener)
Remove a lifecycle event listener from this component.

param
listener The listener to remove


        lifecycle.removeLifecycleListener(listener);

    
public voidremovePropertyChangeListener(java.beans.PropertyChangeListener listener)
Remove a property change listener from this component.

param
listener The listener to remove


        support.removePropertyChangeListener(listener);

    
private voidsetClassPath()
Set the appropriate context attribute for our class path. This is required only because Jasper depends on it.


        // Validate our current state information
        if (!(container instanceof Context))
            return;
        ServletContext servletContext =
            ((Context) container).getServletContext();
        if (servletContext == null)
            return;

        if (container instanceof StandardContext) {
            String baseClasspath = 
                ((StandardContext) container).getCompilerClasspath();
            if (baseClasspath != null) {
                servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
                                            baseClasspath);
                return;
            }
        }

        StringBuffer classpath = new StringBuffer();

        // Assemble the class path information from our class loader chain
        ClassLoader loader = getClassLoader();
        int layers = 0;
        int n = 0;
        while (loader != null) {
            if (!(loader instanceof URLClassLoader)) {
                String cp=getClasspath( loader );
                if( cp==null ) {
                    log.info( "Unknown loader " + loader + " " + loader.getClass());
                    break;
                } else {
                    if (n > 0) 
                        classpath.append(File.pathSeparator);
                    classpath.append(cp);
                    n++;
                }
                break;
                //continue;
            }
            URL repositories[] =
                ((URLClassLoader) loader).getURLs();
            for (int i = 0; i < repositories.length; i++) {
                String repository = repositories[i].toString();
                if (repository.startsWith("file://"))
                    repository = repository.substring(7);
                else if (repository.startsWith("file:"))
                    repository = repository.substring(5);
                else if (repository.startsWith("jndi:"))
                    repository =
                        servletContext.getRealPath(repository.substring(5));
                else
                    continue;
                if (repository == null)
                    continue;
                if (n > 0)
                    classpath.append(File.pathSeparator);
                classpath.append(repository);
                n++;
            }
            loader = loader.getParent();
            layers++;
        }

        this.classpath=classpath.toString();

        // Store the assembled class path as a servlet context attribute
        servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
                                    classpath.toString());

    
public voidsetContainer(org.apache.catalina.Container container)
Set the Container with which this Logger has been associated.

param
container The associated Container


        // Deregister from the old Container (if any)
        if ((this.container != null) && (this.container instanceof Context))
            ((Context) this.container).removePropertyChangeListener(this);

        // Process this property change
        Container oldContainer = this.container;
        this.container = container;
        support.firePropertyChange("container", oldContainer, this.container);

        // Register with the new Container (if any)
        if ((this.container != null) && (this.container instanceof Context)) {
            setReloadable( ((Context) this.container).getReloadable() );
            ((Context) this.container).addPropertyChangeListener(this);
        }
    
public voidsetController(javax.management.ObjectName controller)

        this.controller = controller;
    
public voidsetDebug(int debug)
Set the debugging detail level for this component.

param
debug The new debugging detail level


        int oldDebug = this.debug;
        this.debug = debug;
        support.firePropertyChange("debug", Integer.valueOf(oldDebug),
                                   Integer.valueOf(this.debug));

    
public voidsetDefaultContext(org.apache.catalina.DefaultContext defaultContext)
Set the DefaultContext with which this Loader is associated.

param
defaultContext The newly associated DefaultContext


        DefaultContext oldDefaultContext = this.defaultContext;
        this.defaultContext = defaultContext;
        support.firePropertyChange("defaultContext", oldDefaultContext, this.defaultContext);

    
public voidsetDelegate(boolean delegate)
Set the "follow standard delegation model" flag used to configure our ClassLoader.

param
delegate The new flag


        boolean oldDelegate = this.delegate;
        this.delegate = delegate;
        support.firePropertyChange("delegate", Boolean.valueOf(oldDelegate),
                                   Boolean.valueOf(this.delegate));

    
public voidsetIgnoreHiddenJarFiles(boolean ignoreHiddenJarFiles)

        this.ignoreHiddenJarFiles = ignoreHiddenJarFiles;
    
public voidsetLoaderClass(java.lang.String loaderClass)
Set the ClassLoader class name.

param
loaderClass The new ClassLoader class name


        this.loaderClass = loaderClass;

    
private voidsetPermissions()
Configure associated class loader permissions.


        if (!Globals.IS_SECURITY_ENABLED)
            return;
        if (!(container instanceof Context))
            return;

        // Tell the class loader the root of the context
        ServletContext servletContext =
            ((Context) container).getServletContext();

        // Assigning permissions for the work directory
        File workDir =
            (File) servletContext.getAttribute(Globals.WORK_DIR_ATTR);
        if (workDir != null) {
            try {
                String workDirPath = workDir.getCanonicalPath();
                classLoader.addPermission
                    (new FilePermission(workDirPath, "read,write"));
                classLoader.addPermission
                    (new FilePermission(workDirPath + File.separator + "-", 
                                        "read,write,delete"));
            } catch (IOException e) {
                // Ignore
            }
        }

        try {

            URL rootURL = servletContext.getResource("/");
            classLoader.addPermission(rootURL);

            String contextRoot = servletContext.getRealPath("/");
            if (contextRoot != null) {
                try {
                    contextRoot = (new File(contextRoot)).getCanonicalPath();
                    classLoader.addPermission(contextRoot);
                } catch (IOException e) {
                    // Ignore
                }
            }

            URL classesURL = servletContext.getResource("/WEB-INF/classes/");
            classLoader.addPermission(classesURL);
            URL libURL = servletContext.getResource("/WEB-INF/lib/");
            classLoader.addPermission(libURL);

            if (contextRoot != null) {

                if (libURL != null) {
                    File rootDir = new File(contextRoot);
                    File libDir = new File(rootDir, "WEB-INF/lib/");
                    try {
                        String path = libDir.getCanonicalPath();
                        classLoader.addPermission(path);
                    } catch (IOException e) {
                    }
                }

            } else {

                if (workDir != null) {
                    if (libURL != null) {
                        File libDir = new File(workDir, "WEB-INF/lib/");
                        try {
                            String path = libDir.getCanonicalPath();
                            classLoader.addPermission(path);
                        } catch (IOException e) {
                        }
                    }
                    if (classesURL != null) {
                        File classesDir = new File(workDir, "WEB-INF/classes/");
                        try {
                            String path = classesDir.getCanonicalPath();
                            classLoader.addPermission(path);
                        } catch (IOException e) {
                        }
                    }
                }

            }

        } catch (MalformedURLException e) {
        }

    
public voidsetReloadable(boolean reloadable)
Set the reloadable flag for this Loader.

param
reloadable The new reloadable flag


        // Process this property change
        boolean oldReloadable = this.reloadable;
        this.reloadable = reloadable;
        support.firePropertyChange("reloadable",
                                   Boolean.valueOf(oldReloadable),
                                   Boolean.valueOf(this.reloadable));

    
private voidsetRepositories()
Configure the repositories for our class loader, based on the associated Context.


        if (!(container instanceof Context))
            return;
        ServletContext servletContext =
            ((Context) container).getServletContext();
        if (servletContext == null)
            return;

        loaderRepositories=new ArrayList();
        // Loading the work directory
        File workDir =
            (File) servletContext.getAttribute(Globals.WORK_DIR_ATTR);
        if (workDir == null) {
            log.info("No work dir for " + servletContext);
        }

        if( log.isTraceEnabled()) 
            log.trace(sm.getString("webappLoader.deploy", workDir.getAbsolutePath()));

        classLoader.setWorkDir(workDir);

        DirContext resources = container.getResources();

        // Setting up the class repository (/WEB-INF/classes), if it exists

        String classesPath = "/WEB-INF/classes";
        DirContext classes = null;

        try {
            Object object = resources.lookup(classesPath);
            if (object instanceof DirContext) {
                classes = (DirContext) object;
            }
        } catch(NamingException e) {
            // Silent catch: it's valid that no /WEB-INF/classes collection
            // exists
        }

        if (classes != null) {

            File classRepository = null;

            String absoluteClassesPath =
                servletContext.getRealPath(classesPath);

            if (absoluteClassesPath != null) {

                classRepository = new File(absoluteClassesPath);

            } else {

                classRepository = new File(workDir, classesPath);
                classRepository.mkdirs();
                copyDir(classes, classRepository);

            }

            if(log.isTraceEnabled())
                log.trace(sm.getString("webappLoader.classDeploy", classesPath,
                             classRepository.getAbsolutePath()));


            // Adding the repository to the class loader
            classLoader.addRepository(classesPath + "/", classRepository);
            loaderRepositories.add(classesPath + "/" );

        }

        // Setting up the JAR repository (/WEB-INF/lib), if it exists

        String libPath = "/WEB-INF/lib";

        classLoader.setJarPath(libPath);

        DirContext libDir = null;
        // Looking up directory /WEB-INF/lib in the context
        try {
            Object object = resources.lookup(libPath);
            if (object instanceof DirContext)
                libDir = (DirContext) object;
        } catch(NamingException e) {
            // Silent catch: it's valid that no /WEB-INF/lib collection
            // exists
        }

        if (libDir != null) {

            boolean copyJars = false;
            String absoluteLibPath = servletContext.getRealPath(libPath);

            File destDir = null;

            if (absoluteLibPath != null) {
                destDir = new File(absoluteLibPath);
            } else {
                copyJars = true;
                destDir = new File(workDir, libPath);
                destDir.mkdirs();
            }

            // Looking up directory /WEB-INF/lib in the context
            try {
                NamingEnumeration enumeration = resources.listBindings(libPath);
                while (enumeration.hasMoreElements()) {

                    Binding binding = (Binding) enumeration.nextElement();
                    String filename = libPath + "/" + binding.getName();
                    // START OF IASRI 4657979
                    if (!filename.endsWith(".jar") &&
                        !filename.endsWith(".zip"))
                    // END OF IASRI 4657979
                        continue;

                    // START PWC 1.1 6314481
                    if (binding.getName() != null
                            && binding.getName().startsWith(".")
                            && ignoreHiddenJarFiles) {
                        continue;
                    }
                    // END PWC 1.1 6314481

                    // Copy JAR in the work directory, always (the JAR file
                    // would get locked otherwise, which would make it
                    // impossible to update it or remove it at runtime)
                    File destFile = new File(destDir, binding.getName());

                    if( log.isTraceEnabled()) {
                        log.trace(sm.getString("webappLoader.jarDeploy", filename,
                                  destFile.getAbsolutePath()));
                    }

                    Resource jarResource = (Resource) binding.getObject();
                    if (copyJars) {
                        if (!copy(jarResource.streamContent(),
                                  new FileOutputStream(destFile)))
                            continue;
                    }

                    /** IASRI 4670099
                    JarFile jarFile = new JarFile(destFile);
                    classLoader.addJar(filename, jarFile, destFile);
                    **/

                    // START OF IASRI 4670099
                    try {
                        JarFile jarFile = new JarFile(destFile);
                        classLoader.addJar(filename, jarFile, destFile);
                    } catch (Exception ex) {
                        // Catch the exception if there is an empty jar file
                        // Should ignore and continute loading other jar files 
                        // in the dir
                    }
                    // END OF IASRI 4670099
	
                    loaderRepositories.add( filename );

                }
            } catch (NamingException e) {
                // Silent catch: it's valid that no /WEB-INF/lib directory
                // exists
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

    
public voidsetUseMyFaces(boolean useMyFaces)

        this.useMyFaces = useMyFaces;
    
public voidstart()
Start this component, initializing our associated class loader.

exception
LifecycleException if a lifecycle error occurs

        // Validate and update our current component state
        if( ! initialized ) init();
        if (started)
            throw new LifecycleException
                (sm.getString("webappLoader.alreadyStarted"));
        if (log.isTraceEnabled())
            log.trace(sm.getString("webappLoader.starting"));
        lifecycle.fireLifecycleEvent(START_EVENT, null);
        started = true;

        if (container.getResources() == null) {
            log.info("No resources for " + container);
            return;
        }
        // Register a stream handler factory for the JNDI protocol
        URLStreamHandlerFactory streamHandlerFactory =
            new DirContextURLStreamHandlerFactory();
        if (first) {
            first = false;
            try {
                URL.setURLStreamHandlerFactory(streamHandlerFactory);
            } catch (Exception e) {
                // Log and continue anyway, this is not critical
                log.error("Error registering jndi stream handler", e);
            } catch (Throwable t) {
                // This is likely a dual registration
                log.info("Dual registration of jndi stream handler: " 
                         + t.getMessage());
            }
        }

        // Construct a class loader based on our current repositories list
        try {

            classLoader = createClassLoader();
            classLoader.setResources(container.getResources());
            classLoader.setDebug(this.debug);
            classLoader.setDelegate(this.delegate);

            for (int i = 0; i < repositories.length; i++) {
                classLoader.addRepository(repositories[i]);
            }
            
            // START OF PE 4985680
            if (overridablePackages != null){
                for (int i = 0; i < overridablePackages.size(); i++) {
                    classLoader.addOverridablePackage(
                                            (String)overridablePackages.get(i));
                }
                overridablePackages = null;
            }
            // END OF PE 4985680

            // Configure our repositories
            setRepositories();
            setClassPath();

            setPermissions();

            if (classLoader instanceof Lifecycle)
                ((Lifecycle) classLoader).start();

            // Binding the Webapp class loader to the directory context
            DirContextURLStreamHandler.bind
                ((ClassLoader) classLoader, this.container.getResources());

        } catch (Throwable t) {
            log.error( "LifecycleException ", t );
            throw new LifecycleException("start: ", t);
        }

    
public voidstop()
Stop this component, finalizing our associated class loader.

exception
LifecycleException if a lifecycle error occurs


        // Validate and update our current component state
        if (!started)
            throw new LifecycleException
                (sm.getString("webappLoader.notStarted"));
        if (log.isTraceEnabled())
            log.trace(sm.getString("webappLoader.stopping"));
        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
        started = false;

        // Remove context attributes as appropriate
        if (container instanceof Context) {
            ServletContext servletContext =
                ((Context) container).getServletContext();
            servletContext.removeAttribute(Globals.CLASS_PATH_ATTR);
        }

        // Throw away our current class loader
        if (classLoader instanceof Lifecycle)
            ((Lifecycle) classLoader).stop();
        DirContextURLStreamHandler.unbind((ClassLoader) classLoader);
        classLoader = null;

        destroy();

    
public java.lang.StringtoString()
Return a String representation of this component.


        StringBuffer sb = new StringBuffer("WebappLoader[");
        if (container != null)
            sb.append(container.getName());
        sb.append("]");
        return (sb.toString());