Author: jawi
Date: Fri Nov 8 06:52:14 2013
New Revision: 1539930
URL: http://svn.apache.org/r1539930
Log:
ACE-401 - configurator should be less strict in its substitution:
- the configurator's variable substitution algorithm was very strict
regarding the use of '${' and '}' patterns. In case it finds some-
thing that looks like a substitution pattern, but upon closer look
isn't one, the configurator should leave it alone and proceed;
- also do not replace unknown variables with an empty string, as this
might be an indication of a configuration problem, so let the cause
of it remain.
Modified:
ace/trunk/org.apache.ace.configurator/ (props changed)
ace/trunk/org.apache.ace.configurator/src/org/apache/ace/configurator/Configurator.java
ace/trunk/org.apache.ace.configurator/test/org/apache/ace/configurator/ConfiguratorTest.java
ace/trunk/org.apache.ace.configurator/test/org/apache/ace/configurator/MockConfigAdmin.java
ace/trunk/org.apache.ace.configurator/test/org/apache/ace/configurator/MockConfiguration.java
Propchange: ace/trunk/org.apache.ace.configurator/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Fri Nov 8 06:52:14 2013
@@ -4,3 +4,4 @@ generated
store
bundle-cache
felix-cache
+test-output
Modified:
ace/trunk/org.apache.ace.configurator/src/org/apache/ace/configurator/Configurator.java
URL:
http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.configurator/src/org/apache/ace/configurator/Configurator.java?rev=1539930&r1=1539929&r2=1539930&view=diff
==============================================================================
---
ace/trunk/org.apache.ace.configurator/src/org/apache/ace/configurator/Configurator.java
(original)
+++
ace/trunk/org.apache.ace.configurator/src/org/apache/ace/configurator/Configurator.java
Fri Nov 8 06:52:14 2013
@@ -40,43 +40,49 @@ import org.osgi.service.log.LogService;
/**
* Configures bundles managed by the <code>ConfigurationAdmin</code>. This
Configurator uses text files as configuration
- * files containing properties. When a configuration file is added, the
properties are being read and added. If the config file is
- * removed, the properties are removed as well.
+ * files containing properties. When a configuration file is added, the
properties are being read and added. If the
+ * config file is removed, the properties are removed as well.
* <p>
- * The configuration files should be stored in the configuration directory
(often the 'conf' directory) of the OSGi framework and
- * should have the format: <pid>.cfg
+ * The configuration files should be stored in the configuration directory
(often the 'conf' directory) of the OSGi
+ * framework and should have the format: <pid>.cfg
* <p>
* Note: this Configurator is based upon the principle in the FileInstall
bundle Peter Kriens wrote. (see
* http://www.aqute.biz/Code/FileInstall for more information)
*/
public class Configurator implements Runnable {
-
private static final String DELIM_START = "${";
private static final String DELIM_STOP = "}";
+
private static final FileFilter FILENAME_FILTER = new FileFilter() {
public boolean accept(File file) {
return !file.isHidden() && (file.getName().endsWith(".cfg") ||
file.isDirectory());
}
};
+
private static final String FACTORY_INSTANCE_KEY = "factory.instance.pid";
- private volatile LogService m_log; /* injected by
dependency manager */
- private volatile ConfigurationAdmin m_configAdmin; /* injected by
dependency manager */
- private volatile BundleContext m_context; /* injected by
dependency manager */
+ private volatile LogService m_log; /* injected by dependency manager */
+ private volatile ConfigurationAdmin m_configAdmin; /* injected by
dependency manager */
+ private volatile BundleContext m_context; /* injected by dependency
manager */
private final File m_configDir;
private final long m_pollInterval;
- private final Map m_checksums = new HashMap(); // absolutepath ->
xor(length, date)
- private final Map m_foundFactories = new HashMap(); // absolutedirpath ->
(absolutepath -> xor(length, date))
- private Thread m_configThread;
+ private final Map<String, Long> m_checksums = new HashMap<String, Long>();
+ private final Map<String, Map<String, Long>> m_foundFactories = new
HashMap<String, Map<String, Long>>();
private final boolean m_reconfig;
+ private Thread m_configThread;
+
/**
* Instantiates a new configurator.
- * @param dir The directory to watch.
- * @param pollInterval The poll iterval in ms.
- * @param reconfig Whether or not to use reconfiguration: if
<code>false</code>, existing configuration
- * values will not be overwritten, only new values (for a given pid) will
be added.
+ *
+ * @param dir
+ * The directory to watch.
+ * @param pollInterval
+ * The poll iterval in ms.
+ * @param reconfig
+ * Whether or not to use reconfiguration: if
<code>false</code>, existing configuration values will not
+ * be overwritten, only new values (for a given pid) will be
added.
*/
public Configurator(File dir, long pollInterval, boolean reconfig) {
if ((dir == null) || !dir.isDirectory() || (pollInterval < 0)) {
@@ -100,7 +106,7 @@ public class Configurator implements Run
/**
* Stops the Configuration timer.
- *
+ *
* @throws InterruptedException
*/
synchronized void stop() throws InterruptedException {
@@ -112,8 +118,8 @@ public class Configurator implements Run
}
/**
- * Starts the actual Timer task, and calls the configurator to make sure
the configurations are performed. Checking whether
- * a new configuration is present, will be done with an interval that can
be defined via a system property.
+ * Starts the actual Timer task, and calls the configurator to make sure
the configurations are performed. Checking
+ * whether a new configuration is present, will be done with an interval
that can be defined via a system property.
*/
public void run() {
try {
@@ -133,7 +139,7 @@ public class Configurator implements Run
* the size of the new configuration has changed.
*/
private void doConfigs() {
- Set pids = new HashSet(m_checksums.keySet());
+ Set<String> pids = new HashSet<String>(m_checksums.keySet());
File[] files = m_configDir.listFiles(FILENAME_FILTER);
for (int i = 0; (files != null) && (i < files.length); i++) {
@@ -162,10 +168,10 @@ public class Configurator implements Run
private void doFactoryConfigs(String factoryPid, File[] newInstances) {
if (!m_foundFactories.containsKey(factoryPid)) {
- m_foundFactories.put(factoryPid, new HashMap());
+ m_foundFactories.put(factoryPid, new HashMap<String, Long>());
}
- Map instances = (Map) m_foundFactories.get(factoryPid);
- Set instancesPids = new HashSet(instances.keySet());
+ Map<String, Long> instances = m_foundFactories.get(factoryPid);
+ Set<String> instancesPids = new HashSet<String>(instances.keySet());
for (int j = 0; j < newInstances.length; j++) {
File instanceConfigFile = newInstances[j];
@@ -180,7 +186,7 @@ public class Configurator implements Run
instancesPids.remove(instancePid);
}
- for (Iterator e = instancesPids.iterator(); e.hasNext(); ) {
+ for (Iterator e = instancesPids.iterator(); e.hasNext();) {
String instancePid = (String) e.next();
deleteConfig(instancePid, factoryPid);
instances.remove(instancePid);
@@ -188,8 +194,9 @@ public class Configurator implements Run
}
/**
- * Sets the Configuration and calls update() to do the actual
configuration on the ManagedService. If and only if the configuration
- * did not exist before or has changed. A configuration has changed if the
length or the lastModified date has changed.
+ * Sets the Configuration and calls update() to do the actual
configuration on the ManagedService. If and only if
+ * the configuration did not exist before or has changed. A configuration
has changed if the length or the
+ * lastModified date has changed.
*/
private void processConfigFile(File configFile, String factoryPid) {
InputStream in = null;
@@ -236,7 +243,7 @@ public class Configurator implements Run
properties.put(FACTORY_INSTANCE_KEY, factoryPid + "_" + pid);
}
config.update(properties);
- m_log.log(LogService.LOG_DEBUG, "Updated configuration for pid '"
+ pid + "' (" + properties +")");
+ m_log.log(LogService.LOG_DEBUG, "Updated configuration for pid '"
+ pid + "' (" + properties + ")");
}
catch (IOException ex) {
m_log.log(LogService.LOG_ERROR, "Unable to update configuration
for pid '" + pid + "'", ex);
@@ -292,16 +299,16 @@ public class Configurator implements Run
}
}
-
/**
* Performs variable substitution for a complete set of properties
- *
+ *
* @see #substVars(String, String, java.util.Map, java.util.Properties)
- * @param properties Set of properties to apply substitution on.
+ * @param properties
+ * Set of properties to apply substitution on.
* @return Same set of properties with all variables substituted.
*/
private Properties substVars(Properties properties) {
- for (Enumeration propertyKeys = properties.propertyNames();
propertyKeys.hasMoreElements(); ) {
+ for (Enumeration propertyKeys = properties.propertyNames();
propertyKeys.hasMoreElements();) {
String name = (String) propertyKeys.nextElement();
String value = properties.getProperty(name);
properties.setProperty(name, substVars(value, name, null,
properties));
@@ -311,26 +318,30 @@ public class Configurator implements Run
/**
* <p>
- * This method performs property variable substitution on the specified
value. If the specified value contains the syntax
- * <tt>${<prop-name>}</tt>, where <tt><prop-name></tt> refers
to either a configuration property or a
- * system property, then the corresponding property value is substituted
for the variable placeholder. Multiple variable
- * placeholders may exist in the specified value as well as nested
variable placeholders, which are substituted from inner
- * most to outer most. Configuration properties override system properties.
+ * This method performs property variable substitution on the specified
value. If the specified value contains the
+ * syntax <tt>${<prop-name>}</tt>, where <tt><prop-name></tt>
refers to either a configuration property
+ * or a system property, then the corresponding property value is
substituted for the variable placeholder. Multiple
+ * variable placeholders may exist in the specified value as well as
nested variable placeholders, which are
+ * substituted from inner most to outer most. Configuration properties
override system properties.
* </p>
- *
- * @param val The string on which to perform property substitution.
- * @param currentKey The key of the property being evaluated used to
detect cycles.
- * @param cycleMap Map of variable references used to detect nested cycles.
- * @param configProps Set of configuration properties.
+ *
+ * @param val
+ * The string on which to perform property substitution.
+ * @param currentKey
+ * The key of the property being evaluated used to detect
cycles.
+ * @param cycleMap
+ * Map of variable references used to detect nested cycles.
+ * @param configProps
+ * Set of configuration properties.
* @return The value of the specified string after system property
substitution.
- * @throws IllegalArgumentException If there was a syntax error in the
property placeholder syntax or a recursive variable
- * reference.
+ * @throws IllegalArgumentException
+ * If there was a syntax error in the property placeholder
syntax or a recursive variable reference.
*/
- private String substVars(String val, String currentKey, Map cycleMap,
Properties configProps) throws IllegalArgumentException {
+ private String substVars(String val, String currentKey, Map<String,
String> cycleMap, Properties configProps) throws IllegalArgumentException {
// If there is currently no cycle map, then create
// one for detecting cycles for this invocation.
if (cycleMap == null) {
- cycleMap = new HashMap();
+ cycleMap = new HashMap<String, String>();
}
// Put the current key in the cycle map.
@@ -360,14 +371,10 @@ public class Configurator implements Run
// If we do not have a start or stop delimiter, then just
// return the existing value.
- if ((startDelim < 0) && (stopDelim < 0)) {
+ // ACE-401: be liberal to the content, and do not throw an exception
in case we see something that resembles a substitution but is in fact nothing...
+ if ((startDelim < 0) || (stopDelim < 0) || (startDelim > stopDelim)) {
return val;
}
- // At this point, we found a stop delimiter without a start,
- // so throw an exception.
- else if (((startDelim < 0) || (startDelim > stopDelim)) && (stopDelim
>= 0)) {
- throw new IllegalArgumentException("stop delimiter with no start
delimiter: " + val);
- }
// At this point, we have found a variable placeholder so
// we must perform a variable substitution on it.
@@ -384,10 +391,11 @@ public class Configurator implements Run
// Try to configuration properties first.
String substValue = (configProps != null) ?
configProps.getProperty(variable, null) : null;
if (substValue == null) {
- // Ignore unknown property values.
+ // Ignore unknown property values, check whether it is defined as
framework or system property...
substValue = m_context.getProperty(variable);
if (substValue == null) {
- substValue = "";
+ // Still not found, then ignore this substitution...
+ return val;
}
}
@@ -408,4 +416,4 @@ public class Configurator implements Run
// Return the value.
return val;
}
-}
\ No newline at end of file
+}
Modified:
ace/trunk/org.apache.ace.configurator/test/org/apache/ace/configurator/ConfiguratorTest.java
URL:
http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.configurator/test/org/apache/ace/configurator/ConfiguratorTest.java?rev=1539930&r1=1539929&r2=1539930&view=diff
==============================================================================
---
ace/trunk/org.apache.ace.configurator/test/org/apache/ace/configurator/ConfiguratorTest.java
(original)
+++
ace/trunk/org.apache.ace.configurator/test/org/apache/ace/configurator/ConfiguratorTest.java
Fri Nov 8 06:52:14 2013
@@ -19,6 +19,9 @@
package org.apache.ace.configurator;
import static org.apache.ace.test.utils.TestUtils.UNIT;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
import java.io.File;
import java.io.FileOutputStream;
@@ -26,6 +29,8 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.Dictionary;
import java.util.Properties;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import org.apache.ace.test.utils.FileUtils;
import org.apache.ace.test.utils.TestUtils;
@@ -37,320 +42,272 @@ import org.testng.annotations.BeforeMeth
import org.testng.annotations.Test;
public class ConfiguratorTest {
-
private Configurator m_configurator;
private File m_configDir;
- private ConfigurationAdmin m_configAdmin;
-
- @BeforeMethod(alwaysRun = true)
- protected void setUp() throws Exception {
- setUp(false);
- }
-
- /**
- * Sets up the environment for testing.
- * @param reconfig Indicates whether or not the configurator should use
reconfiguration.
- */
- protected void setUp(boolean reconfig) throws Exception {
- m_configAdmin = new MockConfigAdmin();
-
- m_configDir = FileUtils.createTempFile(null);
- m_configDir.mkdir();
- m_configurator = new Configurator(m_configDir, 400, reconfig);
-
- TestUtils.configureObject(m_configurator, ConfigurationAdmin.class,
m_configAdmin);
- TestUtils.configureObject(m_configurator, LogService.class);
- TestUtils.configureObject(m_configurator, BundleContext.class,
TestUtils.createMockObjectAdapter(BundleContext.class, new Object() {
- @SuppressWarnings("unused")
- public String getProperty(String key) {
- return "contextProp";
- }
- }));
- m_configurator.start();
- }
+ private MockConfigAdmin m_configAdmin;
- /**
- * save the properties into a configuration file the configurator can read.
- * The file is first created and then moved to make sure the configuration
doesn't read an empty file
- */
- private void saveConfiguration(String servicePid, Properties
configuration) {
- saveConfiguration(servicePid, null, configuration);
- }
+ private volatile CountDownLatch m_deleteLatch;
+ private volatile CountDownLatch m_updateLatch;
- /**
- * save the properties into a configuration file stored in a directory
reflecting the factory pid
- */
- private void saveConfiguration(String servicePid, String factoryPid,
Properties configuration) {
- OutputStream fileOutputStream = null;
- File outFile = null;
- try {
- outFile = FileUtils.createTempFile(null);
- fileOutputStream = new FileOutputStream(outFile);
- configuration.store(fileOutputStream, null);
- } catch (IOException ioe) {
- // the test will fail, ignore this.
- } finally {
- if (fileOutputStream != null) {
- try {
- fileOutputStream.close();
- }
- catch (IOException e) {
- // nothing we can do
- }
- }
- }
- if (outFile != null) {
- if (factoryPid == null) {
- File dest = new File(m_configDir, servicePid + ".cfg");
- if (dest.exists()) {
- dest.delete();
- }
- renameFile(outFile, dest);
- }
- else {
- File file = new File(m_configDir, factoryPid);
- file.mkdirs();
- File dest = new File(file, servicePid + ".cfg");
- if (dest.exists()) {
- dest.delete();
- }
- renameFile(outFile, dest);
- }
- }
- }
-
- // remove a created configuration file
- private void removeConfiguration(String servicePid) {
- removeConfiguration(servicePid, null);
- }
-
- private void removeConfiguration(String servicePid, String factoryPid) {
- if (factoryPid != null) {
- new File(m_configDir, factoryPid + File.separator + servicePid +
".cfg").delete();
- } else {
- new File(m_configDir, servicePid + ".cfg").delete();
- }
- }
-
- // set some standard properties for testing
- private Properties createProperties() {
- Properties props = new Properties();
- props.put("test", "value1");
- props.put("test2", "value2");
- return props;
- }
-
- // add a configuration
@Test(groups = { UNIT })
- public void testAddConfiguration() {
+ public void testAddConfiguration() throws Exception {
+ String pid = "test-add";
+
Properties initialConfiguration = createProperties();
- saveConfiguration("test-add", initialConfiguration);
+ saveConfiguration(pid, initialConfiguration);
- Dictionary configuration =
getAndWaitForConfiguration(initialConfiguration);
- assert configuration != null : "No configuration received from
configurator";
- assert configuration.equals(createProperties()) : "Configuration
content is unexpected";
+ Dictionary configuration = getAndWaitForConfigurationUpdate(pid);
+ assertNotNull(configuration, "No configuration received from
configurator");
+ assertEquals(createProperties(), configuration, "Configuration content
is unexpected");
}
@Test(groups = { UNIT })
- public void testAddFactoryConfiguration() {
- Properties props = createProperties();
- saveConfiguration("test-add", "testFactory", props);
-
- Dictionary configuration = getAndWaitForConfiguration(props);
- assert configuration != null : "No configuration received from
configurator";
- assert
"testFactory_test-add".equals(configuration.remove("factory.instance.pid")) :
"Incorrect factory instance pid was added to the configuration";
- assert configuration.equals(createProperties()) : "Configuration
content is unexpected";
- }
+ public void testAddFactoryConfiguration() throws Exception {
+ String pid = "test-add";
+ String factoryPID = "testFactory";
- // remove a configuration
- @Test(groups = { UNIT })
- public void testRemoveFactoryConfiguration() {
Properties props = createProperties();
- saveConfiguration("test-remove", "testFactory", props);
- getAndWaitForConfiguration(props);
-
- removeConfiguration("test-remove", "testFactory");
+ saveConfiguration(pid, "testFactory", props);
- // after some processing time, we should get a message that the
configuration is now removed.
- long startTimeMillis = System.currentTimeMillis();
- boolean isDeleted = false;
- try {
- while (!isDeleted && (System.currentTimeMillis() < startTimeMillis
+ 2000)) {
- isDeleted = ((MockConfiguration)
m_configAdmin.getConfiguration("")).isDeleted();
- if (!isDeleted) {
- Thread.sleep(100);
- }
- }
- } catch (InterruptedException ie) {
- // not much we can do
- }
- catch (IOException e) {
- // cannot come from our mock config admin
- }
- assert isDeleted : "The configuration is not removed as expected";
- }
-
- @Test(groups = { UNIT })
- public void testPropertySubstitution( ) {
- Properties initialConfiguration = createProperties();
- initialConfiguration.put("var", "value");
- initialConfiguration.put("subst", "${var}");
- saveConfiguration("test-subst", initialConfiguration);
-
- Dictionary configuration =
getAndWaitForConfiguration(initialConfiguration);
- assert configuration != null : "No configuration received from
configurator";
- assert configuration.get("subst").equals(configuration.get("var")) :
"Substitution failed";
- }
-
- @Test(groups = { UNIT })
- public void testPropertySubstitutionFromContext() {
- Properties initialConfiguration = createProperties();
- initialConfiguration.put("subst", "${var}");
- saveConfiguration("test-subst", initialConfiguration);
-
- Dictionary configuration =
getAndWaitForConfiguration(initialConfiguration);
- assert configuration != null : "No configuration received from
configurator";
- assert configuration.get("subst") != null : "Substitution failed";
+ Dictionary configuration =
getAndWaitForConfigurationUpdate(factoryPID);
+ assertNotNull(configuration, "No configuration received from
configurator");
+ assertEquals(configuration.remove("factory.instance.pid"),
"testFactory_test-add", "Incorrect factory instance pid was added to the
configuration");
+ assertEquals(createProperties(), configuration, "Configuration content
is unexpected");
}
// update a configuration, only adding a key (this is allowed in all cases)
@Test(groups = { UNIT })
- public void testChangeConfigurationUsingNewKey() {
+ public void testChangeConfigurationUsingNewKey() throws Exception {
+ String pid = "test-change";
+
Properties initialConfiguration = createProperties();
- saveConfiguration("test-change", initialConfiguration);
+ saveConfiguration(pid, initialConfiguration);
- Dictionary configuration =
getAndWaitForConfiguration(initialConfiguration);
- assert configuration != null : "No configuration received from
configurator";
- assert configuration.equals(initialConfiguration) : "Configuration
content not expected. Was expecting " + initialConfiguration.size() + " but got
" + configuration.size();
+ Dictionary configuration = getAndWaitForConfigurationUpdate(pid);
+ assertNotNull(configuration, "No configuration received from
configurator");
+ assertEquals(initialConfiguration, configuration);
- initialConfiguration.put("anotherKey","anotherValue");
+ initialConfiguration.put("anotherKey", "anotherValue");
saveConfiguration("test-change", initialConfiguration);
// now the configuration should be updated
- configuration = getAndWaitForConfiguration(initialConfiguration);
- assert configuration != null : "No configuration received from
configurator";
- assert configuration.equals(initialConfiguration) : "Configuration
content not expected. Was expecting " + initialConfiguration.size() + " but got
" + configuration.size();
+ configuration = getAndWaitForConfigurationUpdate(pid);
+ assertNotNull(configuration, "No configuration received from
configurator");
+ assertEquals(initialConfiguration, configuration);
}
// update a configuration, changing an already existing key, not using
reconfiguration
@Test(groups = { UNIT })
- public void testChangeConfigurationUsingSameKeyNoReconfigure() {
+ public void testChangeConfigurationUsingSameKeyNoReconfigure() throws
Exception {
+ String pid = "test-change";
+
Properties configurationValues = createProperties();
Properties initialConfigurationValues = new Properties();
initialConfigurationValues.putAll(configurationValues);
- saveConfiguration("test-change", configurationValues);
+ saveConfiguration(pid, configurationValues);
- Dictionary configuration =
getAndWaitForConfiguration(configurationValues);
- assert configuration != null : "No configuration received from
configurator";
- assert configuration.equals(configurationValues) : "Configuration
content not expected. Was expecting " + configurationValues.size() + " but got
" + configuration.size();
+ Dictionary configuration = getAndWaitForConfigurationUpdate(pid);
+ assertNotNull(configuration, "No configuration received from
configurator");
+ assertEquals(configurationValues, configuration);
- configurationValues.put("test","value42");
+ configurationValues.put("test", "value42");
saveConfiguration("test-change", configurationValues);
// The update should have been ignored, and the old values should
still be present.
- configuration = getAndWaitForConfiguration(configurationValues);
- assert configuration != null : "No configuration received from
configurator";
- assert configuration.equals(initialConfigurationValues) :
"Configuration content not expected. Was expecting " +
configurationValues.size() + " but got " + configuration.size();
+ configuration = getAndWaitForConfigurationUpdate(pid);
+ assertNotNull(configuration, "No configuration received from
configurator");
+ assertEquals(initialConfigurationValues, configuration);
}
// update a configuration, changing an already existing key, using
reconfiguration
@Test(groups = { UNIT })
public void testChangeConfigurationUsingSameKeyWithReconfigure() throws
Exception {
+ String pid = "test-change";
+
setUp(true); // Instruct the configurator to reconfigure
Properties configurationValues = createProperties();
- saveConfiguration("test-change", configurationValues);
+ saveConfiguration(pid, configurationValues);
- Dictionary configuration =
getAndWaitForConfiguration(configurationValues);
- assert configuration != null : "No configuration received from
configurator";
- assert configuration.equals(configurationValues) : "Configuration
content not expected. Was expecting " + configurationValues.size() + " but got
" + configuration.size();
+ Dictionary configuration = getAndWaitForConfigurationUpdate(pid);
+ assertNotNull(configuration, "No configuration received from
configurator");
+ assertEquals(configurationValues, configuration);
- configurationValues.put("test","value42");
- saveConfiguration("test-change", configurationValues);
+ configurationValues.put("test", "value42");
+ saveConfiguration(pid, configurationValues);
// now the configuration should be updated
- configuration = getAndWaitForConfiguration(configurationValues);
- assert configuration != null : "No configuration received from
configurator";
- assert configuration.equals(configurationValues) : "Configuration
content not expected. Was expecting " + configurationValues.size() + " but got
" + configuration.size();
+ configuration = getAndWaitForConfigurationUpdate(pid);
+ assertNotNull(configuration, "No configuration received from
configurator");
+ assertEquals(configurationValues, configuration);
+ }
+
+ @Test(groups = { UNIT })
+ public void testPropertySubstitution() throws Exception {
+ String pid = "test-subst";
+
+ Properties initial = new Properties();
+ initial.put("key1", "leading ${foo.${bar}} middle ${baz} trailing");
+ initial.put("bar", "a");
+ initial.put("foo.a", "text");
+ initial.put("baz", "word");
+ // ACE-401: use some weird log4j conversion pattern in our config
file, should not confuse the Configurator's
+ // substitution algorithm...
+ initial.put("key2", "%d{ISO8601} | %-5.5p | %C | %X{bundle.name} |
%m%n");
+ // unknown and partially unknown variables shouldn't get substituted...
+ initial.put("key3", "${qux} ${quu.${bar}} ${baz.${bar}}");
+ saveConfiguration(pid, initial);
+
+ Dictionary config = getAndWaitForConfigurationUpdate(pid);
+ assertNotNull(config, "No configuration received from configurator");
+ assertEquals(config.get("key1"), "leading text middle word trailing",
"Substitution failed!");
+ assertEquals(config.get("key2"), "%d{ISO8601} | %-5.5p | %C |
%X{bundle.name} | %m%n", "Substitution failed!");
+ assertEquals(config.get("key3"), "${qux} ${quu.${bar}} ${baz.${bar}}",
"Substitution failed!");
+ }
+
+ @Test(groups = { UNIT })
+ public void testPropertySubstitutionFromContext() throws Exception {
+ String pid = "test-subst";
+
+ Properties initialConfiguration = createProperties();
+ initialConfiguration.put("subst", "${contextProp}");
+ saveConfiguration(pid, initialConfiguration);
+
+ Dictionary configuration = getAndWaitForConfigurationUpdate(pid);
+ assertNotNull(configuration, "No configuration received from
configurator");
+ assertEquals(configuration.get("subst"), "contextVal", "Substitution
failed");
}
// remove a configuration
@Test(groups = { UNIT })
- public void testRemoveConfiguration() {
+ public void testRemoveConfiguration() throws Exception {
+ String pid = "test-remove";
+
Properties initialConfiguration = createProperties();
- saveConfiguration("test-remove", initialConfiguration);
+ saveConfiguration(pid, initialConfiguration);
- Dictionary configuration =
getAndWaitForConfiguration(initialConfiguration);
- assert configuration != null : "No configuration received from
configurator";
- assert configuration.equals(createProperties()) : "Configuration
content is unexpected";
+ Dictionary configuration = getAndWaitForConfigurationUpdate(pid);
+ assertNotNull(configuration, "No configuration received from
configurator");
+ assertEquals(createProperties(), configuration);
// ok, the configuration is done.
// now try to remove it.
- removeConfiguration("test-remove");
+ removeConfiguration(pid);
// after some processing time, we should get a message that the
configuration is now removed.
- long startTimeMillis = System.currentTimeMillis();
- boolean isDeleted = false;
- try {
- while (!isDeleted && (System.currentTimeMillis() < startTimeMillis
+ 2000)) {
- isDeleted = ((MockConfiguration)
m_configAdmin.getConfiguration("")).isDeleted();
- if (!isDeleted) {
- Thread.sleep(100);
- }
- }
- } catch (InterruptedException ie) {
- // not much we can do
- }
- catch (IOException e) {
- // cannot come from our mock config admin
- }
- assert isDeleted : "The configuration is not removed as expected";
+ waitForConfigurationDelete(pid);
+ }
+
+ // remove a configuration
+ @Test(groups = { UNIT })
+ public void testRemoveFactoryConfiguration() throws Exception {
+ String pid = "test-remove";
+ String factoryPID = "testFactory";
+
+ Properties props = createProperties();
+ saveConfiguration(pid, factoryPID, props);
+ getAndWaitForConfigurationUpdate(factoryPID);
+
+ removeConfiguration(pid, factoryPID);
+
+ // after some processing time, we should get a message that the
configuration is now removed.
+ waitForConfigurationDelete(factoryPID);
+ }
+
+ @BeforeMethod(alwaysRun = true)
+ protected void setUp() throws Exception {
+ setUp(false);
}
/**
- * Get the configuration and if it not available yet wait for it.
- * If there is still no configuration after the wait time,
- * null is returned.
+ * Sets up the environment for testing.
+ *
+ * @param reconfig
+ * Indicates whether or not the configurator should use
reconfiguration.
*/
- public Dictionary getAndWaitForConfiguration(Dictionary
expectedConfiguration) {
- long startTimeMillis = System.currentTimeMillis();
- // make sure we iterate at least once
- Dictionary configuration = null;
- try {
- boolean success = false;
- while (!success && (System.currentTimeMillis() < startTimeMillis +
2000)) {
- configuration =
m_configAdmin.getConfiguration("").getProperties();
- if (configuration != null) {
- synchronized(configuration) {
- if (expectedConfiguration.equals(configuration)) {
- success = true;
- }
- }
- }
- if (!success) {
- Thread.sleep(100);
+ protected void setUp(boolean reconfig) throws Exception {
+ m_configAdmin = new MockConfigAdmin() {
+ @Override
+ void configDeleted(MockConfiguration config) {
+ m_deleteLatch.countDown();
+ }
+
+ @Override
+ void configUpdated(MockConfiguration config) {
+ m_updateLatch.countDown();
+ }
+ };
+
+ m_configDir = FileUtils.createTempFile(null);
+ m_configDir.mkdir();
+ m_configurator = new Configurator(m_configDir, 200, reconfig);
+
+ TestUtils.configureObject(m_configurator, ConfigurationAdmin.class,
m_configAdmin);
+ TestUtils.configureObject(m_configurator, LogService.class);
+ TestUtils.configureObject(m_configurator, BundleContext.class,
TestUtils.createMockObjectAdapter(BundleContext.class, new Object() {
+ @SuppressWarnings("unused")
+ public String getProperty(String key) {
+ if ("contextProp".equals(key)) {
+ return "contextVal";
}
+ return null;
}
- } catch (InterruptedException ie) {
- // not much we can do
- }
- catch (IOException e) {
- // cannot come from our mock config admin
- }
- return configuration;
+ }));
+ m_configurator.start();
}
@AfterMethod(alwaysRun = true)
- public void tearDown() throws Exception {
+ protected void tearDown() throws Exception {
m_configurator.stop();
FileUtils.removeDirectoryWithContent(m_configDir);
+
+ m_deleteLatch = null;
+ m_updateLatch = null;
+ }
+
+ // set some standard properties for testing
+ private Properties createProperties() {
+ Properties props = new Properties();
+ props.put("test", "value1");
+ props.put("test2", "value2");
+ return props;
+ }
+
+ /**
+ * Get the configuration and if it not available yet wait for it. If there
is still no configuration after the wait
+ * time, null is returned.
+ */
+ private Dictionary getAndWaitForConfigurationUpdate(String pid) throws
Exception {
+ assertTrue(m_updateLatch.await(2, TimeUnit.SECONDS));
+
+ return m_configAdmin.getConfiguration(pid).getProperties();
+ }
+
+ // remove a created configuration file
+ private void removeConfiguration(String servicePid) {
+ removeConfiguration(servicePid, null);
+ }
+
+ private void removeConfiguration(String servicePid, String factoryPid) {
+ if (factoryPid != null) {
+ new File(m_configDir, factoryPid + File.separator + servicePid +
".cfg").delete();
+ }
+ else {
+ new File(m_configDir, servicePid + ".cfg").delete();
+ }
+
+ m_deleteLatch = new CountDownLatch(1);
}
/**
* Renames a given source file to a new destination file, using Commons-IO.
- * <p>This avoids the problem mentioned in ACE-155.</p>
+ * <p>
+ * This avoids the problem mentioned in ACE-155.
+ * </p>
*
- * @param source the file to rename;
- * @param dest the file to rename to.
+ * @param source
+ * the file to rename;
+ * @param dest
+ * the file to rename to.
*/
private void renameFile(File source, File dest) {
try {
@@ -360,4 +317,66 @@ public class ConfiguratorTest {
throw new RuntimeException("Failed to rename file!", e);
}
}
+
+ /**
+ * save the properties into a configuration file the configurator can
read. The file is first created and then moved
+ * to make sure the configuration doesn't read an empty file
+ */
+ private void saveConfiguration(String servicePid, Properties
configuration) {
+ saveConfiguration(servicePid, null, configuration);
+ }
+
+ /**
+ * save the properties into a configuration file stored in a directory
reflecting the factory pid
+ */
+ private void saveConfiguration(String servicePid, String factoryPid,
Properties configuration) {
+ OutputStream fileOutputStream = null;
+ File outFile = null;
+ try {
+ outFile = FileUtils.createTempFile(null);
+ fileOutputStream = new FileOutputStream(outFile);
+ configuration.store(fileOutputStream, null);
+ }
+ catch (IOException ioe) {
+ // the test will fail, ignore this.
+ }
+ finally {
+ if (fileOutputStream != null) {
+ try {
+ fileOutputStream.close();
+ }
+ catch (IOException e) {
+ // nothing we can do
+ }
+ }
+ }
+ if (outFile != null) {
+ if (factoryPid == null) {
+ File dest = new File(m_configDir, servicePid + ".cfg");
+ if (dest.exists()) {
+ dest.delete();
+ }
+ renameFile(outFile, dest);
+ }
+ else {
+ File file = new File(m_configDir, factoryPid);
+ file.mkdirs();
+ File dest = new File(file, servicePid + ".cfg");
+ if (dest.exists()) {
+ dest.delete();
+ }
+ renameFile(outFile, dest);
+ }
+ }
+
+ m_updateLatch = new CountDownLatch(1);
+ }
+
+ /**
+ * Get the configuration and if it not available yet wait for it. If there
is still no configuration after the wait
+ * time, null is returned.
+ */
+ private void waitForConfigurationDelete(String pid) throws Exception {
+ assertTrue(m_deleteLatch.await(2, TimeUnit.SECONDS));
+ }
}
Modified:
ace/trunk/org.apache.ace.configurator/test/org/apache/ace/configurator/MockConfigAdmin.java
URL:
http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.configurator/test/org/apache/ace/configurator/MockConfigAdmin.java?rev=1539930&r1=1539929&r2=1539930&view=diff
==============================================================================
---
ace/trunk/org.apache.ace.configurator/test/org/apache/ace/configurator/MockConfigAdmin.java
(original)
+++
ace/trunk/org.apache.ace.configurator/test/org/apache/ace/configurator/MockConfigAdmin.java
Fri Nov 8 06:52:14 2013
@@ -19,36 +19,53 @@
package org.apache.ace.configurator;
import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
-public class MockConfigAdmin implements ConfigurationAdmin {
+public abstract class MockConfigAdmin implements ConfigurationAdmin {
+ private final Map<String, Configuration> m_configs;
- private Configuration m_configuration = new MockConfiguration();
+ public MockConfigAdmin() {
+ m_configs = new HashMap<String, Configuration>();
+ }
- public Configuration createFactoryConfiguration(String arg0) throws
IOException {
- // TODO Auto-generated method stub
- return null;
+ public Configuration createFactoryConfiguration(String factoryPID) throws
IOException {
+ return createFactoryConfiguration(factoryPID, null);
}
- public Configuration createFactoryConfiguration(String arg0, String arg1)
throws IOException {
- // TODO Auto-generated method stub
- return null;
+ public Configuration createFactoryConfiguration(String factoryPID, String
location) throws IOException {
+ Configuration config = m_configs.get(factoryPID);
+ if (config == null) {
+ config = new MockConfiguration(this);
+ m_configs.put(factoryPID, config);
+ }
+ return config;
}
public Configuration getConfiguration(String pid) throws IOException {
- return m_configuration;
+ return getConfiguration(pid, null);
}
- public Configuration getConfiguration(String arg0, String arg1) throws
IOException {
- // TODO Auto-generated method stub
- return m_configuration;
+ public Configuration getConfiguration(String pid, String location) throws
IOException {
+ Configuration config = m_configs.get(pid);
+ if (config == null) {
+ config = new MockConfiguration(this);
+ m_configs.put(pid, config);
+ }
+ return config;
}
- public Configuration[] listConfigurations(String arg0) throws IOException,
InvalidSyntaxException {
- return new Configuration[] {m_configuration};
+ public Configuration[] listConfigurations(String filter) throws
IOException, InvalidSyntaxException {
+ Collection<Configuration> configs = m_configs.values();
+ return configs.toArray(new Configuration[configs.size()]);
}
+ abstract void configDeleted(MockConfiguration config);
+
+ abstract void configUpdated(MockConfiguration config);
}
Modified:
ace/trunk/org.apache.ace.configurator/test/org/apache/ace/configurator/MockConfiguration.java
URL:
http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.configurator/test/org/apache/ace/configurator/MockConfiguration.java?rev=1539930&r1=1539929&r2=1539930&view=diff
==============================================================================
---
ace/trunk/org.apache.ace.configurator/test/org/apache/ace/configurator/MockConfiguration.java
(original)
+++
ace/trunk/org.apache.ace.configurator/test/org/apache/ace/configurator/MockConfiguration.java
Fri Nov 8 06:52:14 2013
@@ -24,26 +24,28 @@ import java.util.Dictionary;
import org.osgi.service.cm.Configuration;
public class MockConfiguration implements Configuration {
-
+ private final MockConfigAdmin m_ca;
private Dictionary m_properties = null;
private boolean m_isDeleted = false;
+
+ public MockConfiguration(MockConfigAdmin ca) {
+ m_ca = ca;
+ }
public void delete() throws IOException {
m_isDeleted = true;
+ m_ca.configDeleted(this);
}
public String getBundleLocation() {
- // TODO Auto-generated method stub
return null;
}
public String getFactoryPid() {
- // TODO Auto-generated method stub
return null;
}
public String getPid() {
- // TODO Auto-generated method stub
return null;
}
@@ -51,22 +53,19 @@ public class MockConfiguration implement
return m_properties;
}
- public void setBundleLocation(String arg0) {
- // TODO Auto-generated method stub
+ public boolean isDeleted() {
+ return m_isDeleted;
+ }
+ public void setBundleLocation(String location) {
}
public void update() throws IOException {
- // TODO Auto-generated method stub
-
+ m_ca.configUpdated(this);
}
public synchronized void update(Dictionary newConfiguration) throws
IOException {
m_properties = newConfiguration;
+ m_ca.configUpdated(this);
}
-
- public boolean isDeleted() {
- return m_isDeleted;
- }
-
}