/* JFox, the OpenSource J2EE Application Server
 *
 * Distributable under GNU LGPL license by gun.org
 * more details please visit http://www.huihoo.org/jfox
 */

package org.jfox.framework.loader;

import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.jfox.framework.logger.Logger;

/**
 * public the addURL method
 *
 * @author <a href="mailto:young_yy@hotmail.com">Young Yang</a>
 */

public class ClassLoaderRepository extends URLClassLoader {
    private Logger logger = Logger.getLogger(ClassLoaderRepository.class.getName());
    // LocalClassLoader s
    private Set loaders = new HashSet();

    /**
     * class name => class object
     */
    private Map classes = Collections.synchronizedMap(new HashMap());


    public ClassLoaderRepository(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }

    public void addClassLoader(LocalClassLoader loader) {
        if(loader != null) {
            logger.debug("add class loader " + loader);
            loaders.add(loader);
        }
    }

    public void addURL(URL url) {
        logger.debug("add class path " + url.toString());
        super.addURL(url);
    }

    synchronized Class loadClassSuper(String name)
            throws ClassNotFoundException {
        // First, check if the class has already been loaded
        Class c = findLoadedClass(name);
        if(c == null) {
            try {
                if(getParent() != null) {
                    c = getParent().loadClass(name);
                }
            }
            catch(ClassNotFoundException e) {
                // If still not found, then invoke findClass in order
                // to find the class.
                c = findClassLocally(name);
            }
        }
        return c;
    }

    protected Class findClass(String name) throws ClassNotFoundException {
        try {
            return findClassLocally(name);
        }
        catch(ClassNotFoundException e) {
            try {
                return findClassCached(name);
            }
            catch(ClassNotFoundException e1) {
                try {
                    return findClassWithout(name, null);
                }
                catch(ClassNotFoundException e2) {
                }
            }
        }
        throw new ClassNotFoundException(name);
    }

    /**
     * only load class in repo local libs
     *
     * @param name
     * @return
     * @throws ClassNotFoundException
     */
    Class findClassLocally(String name) throws ClassNotFoundException {
        return super.findClass(name);
    }


    Class findClassCached(String name) throws ClassNotFoundException {
        if(!classes.containsKey(name)) {
            throw new ClassNotFoundException(name);
        }
        return (Class) classes.get(name);
    }

    void cacheClass(String name, Class clz) {
        classes.put(name, clz);
    }

    /**
     * try load class use all loaders except the specified class
     *
     * @param name
     * @param cl
     * @return
     * @throws ClassNotFoundException
     */
    Class findClassWithout(String name, ClassLoader cl) throws ClassNotFoundException {
        for(Iterator it = loaders.iterator(); it.hasNext();) {
            LocalClassLoader clzLoader = (LocalClassLoader) it.next();
            if(clzLoader != cl) {
                try {
                    // only find the class in LocalClassLoader's Common ClassLoader
                    Class clz = clzLoader.findClassPublic(name);
                    cacheClass(name, clz);
                    return clz;
                }
                catch(ClassNotFoundException e) {
                    continue;
                }
            }
        }
        throw new ClassNotFoundException(name);
    }


    public URL getResource(String name) {
        return super.getResource(name);
    }

    public static void main(String[] args) {

    }
}