SLING-4272 : Issues in handling of configurations wrt update handling and write back

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1648985 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/test/java/org/apache/sling/installer/it/ConfigInstallTest.java b/src/test/java/org/apache/sling/installer/it/ConfigInstallTest.java
index 1dfc9f0..a972d5f 100644
--- a/src/test/java/org/apache/sling/installer/it/ConfigInstallTest.java
+++ b/src/test/java/org/apache/sling/installer/it/ConfigInstallTest.java
@@ -151,7 +151,7 @@
     }
 
     @Test
-    public void testInstallUpdateRemoveConfig() throws Exception {
+    public void testInstallUpdateRemoveConfigResource() throws Exception {
         final Dictionary<String, Object> cfgData = new Hashtable<String, Object>();
         cfgData.put("foo", "bar");
         final String cfgPid = getClass().getSimpleName() + "." + uniqueID();
@@ -189,7 +189,7 @@
     }
 
     @Test
-    public void testInstallUpdateRemoveTemplateConfig() throws Exception {
+    public void testInstallUpdateRemoveTemplateConfigResource() throws Exception {
         final Dictionary<String, Object> cfgData = new Hashtable<String, Object>();
         cfgData.put("foo", "bar");
         cfgData.put(InstallableResource.RESOURCE_IS_TEMPLATE, "true");
@@ -228,6 +228,85 @@
     }
 
     @Test
+    public void testInstallUpdateRemoveConfigFactoryResource() throws Exception {
+        final Dictionary<String, Object> cfgData = new Hashtable<String, Object>();
+        cfgData.put("foo", "bar");
+        final String cfgFactoryPid = getClass().getSimpleName() + "." + uniqueID();
+        final String alias = "alias" + uniqueID();
+        assertNull("Factory config " + cfgFactoryPid + " must not be found before test", findFactoryConfiguration(cfgFactoryPid));
+
+        // install factory config
+        final InstallableResource rsrc = new InstallableResource("/configA/" + cfgFactoryPid + "-" + alias,
+                null, cfgData, null, InstallableResource.TYPE_PROPERTIES, 10);
+        installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc}, null);
+
+        // get factory config
+        final Configuration cfg = waitForFactoryConfiguration("After installing", cfgFactoryPid, TIMEOUT, true);
+        assertEquals("Config value must match", "bar", cfg.getProperties().get("foo"));
+
+        // create second factory configuration with same alias
+        final Dictionary<String, Object> secondData = new Hashtable<String, Object>();
+        secondData.put("foo", "bla");
+        final InstallableResource rsrc2 = new InstallableResource("/configB/" + cfgFactoryPid + "-" + alias,
+                null, secondData, null, InstallableResource.TYPE_PROPERTIES, 20);
+        installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc2}, null);
+
+        sleep(200);
+
+        // get updated factory config
+        final Configuration secondCfg = waitForFactoryConfiguration("After updating", cfgFactoryPid, TIMEOUT, true);
+        assertEquals("Config value must match", "bla", secondCfg.getProperties().get("foo"));
+
+        // remove factory config
+        installer.updateResources(URL_SCHEME, null, new String[] {"/configB/" + cfgFactoryPid + "-" + alias});
+
+        sleep(200);
+
+        final Configuration origCfg = waitForFactoryConfiguration("After deleting", cfgFactoryPid, TIMEOUT, true);
+        assertEquals("Config value must match", "bar", origCfg.getProperties().get("foo"));
+    }
+
+    @Test
+    public void testInstallUpdateRemoveTemplateConfigFactoryResource() throws Exception {
+        final Dictionary<String, Object> cfgData = new Hashtable<String, Object>();
+        cfgData.put("foo", "bar");
+        cfgData.put(InstallableResource.RESOURCE_IS_TEMPLATE, "true");
+        final String cfgFactoryPid = getClass().getSimpleName() + "." + uniqueID();
+        final String alias = "alias" + uniqueID();
+        assertNull("Factory config " + cfgFactoryPid + " must not be found before test", findFactoryConfiguration(cfgFactoryPid));
+
+        // install factory config
+        final InstallableResource rsrc = new InstallableResource("/configA/" + cfgFactoryPid + "-" + alias,
+                null, cfgData, null, InstallableResource.TYPE_PROPERTIES, 10);
+        installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc}, null);
+
+        // get factory config
+        final Configuration cfg = waitForFactoryConfiguration("After installing", cfgFactoryPid, TIMEOUT, true);
+        assertEquals("Config value must match", "bar", cfg.getProperties().get("foo"));
+
+        // create second factory configuration
+        final Dictionary<String, Object> secondData = new Hashtable<String, Object>();
+        secondData.put("foo", "bla");
+        final InstallableResource rsrc2 = new InstallableResource("/configB/" + cfgFactoryPid + "-" + alias,
+                null, secondData, null, InstallableResource.TYPE_PROPERTIES, 20);
+        installer.updateResources(URL_SCHEME, new InstallableResource[] {rsrc2}, null);
+
+        sleep(200);
+
+        // get updated factory config
+        final Configuration secondCfg = waitForFactoryConfiguration("After updating", cfgFactoryPid, TIMEOUT, true);
+        assertEquals("Config value must match", "bla", secondCfg.getProperties().get("foo"));
+
+        // remove config
+        installer.updateResources(URL_SCHEME, null, new String[] {"/configB/" + cfgFactoryPid + "-" + alias});
+
+        sleep(200);
+
+        final Configuration noCfg = waitForFactoryConfiguration("After deleting", cfgFactoryPid, TIMEOUT, false);
+        assertNull("Factory configuration should be removed", noCfg);
+    }
+
+    @Test
     public void testDeferredConfigInstall() throws Exception {
         // get config admin bundle and wait for service
     	final Bundle configAdmin = this.getConfigAdminBundle();
diff --git a/src/test/java/org/apache/sling/installer/it/OsgiInstallerTestBase.java b/src/test/java/org/apache/sling/installer/it/OsgiInstallerTestBase.java
index 6ce5595..d35b954 100644
--- a/src/test/java/org/apache/sling/installer/it/OsgiInstallerTestBase.java
+++ b/src/test/java/org/apache/sling/installer/it/OsgiInstallerTestBase.java
@@ -187,23 +187,51 @@
         }
     }
 
-    protected Configuration findConfiguration(String pid) throws Exception {
+    /**
+     * Encode the value for the ldap filter: \, *, (, and ) should be escaped.
+     */
+    private static String encode(final String value) {
+        return value.replace("\\", "\\\\")
+                .replace("*", "\\*")
+                .replace("(", "\\(")
+                .replace(")", "\\)");
+    }
+
+    /**
+     * Find the configuration with the given pid.
+     */
+    protected Configuration findConfiguration(final String pid) throws Exception {
     	final ConfigurationAdmin ca = this.waitForConfigAdmin(true);
     	if (ca != null) {
-	    	final Configuration[] cfgs = ca.listConfigurations(null);
-	    	if (cfgs != null) {
-		    	for(Configuration cfg : cfgs) {
-		    	    try {
-    		    		if(cfg.getPid().equals(pid)) {
-    		    			return cfg;
-    		    		}
-		    	    } catch (IllegalStateException e) {}
-		    	}
+	    	final Configuration[] cfgs = ca.listConfigurations("(" + Constants.SERVICE_PID + "=" + encode(pid) + ")");
+	    	if (cfgs != null && cfgs.length > 0 ) {
+                if ( cfgs.length == 1 ) {
+                    return cfgs[0];
+                }
+                throw new IllegalStateException("More than one configuration for " + pid);
 	    	}
     	}
     	return null;
     }
 
+    /**
+     * Find the configuration with the given factory pid.
+     */
+    protected Configuration findFactoryConfiguration(final String factoryPid) throws Exception {
+        final ConfigurationAdmin ca = this.waitForConfigAdmin(true);
+        if (ca != null) {
+            final Configuration[] cfgs = ca.listConfigurations("("
+                    + ConfigurationAdmin.SERVICE_FACTORYPID + "=" + encode(factoryPid) + ")");
+            if (cfgs != null && cfgs.length > 0 ) {
+                if ( cfgs.length == 1 ) {
+                    return cfgs[0];
+                }
+                throw new IllegalStateException("More than one factory configuration for " + factoryPid);
+            }
+        }
+        return null;
+    }
+
     protected void waitForCondition(String info, long timeoutMsec, Condition c) throws Exception {
         final long end = System.currentTimeMillis() + timeoutMsec;
         do {
@@ -262,6 +290,40 @@
         return result;
     }
 
+    protected Configuration waitForFactoryConfiguration(final String info,
+            final String factoryPid,
+            final long timeoutMsec,
+            final boolean shouldBePresent)
+    throws Exception {
+        String msg;
+        if (info == null) {
+            msg = "";
+        } else {
+            msg = info + ": ";
+        }
+
+        Configuration result = null;
+        final long start = System.currentTimeMillis();
+        final long end = start + timeoutMsec;
+        log(LogService.LOG_DEBUG, "Starting factory config check at " + start + "; ending by " + end);
+        do {
+            result = findFactoryConfiguration(factoryPid);
+            if ((shouldBePresent && result != null) ||
+                    (!shouldBePresent && result == null)) {
+                break;
+            }
+            log(LogService.LOG_DEBUG, "Config check failed at " + System.currentTimeMillis() + "; sleeping");
+            sleep(25);
+        } while(System.currentTimeMillis() < end);
+
+        if (shouldBePresent && result == null) {
+            fail(msg + "Factory Configuration not found (" + factoryPid + ")");
+        } else if (!shouldBePresent && result != null) {
+            fail(msg + "Factory Configuration is still present (" + factoryPid + ")");
+        }
+        return result;
+    }
+
     protected Bundle findBundle(String symbolicName) {
     	for(Bundle b : bundleContext.getBundles()) {
     		if (symbolicName.equals(b.getSymbolicName())) {