/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2008 Bull SAS
 * 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.resourcemonitor.internal;

import java.util.List;

import javax.management.ObjectName;

import org.ow2.jonas.jmx.JmxService;
import org.ow2.jonas.lib.service.AbsServiceImpl;
import org.ow2.jonas.lib.util.JonasObjectName;
import org.ow2.jonas.resourcemonitor.ResourceMonitorService;
import org.ow2.jonas.service.ServiceException;
import org.ow2.util.ee.deploy.api.deployer.DeployerException;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;
import org.ow2.util.plan.deploy.deployable.api.DeploymentPlanDeployable;
import org.ow2.util.plan.deployer.api.FragmentUndeploymentException;
import org.ow2.util.plan.deployer.api.IDeploymentPlanDeployer;
import org.ow2.util.plan.fetcher.api.IResourceFetcher;
import org.ow2.util.plan.monitor.api.IResourceMonitor;

/**
 * Implementation for the ResourceMonitor interface.
 * @author Mickaël LEDUQUE
 *
 */
public class JOnASResourceMonitorService extends AbsServiceImpl implements ResourceMonitorService,
        JOnASResourceMonitorServiceMBean {

    /**
     * Default value for the monitorInterval property.
     */
    private final static long DEFAULT_MONITOR_TIME_INTERVAL = 60000;

    /**
     * The logger.
     */
    private Log logger = LogFactory.getLog(JOnASResourceMonitorService.class);

    /**
     * The resource monitor.
     */
    private IResourceMonitor resourceMonitor = null;

    /**
     * The monitoring interval in milliseconds.
     */
    private Long monitorInterval = null;

    /**
     * The JMX service.
     */
    private JmxService jmxService = null;

    /**
     * The deployment plan deployer.
     */
    private IDeploymentPlanDeployer deploymentPlanDeployer = null;

    /**
     * The monitoring thread.
     */
    private MonitoringThread monitoringThread = new MonitoringThread();

    /**
     * {@inheritDoc}
     */
    @Override
    protected void doStart() throws ServiceException {
        // register the service as Mbean
        try {
            ObjectName objectName = JonasObjectName.resourceMonitor(this.getDomainName());
            jmxService.registerMBean(this, objectName);
        } catch (Exception e) {
            logger.error("JMX registration error, {0}", e);
            throw new ServiceException("JMX registration error", e);
        }

        if (this.monitorInterval == null) {
            this.monitorInterval = DEFAULT_MONITOR_TIME_INTERVAL;
        }

        // start the monitoring.
        monitoringThread.start();

        logger.info("Resource Monitor Service Started");
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void doStop() throws ServiceException {
        // Ask the monitoring thread to stop.
        this.monitoringThread.isStopping = true;

        if (this.jmxService != null) {
            try {
                ObjectName objectName = JonasObjectName.resourceMonitor(this.getDomainName());
                jmxService.unregisterMBean(objectName);
            } catch (Exception e) {
                logger.error("JMX registration error, {0}", e);
                throw new ServiceException("JMX registration error", e);
            }
        }
        logger.info("Resource Monitor Service Stopped");
    }

    /**
     * {@inheritDoc}
     */
    public Integer getMonitoredResourcesCount() {
        return this.resourceMonitor.getMonitoredResourcesCount();
    }

    /**
     * {@inheritDoc}
     */
    public List<String> getMonitoredResourcesDescriptions() {
        return this.resourceMonitor.getMonitoredResourcesDescriptions();
    }

    /**
     * {@inheritDoc}
     */
    public Long getMonitoringInterval() {
        return this.monitorInterval;
    }

    /**
     * {@inheritDoc}
     */
    public void setMonitoringInterval(final Long interval) {
        logger.debug("Monitoring interval set to {0}", interval);
        this.monitorInterval = interval;
    }

    /**
     * Sets the resource monitor.
     * @param resourceMonitor the new resource monitor.
     */
    public void setResourceMonitor(final IResourceMonitor resourceMonitor) {
        this.resourceMonitor = resourceMonitor;
    }

    /**
     * Returns the resource monitor.
     * @return the resource monitor.
     */
    public IResourceMonitor getResourceMonitor() {
        return this.resourceMonitor;
    }

    /**
     * Returns the JMX service.
     * @return the JMX service.
     */
    public JmxService getJmxService() {
        return this.jmxService;
    }

    /**
     * Sets the JMX service.
     * @param jmxService the new JMX service.
     */
    public void setJmxService(final JmxService jmxService) {
        this.jmxService = jmxService;
    }

    /**
     * Sets the deployment plan deployer service.
     * @param deploymentPlanDeployer the new deployment plan deployer service.
     */
    public void setDeploymentPlanDeployer(final IDeploymentPlanDeployer deploymentPlanDeployer) {
        this.deploymentPlanDeployer = deploymentPlanDeployer;
    }

    /**
     * Returns the deployment plan deployer service.
     * @return the deployment plan deployer service.
     */
    public IDeploymentPlanDeployer getDeploymentPlanDeployer() {
        return this.deploymentPlanDeployer;
    }

    /**
     * Thread that take care of resource monitoring.
     * @author Mickaël LEDUQUE
     */
    private class MonitoringThread extends Thread {
        /**
         * Whether the service is stopping.
         */
        boolean isStopping = false;

        /**
         * {@inheritDoc}
         */
        // TODO what if the interval is changed during sleep?
        @Override
        public void run() {
            for (;;) {
                // sleep
                try {
                    Thread.sleep(monitorInterval);
                } catch (InterruptedException e) {
                    logger.warn("Sleep interrupted");
                }
                // Check if it should stop
                if (isStopping) {
                    return;
                }
                // Check the resources
                // TODO : should synchronized(resourceMonitor) ?? -- Potentially
                // too long! but we could be redeploying undeployed resources...
                for (IResourceFetcher resourceFetcher : resourceMonitor.getChangedResources()) {
                    processResource(resourceFetcher);
                    // Before processing the next resource, check if we should stop.
                    if (isStopping) {
                        return;
                    }
                }
            }
        }

        private void processResource(final IResourceFetcher resourceFetcher) {
            try {
                deploymentPlanDeployer.undeployDeployment(resourceFetcher.getDeployment());
            } catch (FragmentUndeploymentException e) {
                logger.error(
                        "Exception while trying to redeploy deployment {0} (during undeployment) - trying to go on.",
                        resourceFetcher.getDeployment());
            }
            try {
                deploymentPlanDeployer.deployDeployment(resourceFetcher.getDeployment());
                logger.debug("Resource {0} changed and was redeployed", resourceFetcher.getDeployment());
            } catch (Exception e) {
                logger.error(
                        "Exception while trying to redeploy deployment {0} (during deployment) - aborting",
                        resourceFetcher.getDeployment());
                // Check if the deployment was in an atomic deployment plan.
                // If so, undeploy the plan.
                DeploymentPlanDeployable deploymentPlanDeployable = deploymentPlanDeployer
                        .getOwnerDeploymentPlanDeployable(resourceFetcher.getDeployment());
                if (deploymentPlanDeployable == null) {
                    logger.error("Could not find which deployment plan owns the deployment {0} - stopping undeployment",
                            resourceFetcher.getDeployment());
                } else {
                    try {
                        deploymentPlanDeployer.undeploy(deploymentPlanDeployable);
                    } catch (DeployerException deployerException) {
                        logger.error("Could not undeploy deployment plan {0}", deploymentPlanDeployable);
                    }
                }
            }
        }
    }
}
