/*
* Copyright 2008, Unitils.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.unitils.core;
import org.apache.commons.lang.text.StrSubstitutor;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.unitils.core.util.PropertiesReader;
import org.unitils.util.PropertyUtils;
import java.util.Properties;
/**
* Utility that loads the configuration of unitils.
* <p/>
* Unitils settings can be defined in 3 files and in the system properties:<ul>
* <li><b>unitils-default.properties</b> - a fixed file packaged in the unitils jar that contains all predefined defaults.
* This file should normally not be modified.</li>
* <li><b>unitils.properties</b> - a file somewhere in the classpath or user.home dir that contains all custom configuration
* settings. Settings in this file will override the default settings. This is where you should put your project
* specific configuration</li>
* <li><b>unitils-local.properties</b> - a file somewhere in the classpath or user.home that contains machine/user local
* configuration. Eg the database schema specific to the local user could be defined here. Settings in this file
* will override the unitil default and custom settings.</li>
* <li><b>system properties</b> - These settings override all other settings.</li>
* </ul>
* The name of the custom settings file (unitils.properties) is defined by the {@link #PROPKEY_CUSTOM_CONFIGURATION}
* property in the default settings. The name of the local settings file (unitils-local.propeties) is defined
* by the {@link #PROPKEY_LOCAL_CONFIGURATION} in the custom or default settings. If these properties are set to
* null or empty, the corresponding property file will not be loaded.
* <p/>
* A runtime exception is thrown when the default properties cannot be loaded.
* A warning is logged when the custom propreties cannot be loaded.
* A debug message is logged when the local properties cannot be loaded.
* <p/>
* Ant-like property place holders, e.g. ${holder} will be expanded if needed all property place holders to actual values.
* For example suppose you have a property defined as follows: root.dir=/usr/home
* Expanding following ${root.dir}/somesubdir
* will then give following result: /usr/home/somesubdir
*
* @author Tim Ducheyne
* @author Filip Neven
* @author Fabian Krueger
*/
public class ConfigurationLoader {
/**
* Name of the fixed configuration file that contains all defaults
*/
public static final String DEFAULT_PROPERTIES_FILE_NAME = "unitils-default.properties";
/**
* Property in the defaults configuration file that contains the name of the custom configuration file
*/
public static final String PROPKEY_CUSTOM_CONFIGURATION = "unitils.configuration.customFileName";
/**
* Property in the defaults and/or custom configuration file that contains the name of
* the user local configuration file
*/
public static final String PROPKEY_LOCAL_CONFIGURATION = "unitils.configuration.localFileName";
/* The logger instance for this class */
private static Log logger = LogFactory.getLog(ConfigurationLoader.class);
/**
* reads properties from configuration file
*/
private PropertiesReader propertiesReader = new PropertiesReader();
/**
* Creates and loads all configuration settings.
*
* @return the settings, not null
*/
public Properties loadConfiguration() {
Properties properties = new Properties();
loadDefaultConfiguration(properties);
loadCustomConfiguration(properties);
loadLocalConfiguration(properties);
loadSystemProperties(properties);
expandPropertyValues(properties);
return properties;
}
/**
* Load the default properties file that is distributed with unitils (unitils-default.properties)
*
* @param properties The instance to add to loaded properties to, not null
*/
protected void loadDefaultConfiguration(Properties properties) {
Properties defaultProperties = propertiesReader.loadPropertiesFileFromClasspath(DEFAULT_PROPERTIES_FILE_NAME);
if (defaultProperties == null) {
throw new UnitilsException("Configuration file: " + DEFAULT_PROPERTIES_FILE_NAME + " not found in classpath.");
}
properties.putAll(defaultProperties);
}
/**
* Load the custom project level configuration file (unitils.properties)
*
* @param properties The instance to add to loaded properties to, not null
*/
protected void loadCustomConfiguration(Properties properties) {
String customConfigurationFileName = getConfigurationFileName(PROPKEY_CUSTOM_CONFIGURATION, properties);
Properties customProperties = propertiesReader.loadPropertiesFileFromClasspath(customConfigurationFileName);
if (customProperties == null) {
logger.warn("No custom configuration file " + customConfigurationFileName + " found.");
} else {
properties.putAll(customProperties);
}
}
/**
* Load the local configuration file from the user home, or from the classpath
*
* @param properties The instance to add to loaded properties to, not null
*/
protected void loadLocalConfiguration(Properties properties) {
String localConfigurationFileName = getConfigurationFileName(PROPKEY_LOCAL_CONFIGURATION, properties);
Properties localProperties = propertiesReader.loadPropertiesFileFromUserHome(localConfigurationFileName);
if (localProperties == null) {
localProperties = propertiesReader.loadPropertiesFileFromClasspath(localConfigurationFileName);
}
if (localProperties == null) {
logger.info("No local configuration file " + localConfigurationFileName + " found.");
} else {
properties.putAll(localProperties);
}
}
/**
* Load the environment properties.
*
* @param properties The instance to add to loaded properties to, not null
*/
protected void loadSystemProperties(Properties properties) {
properties.putAll(System.getProperties());
}
/**
* Expands all property place holders to actual values. For example
* suppose you have a property defined as follows: root.dir=/usr/home
* Expanding following ${root.dir}/somesubdir
* will then give following result: /usr/home/somesubdir
*
* @param properties The properties, not null
*/
protected void expandPropertyValues(Properties properties) {
for (Object key : properties.keySet()) {
Object value = properties.get(key);
try {
String expandedValue = StrSubstitutor.replace(value, properties);
properties.put(key, expandedValue);
} catch (Exception e) {
throw new UnitilsException("Unable to load unitils configuration. Could not expand property value for key: " + key + ", value " + value, e);
}
}
}
/**
* Gets the configuration file name from the system properties or if not defined, from the given loaded properties.
* An exception is raised if no value is defined.
*
* @param propertyName The name of the property that defines the local/custom file name, not null
* @param properties The propertis that were already loaded, not null
* @return The property value, not null
*/
protected String getConfigurationFileName(String propertyName, Properties properties) {
String configurationFileName = System.getProperty(propertyName);
if (configurationFileName != null) {
return configurationFileName;
}
return PropertyUtils.getString(propertyName, properties);
}
}