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

import java.util.LinkedList;
import java.util.List;

import javax.management.ObjectName;

import org.ow2.jonas.jmx.JmxService;
import org.ow2.jonas.lib.management.reconfig.PropertiesConfigurationData;
import org.ow2.jonas.lib.service.AbsServiceImpl;
import org.ow2.jonas.lib.util.JonasObjectName;
import org.ow2.jonas.service.ServiceException;
import org.ow2.jonas.workcleaner.CleanTask;
import org.ow2.jonas.workcleaner.WorkCleanerException;
import org.ow2.jonas.workcleaner.WorkCleanerService;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

/**
 * The JOnAS WorkCleaner is returned by this service.
 * @author Francois Fornaciari
 */
public class JOnASWorkCleanerService extends AbsServiceImpl implements WorkCleanerService {

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

    /**
     * Default clean period the thread of the cleaner (in seconds).
     */
    private static final  int DEFAULT_CLEAN_PERIOD = 300;

    /**
     * Clean period (in seconds).
     */
    private int period = DEFAULT_CLEAN_PERIOD;

    /**
     * JMX Services for MBeans registration.
     */
    private JmxService jmxService;

    /**
     * The list of tasks to execute at each period.
     */
    private List<CleanTask> taskList = null;

    /**
     *  Name as used to label configuration properties.
     */
    public static final String SERVICE_NAME = "workcleaner";

    /**
     * Associated timer executes tasks periodically.
     */
    private WorkCleanerTimer workCleanerTimer = null;

    /**
     * name of the 'period configuration parameter
     */
    static final String PERIOD = "jonas.service.wc.period";

    /**
     * Implement readable management attribute.
     * @return
     */
    public int getExecutePeriod() {
        return getPeriod();
    }

    /**
     * Implement writable management attribute
     * @param period set period used by the WorkCleaner
     */
    public void setExecutePeriod(final int period) {
        setPeriod(period);
        // Send a notification containing the new value of this property to the
        // listener MBean
        String propName = PERIOD;
        String propValue = new Integer(period).toString();
        sendReconfigNotification(getSequenceNumber(), SERVICE_NAME, new PropertiesConfigurationData(propName, propValue));
    }

    /**
     * Create a JOnASWorkCleanerService.
     */
    public JOnASWorkCleanerService() {
        taskList = new LinkedList<CleanTask>();
        workCleanerTimer = new WorkCleanerTimer();
    }

    /**
     * Returns the WorkCleaner clean period.
     * @return The WorkCleaner clean period
     */
    public int getPeriod() {
        return period;
    }

    /**
     * @param period Clean period used by the WorkCleaner
     */
    public void setPeriod(final int period) {
        this.period = period;
        workCleanerTimer.setPeriod(period);
    }

    /**
     * Start a new WorkCleaner service.
     * {@inheritDoc}
     */
    @Override
    protected void doStart() throws ServiceException {
        // Create a new instance if necessary.
        if (workCleanerTimer == null) {
            workCleanerTimer = new WorkCleanerTimer();
        }

        workCleanerTimer.setWorkCleanerService(this);
        workCleanerTimer.setPriority(Thread.MIN_PRIORITY);
        workCleanerTimer.start();

        // Register the MBean
        registerWorkCleanerMBean(this, getDomainName());

        logger.info("WorkCleaner Service started.");
    }

    /**
     * Stop the WorkCleaner.
     * {@inheritDoc}
     */
    @Override
    protected void doStop() throws ServiceException {
        workCleanerTimer.setStarted(false);
        /**
         * Don't keep the resources.
         */
        workCleanerTimer = null;

        if (jmxService != null) {
            // unregister the MBean
            unregisterWorkCleanerMBean(getDomainName());
        }

        logger.info("WorkCleaner Service stopped.");
    }

    /**
     * Register a new clean task.
     * @param cleanTask the task to add
     * @throws WorkCleanerException if the task cannot be registred
     */
    public synchronized void registerTask(final CleanTask cleanTask) throws WorkCleanerException {
        logger.debug("Register task {0}", cleanTask);
        // checks
        if (taskList == null) {
            throw new WorkCleanerException("Can not add an entry, the vector is null");
        }
        if (cleanTask == null) {
            throw new WorkCleanerException("Can not add a null entry");
        }
        // Add if don't already exist
        if (!taskList.contains(cleanTask)) {
            taskList.add(cleanTask);
        }
    }

    /**
     * Unregister a clean task.
     * @param cleanTask The task to unregister
     * @throws WorkCleanerException If the task cannot be unregistered
     */
    public synchronized void unregisterTask(final CleanTask cleanTask) throws WorkCleanerException {
        logger.debug("Unregister task {0}", cleanTask);
        // checks
        if (taskList == null) {
            throw new WorkCleanerException("Can not unregister an entry, the vector is null");
        }
        if (cleanTask == null) {
            throw new WorkCleanerException("Can not add a null entry");
        }
        // Add exists
        if (taskList.contains(cleanTask)) {
            taskList.remove(cleanTask);
        }
    }

    /**
     * Execute the registered tasks.
     */
    public synchronized void executeTasks() {
        logger.debug("Execute tasks");

        for (CleanTask cleanTask : taskList) {
            try {
                cleanTask.execute();
            } catch (WorkCleanerException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Create an MBean for the JOnAS WorkCleaner.
     * @param service WorkCleaner service to manage
     * @param domainName domain name
     */
    private void registerWorkCleanerMBean(final Object service, final String domainName) {
        // Create and register the MBean for this WorkCleaner
        ObjectName on = JonasObjectName.workCleaner(domainName);
        try {
            jmxService.registerModelMBean(service, on);
        } catch (Exception e) {
            logger.warn("Could not register WorkCleaner MBean", e);
        }
    }

    /**
     * Unregister the WorkCleaner MBean.
     * @param domainName WorkCleaner ObjectName.
     */
    private void unregisterWorkCleanerMBean(final String domainName) {
        ObjectName on = JonasObjectName.workCleaner(domainName);
        try {
            jmxService.unregisterModelMBean(on);
        } catch (Exception e) {
            logger.warn("Could not unregister WorkCleaner MBean", e);
        }
    }

    /**
     * @param jmxService the jmxService to set
     */
    public void setJmxService(final JmxService jmxService) {
        this.jmxService = jmxService;
    }
    
    /**
     * Save updated configuration.
     */
    public void saveConfig() {
        // Send save reconfig notification
        sendSaveNotification(getSequenceNumber(), SERVICE_NAME);
    }

}
