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