org.lsc.Configuration.java Source code

Java tutorial

Introduction

Here is the source code for org.lsc.Configuration.java

Source

/*
 ****************************************************************************
 * Ldap Synchronization Connector provides tools to synchronize
 * electronic identities from a list of data sources including
 * any database with a JDBC connector, another LDAP directory,
 * flat files...
 *
 *                  ==LICENSE NOTICE==
 * 
 * Copyright (c) 2008 - 2011 LSC Project 
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
    
 *  * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *   * Neither the name of the LSC Project nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *                  ==LICENSE NOTICE==
 *
 *               (c) 2008 - 2011 LSC Project
 *         Sebastien Bahloul <seb@lsc-project.org>
 *         Thomas Chemineau <thomas@lsc-project.org>
 *         Jonathan Clarke <jon@lsc-project.org>
 *         Remy-Christophe Schermesser <rcs@lsc-project.org>
 ****************************************************************************
 */
package org.lsc;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.lang.StringUtils;
import org.lsc.configuration.CsvAuditType;
import org.lsc.configuration.JaxbXmlConfigurationHelper;
import org.lsc.configuration.LdapConnectionType;
import org.lsc.configuration.LdifAuditType;
import org.lsc.configuration.LscConfiguration;
import org.lsc.configuration.PropertiesConfigurationHelper;
import org.lsc.exception.LscConfigurationException;
import org.lsc.exception.LscException;
import org.lsc.jndi.JndiServices;
import org.lsc.utils.output.CsvLayout;
import org.lsc.utils.output.LdifLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.joran.spi.JoranException;

/**
 * Ldap Synchronization Connector Configuration.
 * 
 * This class was initially handling only properties configuration types
 * but with XML evolution, LscConfiguration is now the central point. This point
 * is maintained for historic compatibility issues 
 * 
 * It contains deprecated properties based methods to allow smooth updates of 
 * plugins and external methods components 
 * 
 * @author Sebastien Bahloul <seb@lsc-project.org>
 * @author Remy-Christophe Schermesser <rcs@lsc-project.org>
 */
public class Configuration {

    // Logger
    private static final Logger LOGGER = LoggerFactory.getLogger(Configuration.class);

    /** Filename of the <code>lsc.properties</code> file */
    public static final String PROPERTIES_FILENAME = "lsc.properties";

    /** Filename of the <code>database.properties</code> file */
    public static final String DATABASE_PROPERTIES_FILENAME = "database.properties";

    /** Default location for configuration filename */
    public static String location;

    /** Flag to detect if logging is configured or not yet */
    private static boolean loggingSetup = false;

    // People DN
    public static String DN_PEOPLE = "ou=People";

    // LDAP schema DN
    public static String DN_LDAP_SCHEMA = "cn=Subschema";

    // Enhanced schema DN
    public static String DN_ENHANCED_SCHEMA = "ou=Schema,ou=System";

    // Structures DN
    public static String DN_STRUCTURES = "ou=Structures";

    // Accounts DN
    public static String DN_ACCOUNTS = "ou=Accounts";

    // objectClass for a person
    public static String OBJECTCLASS_PERSON = "inetOrgPerson";

    // objectClass for an employee
    public static String OBJECTCLASS_EMPLOYEE = "inetOrgPerson";

    /**
     * Numbers of days between an entry is set to be deleted and its actual
     * deletion.
     */
    public static int DAYS_BEFORE_SUPPRESSION = 90;

    // The real LDAP base DN
    public static String DN_REAL_ROOT = "dc=lsc-project,dc=org";

    // The maximum user identifier length
    public static int UID_MAX_LENGTH = 8;

    // LSC configuration of the application
    private static PropertiesConfiguration config = null;

    /** Prefix for tasks configuration elements in lsc.properties */
    public static final String LSC_TASKS_PREFIX = "lsc.tasks";

    /** Prefix for syncoptions configuration elements in lsc.properties */
    public static final String LSC_SYNCOPTIONS_PREFIX = "lsc.syncoptions";

    /** The maximum limit of data that can be synchronized by a synchronous task */
    public static final int MAX_CONCURRENT_SYNCHRONIZED = 100000;

    // Default constructor.
    protected Configuration() {
    }

    /**
     * Get data source connection properties.
     * 
     * @return the data source connection properties
     * @throws LscConfigurationException 
     */
    @Deprecated
    public static Properties getSrcProperties() throws LscConfigurationException {
        return JndiServices.getLdapProperties(
                (org.lsc.configuration.LdapConnectionType) LscConfiguration.getConnection("src-ldap"));
    }

    /**
     * Get data destination connection properties.
     * 
     * @return the data destination connection properties
     * @throws LscConfigurationException 
     */
    @Deprecated
    public static Properties getDstProperties() throws LscConfigurationException {
        return JndiServices.getLdapProperties((LdapConnectionType) LscConfiguration.getConnection("dst-ldap"));
    }

    /**
     * Create a Properties object that is a subset of this configuration.
     * If there are no properties matching the prefix, an empty Properties
     * object is returned.
     * 
     * @param prefix
     *            The prefix used to select the properties.
     * @return Properties object with the requests properties without the prefix
     */
    @Deprecated
    public static Properties getAsProperties(final String prefix) {
        return getAsProperties(getConfiguration(), prefix);
    }

    @Deprecated
    public static Properties getAsProperties(final String filename, final String prefix) {
        return getAsProperties(getConfiguration(filename), prefix);
    }

    @Deprecated
    public static Properties getAsProperties(PropertiesConfiguration propsConf, final String prefix) {
        org.apache.commons.configuration.Configuration conf = propsConf.subset(prefix);
        if (conf == null) {
            return null;
        }
        Iterator<?> it = conf.getKeys();
        Properties result = new Properties();
        String key = null;
        Object value = null;
        while (it.hasNext()) {
            key = (String) it.next();
            value = asString(conf.getProperty(key));
            result.put(key, value);
        }
        return result;
    }

    public static Properties getPropertiesSubset(final Properties originalProperties, String prefix) {
        if (originalProperties == null) {
            return null;
        }
        Properties result = new Properties();
        for (Object propertyName : originalProperties.keySet()) {
            String propertyNameStr = (String) propertyName;
            if (propertyNameStr.startsWith(prefix + ".")) {
                String newPropertyName = propertyNameStr.substring(prefix.length() + 1);
                result.put(newPropertyName, originalProperties.getProperty(propertyNameStr));
            }
        }
        return result;
    }

    /**
     * Get a int associated with the given property key
     * 
     * @param key
     *            The property key.
     * @param defaultValue
     *            The default value.
     * @return The associated int.
     */
    @Deprecated
    public static int getInt(final String key, int defaultValue) {
        return getConfiguration().getInt(key, defaultValue);
    }

    /**
     * Get a string associated with the given property key
     * 
     * @param key
     *            The property key.
     * @return The associated string.
     */
    @Deprecated
    public static String getString(final String key) {
        // beware of List problems, so get the object and convert it to a string
        return asString(getConfiguration().getProperty(key));
    }

    /**
     * Get a string associated with the given property key
     * 
     * @param key
     *            The property key.
     * @param defaultValue
     *            The default value.
     * @return The associated string.
     */
    @Deprecated
    public static String getString(final String key, String defaultValue) {
        // beware of List problems, so get the object and convert it to a string
        Object o = getConfiguration().getProperty(key);
        if (o == null) {
            return defaultValue;
        }
        return asString(o);
    }

    private static String cleanup(String path) {
        String ret = path.trim();
        if (ret.charAt(0) == '\'' && ret.charAt(ret.length() - 1) == '\'') {
            ret = ret.substring(1, ret.length() - 1);
        }
        return ret;
    }

    /**
     * Get the path to the directory where configuration files
     * are stored, with a "/" at the end (or "\" on Windows).
     * 
     * All configuration files MUST be read from this directory.
     * 
     * If no directory is specified when launching LSC, this returns 
     * the user's current directory.
     * @return Path to configuration directory
     */
    public static String getConfigurationDirectory() {
        if (location == null) {
            setUp();
        }
        return (location != null ? new File(location).getAbsolutePath() + File.separator : "");
    }

    /**
     * Helper method to do lazy default configuration. This was mainly done to
     * make this class easily testable.
     * 
     * @return the configuration instance used by this class.
     */
    @Deprecated
    protected static PropertiesConfiguration getConfiguration() {
        return getConfiguration(new File(location, PROPERTIES_FILENAME).getAbsolutePath());
    }

    @Deprecated
    protected static PropertiesConfiguration getConfiguration(String filename) {
        if (config == null) {
            URL url = null;
            try {
                url = new File(filename).toURI().toURL();
                LOGGER.debug("Loading configuration url: {}", url);
                config = new PropertiesConfiguration(url);
                config.getKeys();

                DN_PEOPLE = Configuration.getString("dn.people", DN_PEOPLE);
                DN_LDAP_SCHEMA = Configuration.getString("dn.ldap_schema", DN_LDAP_SCHEMA);
                DN_ENHANCED_SCHEMA = Configuration.getString("dn.ldap_schema", DN_ENHANCED_SCHEMA);
                DN_STRUCTURES = Configuration.getString("dn.structures", DN_STRUCTURES);
                DN_ACCOUNTS = Configuration.getString("dn.accounts", DN_STRUCTURES);
                OBJECTCLASS_PERSON = Configuration.getString("objectclass.person", OBJECTCLASS_PERSON);
                OBJECTCLASS_EMPLOYEE = Configuration.getString("objectclass.employee", OBJECTCLASS_EMPLOYEE);
                DAYS_BEFORE_SUPPRESSION = Configuration.getInt("suppression.MARQUAGE_NOMBRE_DE_JOURS",
                        DAYS_BEFORE_SUPPRESSION);
                DN_REAL_ROOT = Configuration.getString("dn.real_root", DN_REAL_ROOT);
                UID_MAX_LENGTH = Configuration.getInt("uid.maxlength", UID_MAX_LENGTH);

            } catch (ConfigurationException e) {
                throw new RuntimeException("Unable to find '" + url + "' file", e);
            } catch (MalformedURLException e) {
                throw new RuntimeException("Unable to find file", e);
            }
        }
        return config;
    }

    /**
     * commons-configuration automatically parse a comma separated value in key
     * and return a list, that's not what we want here, we need to conserve the
     * commas. An appropriate method should be added soon to the API.
     * 
     * @param value
     *            the value to convert, it should be either a String or a List
     * @return the object as a string.
     * @throws ClassCastException
     *             if the object is not a string nor a list.
     */
    private static String asString(Object value) {
        if (value instanceof List) {
            List<?> list = (List<?>) value;
            value = StringUtils.join(list.iterator(), ",");
        }
        return (String) value;
    }

    /**
     * Set the new properties
     * @param prefix the prefix or null
     * @param props the news properties
     * @throws ConfigurationException
     */
    public static void setProperties(String prefix, Properties props) throws ConfigurationException {
        Enumeration<Object> propsEnum = props.keys();
        PropertiesConfiguration conf = Configuration.getConfiguration();
        while (propsEnum.hasMoreElements()) {
            String key = (String) propsEnum.nextElement();
            conf.setProperty((prefix != null ? prefix + "." : "") + key, props.getProperty(key));
        }
        conf.save();
    }

    /**
     * Helper method to read a file from the filesystem and return it as Properties.
     * 
     * @param pathToFile Absolute filename on the filesystem to read.
     * @return Properties from the file
     * @throws FileNotFoundException
     * @throws IOException
     */
    @Deprecated
    public static Properties getPropertiesFromFile(String pathToFile) throws FileNotFoundException, IOException {
        File propertiesFile = new File(pathToFile);
        Properties props = new Properties();
        InputStream st = new FileInputStream(propertiesFile);
        try {
            props.load(st);
        } finally {
            st.close();
        }
        return props;
    }

    /**
     * Helper method to read a file from the configuration directory and return it as Properties.
        
     * @param fileName Filename relative to the configuration directory.
     * @return Properties from the file
     * @throws FileNotFoundException
     * @throws IOException
     */
    @Deprecated
    public static Properties getPropertiesFromFileInConfigDir(String fileName)
            throws FileNotFoundException, IOException {
        return getPropertiesFromFile(Configuration.getConfigurationDirectory() + fileName);
    }

    /**
     * Set up configuration for the given location, including logback.
     * MUST NEVER BE CALLED DIRECTLY : ONLY USED BY LscConfiguration static code instantiation
     * IMPORTANT: don't log ANYTHING before calling this method!
     */
    public static void setUp() {
        if (LscConfiguration.isInitialized()) {
            // Nothing to do there : default configuration must only be used if LSC is not already configured
            return;
        }
        try {
            if (new File(System.getProperty("LSC_HOME"), "etc").isDirectory()
                    && new File(System.getProperty("LSC_HOME"), "etc/lsc.xml").exists()) {
                Configuration.setUp(new File(System.getProperty("LSC_HOME"), "etc").getAbsolutePath(), false);
            } else {
                // Silently bypass mis-configuration because if setUp(String) is called, this method is run first, probably with bad default settings
                if (Configuration.class.getClassLoader().getResource("etc") != null) {
                    Configuration.setUp(Configuration.class.getClassLoader().getResource("etc").getPath(), false);
                }
            }
        } catch (LscException le) {
            System.err.println("Something strange appened: " + le.getMessage());// Silently forget le
        }
    }

    /**
     * Set up configuration for the given location, including logback.
     * IMPORTANT: don't log ANYTHING before calling this method!
     * @param lscConfigurationPath
     * @throws LscException 
     */
    public static void setUp(String lscConfigurationPath) throws LscException {
        setUp(lscConfigurationPath, true);
    }

    /**
     * Set up configuration for the given location, including logback.
     * IMPORTANT: don't log ANYTHING before calling this method!
     * @param lscConfigurationPath
     * @throws LscException 
     */
    public static void setUp(String lscConfigurationPath, boolean validate) throws LscException {
        String message = null;
        if (lscConfigurationPath == null || !new File(lscConfigurationPath).isDirectory()
                || (!new File(lscConfigurationPath, JaxbXmlConfigurationHelper.LSC_CONF_XML).isFile()
                        && !new File(lscConfigurationPath, PROPERTIES_FILENAME).isFile())) {
            message = "Defined configuration location (" + lscConfigurationPath
                    + ") points to a non existing LSC configured instance. "
                    + "LSC configuration loading will fail !";
            LOGGER.error(message);
            throw new RuntimeException(message);
        }
        try {
            location = cleanup(lscConfigurationPath);
            if (!LscConfiguration.isInitialized()) {
                File xml = new File(location, JaxbXmlConfigurationHelper.LSC_CONF_XML);
                File properties = new File(location, Configuration.PROPERTIES_FILENAME);
                if (xml.exists() && xml.isFile()) {
                    LscConfiguration
                            .loadFromInstance(new JaxbXmlConfigurationHelper().getConfiguration(xml.toString()));
                } else if (properties.exists() && properties.isFile()) {
                    LOGGER.warn("LSC configuration loaded from old properties file format !");
                    PropertiesConfigurationHelper.loadConfigurationFrom(properties.getAbsolutePath());
                } else {
                    message = "Unable to load configuration configuration inside the directory: " + location;
                    LOGGER.error(message);
                    return;
                }
            } else {
                LOGGER.error("LSC already configured. Unable to load new parameters ...");
            }
        } catch (LscConfigurationException e) {
            message = "Unable to load configuration (" + e + ")";
            LOGGER.error(message, e);
            return;
        }

        // setup LogBack
        // first, reset the configuration because LogBack automatically loads it from xml
        // while this may be the Java way, it's not our way, we like real text files, not JARs.
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
        JoranConfigurator configurator = new JoranConfigurator();
        configurator.setContext(context);
        context.reset(); //reset configuration

        String logBackXMLPropertiesFile = new File(Configuration.getConfigurationDirectory(), "logback.xml")
                .getAbsolutePath();
        try {
            configurator.doConfigure(logBackXMLPropertiesFile);
            LOGGER.info("Logging configuration successfully loaded from " + logBackXMLPropertiesFile + " ");
            if (LscConfiguration.getAudit("CSV") != null) {
                setUpCsvLogging(context);
            }

            if (LscConfiguration.getAudit("LDIF") != null) {
                setUpLdifLogging(context);
            }
        } catch (JoranException je) {
            System.err.println("Cannot find logging configuration file (" + logBackXMLPropertiesFile + ") !");
        }

        // Logging configured
        setLoggingSetup(true);

        if (validate) {
            LscConfiguration.getInstance().validate();
        }

        // WARNING: don't log anything before HERE!
        LOGGER.info("LSC configuration successfully loaded from {}", Configuration.getConfigurationDirectory());
    }

    /**
     * <P>Helper method to check that a String read in from a property is not empty or null</P>
     * 
     * @param propertyName Name of the property key as read from lsc.properties
     * @param propertyValue Value read from the configuration
     * @param location Where this property is read from, to display a meaningful error message (example: class name, task name, etc)
     * @throws RuntimeException If the property is null or empty.
     */
    public static void assertPropertyNotEmpty(String propertyName, String propertyValue, String location)
            throws LscConfigurationException {
        if (propertyValue == null || propertyValue.length() == 0) {
            throw new RuntimeException("No " + propertyName + " property specified in " + location + ". Aborting.");
        }
    }

    /**
     * Set the flag to determine if logging is configured or not yet
     * 
     * @param loggingSetup Is logging setup yet?
     */
    public static void setLoggingSetup(boolean loggingSetup) {
        Configuration.loggingSetup = loggingSetup;
    }

    /**
     * Get the flag to determine if logging is configured or not yet
     * 
     * @return boolean loggingSetup
     */
    public static boolean isLoggingSetup() {
        return loggingSetup;
    }

    protected static void setUpCsvLogging(LoggerContext context) {
        CsvAuditType audit = (CsvAuditType) LscConfiguration.getAudit("CSV");

        FileAppender<ILoggingEvent> appender = new FileAppender<ILoggingEvent>();
        appender.setName(audit.getName());
        appender.setAppend(audit.isAppend());
        appender.setFile(audit.getFile());
        appender.setContext(context);

        CsvLayout csvLayout = new CsvLayout();
        csvLayout.setLogOperations(audit.getOperations());
        csvLayout.setAttrs(audit.getDatasets());
        csvLayout.setSeparator(audit.getSeparator());
        csvLayout.setOutputHeader(audit.isOutputHeader());
        if (audit.getTaskNames() != null && audit.getTaskNames().getString() != null) {
            csvLayout.setTaskNames(
                    audit.getTaskNames().getString().toArray(new String[audit.getTaskNames().getString().size()]));
        }
        csvLayout.setContext(context);
        csvLayout.start();

        appender.setLayout(csvLayout);
        appender.start();
        ch.qos.logback.classic.Logger rootLogger = context
                .getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
        rootLogger.addAppender(appender);
    }

    protected static void setUpLdifLogging(LoggerContext context) {
        LdifAuditType audit = (LdifAuditType) LscConfiguration.getAudit("LDIF");

        FileAppender<ILoggingEvent> appender = new FileAppender<ILoggingEvent>();
        appender.setName(audit.getName());
        appender.setAppend(audit.isAppend());
        appender.setFile(audit.getFile());
        appender.setContext(context);

        LdifLayout ldifLayout = new LdifLayout();
        ldifLayout.setLogOperations(audit.getOperations());
        if (audit.isLogOnlyLdif() != null) {
            ldifLayout.setOnlyLdif(audit.isLogOnlyLdif());
        }
        ldifLayout.setContext(context);
        ldifLayout.start();

        appender.setLayout(ldifLayout);
        appender.start();
        ch.qos.logback.classic.Logger rootLogger = context
                .getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
        rootLogger.addAppender(appender);
    }

}