ace-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From j...@apache.org
Subject svn commit: r1539930 - in /ace/trunk/org.apache.ace.configurator: ./ src/org/apache/ace/configurator/ test/org/apache/ace/configurator/
Date Fri, 08 Nov 2013 06:52:15 GMT
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: &lt;pid&gt;.cfg
+ * The configuration files should be stored in the configuration directory (often the 'conf'
directory) of the OSGi
+ * framework and should have the format: &lt;pid&gt;.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>${&lt;prop-name&gt;}</tt>, where <tt>&lt;prop-name&gt;</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>${&lt;prop-name&gt;}</tt>, where <tt>&lt;prop-name&gt;</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;
-    }
-
 }



Mime
View raw message