/*
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999 Bull S.A.
 * Contact: jonas-team@objectweb.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
 *
 * Initial developer(s): Adriana Danes
 * Contributor(s):
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.objectweb.jonas.management;

// general Java imports
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Properties;
import java.util.StringTokenizer;

import org.objectweb.util.monolog.api.BasicLevel;
/**
 * This class allows for persistent reconfiguration of a JOnAS service or of a JOnAS resource
 * being configured by a .properties file.
 * @author Adriana Danes
 */
public class ReconfiguratorProp extends AbsReconfigurator {

    /**
     * Initial configuration values, than saved reconfigured values
     */
    private Properties stableConfig;

    /**
     * Reconfigured values (non yet saved)
     */
    private Properties currentConfig;

    /**
     * Construct a reconfigurator for a JOnAS service or a JOnAS resource
     * @param name the name of the .properties configuration file to be updated. It may be 'jonas.properties' if the Reconfigurator
     * is associated to a JOnAS service, or a 'resource.properties' if the Reconfigurator is associated to a
     * data source or a mail factory.
     * @param conf the initial content of the .properties configuration file
     */
    public ReconfiguratorProp(String name, String configFileName, Properties config) {
        super(name, configFileName);
        stableConfig = config;
        currentConfig = new Properties();
    }

    /**
     * Updates the configuration file
     * @param key the modified property name
     * @param value the new value
     * @param sequence the sequence number of management notification producing the update
     */
    public void updateConfig(String key, String value, long sequence) {
        if (sequence > lastSequence) {
            currentConfig.setProperty(key, value);
            lastSequence = sequence;
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "- " + sequence + " - Recufigured property to update " + key + " with value : " + value);
            }
        } else {
            logger.log(BasicLevel.WARN, "Received out of order reconfiguration message !");
            logger.log(BasicLevel.WARN, "Reconfiguration value for property " + key + " : " + value + " lost!");
        }
    }

    /**
     * Updates the configuration file
     * @param key the modified property name
     * @param value the new value
     * @param add if true, the value has to be appended to the old value, if false, it has to be removed from the old value
     * @param sequence the sequence number of management notification producing the update
     */
    void updateConfig(String key, String value, boolean add, long sequence) {
        if (sequence > lastSequence) {
            String oldValue = currentConfig.getProperty(key);
            if (oldValue == null)
                oldValue = stableConfig.getProperty(key);
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "- " + sequence + " - Recufigured property to update " + key + " having value : " + oldValue);
            }
            String newValue = updateValue(oldValue, value, add);
            currentConfig.setProperty(key, newValue);
            lastSequence = sequence;
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "- " + sequence + " - Updated property " + key + " with value : " + newValue);
            }
        } else {
            logger.log(BasicLevel.WARN, "Received out of order reconfiguration message !");
            logger.log(BasicLevel.WARN, "Reconfiguration value for property " + key + " : " + value + " lost!");
        }
    }
    /**
     * Updates the configuration file
     * @param props set of modified properties with their associated values
     * @param sequence the sequence number of management notification producing the update
     */
    void updateConfig(Properties props, long sequence) {
        if (sequence > lastSequence) {
            for(Enumeration pNames = props.propertyNames(); pNames.hasMoreElements() ; ) {
                String aName = (String)pNames.nextElement();
                String aValue = (String)props.getProperty(aName);
                if (aValue != null) {
                    currentConfig.setProperty(aName, aValue);
                    if (logger.isLoggable(BasicLevel.DEBUG)) {
                        logger.log(BasicLevel.DEBUG, "- " + sequence + " - Updated property " + aName + " with value : " + aValue);
                    }
                }
            }
            lastSequence = sequence;
        } else {
            logger.log(BasicLevel.WARN, "Received out of order reconfiguration message !");
        }
    }

    String updateValue(String oldValue, String value, boolean add) {
        value = value.trim();
        oldValue = oldValue.trim();
        String returnValue;
        if (add) {
            returnValue = new String(oldValue);
            returnValue = returnValue + ',' + value;
        } else {
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "Remove " + value + " from " + oldValue);
            }
            returnValue = new String();
            boolean firstToken = true;
            StringTokenizer st = new StringTokenizer(oldValue, ",");
            while (st.hasMoreTokens()) {
                String token = st.nextToken().trim();
                if (!token.equals(value)) {
                    // add the token
                    if (firstToken) {
                        returnValue = new String(token);
                        firstToken = false;
                    } else {
                        returnValue = returnValue + ',' + token;
                    }
                }
            }
        }
        return returnValue;
    }

    /**
     * Saves the updated configuration
     * @param sequence the sequence number of management notification producing the save (in fact store) operation
     */
    public void saveConfig(long sequence) throws ReconfigException {
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "");
        }
        if (sequence > lastSequence) {
            // Update stableConfig with properties in currentConfig
            for (Enumeration props = currentConfig.keys() ; props.hasMoreElements() ;) {
                String reconfiguredProp = (String)props.nextElement();
                String reconfiguredPropValue = currentConfig.getProperty(reconfiguredProp);
                stableConfig.setProperty(reconfiguredProp, reconfiguredPropValue);
            }
            try {
                // Store stableConfig
                FileOutputStream fo = new FileOutputStream(configFileName);
                stableConfig.store(fo,  "Saved configuration file at ");
                fo.close();
                lastSequence = sequence;
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "Configuration file " + configFileName + " updated");
                }
            } catch (FileNotFoundException e) {
                throw new ReconfigException("Cant' save configuration file: " + e.toString());
            } catch(IOException ioe) {
                throw new ReconfigException("Cant' save configuration file: " + ioe.toString());
            }
        } else {
            logger.log(BasicLevel.WARN, "Received out of order save reconfiguration message for " + name + " !");
            logger.log(BasicLevel.WARN, "Can not save !!");
            logger.log(BasicLevel.WARN, "Please reconfigure and than save !!");
            currentConfig = new Properties();
        }
    }
}
