org.ejbca.config.ConfigurationHolder.java Source code

Java tutorial

Introduction

Here is the source code for org.ejbca.config.ConfigurationHolder.java

Source

/*************************************************************************
 *                                                                       *
 *  EJBCA: The OpenSource Certificate Authority                          *
 *                                                                       *
 *  This software 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.                    *
 *                                                                       *
 *  See terms of license at gnu.org.                                     *
 *                                                                       *
 *************************************************************************/

package org.ejbca.config;

import java.io.File;
import java.net.URL;
import java.util.Iterator;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.SystemConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
import org.apache.log4j.Logger;

/**
 * This is a singleton. Used to configure common-configuration with our sources.
 * 
 * Use like this:
 *   String value = ConfigurationHolder.getString("my.conf.property.key"); or
 *   String value = ConfigurationHolder.getString("my.conf.property.key", "default value");
 * or
 *   String value = ConfigurationHolder.getExpandedString("my.conf.property.key", "default value");
 * to be able to parse values containing ${property}
 * 
 * See in-line comments below for the sources added to the configuration.
 * 
 * @version $Id: ConfigurationHolder.java 11941 2011-05-10 13:18:35Z aveen4711 $
 */
public final class ConfigurationHolder {

    private static final Logger log = Logger.getLogger(ConfigurationHolder.class);

    private static CompositeConfiguration config = null;
    private static CompositeConfiguration configBackup = null;

    /** This is a singleton so it's not allowed to create an instance explicitly */
    private ConfigurationHolder() {
    }

    /** ejbca.properties must be first in this file, because CONFIGALLOWEXTERNAL is defined in there. */
    public static final String[] CONFIG_FILES = { "ejbca.properties", "web.properties", "cmp.properties",
            "externalra-caservice.properties", "ocsp.properties", "extendedkeyusage.properties", "jaxws.properties",
            "xkms.properties", "log.properties", "logdevices/log4j.properties", "cache.properties",
            "database.properties", "va.properties", "va-publisher.properties" };

    /** Configuration property that enables dynamic reading of properties from the file system. This is not allowed by default for security reasons. */
    public static final String CONFIGALLOWEXTERNAL = "allow.external-dynamic.configuration";

    public static Configuration instance() {
        if (config == null) {
            // read ejbca.properties, from config file built into jar, and see if we allow configuration by external files
            boolean allowexternal = false;
            try {
                final URL url = ConfigurationHolder.class.getResource("/conf/" + CONFIG_FILES[0]);
                if (url != null) {
                    final PropertiesConfiguration pc = new PropertiesConfiguration(url);
                    allowexternal = "true".equalsIgnoreCase(pc.getString(CONFIGALLOWEXTERNAL, "false"));
                    log.info("Allow external re-configuration: " + allowexternal);
                }
            } catch (ConfigurationException e) {
                log.error("Error intializing configuration: ", e);
            }
            config = new CompositeConfiguration();

            // Only add these config sources if we allow external configuration
            if (allowexternal) {
                // Override with system properties, this is prio 1 if it exists (java -Dscep.test=foo)
                config.addConfiguration(new SystemConfiguration());
                log.info("Added system properties to configuration source (java -Dfoo.prop=bar).");

                // Override with file in "application server home directory"/conf, this is prio 2
                for (int i = 0; i < CONFIG_FILES.length; i++) {
                    File f = null;
                    try {
                        f = new File("conf" + File.separator + CONFIG_FILES[i]);
                        final PropertiesConfiguration pc = new PropertiesConfiguration(f);
                        pc.setReloadingStrategy(new FileChangedReloadingStrategy());
                        config.addConfiguration(pc);
                        log.info("Added file to configuration source: " + f.getAbsolutePath());
                    } catch (ConfigurationException e) {
                        log.error("Failed to load configuration from file " + f.getAbsolutePath());
                    }
                }
                // Override with file in "/etc/ejbca/conf/, this is prio 3
                for (int i = 0; i < CONFIG_FILES.length; i++) {
                    File f = null;
                    try {
                        f = new File("/etc/ejbca/conf/" + CONFIG_FILES[i]);
                        final PropertiesConfiguration pc = new PropertiesConfiguration(f);
                        pc.setReloadingStrategy(new FileChangedReloadingStrategy());
                        config.addConfiguration(pc);
                        log.info("Added file to configuration source: " + f.getAbsolutePath());
                    } catch (ConfigurationException e) {
                        log.error("Failed to load configuration from file " + f.getAbsolutePath());
                    }
                }
            } // if (allowexternal)

            // Default values build into jar file, this is last prio used if no of the other sources override this
            for (int i = 0; i < CONFIG_FILES.length; i++) {
                addConfigurationResource(CONFIG_FILES[i]);
            }
            // Load internal.properties only from built in configuration file
            try {
                final URL url = ConfigurationHolder.class.getResource("/internal.properties");
                if (url != null) {
                    final PropertiesConfiguration pc = new PropertiesConfiguration(url);
                    config.addConfiguration(pc);
                    log.debug("Added url to configuration source: " + url);
                }
            } catch (ConfigurationException e) {
                log.error("Failed to load configuration from resource internal.properties", e);
            }
        }
        return config;
    }

    /** Method used primarily for JUnit testing, where we can add a new properties file (in tmp directory)
     * to the configuration.
     * @param filename the full path to the properties file used for configuration.
     */
    public static void addConfigurationFile(final String filename) {
        // Make sure the basic initialization has been done
        instance();
        File f = null;
        try {
            f = new File(filename);
            final PropertiesConfiguration pc = new PropertiesConfiguration(f);
            pc.setReloadingStrategy(new FileChangedReloadingStrategy());
            config.addConfiguration(pc);
            log.info("Added file to configuration source: " + f.getAbsolutePath());
        } catch (ConfigurationException e) {
            log.error("Failed to load configuration from file " + f.getAbsolutePath());
        }
    }

    /**
     * Add built in config file
     */
    public static void addConfigurationResource(final String resourcename) {
        // Make sure the basic initialization has been done
        instance();
        try {
            final URL url = ConfigurationHolder.class.getResource("/conf/" + resourcename);
            if (url != null) {
                final PropertiesConfiguration pc = new PropertiesConfiguration(url);
                config.addConfiguration(pc);
                log.debug("Added url to configuration source: " + url);
            }
        } catch (ConfigurationException e) {
            log.error("Failed to load configuration from resource " + "/conf/" + resourcename, e);
        }
    }

    /**
     * @return the configuration as a regular Properties object
     */
    public static Properties getAsProperties() {
        final Properties properties = new Properties();
        final Iterator i = instance().getKeys();
        while (i.hasNext()) {
            final String key = (String) i.next();
            properties.setProperty(key, instance().getString(key));
        }
        return properties;
    }

    /**
     * @param property the property to look for
     * @param defaultValue default value to use if property is not found
     * @return String configured for property, or default value, if defaultValue is null and property is not found null is returned.
     */
    public static String getString(final String property, final String defaultValue) {
        // Commons configuration interprets ','-separated values as an array of Strings, but we need the whole String for example SubjectDNs.
        final String ret;
        final StringBuilder str = new StringBuilder();
        final String rets[] = instance().getStringArray(property);
        for (int i = 0; i < rets.length; i++) {
            if (i != 0) {
                str.append(',');
            }
            str.append(rets[i]);
        }
        if (str.length() != 0) {
            ret = str.toString();
        } else {
            ret = defaultValue;
        }
        return ret;
    }

    /**
     * Return a the expanded version of a property. E.g.
     *  property1=foo
     *  property2=${property1}bar
     * would return "foobar" for property2
     * @param defaultValue to use if no property of such a name is found
     */
    public static String getExpandedString(final String property, final String defaultValue) {
        String ret = getString(property, defaultValue);
        if (ret != null) {
            while (ret.indexOf("${") != -1) {
                ret = interpolate(ret);
            }
        }
        return ret;
    }

    private static String interpolate(final String orderString) {
        final Pattern PATTERN = Pattern.compile("\\$\\{(.+?)\\}");
        final Matcher m = PATTERN.matcher(orderString);
        final StringBuffer sb = new StringBuffer(orderString.length());
        m.reset();
        while (m.find()) {
            // when the pattern is ${identifier}, group 0 is 'identifier'
            final String key = m.group(1);
            final String value = getExpandedString(key, "");

            // if the pattern does exists, replace it by its value
            // otherwise keep the pattern ( it is group(0) )
            if (value != null) {
                m.appendReplacement(sb, value);
            } else {
                // I'm doing this to avoid the backreference problem as there will be a $
                // if I replace directly with the group 0 (which is also a pattern)
                m.appendReplacement(sb, "");
                final String unknown = m.group(0);
                sb.append(unknown);
            }
        }
        m.appendTail(sb);
        return sb.toString();
    }

    /**
     * Backups the original configuration in a non thread safe way.
     * 
      * NOTE: This method should only be used by tests through ConfigurationSessionBean!
     */
    public static boolean backupConfiguration() {
        if (configBackup != null) {
            return false;
        }
        configBackup = (CompositeConfiguration) config.clone();
        return true;
    }

    /**
     * Restores the original configuration in a non thread safe way.
     * 
     * NOTE: This method should only be used by tests through ConfigurationSessionBean!
     */
    public static boolean restoreConfiguration() {
        if (configBackup == null) {
            return false;
        }
        config = configBackup;
        configBackup = null;
        return true;
    }

    /**
     * Takes a backup of the active configuration if necessary and updates the active configuration. 
     * 
     * NOTE: This method should only be used by tests through ConfigurationSessionBean!
     */
    public static boolean updateConfiguration(final Properties properties) {
        backupConfiguration(); // Only takes a backup if necessary.
        final Iterator i = properties.keySet().iterator();
        while (i.hasNext()) {
            final String key = (String) i.next();
            final String value = (String) properties.get(key);
            config.setProperty(key, value);
        }
        return true;
    }

    /**
     * Takes a backup of the active configuration if necessary and updates the active configuration. 
     * 
     * NOTE: This method should only be used by tests through ConfigurationSessionBean!
     */
    public static boolean updateConfiguration(final String key, final String value) {
        backupConfiguration(); // Only takes a backup if necessary.
        config.setProperty(key, value);
        return true;
    }

}