/*
* JBoss, the OpenSource EJB server
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/

package org.jboss.deployment.scope;

import org.jboss.deployment.Deployment;
import org.jboss.deployment.J2eeDeploymentException;
import java.net.URL;
import java.net.URLClassLoader;

import java.util.Collection;

/**
 * A URLClassLoader that is tight to some J2EE deployment and that is 
 * able to share classes/resources under the associated scope. Hey, JDK-S**ckers
 * why did you annotate getResources final? 
 * @author  cgjung
 * @version 0.9
 */
public class ScopedURLClassLoader extends URLClassLoader {

    /** reference to the scope to which resource loading calls
     *  can be delegated to. 
     */
    final protected Scope scope;
    
    /**
     * reference to the deployment that is associated
     * with this classloader
     */
    final protected Deployment deployment;
    

    /** Creates new ScopedURLClassLoader given a set of urls and a parent,
     * representing a particular deployment
     * @param urls the urls for which this classloader is built.
     *
     * @param parent parent classloader
     *
     * @param deployment deployment to deploy into this loader
     *
     * @param scope the scope that this classloader takes part
     */
    public ScopedURLClassLoader(URL[] urls, ClassLoader parent, Deployment deployment, Scope scope) {
        super(urls,parent);
        this.scope=scope;
        this.deployment=deployment;
        scope.registerClassLoader(this);
    }
    
    /** exposes the proper loadClass call
     * @param name name of the class
     *
     * @param resolve whether the class should be resolved
     *
     * @throws ClassNotFoundException if the class cannot be found
     *
     * @return the found class
     *
     */
    protected Class loadClassProperly(String name, boolean resolve) throws ClassNotFoundException {
        return super.loadClass(name,resolve);
    }
    
    /** redirects loadClass in case that it could not be found locally
     * @param name name of the class
     * @param resolve whether the class should be resolved
     * @throws ClassNotFoundException if the class could not be found
     * @return the found java class
     */
    protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
        try{
            return super.loadClass(name,resolve);
        } catch(ClassNotFoundException e) {
            // redirect
            return scope.loadClass(name,resolve,this);
        }
    }
            
    /** exposes the proper getResource call
     * @param name name of the resource
     * @return URL pointing to the resource, null if it could not be found
     *
     */
    protected URL getResourceProperly(String name) {
        return super.getResource(name);
    }
    
    /** redirects getResource in case that it could not be found locally
     * @param name name of the resource
     *
     * @return URL pointing to the resource, null if it cannot
     * be found
     *
     */
    public URL getResource(String name) {
        URL result=super.getResource(name);
        if(result==null)
            result=scope.getResource(name,this);
        return result;
    }
    
    /** what happens on deploy, could be overridden to setup down meta-data and such 
     *  @throws J2eeDeploymentException if this stage of deployment fails for some reason
     */
    protected void onDeploy() throws J2eeDeploymentException {
        // nothing
    }

    /** what happens on undeploy, could be overridden to tear down meta-data and such 
     *  @throws J2eeDeploymentException if this last stage of undeployment fails for some reason
     */
    protected void onUndeploy() throws J2eeDeploymentException {
        // nothing
    }


    /** returns a set of relative urls in string spec that
     * point to applications to which this application
     * is (most likely) dependent on. Works via analysing the
     * Class-Path: in the Manifest.mf.
     * Marc: Please note that sharing scope works also without
     * having these explicit annotations. However,
     * there may be circular dependencies that are already relevant
     * at deployment time (e.g., bean verification) which you otherwise
     * could never setup manually (i.e., by calling the deploy method).
     * @return relative string-url specs pointing to ears.
     *
     */
    public String[] getDependingApplications() {
        try{
            java.util.jar.Manifest manifest=deployment.getManifest();
            
            java.util.jar.Attributes attributes=
                manifest.getMainAttributes();
            
            java.util.StringTokenizer tok=
                new java.util.StringTokenizer(attributes.getValue(java.util.jar.Attributes.Name.CLASS_PATH)," ");
            
            Collection allDeps=new java.util.ArrayList();
            
            while(tok.hasMoreTokens())
                allDeps.add(tok.nextToken());
            
            return (String[]) allDeps.toArray(new String[allDeps.size()]);
        } catch(Exception e) {
            return new String[0];
        }
    }
        
}
