Initial refactor commit. Swap out DSpaceConfig/ServiceConfig for Apache Commons Configuration

This commit is contained in:
Tim Donohue
2015-07-14 11:30:44 -05:00
parent f549eedf7b
commit 5d6b6827e5
21 changed files with 598 additions and 987 deletions

View File

@@ -94,6 +94,15 @@
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
</dependency>
<!-- Required by Commons Configuration -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
</dependency>
</dependencies>
<developers>

View File

@@ -142,6 +142,6 @@ public interface ServiceManager {
*
* @param settings a map of keys (names) and values
*/
public void pushConfig(Map<String, String> settings);
public void pushConfig(Map<String, Object> settings);
}

View File

@@ -17,9 +17,9 @@ import org.dspace.kernel.mixins.InitializedService;
import org.dspace.kernel.mixins.ServiceChangeListener;
import org.dspace.kernel.mixins.ServiceManagerReadyAware;
import org.dspace.kernel.mixins.ShutdownService;
import org.dspace.servicemanager.config.DSpaceConfig;
import org.dspace.servicemanager.config.DSpaceConfigurationService;
import org.dspace.servicemanager.spring.SpringServiceManager;
import org.dspace.services.ConfigurationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
@@ -100,7 +100,7 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
activators.add(activator);
log.info("Started and registered activator: " + activator.getClass().getName());
} catch (Exception e1) {
log.error("ERROR: Failed to start activator ("+ activator.getClass().getName() +"): " + e1, e1);
log.error("ERROR: Failed to start activator ("+ activator.getClass().getName() +"): " + e1.getMessage(), e1);
}
}
}
@@ -117,7 +117,7 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
activator.stop(this);
log.info("Stopped and unregistered activator: " + activatorClassName);
} catch (Exception e1) {
log.error("ERROR: Failed to stop activator ("+activatorClassName+"): " + e1,e1);
log.error("ERROR: Failed to stop activator ("+activatorClassName+"): " + e1.getMessage(), e1);
}
}
}
@@ -134,7 +134,7 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
try {
serviceManagerReadyAware.serviceManagerReady(this);
} catch (Exception e) {
System.err.println("ERROR: Failure in service when calling serviceManagerReady: " + e);
log.error("ERROR: Failure in service when calling serviceManagerReady: " + e.getMessage(), e);
}
}
}
@@ -168,6 +168,7 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
/**
* Shut down all service managers, including this one.
*/
@Override
public void shutdown() {
unregisterActivators();
for (ServiceManagerSystem sms : serviceManagers) {
@@ -175,7 +176,7 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
sms.shutdown();
} catch (Exception e) {
// shutdown failures are not great but should NOT cause an interruption of processing
System.err.println("Failure shutting down service manager ("+sms+"): " + e.getMessage());
log.error("Failure shutting down service manager ("+sms+"): " + e.getMessage(), e);
}
}
this.running = false; // wait til the end
@@ -184,6 +185,10 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
log.info("Shutdown DSpace core service manager");
}
/* (non-Javadoc)
* @see org.dspace.servicemanager.ServiceManagerSystem#startup()
*/
@Override
public void startup() {
if (!testing) {
// try to load up extra config files for spring
@@ -220,7 +225,7 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
} catch (Exception e) {
shutdown(); // execute the shutdown
String message = "Failed to startup the DSpace Service Manager: " + e.getMessage();
System.err.println(message);
log.error(message, e);
throw new RuntimeException(message, e);
}
}
@@ -358,7 +363,7 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
}
} catch (Exception e) {
// keep going if it fails for one
System.err.println("Failed to get list of services from service manager ("+sms.getClass()+"): " + e);
log.error("Failed to get list of services from service manager ("+sms.getClass()+"): " + e.getMessage(), e);
}
}
return services;
@@ -369,11 +374,11 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
* Every service gets called to notify them of the config change
* depending on the the listener they are using.
*/
public void pushConfig(Map<String, String> properties) {
public void pushConfig(Map<String, Object> properties) {
checkRunning();
if (properties != null && !properties.isEmpty()) {
// load in the new settings to the config service
String[] changedNames = configurationService.loadConfiguration(properties, false);
String[] changedNames = configurationService.loadConfiguration(properties);
if (changedNames.length > 0) {
// some configs changed so push the changes to the listeners in all known services and providers
// make the list of changed setting names and map of changed settings
@@ -381,13 +386,12 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
Map<String, String> changedSettings = new LinkedHashMap<String, String>();
for (String configName : changedNames) {
changedSettingNames.add(configName);
changedSettings.put( getSimplerName(configName), configurationService.getProperty(configName) );
changedSettings.put( configName, configurationService.getProperty(configName) );
}
// notify the services that implement the mixin
for (ServiceManagerSystem sms : serviceManagers) {
List<ConfigChangeListener> configChangeListeners = sms.getServicesByType(ConfigChangeListener.class);
for (ConfigChangeListener configChangeListener : configChangeListeners) {
String serviceImplName = configChangeListener.getClass().getName();
// notify this service
try {
boolean notify = false;
@@ -396,17 +400,9 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
notify = true;
} else {
for (String notifyName : notifyNames) {
// check to see if the change was one of the bean properties for our service
String simplerName = getSimplerName(notifyName);
String notifyBeanName = DSpaceConfig.getBeanName(notifyName);
if (notifyBeanName != null && notifyBeanName.equals(serviceImplName)) {
// this is a bean key
notify = true;
break;
}
// check to see if the name matches one of those the listener cares about
for (String changedName : changedNames) {
if (simplerName != null && simplerName.equals(changedName)) {
if (notifyName != null && notifyName.equals(changedName)) {
notify = true;
break;
}
@@ -418,7 +414,7 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
configChangeListener.configurationChanged(changedSettingNames, changedSettings);
}
} catch (Exception e) {
System.err.println("Failure occurred while trying to notify service of config change: " + e.getMessage());
log.error("Failure occurred while trying to notify service of config change: " + e.getMessage(), e);
}
}
}
@@ -426,44 +422,43 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
}
}
/**
* @param key a DSpace config key
* @return the simpler name (property name or key without property)
*/
protected static String getSimplerName(String key) {
String simpleName = key;
if (key != null) {
String propertyName = DSpaceConfig.getBeanProperty(key);
if (propertyName != null) {
simpleName = propertyName;
}
}
return simpleName;
}
// STATICS
/**
* Adds configuration settings into services if possible.
* Skips any that are invalid.
* Configures a given service (i.e. bean) based on any DSpace configuration
* settings which refer to it by name. .
* <P>
* NOTE: Any configurations related to a specific service MUST be prefixed
* with the given service's name (e.g. [serviceName].setting = value)
* <P>
* This method logs an error if it encounters configs which refer to a
* service by name, but is an invalid setting for that service.
*
* @param serviceName the name of the service
* @param service the service object
* @param serviceNameConfigs all known service configuration settings (from the DS config service impl)
* @param service the service object (which will be configured)
* @param config the running configuration service
*/
public static void configureService(String serviceName, Object service, Map<String, Map<String, ServiceConfig>> serviceNameConfigs) {
// stuff the config settings into the bean if there are any
if (serviceNameConfigs.containsKey(serviceName)) {
public static void configureService(String serviceName, Object service, ConfigurationService config) {
// Check if the configuration has any properties whose prefix
// corresponds to this service's name
List<String> configKeys = config.getPropertyKeys(serviceName);
if(configKeys!=null && !configKeys.isEmpty())
{
BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(service);
for (String key : configKeys) {
// Remove serviceName prefix from key. This is the name of the actual bean's parameter
// This removes the first x chars, where x is length of serviceName + 1 char
// Format of Key: [serviceName].[param]
String param = key.substring(serviceName.length() + 1);
Map<String, ServiceConfig> configs = serviceNameConfigs.get(serviceName);
for (ServiceConfig config : configs.values()) {
try {
beanWrapper.setPropertyValue(config.getParamName(), config.getValue());
log.info("Set param ("+config.getParamName()+") on service bean ("+serviceName+") to: " + config.getValue());
// Attempt to set this configuration on the given service's bean
beanWrapper.setPropertyValue(param, config.getProperty(key));
log.info("Set param ("+param+") on service bean ("+serviceName+") to: " + config.getProperty(key));
} catch (RuntimeException e) {
log.error("Unable to set param ("+config.getParamName()+") on service bean ("+serviceName+"): " + e.getMessage(), e);
// If an error occurs, just log it
log.error("Unable to set param ("+param+") on service bean ("+serviceName+") to: " + config.getProperty(key), e);
}
}
}
@@ -479,7 +474,7 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
try {
((InitializedService) service).init();
} catch (Exception e) {
throw new IllegalStateException("Failure attempting to initialize service (" + service + "): " + e.getMessage());
throw new IllegalStateException("Failure attempting to initialize service (" + service + "): " + e.getMessage(), e);
}
}
}
@@ -493,7 +488,7 @@ public final class DSpaceServiceManager implements ServiceManagerSystem {
try {
((ShutdownService) service).shutdown();
} catch (Exception e) {
System.err.println("Failure shutting down service: " + service);
log.error("Failure shutting down service: " + service, e);
}
}
}

View File

@@ -1,58 +0,0 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.servicemanager;
import org.dspace.servicemanager.config.DSpaceConfig;
/**
* This holds the settings for a service config triple.
* Create this from a {@link DSpaceConfig} object, or by name and value.
*
* @author Aaron Zeckoski (azeckoski @ gmail.com)
*/
public class ServiceConfig {
private String paramName;
private String serviceName;
private Object value;
/** Construct from scratch. */
public ServiceConfig(String paramName, String serviceName, Object value) {
if (paramName == null || serviceName == null) {
throw new IllegalArgumentException("paramName and serviceName must not be null");
}
this.paramName = paramName;
this.serviceName = serviceName;
this.value = value;
}
/** Construct from an existing DSpaceConfig. */
public ServiceConfig(DSpaceConfig dspaceConfig) {
this.paramName = dspaceConfig.getBeanProperty();
this.serviceName = dspaceConfig.getBeanName();
if (paramName == null || serviceName == null) {
throw new IllegalArgumentException("paramName and serviceName must not be null");
}
this.value = dspaceConfig.getValue();
}
public String getParamName() {
return paramName;
}
public String getServiceName() {
return serviceName;
}
public Object getValue() {
return value;
}
}

View File

@@ -1,243 +0,0 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.servicemanager.config;
/**
* This represents a single config setting for a DSpace instance.
*
* @author Aaron Zeckoski (azeckoski @ gmail.com)
*/
public final class DSpaceConfig {
public static final String ACTIVATOR_PREFIX = "activator.";
public static final String ACTIVATOR_CLASS_PREFIX = ACTIVATOR_PREFIX + "class.";
private String key;
private String value;
private String beanProperty;
private String beanName;
private boolean activator = false;
private boolean activatorClass = false;
private String activatorClassName;
private String activatorName;
private String activatorAutowire;
public DSpaceConfig(String key, String value) {
if (key == null || "".equals(key)) {
throw new IllegalArgumentException("Failure with config creation, key is empty or null");
}
this.key = key.trim();
this.value = value;
// extract the property and class if possible
int atLoc = key.indexOf('@');
if (atLoc > 0) {
try {
this.beanProperty = key.substring(0, atLoc);
this.beanName = key.substring(atLoc + 1);
} catch (RuntimeException e) {
throw new IllegalArgumentException("Invalid bean key ("+key+"): could not parse key (should be like param@org.dspace.BeanName): " + e.getMessage(), e);
}
}
// extract the activator info if possible
if (this.key.startsWith(ACTIVATOR_CLASS_PREFIX)) {
if (this.value == null || this.value.length() <= 2) {
throw new IllegalArgumentException("Invalid configuration value for key ("+this.key
+"), must not be blank or null and must be set to a class activator string (e.g. org.dspace.MyClass;org.dspace.MyServiceName)");
}
this.activatorClass = true;
if (this.value.indexOf(';') == -1) {
this.activatorClassName = this.value.trim();
} else {
// split it
String[] parts = this.value.trim().split(";");
this.activatorClassName = parts[0].trim();
if (parts.length > 1) {
this.activatorName = parts[1].trim();
}
if (parts.length > 2) {
this.activatorAutowire = parts[2].trim().toLowerCase();
}
}
if (this.activatorName == null) {
this.activatorName = this.activatorClassName;
}
if (this.activatorAutowire == null) {
this.activatorAutowire = "auto";
}
} else if (this.key.startsWith(ACTIVATOR_PREFIX)) {
if (this.value == null || this.value.length() <= 2) {
throw new IllegalArgumentException("Invalid configuration value for key ("+this.key
+"), must not be blank or null and must be set to an activator class (e.g. org.dspace.MyActivator)");
}
this.activator = true;
this.activatorClassName = this.value.trim();
}
}
/**
* Gets the part before the @ in a config line.
* @return the bean property if there is one OR null if this is not a bean config
*/
public String getBeanProperty() {
return beanProperty;
}
/**
* Get the part after the @ in a config line.
* @return the bean name which the property goes with OR null if there is none
*/
public String getBeanName() {
return beanName;
}
/**
* Indicates that this configuration is for an activator (a class
* that implements Activator).
* @return true if this config is for an activator OR false if not
*/
public boolean isActivator() {
return activator;
}
/**
* Indicates this is a config for an activator class (a class which
* will be started as a service during the service manager startup).
* @return true if this is a config for an activator class OR false if not
*/
public boolean isActivatorClass() {
return activatorClass;
}
/**
* Get the classname of the activator defined by the activator
* string if there is one.
* This will be the activator class or the class which should be
* activated.
* Check the {@link #isActivator()} and {@link #isActivatorClass()}
* booleans to see which.
*
* @return the activator class name OR null if this is not an activator config
*/
public String getActivatorClassName() {
return activatorClassName;
}
/**
* Get the name to use for this activator (as defined by the
* activator string).
* This will always be set if the {@link #activatorClassName} is set.
* @return the activator name OR null if this is not an activator config
*/
public String getActivatorName() {
return activatorName;
}
/**
* Get the activator autowire string. Returns one of:
* <dl>
* <dt>auto</dt><dd>determine which type of autowiring automatically</dd>
* <dt>constructor</dt><dd>autowire the constructor</dd>
* <dt>setter</dt><dd>autowire the setters by type</dd>
* <dt>none</dt><dd>disable any autowiring (this will only start up
* the class using the default constructor)</dd>
* </dl>
*
* @return the autowiring setting (auto/constructor/setter/none) OR
* null if this is not an activator config
*/
public String getActivatorAutowire() {
return activatorAutowire;
}
public String getKey() {
return key;
}
public String getValue() {
return value;
}
protected void setValue(String value) {
this.value = value;
}
@Override
public boolean equals(Object obj) {
if (null == obj) {
return false;
}
if (!(obj instanceof DSpaceConfig)) {
return false;
} else {
DSpaceConfig castObj = (DSpaceConfig) obj;
if (this.key == null || this.value == null) {
return false;
} else {
return (this.key.equals(castObj.key) &&
this.value.equals(castObj.value));
}
}
}
@Override
public int hashCode() {
if (null == key) {
return super.hashCode();
}
return key.hashCode() + value.hashCode();
}
@Override
public String toString() {
return (beanName == null ? key : beanName+"("+beanProperty+")") + " => " + value;
}
/**
* Get the bean name from a configuration key if it contains one.
* @param key a config key
* @return the bean name if there is one OR null if none
*/
public static String getBeanName(String key) {
// extract the property and class if possible
String name = null;
int atLoc = key.indexOf('@');
if (atLoc > 0) {
try {
//property = key.substring(0, atLoc);
name = key.substring(atLoc + 1);
} catch (RuntimeException e) {
name = null;
}
}
return name;
}
/**
* Get the bean property from a configuration key if it contains one.
* @param key a config key
* @return the bean property if there is one OR null if none
*/
public static String getBeanProperty(String key) {
// extract the property and class if possible
String property = null;
int atLoc = key.indexOf('@');
if (atLoc > 0) {
try {
property = key.substring(0, atLoc);
//name = key.substring(atLoc + 1);
} catch (RuntimeException e) {
property = null;
}
}
return property;
}
}

View File

@@ -7,29 +7,32 @@
*/
package org.dspace.servicemanager.config;
import java.io.*;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
import java.io.File;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.dspace.constants.Constants;
import org.dspace.servicemanager.ServiceConfig;
import java.util.Properties;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationConverter;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.DefaultConfigurationBuilder;
import org.dspace.services.ConfigurationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
/**
* The central DSpace configuration service.
* This is effectively immutable once the config has loaded.
* The central DSpace configuration service. Uses Apache Commons Configuration
* to provide the ability to reload Property files.
*
* @author Aaron Zeckoski (azeckoski @ gmail.com)
* @author Kevin Van de Velde (kevin at atmire dot com)
* @author Mark Diggory (mdiggory at atmire dot com)
* @author Tim Donohue (rewrote to use Apache Commons Config
* @author Aaron Zeckoski
* @author Kevin Van de Velde
* @author Mark Diggory
*/
public final class DSpaceConfigurationService implements ConfigurationService {
@@ -40,17 +43,18 @@ public final class DSpaceConfigurationService implements ConfigurationService {
public static final String EXT_CONFIG = "cfg";
public static final String DOT_CONFIG = "." + EXT_CONFIG;
public static final String DSPACE_PREFIX = "dspace.";
public static final String DSPACE_HOME = DSPACE + ".dir";
public static final String DEFAULT_CONFIGURATION_FILE_NAME = "dspace-defaults" + DOT_CONFIG;
public static final String DEFAULT_DSPACE_CONFIG_PATH = "config/" + DEFAULT_CONFIGURATION_FILE_NAME;
public static final String DEFAULT_CONFIG_DIR = "config";
public static final String DEFAULT_CONFIG_DEFINITION_FILE = "config-definition.xml";
public static final String DSPACE_CONFIG_DEFINITION_PATH = DEFAULT_CONFIG_DIR + File.separatorChar + DEFAULT_CONFIG_DEFINITION_FILE;
public static final String DSPACE_CONFIG_PATH = "config/" + DSPACE + DOT_CONFIG;
public static final String DSPACE_CONFIG_PATH = DEFAULT_CONFIG_DIR + File.separatorChar + DSPACE + DOT_CONFIG;
public static final String DSPACE_MODULES_CONFIG_PATH = "config" + File.separator + "modules";
// Current ConfigurationBuilder
DefaultConfigurationBuilder configurationBuilder = null;
protected transient Map<String, Map<String, ServiceConfig>> serviceNameConfigs;
public static final String DSPACE_CONFIG_ADDON = "dspace/config-*";
// Current Configuration
protected Configuration configuration = null;
public DSpaceConfigurationService() {
// init and load up current config settings
@@ -61,32 +65,61 @@ public final class DSpaceConfigurationService implements ConfigurationService {
loadInitialConfig(providedHome);
}
/* (non-Javadoc)
* @see org.dspace.services.ConfigurationService#getAllProperties()
*/
@Override
public Map<String, String> getAllProperties() {
Map<String, String> props = new LinkedHashMap<String, String>();
// for (Entry<String, DSpaceConfig> config : configuration.entrySet()) {
// props.put(config.getKey(), config.getValue().getValue());
// }
for (DSpaceConfig config : configuration.values()) {
props.put(config.getKey(), config.getValue());
}
return props;
}
/* (non-Javadoc)
* @see org.dspace.services.ConfigurationService#getProperties()
*/
@Override
public Properties getProperties() {
Properties props = new Properties();
for (DSpaceConfig config : configuration.values()) {
props.put(config.getKey(), config.getValue());
// Return our configuration as a set of Properties
return ConfigurationConverter.getProperties(configuration);
}
/* (non-Javadoc)
* @see org.dspace.services.ConfigurationService#getPropertyKeys()
*/
@Override
public List<String> getPropertyKeys() {
Iterator<String> keys = configuration.getKeys();
List<String> keyList = new ArrayList<>();
while(keys.hasNext())
{
keyList.add(keys.next());
}
return props;
return keyList;
}
/* (non-Javadoc)
* @see org.dspace.services.ConfigurationService#getPropertyKeys(java.lang.String)
*/
@Override
public List<String> getPropertyKeys(String prefix) {
Iterator<String> keys = configuration.getKeys(prefix);
List<String> keyList = new ArrayList<>();
while(keys.hasNext())
{
keyList.add(keys.next());
}
return keyList;
}
/* (non-Javadoc)
* @see org.dspace.services.ConfigurationService#getConfiguration()
*/
@Override
public Configuration getConfiguration() {
return configuration;
}
/* (non-Javadoc)
* @see org.dspace.services.ConfigurationService#getPropertyValue(java.lang.Object)
*/
@Override
public Object getPropertyValue(String name) {
return configuration.getProperty(name);
}
/* (non-Javadoc)
@@ -94,12 +127,7 @@ public final class DSpaceConfigurationService implements ConfigurationService {
*/
@Override
public String getProperty(String name) {
DSpaceConfig config = configuration.get(name);
String value = null;
if (config != null) {
value = config.getValue();
}
return value;
return configuration.getString(name);
}
/* (non-Javadoc)
@@ -107,8 +135,7 @@ public final class DSpaceConfigurationService implements ConfigurationService {
*/
@Override
public <T> T getPropertyAsType(String name, Class<T> type) {
String value = getProperty(name);
return convert(value, type);
return convert(name, type);
}
/* (non-Javadoc)
@@ -122,24 +149,26 @@ public final class DSpaceConfigurationService implements ConfigurationService {
/* (non-Javadoc)
* @see org.dspace.services.ConfigurationService#getPropertyAsType(java.lang.String, java.lang.Object, boolean)
*/
@SuppressWarnings("unchecked")
@Override
public <T> T getPropertyAsType(String name, T defaultValue, boolean setDefaultIfNotFound) {
String value = getProperty(name);
T property = null;
if (defaultValue == null) {
property = null; // just return null when default value is null
} else if (value == null) {
property = defaultValue; // just return the default value if nothing is currently set
// also set the default value as the current stored value
if (setDefaultIfNotFound) {
// If this key doesn't exist, immediately return a value
if(!configuration.containsKey(name))
{
// if flag is set, save the default value as the new value for this property
if(setDefaultIfNotFound)
{
setProperty(name, defaultValue);
}
} else {
// something is already set so we convert the stored value to match the type
property = (T)convert(value, defaultValue.getClass());
// Either way, return our default value as if it was the setting
return defaultValue;
}
return property;
// Get the class associated with our default value
Class type = defaultValue.getClass();
return (T) convert(name, type);
}
// config loading methods
@@ -148,492 +177,197 @@ public final class DSpaceConfigurationService implements ConfigurationService {
* @see org.dspace.services.ConfigurationService#setProperty(java.lang.String, java.lang.Object)
*/
@Override
public boolean setProperty(String name, Object value) {
public synchronized boolean setProperty(String name, Object value)
{
boolean changed = false;
if (name == null) {
throw new IllegalArgumentException("name cannot be null for setting configuration");
}
boolean changed = false;
if (value == null) {
changed = this.configuration.remove(name) != null;
log.info("Cleared the configuration setting for name ("+name+")");
} else {
SimpleTypeConverter converter = new SimpleTypeConverter();
String sVal = (String)converter.convertIfNecessary(value, String.class);
changed = loadConfig(name, sVal);
}
return changed;
}
else
{
Object oldValue = configuration.getProperty(name);
// INTERNAL loading methods
public List<DSpaceConfig> getConfiguration() {
return new ArrayList<DSpaceConfig>( configuration.values() );
}
/**
* Get all configs that start with the given value.
* @param prefix a string which the configs to return must start with
* @return the list of all configs that start with the given string
*/
public List<DSpaceConfig> getConfigsByPrefix(String prefix) {
List<DSpaceConfig> configs = new ArrayList<DSpaceConfig>();
if (prefix != null && prefix.length() > 0) {
for (DSpaceConfig config : configuration.values()) {
if (config.getKey().startsWith(prefix)) {
configs.add(config);
}
if (value == null && oldValue!=null)
{
changed = true;
configuration.clearProperty(name);
log.info("Cleared the configuration setting for name ("+name+")");
}
else if(!value.equals(oldValue))
{
changed = true;
configuration.setProperty(name, value);
}
}
return configs;
}
protected Map<String, DSpaceConfig> configuration = Collections.synchronizedMap(new LinkedHashMap<String, DSpaceConfig>());
/**
* @return a map of the service name configurations that are known for fast resolution
*/
public Map<String, Map<String, ServiceConfig>> getServiceNameConfigs() {
return serviceNameConfigs;
}
public void setConfiguration(Map<String, DSpaceConfig> configuration) {
if (configuration == null) {
throw new IllegalArgumentException("configuration cannot be null");
}
this.configuration = configuration;
replaceVariables(this.configuration);
// refresh the configs
serviceNameConfigs = makeServiceNameConfigs();
return changed;
}
/**
* Load a series of properties into the configuration.
* Checks to see if the settings exist or are changed and only loads
* changes. Clears out existing ones depending on the setting.
* changes.
* <P>
* This only adds/updates configurations, if you wish to first clear all
* existing configurations, see clear() method.
*
* @param properties a map of key -> value strings
* @param clear if true then clears the existing configuration settings first
* @return the list of changed configuration names
* @param properties a map of key -> value settings
* @return the list of changed configuration keys
*/
public String[] loadConfiguration(Map<String, String> properties, boolean clear) {
public String[] loadConfiguration(Map<String, Object> properties) {
if (properties == null) {
throw new IllegalArgumentException("properties cannot be null");
}
// transform to configs and call load
ArrayList<DSpaceConfig> dspaceConfigs = new ArrayList<DSpaceConfig>();
for (Entry<String, String> entry : properties.entrySet()) {
String key = entry.getKey();
if (key != null && ! "".equals(key)) {
String val = entry.getValue();
if (val != null && ! "".equals(val)) {
dspaceConfigs.add( new DSpaceConfig(entry.getKey(), entry.getValue()) );
}
}
}
return loadConfiguration(dspaceConfigs, clear);
}
/**
* Load up a bunch of {@link DSpaceConfig}s into the configuration.
* Checks to see if the settings exist or are changed and only
* loads changes. Clears out existing ones depending on the setting.
*
* @param dspaceConfigs a list of {@link DSpaceConfig} objects
* @param clear if true then clears the existing configuration settings first
* @return the list of changed configuration names
*/
public String[] loadConfiguration(List<DSpaceConfig> dspaceConfigs, boolean clear) {
ArrayList<String> changed = new ArrayList<String>();
if (clear) {
this.configuration.clear();
}
for (DSpaceConfig config : dspaceConfigs) {
String key = config.getKey();
boolean same = true;
if (clear) {
// all are new
same = false;
} else {
if (this.configuration.containsKey(key)) {
if (this.configuration.get(key).equals(config)) {
// this one has changed
same = false;
}
} else {
// this one is new
same = false;
}
}
if (!same) {
// loop through each new property entry
for (Entry<String, Object> entry : properties.entrySet())
{
String key = entry.getKey();
Object value = entry.getValue();
// Load this new individual key
boolean updated = loadConfig(key, value);
// If it was updated, add to our list of changed settings
if(updated)
{
changed.add(key);
this.configuration.put(key, config);
}
}
if (changed.size() > 0) {
replaceVariables(this.configuration);
// refresh the configs
serviceNameConfigs = makeServiceNameConfigs();
}
// Return an array of updated keys
return changed.toArray(new String[changed.size()]);
}
/**
* Loads an additional config setting into the system.
* Loads a single additional config setting into the system.
* @param key
* @param value
* @return true if the config is new or changed
*/
public boolean loadConfig(String key, String value) {
public boolean loadConfig(String key, Object value) {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}
// update replacements and add
boolean changed = replaceAndAddConfig( new DSpaceConfig(key, value) );
if (changed) {
// refresh the configs
serviceNameConfigs = makeServiceNameConfigs();
// Check if the value has changed
if (this.configuration.containsKey(key) &&
this.configuration.getProperty(key).equals(value))
{
// no change to the value
return false;
}
else
{
// Either this config doesn't exist, or it is not the same value,
// so we'll update it.
this.configuration.setProperty(key, value);
return true;
}
return changed;
}
/**
* Clears the configuration settings.
* Clears all the configuration settings.
*/
public void clear() {
this.configuration.clear();
this.serviceNameConfigs.clear();
log.info("Cleared all configuration settings");
}
/**
* Clears a single configuration
* @param key key of the configuration
*/
public void clearConfig(String key) {
this.configuration.clearProperty(key);
}
// loading from files code
/**
* Loads up the default initial configuration from the DSpace configuration
* files in the file home and on the classpath. Order:
* <ol>
* <li>Create {@code serverId} from local host name if available.</li>
* <li>Create {@code dspace.testing = false}.
* <li>Determine the value of {@code dspace.dir} and add to configuration.</li>
* <li>Load {@code classpath:config/dspace_defaults.cfg}.</li>
* <li>Copy system properties with names beginning "dspace." <em>except</em>
* {@code dspace.dir}, removing the "dspace." prefix from the name.</li>
* <li>Load all {@code classpath:dspace/config-*.cfg} using whatever
* matched "*" as module prefix.</li>
* <li>Load all {@code ${dspace.dir}/config/modules/*.cfg} using whatever
* matched "*" as module prefix.</li>
* <li>Load {@code classpath:dspace.cfg}.</li>
* <li>Load from the path in the system property {@code dspace.configuration}
* if defined, or {@code ${dspace.dir}/config/dspace.cfg}.</li>
* <li>Perform variable substitutions throughout the assembled configuration.</li>
* </ol>
*
* <p>The initial value of {@code dspace.dir} will be:</p>
* <ol>
* <li>the value of the system property {@code dspace.dir} if defined;</li>
* <li>else the value of {@code providedHome} if not null;</li>
* <li>else the servlet container's home + "/dspace/" if defined (see {@link getCatalina()});</li>
* <li>else the user's home directory if defined;</li>
* <li>else "/".
* </ol>
*
* Loads up the configuration from the DSpace configuration files.
* <P>
* Determines the home directory of DSpace, and then loads the configurations
* based on the configuration definition file in that location
* (using Apache Commons Configuration).
* @param providedHome DSpace home directory, or null.
*/
public void loadInitialConfig(String providedHome) {
Map<String, String> configMap = new LinkedHashMap<String, String>();
// load default settings
try {
String defaultServerId = InetAddress.getLocalHost().getHostName();
configMap.put("serverId", defaultServerId);
} catch (UnknownHostException e) {
// oh well
}
// default is testing mode off
configMap.put(Constants.DSPACE_TESTING_MODE, "false");
// now we load the settings from properties files
public void loadInitialConfig(String providedHome)
{
// See if homePath is specified as a System Property
String homePath = System.getProperty(DSPACE_HOME);
// now we load from the provided parameter if its not null
// If a provided home was passed in & no system property, use provided home
if (providedHome != null && homePath == null) {
homePath = providedHome;
}
// If still null, check Catalina
if (homePath == null) {
String catalina = getCatalina();
if (catalina != null) {
homePath = catalina + File.separatorChar + DSPACE + File.separatorChar;
}
}
// If still null, check "user.home" system property
if (homePath == null) {
homePath = System.getProperty("user.home");
}
// Finally, no other option but to assume root path ("/")
if (homePath == null) {
homePath = "/";
}
// make sure it's set properly
//System.setProperty(DSPACE_HOME, homePath);
configMap.put(DSPACE_HOME, homePath);
// Based on homePath get full path to the configuration definition
String configDefinition = homePath + File.separatorChar + DSPACE_CONFIG_DEFINITION_PATH;
// LOAD the internal defaults
Properties defaultProps = readPropertyResource(DEFAULT_DSPACE_CONFIG_PATH);
if (defaultProps.size() <= 0) {
// failed to load defaults!
throw new RuntimeException("Failed to load default dspace config properties: " + DEFAULT_DSPACE_CONFIG_PATH);
}
pushPropsToMap(configMap, defaultProps);
// load all properties from the system which begin with the prefix
Properties systemProps = System.getProperties();
for (Object o : systemProps.keySet()) {
String key = (String) o;
if (key != null
&& ! key.equals(DSPACE_HOME)) {
try {
if (key.startsWith(DSPACE_PREFIX)) {
String propName = key.substring(DSPACE_PREFIX.length());
String propVal = systemProps.getProperty(key);
log.info("Loading system property as config: "+propName+"=>"+propVal);
configMap.put(propName, propVal);
}
} catch (RuntimeException e) {
log.error("Failed to properly get config value from system property: " + o, e);
}
}
}
// Collect values from all the properties files: the later ones loaded override settings from prior.
//Find any addon config files found in the config dir in our jars
try {
PathMatchingResourcePatternResolver patchMatcher = new PathMatchingResourcePatternResolver();
Resource[] resources = patchMatcher.getResources("classpath*:" + DSPACE_CONFIG_ADDON + DOT_CONFIG);
for (Resource resource : resources) {
String prefix = resource.getFilename().substring(0, resource.getFilename().lastIndexOf(".")).replaceFirst("config-", "");
pushPropsToMap(configMap, prefix, readPropertyStream(resource.getInputStream()));
}
}catch (Exception e){
log.error("Failed to retrieve properties from classpath: " + e.getMessage(), e);
}
//Attempt to load up all the config files in the modules directory
try{
File modulesDirectory = new File(homePath + File.separator + DSPACE_MODULES_CONFIG_PATH + File.separator);
if(modulesDirectory.exists()){
try{
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(modulesDirectory.toURI().toURL().toString() + "*" + DOT_CONFIG);
if(resources != null){
for(Resource resource : resources){
String prefix = resource.getFilename().substring(0, resource.getFilename().lastIndexOf("."));
pushPropsToMap(configMap, prefix, readPropertyStream(resource.getInputStream()));
}
}
}catch (IOException e){
log.error("Error while loading the modules properties from:" + modulesDirectory.getAbsolutePath());
}
}else{
log.info("Failed to load the modules properties since (" + homePath + File.separator + DSPACE_MODULES_CONFIG_PATH + "): Does not exist");
}
}catch (IllegalArgumentException e){
//This happens if we don't have a modules directory
log.error("Error while loading the module properties since (" + homePath + File.separator + DSPACE_MODULES_CONFIG_PATH + "): is not a valid directory", e);
}
// attempt to load from the current classloader also (works for commandline config sitting on classpath
pushPropsToMap(configMap, readPropertyResource(DSPACE + DOT_CONFIG));
// read all the known files from the home path that are properties files
String configPath = System.getProperty("dspace.configuration");
if (null == configPath)
try
{
configPath = homePath + File.separatorChar + DSPACE_CONFIG_PATH;
// Load our configuration definition, which in turn loads all our config files/settings
// See: http://commons.apache.org/proper/commons-configuration/userguide_v1.10/howto_configurationbuilder.html
configurationBuilder = new DefaultConfigurationBuilder(configDefinition);
// Actually parser our configuration definition & return the resulting Configuration
configuration = configurationBuilder.getConfiguration();
}
pushPropsToMap(configMap, readPropertyFile(configPath));
// TODO: still use this local file loading?
// pushPropsToMap(configMap, readPropertyFile(homePath + File.separatorChar + "local" + DOT_PROPERTIES));
// pushPropsToMap(configMap, readPropertyResource(DSPACE + DOT_PROPERTIES));
// pushPropsToMap(configMap, readPropertyResource("local" + DOT_PROPERTIES));
// pushPropsToMap(configMap, readPropertyResource("webapp" + DOT_PROPERTIES));
// now push all of these into the config service store
loadConfiguration(configMap, true);
log.info("Started up configuration service and loaded "+configMap.size()+" settings");
}
/**
* Adds in this DSConfig and then updates the config by checking for
* replacements everywhere else.
* @param dsConfig a DSConfig to update the value of and then add in to the main config
* @return true if the config changed or is new
*/
protected boolean replaceAndAddConfig(DSpaceConfig dsConfig) {
DSpaceConfig newConfig = null;
String key = dsConfig.getKey();
if (dsConfig.getValue().contains("${")) {
String value = dsConfig.getValue();
int start = -1;
while ((start = value.indexOf("${")) > -1) {
int end = value.indexOf('}', start);
if (end > -1) {
String newKey = value.substring(start+2, end);
if (newKey.equals(key)) {
log.warn("Found circular reference for key ("+newKey+") in config value: " + value);
break;
}
DSpaceConfig dsc = this.configuration.get(newKey);
if (dsc == null) {
log.warn("Could not find key ("+newKey+") for replacement in value: " + value);
break;
}
String newVal = dsc.getValue();
value = value.replace("${"+newKey+"}", newVal);
newConfig = new DSpaceConfig(key, value);
} else {
log.warn("Found '${' but could not find a closing '}' in the value: " + value);
break;
}
}
catch(ConfigurationException ce)
{
log.error("Unable to load configurations based on definition at " + configDefinition);
System.err.println("Unable to load configurations based on definition at " + configDefinition);
throw new RuntimeException(ce);
}
// add the config
if (this.configuration.containsKey(key) && this.configuration.get(key).equals(dsConfig)) {
return false; // SHORT CIRCUIT
}
// config changed or new
this.configuration.put(key, newConfig != null ? newConfig : dsConfig);
// update replacements
replaceVariables(this.configuration);
return true;
log.info("Started up configuration service and loaded settings: " + toString());
}
/**
* This will replace the ${key} with the value from the matching key
* if it exists. Logs a warning if the key does not exist.
* Goes through and updates the replacements for the the entire
* configuration and updates any replaced values.
* Reload the configuration from the DSpace configuration files.
* <P>
* Uses the initialized ConfigurationBuilder to reload all configurations.
*/
protected void replaceVariables(Map<String, DSpaceConfig> dsConfiguration) {
for (Entry<String, DSpaceConfig> entry : dsConfiguration.entrySet()) {
if (entry.getValue().getValue().contains("${")) {
String value = entry.getValue().getValue();
int start = -1;
while ((start = value.indexOf("${")) > -1) {
int end = value.indexOf('}', start);
if (end > -1) {
String newKey = value.substring(start+2, end);
DSpaceConfig dsc = dsConfiguration.get(newKey);
if (dsc == null) {
log.warn("Could not find key ("+newKey+") for replacement in value: " + value);
break;
}
String newVal = dsc.getValue();
String oldValue = value;
value = value.replace("${"+newKey+"}", newVal);
if (value.equals(oldValue)) {
log.warn("No change after variable replacement -- is "
+ newKey + " = " + newVal +
" a circular reference?");
break;
}
entry.setValue( new DSpaceConfig(entry.getValue().getKey(), value) );
} else {
log.warn("Found '${' but could not find a closing '}' in the value: " + value);
break;
}
}
}
@Override
public void reloadConfig()
{
try
{
configurationBuilder.reload();
this.configuration = configurationBuilder.getConfiguration();
}
catch(ConfigurationException ce)
{
log.error("Unable to reload configurations based on definition at " + configurationBuilder.getFile().getAbsolutePath(), ce);
}
log.info("Reloaded configuration service: " + toString());
}
protected Properties readPropertyFile(String filePathName) {
Properties props = new Properties();
InputStream is = null;
try {
File f = new File(filePathName);
if (f.exists()) {
is = new FileInputStream(f);
props.load(is);
log.info("Loaded "+props.size()+" config properties from file: " + f);
}
else
{
log.info("Failed to load config properties from file ("+filePathName+"): Does not exist");
@Override
public String toString() {
// Get the size of the generated Properties
Properties props = getProperties();
int size = props!=null ? props.size() : 0;
}
} catch (Exception e) {
log.warn("Failed to load config properties from file ("+filePathName+"): " + e.getMessage(), e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ioe) {
// Ignore exception on close
}
}
}
return props;
}
protected Properties readPropertyResource(String resourcePathName) {
Properties props = new Properties();
try {
ClassPathResource resource = new ClassPathResource(resourcePathName);
if (resource.exists()) {
props.load(resource.getInputStream());
log.info("Loaded "+props.size()+" config properties from resource: " + resource);
}
} catch (Exception e) {
log.warn("Failed to load config properties from resource ("+resourcePathName+"): " + e.getMessage(), e);
}
return props;
}
protected Properties readPropertyFile(File propertyFile) {
Properties props = new Properties();
try{
if(propertyFile.exists()){
props.load(new FileInputStream(propertyFile));
log.info("Loaded"+props.size() + " config properties from file: " + propertyFile.getName());
}
} catch (Exception e){
log.warn("Failed to load config properties from file (" + propertyFile.getName() + ": "+ e.getMessage(), e);
}
return props;
}
protected Properties readPropertyStream(InputStream propertyStream){
Properties props = new Properties();
try{
props.load(propertyStream);
log.info("Loaded"+props.size() + " config properties from stream");
} catch (Exception e){
log.warn("Failed to load config properties from stream: " + e.getMessage(), e);
}
return props;
}
protected void pushPropsToMap(Map<String, String> map, Properties props) {
pushPropsToMap(map, null, props);
}
protected void pushPropsToMap(Map<String, String> map, String prefix, Properties props) {
for (Entry<Object, Object> entry : props.entrySet()) {
String key = entry.getKey().toString();
if(prefix != null){
key = prefix + "." + key;
}
map.put(key, entry.getValue() == null ? "" : entry.getValue().toString());
}
// Return the configuration directory and number of configs loaded
return "ConfigDir=" + configuration.getString(DSPACE_HOME) + File.separatorChar + DEFAULT_CONFIG_DIR + ", Size=" + size;
}
/**
@@ -648,55 +382,58 @@ public final class DSpaceConfigurationService implements ConfigurationService {
return catalina;
}
@Override
public String toString() {
return "Config:" + DSPACE_HOME + ":size=" + configuration.size();
}
/**
* Constructs service name configs map for fast lookup of service
* configurations.
* @return the map of config service settings
* Convert the value of a given property to a specific object type.
* <P>
* Note: in most cases we can just use Configuration get*() methods.
*
* @param name Key of the property to convert
* @param <T> object type
* @return converted value
*/
public Map<String, Map<String, ServiceConfig>> makeServiceNameConfigs() {
Map<String, Map<String, ServiceConfig>> serviceNameConfigs = new HashMap<String, Map<String,ServiceConfig>>();
for (DSpaceConfig dsConfig : getConfiguration()) {
String beanName = dsConfig.getBeanName();
if (beanName != null) {
Map<String, ServiceConfig> map = null;
if (serviceNameConfigs.containsKey(beanName)) {
map = serviceNameConfigs.get(beanName);
} else {
map = new HashMap<String, ServiceConfig>();
serviceNameConfigs.put(beanName, map);
}
map.put(beanName, new ServiceConfig(dsConfig));
}
}
return serviceNameConfigs;
}
private <T> T convert(String name, Class<T> type) {
private <T> T convert(String value, Class<T> type) {
SimpleTypeConverter converter = new SimpleTypeConverter();
if (value != null) {
if (type.isArray()) {
String[] values = value.split(",");
return (T)converter.convertIfNecessary(values, type);
}
if (type.isAssignableFrom(String.class)) {
return (T)value;
}
} else {
if (boolean.class.equals(type)) {
return (T)Boolean.FALSE;
} else if (int.class.equals(type) || long.class.equals(type)) {
return (T)converter.convertIfNecessary(0, type);
}
// If this key doesn't exist, just return null
if(!configuration.containsKey(name))
{
// Special case. For booleans, return false if key doesn't exist
if(Boolean.class.equals(type) || boolean.class.equals(type))
return (T) Boolean.FALSE;
else
return null;
}
return (T)converter.convertIfNecessary(value, type);
// Based on the type of class, call the appropriate
// method of the Configuration object
if(type.isArray())
return (T) configuration.getStringArray(name);
else if(String.class.equals(type) || type.isAssignableFrom(String.class))
return (T) configuration.getString(name);
else if(BigDecimal.class.equals(type))
return (T) configuration.getBigDecimal(name);
else if(BigInteger.class.equals(type))
return (T) configuration.getBigInteger(name);
else if(Boolean.class.equals(type) || boolean.class.equals(type))
return (T) Boolean.valueOf(configuration.getBoolean(name));
else if(Byte.class.equals(type) || byte.class.equals(type))
return (T) Byte.valueOf(configuration.getByte(name));
else if(Double.class.equals(type) || double.class.equals(type))
return (T) Double.valueOf(configuration.getDouble(name));
else if(Float.class.equals(type) || float.class.equals(type))
return (T) Float.valueOf(configuration.getFloat(name));
else if(Integer.class.equals(type) || int.class.equals(type))
return (T) Integer.valueOf(configuration.getInt(name));
else if(List.class.equals(type))
return (T) configuration.getList(name);
else if(Long.class.equals(type) || long.class.equals(type))
return (T) Long.valueOf(configuration.getLong(name));
else if(Short.class.equals(type) || short.class.equals(type))
return (T) Short.valueOf(configuration.getShort(name));
else
{
// If none of the above works, try to convert the value to the required type
SimpleTypeConverter converter = new SimpleTypeConverter();
return (T) converter.convertIfNecessary(configuration.getProperty(name), type);
}
}
}

View File

@@ -34,15 +34,19 @@ public final class DSpaceBeanPostProcessor implements BeanPostProcessor, Destruc
/* (non-Javadoc)
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
DSpaceServiceManager.configureService(beanName, bean, configurationService.getServiceNameConfigs());
// Before initializing the service, first configure it based on any related settings in the configurationService
// NOTE: configs related to this bean MUST be prefixed with the bean's name (e.g. [beanName].setting = value)
DSpaceServiceManager.configureService(beanName, bean, configurationService);
return bean;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String)
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
DSpaceServiceManager.initService(bean);
@@ -52,6 +56,7 @@ public final class DSpaceBeanPostProcessor implements BeanPostProcessor, Destruc
/* (non-Javadoc)
* @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor#postProcessBeforeDestruction(java.lang.Object, java.lang.String)
*/
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
DSpaceServiceManager.shutdownService(bean);
}

View File

@@ -319,7 +319,7 @@ public final class SpringServiceManager implements ServiceManagerSystem {
return services;
}
public void pushConfig(Map<String, String> settings) {
public void pushConfig(Map<String, Object> settings) {
throw new UnsupportedOperationException("Not implemented for individual service manager systems");
}

View File

@@ -7,8 +7,9 @@
*/
package org.dspace.services;
import java.util.Map;
import java.util.List;
import java.util.Properties;
import org.apache.commons.configuration.Configuration;
/**
@@ -90,29 +91,57 @@ public interface ConfigurationService {
public <T> T getPropertyAsType(String name, T defaultValue, boolean setDefaultIfNotFound);
/**
* Get all currently known configuration settings
* Get keys all currently known configuration settings
*
* @return all the configuration properties as a map of name -> value
* @return all the configuration keys as a List
*/
public Map<String, String> getAllProperties();
public List<String> getPropertyKeys();
/**
* Get keys all currently known configuration settings, which
* begin with a given prefix.
* <P>
* For example, passing in "db" would return the keys "db.url", "db.username", etc.
*
* @param prefix prefix of key
* @return all the configuration keys as a List
*/
public List<String> getPropertyKeys(String prefix);
/**
* Convenience method - get a configuration property (setting) from
* the system.
* the system as a String.
*
* @param name the property name
* @return the property value OR null if none is found
*/
public String getProperty(String name);
/**
* Convenience method - get a configuration property (setting) from
* the system as its stored object
*
* @param name the property name
* @return the property value OR null if none is found
*/
public Object getPropertyValue(String name);
/**
* Convenience method - get all configuration properties (settings)
* from the system.
*
*
* @return all the configuration properties in a properties object (name -> value)
*/
public Properties getProperties();
/**
* Convenience method - get entire configuration (settings)
* from the system.
*
* @return Configuration object representing the system configuration
*/
public Configuration getConfiguration();
/**
* Set a configuration property (setting) in the system.
* Type is not important here since conversion happens automatically
@@ -126,4 +155,11 @@ public interface ConfigurationService {
*/
public boolean setProperty(String name, Object value);
/**
* Reload the configuration from the DSpace configuration files.
* <P>
* Uses the initialized ConfigurationService to reload all configurations.
*/
public void reloadConfig();
}

View File

@@ -141,11 +141,12 @@ public final class CachingServiceImpl implements CachingService, InitializedServ
* Reloads the config settings from the configuration service.
*/
protected void reloadConfig() {
useClustering = configurationService.getPropertyAsType(knownConfigNames[0], boolean.class);
useDiskStore = configurationService.getPropertyAsType(knownConfigNames[1], boolean.class);
maxElementsInMemory = configurationService.getPropertyAsType(knownConfigNames[2], int.class);
timeToLiveSecs = configurationService.getPropertyAsType(knownConfigNames[3], int.class);
timeToIdleSecs = configurationService.getPropertyAsType(knownConfigNames[4], int.class);
// Reload caching configurations, but have sane default values if unspecified in configs
useClustering = configurationService.getPropertyAsType(knownConfigNames[0], false);
useDiskStore = configurationService.getPropertyAsType(knownConfigNames[1], true);
maxElementsInMemory = configurationService.getPropertyAsType(knownConfigNames[2], 2000);
timeToLiveSecs = configurationService.getPropertyAsType(knownConfigNames[3], 3600);
timeToIdleSecs = configurationService.getPropertyAsType(knownConfigNames[4], 600);
}
/**

View File

@@ -18,10 +18,6 @@
default-autowire-candidates="*Service,*DAO,javax.sql.DataSource">
<context:annotation-config /> <!-- allows us to use spring annotations in beans -->
<!-- NOTE: I am not convinced this is a good idea, it is really slow and I think possibly dangerous -AZ -->
<!--
<context:component-scan base-package="org.dspace" name-generator="org.dspace.servicemanager.spring.FullPathBeanNameGenerator" />
-->
<!-- the bean processor interceptor -->
<bean class="org.dspace.servicemanager.spring.DSpaceBeanPostProcessor" />

View File

@@ -38,8 +38,10 @@ public class DSpaceServiceManagerTest {
@Before
public void init() {
configurationService = new DSpaceConfigurationService();
configurationService.loadConfig("testName@" + SampleAnnotationBean.class.getName(), "beckyz");
configurationService.loadConfig("fakeParam@fakeBean", "beckyz");
// Set some sample configurations relating to services/beans
configurationService.loadConfig(SampleAnnotationBean.class.getName() + ".sampleValue", "beckyz");
configurationService.loadConfig("fakeBean.fakeParam", "beckyz");
dsm = new DSpaceServiceManager(configurationService, TestSpringServiceManager.SPRING_TEST_CONFIG_FILE);
}
@@ -168,8 +170,11 @@ public class DSpaceServiceManagerTest {
assertEquals("azeckoski", concrete.getName());
concrete = null;
// initialize a SampleAnnotationBean
SampleAnnotationBean sab = dsm.getServiceByName(SampleAnnotationBean.class.getName(), SampleAnnotationBean.class);
assertNotNull(sab);
// Based on the configuration for "sampleValue" in the init() method above,
// a value should be pre-set!
assertEquals("beckyz", sab.getSampleValue());
sab = null;
}
@@ -193,11 +198,6 @@ public class DSpaceServiceManagerTest {
assertNotNull(l2);
assertTrue(l2.size() >= 1);
l2 = null;
List<ServiceConfig> l3 = dsm.getServicesByType(ServiceConfig.class);
assertNotNull(l3);
assertEquals(0, l3.size());
l3 = null;
}
/**
@@ -248,7 +248,7 @@ public class DSpaceServiceManagerTest {
public void testPushConfig() {
dsm.startup();
Map<String, String> properties = new HashMap<String, String>();
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("some.test.thing", "A value");
dsm.pushConfig(properties);
@@ -289,7 +289,7 @@ public class DSpaceServiceManagerTest {
assertEquals(1, service.getTriggers());
// now we do a config change
Map<String, String> properties = new HashMap<String, String>();
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("azeckoski.FakeService1.something", "THING");
dsm.pushConfig(properties);
assertEquals("config:THING", service.getSomething());

View File

@@ -83,7 +83,7 @@ public class MockServiceManagerSystem implements ServiceManagerSystem {
/* (non-Javadoc)
* @see org.dspace.kernel.ServiceManager#pushConfig(java.util.Map)
*/
public void pushConfig(Map<String, String> settings) {
public void pushConfig(Map<String, Object> settings) {
this.sms.pushConfig(settings);
}

View File

@@ -58,7 +58,7 @@ public class SampleAnnotationBean implements InitializedService, ShutdownService
}
private String value = null;
public void setTestName(String value) {
public void setSampleValue(String value) {
this.value = value;
}

View File

@@ -9,9 +9,7 @@ package org.dspace.servicemanager.config;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -31,19 +29,26 @@ public class DSpaceConfigurationServiceTest {
@Before
public void init() {
// Give a default setting to DSPACE_HOME
System.setProperty(DSpaceConfigurationService.DSPACE_HOME, "src/test/resources");
configurationService = new DSpaceConfigurationService();
List<DSpaceConfig> l = new ArrayList<DSpaceConfig>();
l.add( new DSpaceConfig("service.name", "DSpace") );
l.add( new DSpaceConfig("sample.array", "itemA,itemB,itemC") );
l.add( new DSpaceConfig("sample.number", "123") );
l.add( new DSpaceConfig("sample.boolean", "true") );
l.add( new DSpaceConfig("aaronz", "Aaron Zeckoski") );
l.add( new DSpaceConfig("current.user", "${aaronz}") );
l.add( new DSpaceConfig("test.key1", "This is a value") );
l.add( new DSpaceConfig("test.key2", "This is key1=${test.key1}") );
// clear out default configs
configurationService.clear();
configurationService.loadConfiguration(l, true);
// Start fresh with out own set of 8 configs
Map<String,Object> l = new HashMap<String,Object>();
l.put("service.name", "DSpace");
l.put("sample.array", "itemA,itemB,itemC");
l.put("sample.number", "123");
l.put("sample.boolean", "true");
l.put("aaronz", "Aaron Zeckoski");
l.put("current.user", "${aaronz}");
l.put("test.key1", "This is a value");
l.put("test.key2", "This is key1=${test.key1}");
configurationService.loadConfiguration(l);
l = null;
}
@@ -53,60 +58,51 @@ public class DSpaceConfigurationServiceTest {
}
/**
* Test method for {@link org.dspace.servicemanager.config.DSpaceConfigurationService#replaceVariables(java.util.Map)}.
* A generic method to test that variable replacement is happening properly.
*/
@Test(timeout=10000)
public void testReplaceVariables() {
public void testVariableReplacement() {
List<DSpaceConfig> l = new ArrayList<DSpaceConfig>();
l.add( new DSpaceConfig("service.name", "DSpace") );
l.add( new DSpaceConfig("aaronz", "Aaron Zeckoski") );
l.add( new DSpaceConfig("current.user", "${aaronz}") );
l.add( new DSpaceConfig("test.key1", "This is a value") );
l.add( new DSpaceConfig("test.key2", "This is key1=${test.key1}") );
l.add( new DSpaceConfig("test.key3", "This is key2=${test.key2}") );
int dirIdx = l.size();
l.add( new DSpaceConfig("circular", "${circular}"));
int indirIdx = l.size();
l.add( new DSpaceConfig("indirect.circular", "${circular} square"));
Map<String,Object> l = new HashMap<String,Object>();
l.put("service.name", "DSpace");
l.put("aaronz", "Aaron Zeckoski");
l.put("current.user", "${aaronz}");
l.put("test.key1", "This is a value");
l.put("test.key2", "This is key1=${test.key1}");
l.put("test.key3", "This is key2=${test.key2}");
Map<String, DSpaceConfig> configMap = new HashMap<String, DSpaceConfig>();
for (DSpaceConfig config : l) {
configMap.put(config.getKey(), config);
}
configurationService.replaceVariables(configMap);
configurationService.loadConfiguration(l);
assertEquals("DSpace", configurationService.getProperty("service.name"));
assertEquals("Aaron Zeckoski", configurationService.getProperty("aaronz"));
assertEquals("Aaron Zeckoski", configurationService.getProperty("current.user"));
assertEquals("This is a value", configurationService.getProperty("test.key1"));
assertEquals("This is key1=This is a value", configurationService.getProperty("test.key2"));
assertEquals("This is key2=This is key1=This is a value", configurationService.getProperty("test.key3"));
assertEquals("all configuration list members should be map members",
l.size(), configMap.size());
assertEquals("DSpace", configMap.get("service.name").getValue());
assertEquals("Aaron Zeckoski", configMap.get("aaronz").getValue());
assertEquals("Aaron Zeckoski", configMap.get("current.user").getValue());
assertEquals("This is a value", configMap.get("test.key1").getValue());
assertEquals("This is key1=This is a value", configMap.get("test.key2").getValue());
assertEquals("This is key2=This is key1=This is a value", configMap.get("test.key3").getValue());
assertEquals("Direct circular reference should not be replaced",
configMap.get("circular").getValue(), l.get(dirIdx).getValue());
assertEquals("Indirect circular reference should not be replaced",
configMap.get("indirect.circular").getValue(), l.get(indirIdx).getValue());
//trash the references
l = null;
configMap = null;
}
/**
* Test method for {@link org.dspace.servicemanager.config.DSpaceConfigurationService#getAllProperties()}.
*/
@Test
public void testGetAllProperties() {
Map<String, String> props = configurationService.getAllProperties();
assertNotNull(props);
assertEquals(8, props.size());
assertNotNull(props.get("service.name"));
assertEquals("DSpace", props.get("service.name"));
//trash the references
props = null;
@Test(expected=IllegalStateException.class)
public void testVariableReplacementCircular()
{
// add a circular reference
configurationService.loadConfig("circular", "${circular}");
// try to get the value (should throw an error)
configurationService.getProperty("circular");
}
@Test(expected=IllegalStateException.class)
public void testVariableReplacementIndirectCircular()
{
// add a circular reference
configurationService.loadConfig("circular", "${circular}");
// add an indirect reference to that circular reference
configurationService.loadConfig("indirect.circular", "$indirect ${circular}");
// try to get the value (should throw an error)
configurationService.getProperty("indirect.circular");
}
/**
@@ -155,9 +151,19 @@ public class DSpaceConfigurationServiceTest {
Integer number = configurationService.getPropertyAsType("sample.number", Integer.class);
assertNotNull(number);
assertEquals(new Integer(123), number);
Boolean bool = configurationService.getPropertyAsType("sample.boolean", Boolean.class);
assertNotNull(bool);
assertEquals(Boolean.TRUE, bool);
Boolean bool2 = configurationService.getPropertyAsType("INVALID.PROPERTY", Boolean.class);
assertNotNull(bool2);
assertEquals(Boolean.FALSE, bool2);
boolean bool3 = configurationService.getPropertyAsType("INVALID.PROPERTY", boolean.class);
assertNotNull(bool3);
assertEquals(false, bool3);
assertEquals(123, (int) configurationService.getPropertyAsType("sample.number", int.class) );
assertEquals(true, (boolean) configurationService.getPropertyAsType("sample.boolean", boolean.class) );
@@ -199,21 +205,21 @@ public class DSpaceConfigurationServiceTest {
@Test
public void testGetPropertyAsTypeStringTBoolean() {
String prop = configurationService.getProperty("service.fake.thing");
Object prop = configurationService.getPropertyValue("service.fake.thing");
assertNull(prop);
prop = configurationService.getPropertyAsType("service.fake.thing", "Fakey", false);
assertNotNull(prop);
assertEquals("Fakey", prop);
prop = configurationService.getProperty("service.fake.thing");
prop = configurationService.getPropertyValue("service.fake.thing");
assertNull(prop);
prop = configurationService.getPropertyAsType("service.fake.thing", "Fakey", true);
assertNotNull(prop);
assertEquals("Fakey", prop);
prop = configurationService.getProperty("service.fake.thing");
prop = configurationService.getPropertyValue("service.fake.thing");
assertNotNull(prop);
assertEquals("Fakey", prop);
prop = null;
@@ -221,25 +227,25 @@ public class DSpaceConfigurationServiceTest {
@Test
public void testSetProperty() {
String prop = configurationService.getProperty("newOne");
Object prop = configurationService.getPropertyValue("newOne");
assertNull(prop);
boolean changed = configurationService.setProperty("newOne", "1111111");
assertTrue(changed);
prop = configurationService.getProperty("newOne");
prop = configurationService.getPropertyValue("newOne");
assertNotNull(prop);
assertEquals("1111111", prop);
prop = configurationService.getProperty("newBool");
prop = configurationService.getPropertyValue("newBool");
assertNull(prop);
changed = configurationService.setProperty("newBool", true);
assertTrue(changed);
prop = configurationService.getProperty("newBool");
prop = configurationService.getPropertyValue("newBool");
assertNotNull(prop);
assertEquals("true", prop);
assertEquals(Boolean.TRUE, prop);
changed = configurationService.setProperty("newBool", true);
assertFalse(changed);
@@ -247,7 +253,7 @@ public class DSpaceConfigurationServiceTest {
changed = configurationService.setProperty("newBool", null);
assertTrue(changed);
prop = configurationService.getProperty("newBool");
prop = configurationService.getPropertyValue("newBool");
assertNull(prop);
prop = null;
}
@@ -258,7 +264,7 @@ public class DSpaceConfigurationServiceTest {
@Test
public void testGetConfiguration() {
assertNotNull( configurationService.getConfiguration() );
assertEquals(8, configurationService.getConfiguration().size() );
assertEquals(8, configurationService.getProperties().size() );
}
/**
@@ -266,16 +272,16 @@ public class DSpaceConfigurationServiceTest {
*/
@Test
public void testLoadConfig() {
assertEquals(8, configurationService.getConfiguration().size());
assertEquals(8, configurationService.getProperties().size());
configurationService.loadConfig("newA", "A");
assertEquals(9, configurationService.getConfiguration().size());
assertEquals(9, configurationService.getProperties().size());
assertEquals("A", configurationService.getProperty("newA"));
configurationService.loadConfig("newB", "service is ${service.name}");
assertEquals(10, configurationService.getConfiguration().size());
assertEquals(10, configurationService.getProperties().size());
assertEquals("service is DSpace", configurationService.getProperty("newB"));
configurationService.loadConfig("newA", "aaronz");
assertEquals(10, configurationService.getConfiguration().size());
assertEquals(10, configurationService.getProperties().size());
assertEquals("aaronz", configurationService.getProperty("newA"));
}
@@ -286,24 +292,77 @@ public class DSpaceConfigurationServiceTest {
@Test
public void testClear() {
configurationService.clear();
assertEquals(0, configurationService.getAllProperties().size());
assertEquals(0, configurationService.getProperties().size());
}
/**
* Tests the ability of the system to properly extract system properties into the configuration
* Test method for {@link org.dspace.servicemanager.config.DSpaceConfigurationService#reloadConfig()}.
*/
@Test
public void testReloadConfig() {
// Initialize new config service
DSpaceConfigurationService dscs = new DSpaceConfigurationService();
int size = dscs.getProperties().size();
// Add two new Sytem properties
System.setProperty("Hello","World");
System.setProperty("Tim", "Donohue");
// Assert the new properties are not yet loaded
assertEquals(size, dscs.getProperties().size());
dscs.reloadConfig();
// Assert the new properties now exist
assertEquals(size + 2, dscs.getProperties().size());
// Set a new value
System.setProperty("Hello", "There");
// Assert old value still in Configuration
assertEquals("World", dscs.getProperty("Hello"));
dscs.reloadConfig();
// Now, should be new value
assertEquals("There", dscs.getProperty("Hello"));
// Clear set properties
System.clearProperty("Hello");
System.clearProperty("Tim");
// Assert value not yet cleared from Configuration
assertEquals("There", dscs.getProperty("Hello"));
dscs.reloadConfig();
// Now, should be null
assertNull(dscs.getProperty("Hello"));
dscs.clear();
dscs = null;
}
/**
* Tests the ability of the system to properly extract system properties into the configuration.
* (NOTE: This ability to load system properties is specified in the test "config-definition.xml")
*/
@Test
public void testGetPropertiesFromSystem() {
DSpaceConfigurationService dscs = new DSpaceConfigurationService();
int size = dscs.getConfiguration().size();
int size = dscs.getProperties().size();
System.setProperty("dspace.az.system.config", "Hello");
System.setProperty("not.dspace", "Adios");
System.setProperty("dspace.system.config", "Hello");
System.setProperty("another.property", "Adios");
dscs = new DSpaceConfigurationService();
assertEquals(size + 1, dscs.getConfiguration().size());
assertEquals("Hello", dscs.getProperty("az.system.config"));
dscs.reloadConfig();
assertEquals(size + 2, dscs.getProperties().size());
assertEquals("Hello", dscs.getProperty("dspace.system.config"));
assertEquals("Adios", dscs.getProperty("another.property"));
System.clearProperty("dspace.system.config");
System.clearProperty("another.property");
dscs.clear();
dscs = null;
}

View File

@@ -17,10 +17,8 @@ import java.util.List;
import org.dspace.servicemanager.MockServiceManagerSystem;
import org.dspace.servicemanager.SampleAnnotationBean;
import org.dspace.servicemanager.ServiceConfig;
import org.dspace.servicemanager.config.DSpaceConfigurationService;
import org.dspace.servicemanager.example.ConcreteExample;
import org.dspace.services.ConfigurationService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -41,8 +39,8 @@ public class TestSpringServiceManager {
@Before
public void init() {
configurationService = new DSpaceConfigurationService();
configurationService.loadConfig("testName@" + SampleAnnotationBean.class.getName(), "beckyz");
configurationService.loadConfig("fakeParam@fakeBean", "beckyz");
configurationService.loadConfig(SampleAnnotationBean.class.getName() + ".sampleValue", "beckyz");
configurationService.loadConfig("fakeBean.fakeParam", "beckyz");
ssm = new SpringServiceManager(new MockServiceManagerSystem(ssm), configurationService, true, true, SPRING_TEST_CONFIG_FILE);
}
@@ -139,11 +137,6 @@ public class TestSpringServiceManager {
assertNotNull(l2);
assertEquals(1, l2.size());
l2 = null;
List<ServiceConfig> l3 = ssm.getServicesByType(ServiceConfig.class);
assertNotNull(l3);
assertEquals(0, l3.size());
l3 = null;
}
/**

View File

@@ -69,7 +69,7 @@ public class ProviderStackTest {
public boolean isServiceExists(String name) {
return false;
}
public void pushConfig(Map<String, String> settings) {
public void pushConfig(Map<String, Object> settings) {
}
public void registerService(String name, Object service) {
}

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The contents of this file are subject to the license and copyright
detailed in the LICENSE and NOTICE files at the root of the source
tree and available online at
http://www.dspace.org/license/
-->
<!--
Test Configuration Definition
This file defines the ordering of configuration files loaded by the ConfigurationService
For more information and examples of valid configuration definition files, see:
http://commons.apache.org/proper/commons-configuration/userguide_v1.10/howto_configurationbuilder.html
-->
<configuration>
<!-- Configurations in this section override one another.
Earlier values take precedence, and override any values in later config files -->
<override>
<!-- NOTE: Optionally, you could just use Variable Interpolation instead
of directly loading these System/Env properties:
http://commons.apache.org/proper/commons-configuration/userguide_v1.10/howto_basicfeatures.html#Variable_Interpolation -->
<!-- Load System properties -->
<system/>
<!-- Load Environment variables -->
<env/>
<!-- Load test local.properties (for unit testing)-->
<properties fileName="local.properties" throwExceptionOnMissing="true"/>
</override>
</configuration>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0"?>
<!--
DSpace Configuration Definition
This file defines the ordering of configuration files loaded by DSpace.
For more information and examples of valid configuration definition files, see:
http://commons.apache.org/proper/commons-configuration/userguide_v1.10/howto_configurationbuilder.html
-->
<configuration>
<header>
<!-- Necessary for CombinedConfiguration to detect & reload individual config files.
See JavaDocs for org.apache.commons.configuration.CombinedConfiguration setForceReloadCheck() method -->
<result forceReloadCheck="true"/>
</header>
<!-- Configurations in this section override one another.
Earlier values take precedence, and override any values in later config files -->
<override>
<!-- NOTE: Optionally, you could just use Variable Interpolation instead
of directly loading these System/Env properties:
http://commons.apache.org/proper/commons-configuration/userguide_v1.10/howto_basicfeatures.html#Variable_Interpolation -->
<!-- Load System properties -->
<system/>
<!-- Load Environment variables -->
<env/>
<!-- Load our dspace.cfg-->
<properties fileName="dspace.cfg" throwExceptionOnMissing="true">
<!-- Reload this file if it has changed, and at least 1 minute (60,000 ms) has passed -->
<reloadingStrategy refreshDelay="60000"
config-class="org.apache.commons.configuration.reloading.FileChangedReloadingStrategy"/>
</properties>
<!-- Load dspace-defaults.cfg (defines default settings for dspace-services) -->
<properties fileName="dspace-defaults.cfg" config-optional="true"/>
</override>
</configuration>

11
pom.xml
View File

@@ -1007,6 +1007,12 @@
<artifactId>dspace-tm-extractors</artifactId>
<version>1.0.1</version>
</dependency>
<!-- Required by Commons Configuration -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
@@ -1023,6 +1029,11 @@
<version>3.2</version>
<!-- <version>3.1</version> xmlui - wing -->
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>