/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2008-2009 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$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.services.bootstrap;

import java.io.File;
import java.io.FilenameFilter;

import org.ow2.jonas.configuration.ConfigurationManager;
import org.ow2.jonas.configuration.DeploymentPlanDeployer;
import org.ow2.jonas.lib.bootstrap.JProp;
import org.ow2.util.archive.api.IArchive;
import org.ow2.util.archive.impl.ArchiveManager;
import org.ow2.util.ee.deploy.api.deployable.IDeployable;
import org.ow2.util.ee.deploy.api.deployer.DeployerException;
import org.ow2.util.ee.deploy.api.deployer.IDeployerManager;
import org.ow2.util.ee.deploy.impl.helper.DeployableHelper;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

/**
 * Allows to deploy or undeploy all bundles for a particular deployment plan. At the
 * server initialization, deploy all JOnAS services defined in the configuration.
 * @author Francois Fornaciari
 */
public class JOnASDeploymentPlanDeployer implements DeploymentPlanDeployer {

    /**
     * Deployer Manager service reference
     */
    private IDeployerManager deployerManager = null;

    /**
     * ConfigurationManager service reference.
     */
    private ConfigurationManager configurationManager = null;

    /**
     * Logger.
     */
    private Log logger = LogFactory.getLog(JOnASDeploymentPlanDeployer.class);

    /**
     * URL internal repository location.
     */
    private static final String URL_INTERNAL_REPOSITORY = JProp.getRepositoriesRootDir() + File.separator + "url-internal";

    /**
     * {@inheritDoc}
     */
    public void deploy(final String name) throws DeployerException {
        String service = name;
        String baseName = getBaseName(service);
        if (baseName != null) {
            deploy(baseName);
            service = getImplementationName(service);
        }

        File file = new File(URL_INTERNAL_REPOSITORY, service + ".xml");
        if (file.exists()) {
            deployDeploymentPlan(file);
        } else {
            throw new DeployerException("Cannot deploy the deployment plan '" + name + "', file '" + file + "' not found");
        }
    }

    /**
     * {@inheritDoc}
     */
    public void undeploy(final String name) throws DeployerException {
        String service = name;
        String baseName = getBaseName(service);
        if (baseName != null) {
            undeploy(baseName);
            service = getImplementationName(service);
        }

        File file = new File(URL_INTERNAL_REPOSITORY, service + ".xml");
        if (file.exists()) {
            undeployDeploymentPlan(file);
        } else {
            throw new DeployerException("Cannot undeploy the deployment plan '" + name + "', file '" + file + "' not found");
        }
    }

    /**
     * Deploy a deployment plan using its associated deployment plan file.
     * @param file File corresponding to the plan to deploy
     * @throws Exception If the deployment of the deployment plan fails
     */
    private void deployDeploymentPlan(final File file) throws DeployerException {
        logger.debug("Deploying the deployment plan for ''{0}''", file);

        // Get the deployable
        IArchive archive = ArchiveManager.getInstance().getArchive(file);
        if (archive == null) {
            throw new DeployerException("Ignoring invalid file '" + file + "'");
        }

        IDeployable deployable = null;
        try {
            deployable = DeployableHelper.getDeployable(archive);
            if (!this.deployerManager.isDeployed(deployable)) {
                this.deployerManager.deploy(deployable);
            }
        } catch (Exception e) {
            throw new DeployerException("Cannot deploy the deployment plan for '" + file + "'", e);
        }
    }

    /**
     * Undeploy a deployment plan using its associated deployment plan file.
     * @param file File corresponding to the plan to undeploy
     * @throws org.ow2.util.ee.deploy.api.deployer.DeployerException If the undeployment of the deployment plan fails
     */
    private void undeployDeploymentPlan(final File file) throws DeployerException {
        logger.debug("Undeploying the deployment plan for ''{0}''", file);

        // Get the deployable
        IArchive archive = ArchiveManager.getInstance().getArchive(file);
        if (archive == null) {
            throw new DeployerException("Ignoring invalid file '" + file + "'");
        }

        IDeployable deployable = null;
        try {
            deployable = DeployableHelper.getDeployable(archive);
            if (this.deployerManager.isDeployed(deployable)) {
                this.deployerManager.undeploy(deployable);
            }
        } catch (Exception e) {
            throw new DeployerException("Cannot undeploy the deployment plan for '" + file + "'", e);
        }
    }

    /**
     * @param deployerManager the {@link org.ow2.util.ee.deploy.api.deployer.IDeployerManager} to set
     */
    public void setDeployerManager(final IDeployerManager deployerManager) {
        this.deployerManager = deployerManager;
    }

    /**
     * Set the ConfigurationManager service.
     * @param configurationManager the configurationManager to set
     */
    public void setConfigurationManager(final ConfigurationManager configurationManager) {
        this.configurationManager = configurationManager;
    }

    /**
     * Returns the name of the user-defined implementation service.
     * @param service The given service name
     * @return The name of the user-defined implementation service
     */
    private String getImplementationName (final String service) {
        String serviceClass = configurationManager.getServiceProperties(service).get("class");
        // The value follows this construction:
        // org.ow2.jonas.<service>.<implementation>.<...>
        // The implementation name is the 5th element.
        // TODO: method not easily extensible if the package name doesn't
        // start with org.ow2.jonas
        String implementationName = serviceClass.split("\\.")[4];

        // Return the <service>-<implementationName>.xml file name
        return service + "-" + implementationName;
    }

    /**
     * Returns the base deployment plan for a given service or null if it doesn't exist.
     * @param service The service name
     * @return The base deployment plan for a given service
     */
    private String getBaseName (final String service) {
        File configDir = new File(URL_INTERNAL_REPOSITORY);
        final String baseName = service + "-base";
        File[] base = configDir.listFiles(new FilenameFilter() {
            public boolean accept(final File dir, final String name) {
                return (name.equalsIgnoreCase(baseName.concat(".xml")));
            }
        });
        if (base != null && base.length == 1) {
            return baseName;
        } else {
            return null;
        }
    }
}
