/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2007 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 (at your option) 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.deployablemonitor;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.management.Attribute;
import javax.management.ObjectName;

import org.ow2.jonas.depmonitor.MonitoringService;
import org.ow2.jonas.jmx.JmxService;
import org.ow2.jonas.lib.bootstrap.JProp;
import org.ow2.jonas.lib.service.AbsServiceImpl;
import org.ow2.jonas.lib.util.JonasObjectName;
import org.ow2.jonas.service.ServiceException;
import org.ow2.util.ee.deploy.api.deployer.IDeployerManager;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

/**
 * Service that will launch the Deployable Monitor.
 * @author Florent BENOIT
 */
public class DeployableMonitorService extends AbsServiceImpl implements MonitoringService {

    /**
     * Property for the development mode.
     */
    public static final String DEVELOPMENT_MODE_PROPERTY = "development";

    /**
     * Property for the list of extra directories to analyze.
     */
    public static final String DIRECTORIES_LIST_PROPERTY = "directories";

    /**
     * The name of the JONAS_BASE directory.
     */
    protected static final String JONAS_BASE = JProp.getJonasBase();

    /**
     * Default directory to analyze.
     */
    public static final String DEFAULT_DIRECTORY = JONAS_BASE + File.separator + "deploy";

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

    /**
     * Development mode or not ? Default is true.
     */
    private boolean developmentMode = true;

    /**
     *  JMX Service.
     */
    private JmxService jmxService = null;

    /**
     * List of directory names.
     */
    private List<String> directoryNames = null;

    /**
     * List of directories.
     */
    private List<File> directories = null;

    /**
     * Link to the deployable monitor.
     */
    private DeployableMonitor deployableMonitor = null;

    /**
     * When the DeployableMonitor has finished its first monitoring cycle,
     * this attribute is set to 'true'.
     */
    private boolean readyToRunning = false;

    /**
     * Default constructor.
     */
    public DeployableMonitorService() {
        this.directories = new LinkedList<File>();
        this.deployableMonitor = new DeployableMonitor(this);
    }

    /**
     * Start the Deployable Monitor service.
     * @throws ServiceException if the startup failed.
     */
    @Override
    protected void doStart() throws ServiceException {
        logger.info("Use the deploy directories ''{0}'', development mode is ''{1}''", directories, developmentMode);
        /**
         * Create a new instance if necessary.
         */
        if (deployableMonitor == null) {
            deployableMonitor = new DeployableMonitor(this);
        }

        // Add the default directory
        File defaultDirectory = new File(DEFAULT_DIRECTORY);
        if (!defaultDirectory.exists()) {
            logger.info("Creating default deploy directory ''{0}''", defaultDirectory);
            defaultDirectory.mkdirs();
        }
        directories.add(defaultDirectory);

        // Load the Descriptors
        if (jmxService != null) {
            jmxService.loadDescriptors(getClass().getPackage().getName(),
                                getClass().getClassLoader());

            // And register MBean
            try {
                jmxService.registerModelMBean(this, JonasObjectName.deployableMonitorService(getDomainName()));
            } catch (Exception e) {
                logger.warn("Cannot register MBean for Deployable Monitor service", e);
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
    }

    /**
     * Starts the {@link DeployableMonitor} instance.
     */
    public void startMonitoring() {
        logger.debug("Start DeployableMonitor");
        deployableMonitor.start();
    }

    /**
     * Stop the Deployable Monitor service.
     * @throws ServiceException if the stop failed.
     */
    @Override
    protected void doStop() throws ServiceException {
        // Unregister MBean
        if (jmxService != null) {
            try {
                jmxService.unregisterModelMBean(JonasObjectName.deployableMonitorService(getDomainName()));
            } catch (Exception e) {
                logger.warn("Cannot unregister MBean for Deployable Monitor service", e);
            }
        }
        // Stop deploy monitor
        deployableMonitor.stopOrder();
        /**
         * Don't keep the resources.
         */
        deployableMonitor = null;

        logger.info("DeployableMonitor stopped successfully");

    }

    /**
     * @param dirs List of deployment directories to observe.
     */
    public void setDirectories(final String dirs) {
        List<String> additionalDirectories = convertToList(dirs);
        for (String dir : additionalDirectories) {
            File tmpFile = new File(dir);

            // Not an absolute file, should be relative to JONAS_BASE.
            if (!tmpFile.isAbsolute()) {
                tmpFile = new File(JONAS_BASE + File.separator + dir);
            }

            // Directory exists ?
            if (!tmpFile.exists()) {
                logger.warn("The given directory ''{0}'' is neither present on the filesystem or in JONAS_BASE ''{1}''",
                        tmpFile, JONAS_BASE);
            } else {
                // Add the directory
                directories.add(tmpFile);
            }
        }

        deployableMonitor.setDirectories(directories);
    }

    /**
     * The list of the directory names.
     * @return The list of the directory names.
     */
    public String[] getDirectoryNames() {
        ArrayList<String> result = new ArrayList<String>();
        Iterator<File> it = directories.iterator();
        while (it.hasNext()) {
            File directory = it.next();
            result.add(directory.getName());
        }
        String[] dirNames = new String[result.size()];
        int i = 0;
        for (String dir : result) {
            dirNames[i++] = dir;
        }
        return dirNames;
    }

    /**
     * @param exclusionList The list of exclusion patterns.
     */
    public void setExclusions(final String exclusionList) {
        List<String> exclusions = convertToList(exclusionList);
        deployableMonitor.setExclusionPatterns(exclusions);
    }

    /**
     * @param deployerManager the {@link IDeployerManager} to set
     */
    public void setDeployerManager(final IDeployerManager deployerManager) {
        // update the monitor object
        deployableMonitor.setDeployerManager(deployerManager);
    }

    /**
     * @param jmxService the jmxService to set
     */
    public void setJmxService(final JmxService jmxService) {
        this.jmxService = jmxService;
    }

    /**
     * Called when the DeploymentMonitor has finished its first round of
     * deployment (all that was under deploy/ at startup time).
     */
    public void firstCheckEnded() {
        try {
            // TODO Maybe that could be simplified a bit ? like just a method call ?
            // This code is here to notify the J2EEServer that we've
            // finished our first deployment round and that the
            // J2EEServer' state can be set to RUNNING
            ObjectName on = JonasObjectName.deployableMonitorService(getDomainName());
            Attribute att = new Attribute("readyToRunning", true);
            jmxService.getJmxServer().setAttribute(on, att);
        } catch (Exception e) {
            logger.warn("Problem when trying to modify 'readyToRunning' attribute");
        }
    }

    /**
     * @return <tt>true</tt> if the monitor is ready to run.
     */
    public boolean isReadyToRunning() {
        return readyToRunning;
    }

    /**
     * Set the readyToRunning flag.
     * @param readyToRunning new value
     */
    public void setReadyToRunning(final boolean readyToRunning) {
        this.readyToRunning = readyToRunning;
    }

    /**
     * @return <tt>true</tt> if the development mode set to true.
     */
    public boolean isDevelopmentMode() {
        return developmentMode;
    }

    /**
     * Set developmentMode value to true (enable), or to false (disable).
     * @param developmentMode value to set
     */
    public void setDevelopmentMode(final boolean mode) {
        developmentMode = mode;
        // update the monitor object
        deployableMonitor.setDevelopmentMode(mode);
    }
}
