/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2010 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id:LoaderManager.java 10822 2007-07-04 08:26:06Z durieuxp $
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.lib.bootstrap;

import java.io.File;
import java.net.URL;

import org.ow2.jonas.lib.bootstrap.loader.JClassLoader;
import org.ow2.jonas.lib.loader.FilteringClassLoader;
import org.ow2.jonas.lib.loader.OSGiClassLoader;
import org.ow2.util.url.URLUtils;

/**
 * This class create all the ClassLoader necessary for JOnAS.
 * @author Guillaume Sauthier
 * @author Francois Fornaciari
 */
public final class LoaderManager {

    /**
     * LoaderManager unique instance.
     */
    private static LoaderManager instance = null;

    /**
     * External ClassLoader.
     */
    private JClassLoader externalClassLoader = null;

    /**
     * $JONAS_ROOT.
     */
    private String jonasRoot = null;

    /**
     * $JONAS_BASE.
     */
    private String jonasBase = null;

    /**
     * $JONAS_ROOT/lib.
     */
    private File jonasRootLib = null;

    /**
     * $JONAS_ROOT/lib/ext.
     */
    private File jonasRootLibExt = null;

    /**
     * Relative library path.
     */
    private static final String LIB = "lib";

    /**
     * Relative external library path : ext.
     */
    private static final String EXTERNAL_FOLDER = "ext";

    /**
     * Relative external library path : lib/ext.
     */
    private static final String LIB_EXTERNAL = LIB + File.separator + "ext";

    /**
     * Relative external library path : ext.
     */
    private static final String TLD_FOLDER = "internal-ee-tld";

    /**
     * Initialize main directories.
     */
    private LoaderManager() {
        jonasRoot = JProp.getJonasRoot();
        jonasBase = JProp.getJonasBase();
        jonasRootLib = new File(jonasRoot, LIB);
        jonasRootLibExt = new File(jonasRootLib, EXTERNAL_FOLDER);
    }

    /**
     * @return Returns the unique instance of LoaderManager.
     */
    public static LoaderManager getInstance() {
        if (instance == null) {
            instance = new LoaderManager();
        }
        return instance;
    }

    /**
     * @return Returns the External ClassLoader which contains user libraries.
     * @throws Exception When ClassLoader cannot be created.
     */
    public JClassLoader getExternalLoader() throws Exception {
        if (externalClassLoader == null) {
            externalClassLoader = createExternalClassLoader();
        }
        return externalClassLoader;
    }

    /**
     * Constructs the External ClassLoader.
     *
     * @return Returns the External ClassLoader.
     * @throws Exception When ClassLoader cannot be created from CLASSPATH or resources cannot be added to URLs List.
     */
    private JClassLoader createExternalClassLoader() throws Exception {
        JURLs jurls = new JURLs();

        // Add TLD Java EE file
        jurls.add(new File(jonasRootLib, TLD_FOLDER), "javaee-tld.jar");

        // Load $JONAS_ROOT/lib/ext/*.jar
        jurls.add(jonasRootLibExt, ".jar");

        // Add $JONAS_BASE/lib/ext only if $JONAS_BASE != $JONAS_ROOT
        // $JONAS_BASE/lib/ext is added before $JONAS_ROOT/lib/ext
        if (!jonasRoot.toLowerCase().equals(jonasBase.toLowerCase())) {
            File jonasBaseLibExt = new File(jonasBase, LIB_EXTERNAL);
            if (jonasBaseLibExt.exists()) {
                jurls.add(jonasBaseLibExt, ".jar");
            }
        }

        return new JClassLoader("External",
                                jurls.toURLs(),
                                createSystemFilteringClassLoader());
    }

    /**
     * Creates a {@code FilteringClassLoader} instance delegating to its parent
     * {@code OSGiClassLoader}.
     * Filters are loaded from the {@code $[jonas.base]/conf/classloader-default-filtering.xml}.
     * If no XML definition file is found, it returns a non-filtrant loader.
     * @return a configured FilteringClassLoader
     */
    private FilteringClassLoader createSystemFilteringClassLoader() {

        // OSGiClassLoader delegates class loading to the bundle ClassLoader
        // so we can benefit of OSGi properties (Dynamic-Import, ...)
        FilteringClassLoader loader = new FilteringClassLoader(new OSGiClassLoader());

        File xmlConfigFile = new File(new File(JProp.getConfDir()),
                                      FilteringClassLoader.XML_FILE);
        if (xmlConfigFile.isFile()) {
            URL systemDefinitionUrl = URLUtils.fileToURL(xmlConfigFile);
            loader.setDefinitionUrl(systemDefinitionUrl);
        } // else will be a transparent ClassLoader (no filters)

        loader.start();

        return loader;
    }
}