/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the License).  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the license at
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */

package com.sun.enterprise.addons;

import java.io.File;
import java.io.FilenameFilter;
import java.util.HashMap;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.jar.Manifest;
import java.util.jar.Attributes;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.net.URL;
import java.net.URLClassLoader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import com.sun.appserv.addons.AddonFatalException;
import com.sun.enterprise.addons.util.JarFileFilter;
import com.sun.enterprise.util.i18n.StringManager;

/**
 * Abstract Controller class. This gives all the common 
 * functionality for AddonInstallationController and 
 * AddonConfigurationController. It is also the basic
 * factory that provide concrete Controller classes.
 *  
 * @see AddonInstallationController
 * @see AddonConfigurationController
 * @since 9.1
 * @author binod@dev.java.net
 */
public abstract class AddonController {

    private HashMap apiBasedServices = new HashMap();
    private HashMap mainClassBasedServices = new HashMap();
    private HashMap simpleJars = new HashMap();
    private File installRoot = null;
    private boolean servicesAreLoaded = false;

    private Logger logger = null;
    protected static StringManager localStrings =
    StringManager.getManager(AddonController.class);

    /*
     * Examine the plugins and load the necessary services.
     * The loaded services and their names will be saved in HashMap
     * that can be used by others.
     */
    protected void loadServices(File jarDir) throws AddonFatalException {

        if (servicesAreLoaded) {
            return;
        } else {
            servicesAreLoaded = true;
        }

        try {
            File[] jars = jarDir.listFiles(getFilenameFilter());
            if (jars == null) return;
            for (File jar : jars) {
                 if (jar != null) {
                    if (jar.getName().startsWith("grizzly")) {
                        continue;
                    } else if (jar.getName().startsWith("freemarker")) {
                        continue;
                    } else if (jar.getName().startsWith("wadl2java")) {
                        continue;
                    }
                 }
                 JarFile jF = new JarFile(jar);
                 String serviceName = findApiBasedService(jF);
                 ClassLoader cl = createClassLoader(jar.toURI().toURL());
                 if (serviceName != null) {
                     apiBasedServices.put(cl.loadClass
                     (serviceName).newInstance(), getName(jar));
                 } else {
                     String mainClass = findMainClassBasedService(jF);
                     if (mainClass != null) {
                         mainClassBasedServices.put(cl.loadClass
                         (mainClass).newInstance(), getName(jar));
                     } else {
                         simpleJars.put(jar, getName(jar));
                     }
                 }
            }
        } catch (AddonFatalException afe) {
            throw afe;
        }catch (Exception e) {
            throw new AddonFatalException(e);
        }
    }

    /**
     * Create a classloader that load the service.
     */
    protected ClassLoader createClassLoader(URL jar) 
    throws AddonFatalException {
        URL[] classpath = null;
        try {
            classpath = AddonClassPath.getClassPath(jar, 
            getInstallRoot().getCanonicalPath(), getLogger());
        } catch (Exception e) {
            throw new AddonFatalException(e);
        }
        return new URLClassLoader(classpath, this.getClass().getClassLoader());
    }

    /**
     * Return the services that are based on the java api for addons.
     */
    protected HashMap getApiBasedServices() {
        return apiBasedServices;
    }

    /**
     * Return the services that are based on the manifest main class.
     */
    protected HashMap getMainClassBasedServices() {
        return mainClassBasedServices;
    }

    /**
     * Return the services that are neither api based nor main class 
     * based. These are simple jar files. 
     */
    protected HashMap getSimpleJars() {
        return simpleJars;
    }

    /**
     * Each controller use specific naming scheme for the 
     * finding the plugins. 
     */
    abstract protected FilenameFilter getFilenameFilter();

    /**
     * Retrive the name of the addon from the file name.
     */
    abstract protected String getName(File jar) throws AddonFatalException;
    

    /**
     * Return the directory where the plugin jar files  
     * will be kept.
     */
    abstract protected File getServiceJarLocation(); 

    /**
     * Return the service interface name.
     */
    abstract protected String getServiceInterface(); 

    /**
     * Find the api based service from the jar file.
     */
    protected String findApiBasedService(JarFile jF) 
    throws AddonFatalException {
        try {
            ZipEntry zE = jF.getEntry(getServiceInterface());
            if (zE != null) {
                InputStream in = jF.getInputStream(zE);
                BufferedReader br = new BufferedReader(
                                    new InputStreamReader(in));
                return br.readLine();
            }
        } catch (Exception e) {
            getLogger().log(Level.FINE, e.getMessage(), e);
            throw new AddonFatalException(e);
        }
        return null;
    }

    /**
     * Find the main class based service from the jarFile.
     */
    protected String findMainClassBasedService(JarFile jF) 
    throws AddonFatalException {
        try {
            Manifest mf = jF.getManifest();
            if(mf != null) {
                Attributes attrs = mf.getMainAttributes();
                if(attrs != null) {
                    String main = attrs.getValue(Attributes.Name.MAIN_CLASS);
                    if(main != null) {
                        return main;
                    }
                }
            }
        } catch (Exception e) {
            throw new AddonFatalException(e);
        }
        return null;
    }


    /**
     * Set the installation root directory.
     */
    public void setInstallRoot(File installRoot) {
        this.installRoot = installRoot;
    }

    /**
     * Return the installation root directory.
     */
    protected File getInstallRoot() {
        return this.installRoot;
    }


    /**
     * Create a controller class for Addon Installation.
     */
    public static AddonInstallationController 
        getAddonInstallationController() {
        return new AddonInstallationController();
    }
    
    /**
     * Create a controller class for Addon Configuration.
     */
    public static AddonConfigurationController 
        getAddonConfigurationController() {
        return new AddonConfigurationController();
    }


    /**
     * Return the logger instance used for logging.
     */
    protected Logger getLogger() {
        if (logger == null) {
            return Logger.getAnonymousLogger();
        }
        return logger;
    }

    /**
     * Set an appropriate logger.
     */
    public void setLogger(Logger logger) {
        this.logger = logger; 
    }

}
