/*
 * Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
 * Distributed under the terms of either:
 * - the common development and distribution license (CDDL), v1.0; or
 * - the GNU Lesser General Public License, v2.1 or later
 */
package winstone.classLoader;

import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandlerFactory;

import winstone.Logger;
import winstone.WinstoneResourceBundle;

/**
 * Implements the servlet spec model (v2.3 section 9.7.2) for classloading, which
 * is different to the standard JDK model in that it delegates *after* checking
 * local repositories. This has the effect of isolating copies of classes that exist
 * in 2 webapps from each other.
 * 
 * Thanks to James Berry for the changes to use the system classloader to prevent 
 * loading servlet spec or system classpath classes again.  
 * 
 * @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
 * @version $Id$
 */
public class WebappClassLoader extends URLClassLoader {
    private static final WinstoneResourceBundle CL_RESOURCES = new WinstoneResourceBundle("winstone.classLoader.LocalStrings");
    
    protected ClassLoader system = getSystemClassLoader();

    public WebappClassLoader(URL[] urls) {
        super(urls);
    }

    public WebappClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }
    
    public WebappClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
        super(urls, parent, factory);
    }

    protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
        // First, check if the class has already been loaded
        Class c = findLoadedClass(name);
        
        // Try the system loader first, to ensure that system classes are not
        // overridden by webapps. Note that this includes any classes in winstone,
        // including the javax.servlet classes
        if (c == null) {
            try {
                c = system.loadClass(name);
                if (c != null) {
                    Logger.log(Logger.MAX, CL_RESOURCES, "WebappClassLoader.LoadedBySystemCL", name);
                }
            } catch (ClassNotFoundException e) {
                c = null;
            }
        }
        
        // If an allowed class, load it locally first
        if (c == null) {
            try {
                // If still not found, then invoke findClass in order to find the class.
                c = findClass(name);
                if (c != null) {
                    Logger.log(Logger.MAX, CL_RESOURCES, "WebappClassLoader.LoadedByThisCL", name);
                }
            } catch (ClassNotFoundException e) {
                c = null;
            }
        }
        
        // otherwise, and only if we have a parent, delegate to our parent
        // Note that within winstone, the only difference between this and the system
        // class loader we've already tried is that our parent might include the common/shared lib.
        if (c == null) {
            ClassLoader parent = getParent();
            if (parent != null) {
                c = parent.loadClass(name);
                if (c != null) {
                    Logger.log(Logger.MAX, CL_RESOURCES, "WebappClassLoader.LoadedByParentCL", name);
                }
            } else {
                // We have no other hope for loading the class, so throw the class not found exception
                throw new ClassNotFoundException(name);
            }
        }
        
        if (resolve && (c != null)) {
            resolveClass(c);
        }
        return c;
    }

    public InputStream getResourceAsStream(String name) {
        if ((name != null) && name.startsWith("/")) {
            name = name.substring(1);
        }
        return super.getResourceAsStream(name);
    }
}

@@ -1568,24 +1568,37 @@
   protected 
+synchronized 
 Class loadCl

Add synchronization to loadClass() - thanks to kohsuke kawaguchi