Java tutorial
/* **************************************************************************** * 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); } }