This is an automated email from the ASF dual-hosted git repository.

jsedding pushed a commit to branch issus/SLING-11741-config-plugin
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-base.git

commit 82f057e9601c8e4fd4769f6fe97a7ecdd3eca032
Author: Julian Sedding <jsedd...@apache.org>
AuthorDate: Wed Sep 20 09:59:22 2023 +0200

    untested - refactored to ConfigurationPlugin
---
 pom.xml                                            |  31 ++-
 .../sling/jcr/base/internal/AllowListFragment.java |  64 +++---
 .../BackwardsCompatibilityConfigurationPlugin.java |  73 +++++++
 .../jcr/base/internal/ConfigurationUpdater.java    | 236 ---------------------
 .../jcr/base/internal/LoginAdminAllowList.java     |  22 +-
 ...kwardsCompatibilityConfigurationPluginTest.java | 164 ++++++++++++++
 .../base/internal/ConfigurationUpdaterTest.java    | 175 ---------------
 7 files changed, 306 insertions(+), 459 deletions(-)

diff --git a/pom.xml b/pom.xml
index b2e04d2..a33ddae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -146,32 +146,45 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>5.10.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.vintage</groupId>
+            <artifactId>junit-vintage-engine</artifactId>
+            <version>5.10.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest</artifactId>
+            <version>2.2</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.testing.osgi-mock</artifactId>
-            <version>3.3.6</version>
+            <artifactId>org.apache.sling.testing.osgi-mock.junit4</artifactId>
+            <version>3.3.8</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.testing.sling-mock</artifactId>
-            <version>3.4.4</version>
+            <artifactId>org.apache.sling.testing.sling-mock.junit4</artifactId>
+            <version>3.4.10</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.testing.jcr-mock</artifactId>
-            <version>1.6.6</version>
+            <version>1.6.10</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.mockito</groupId>
-            <artifactId>mockito-all</artifactId>
-            <version>1.9.5</version>
+            <artifactId>mockito-core</artifactId>
+            <version>4.11.0</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
diff --git 
a/src/main/java/org/apache/sling/jcr/base/internal/AllowListFragment.java 
b/src/main/java/org/apache/sling/jcr/base/internal/AllowListFragment.java
index 3716931..f1c3203 100644
--- a/src/main/java/org/apache/sling/jcr/base/internal/AllowListFragment.java
+++ b/src/main/java/org/apache/sling/jcr/base/internal/AllowListFragment.java
@@ -18,9 +18,14 @@
  */
 package org.apache.sling.jcr.base.internal;
 
+import org.osgi.service.cm.ConfigurationPlugin;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.ConfigurationPolicy;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
 import org.osgi.service.metatype.annotations.AttributeDefinition;
 import org.osgi.service.metatype.annotations.Designate;
 import org.osgi.service.metatype.annotations.ObjectClassDefinition;
@@ -31,38 +36,43 @@ import java.util.Set;
 
 import static java.util.Arrays.asList;
 
-@ObjectClassDefinition(
-        name = "Apache Sling Login Admin Allow List Configuration Fragment",
-        description = "This list of Bundle Symbolic Names is added to the list 
of bundles which are allowed " +
-            "to use Administrative Login. The full list is built in a modular 
way out of all such configuration fragments."
-)
-@interface Configuration {
-
-    @AttributeDefinition(
-            name = "Name",
-            description = "Optional name to disambiguate configurations."
-    )
-    String allowlist_name() default "[unnamed]";
-
-    @AttributeDefinition(
-            name = "Allow listed BSNs",
-            description = "A list of bundle symbolic names allowed to use 
loginAdministrative()."
-    )
-    String[] allowlist_bundles();
-
-    @SuppressWarnings("unused")
-    String webconsole_configurationFactory_nameHint() default 
"{allowlist.name}: [{allowlist.bundles}]";
-}
-
 @Component(
-        configurationPid = AllowListFragment.FACTORY_PID,
-        configurationPolicy = ConfigurationPolicy.REQUIRE,
-        service = AllowListFragment.class
+        service = AllowListFragment.class,
+        configurationPid = {
+                AllowListFragment.FACTORY_PID,
+                
BackwardsCompatibilityConfigurationPlugin.WHITELIST_FRAGMENT_PID
+        },
+        configurationPolicy = ConfigurationPolicy.REQUIRE
 )
-@Designate(ocd = Configuration.class, factory = true)
+//@Designate(ocd = AllowListFragment.Configuration.class, factory = true)
 public class AllowListFragment {
 
     public static final String FACTORY_PID = 
"org.apache.sling.jcr.base.LoginAdminAllowList.fragment";
+
+
+    @ObjectClassDefinition(
+            name = "Apache Sling Login Admin Allow List Configuration 
Fragment",
+            description = "This list of Bundle Symbolic Names is added to the 
list of bundles which are allowed " +
+                    "to use Administrative Login. The full list is built in a 
modular way out of all such configuration fragments."
+    )
+    public @interface Configuration {
+
+        @AttributeDefinition(
+                name = "Name",
+                description = "Optional name to disambiguate configurations."
+        )
+        String allowlist_name() default "[unnamed]";
+
+        @AttributeDefinition(
+                name = "Allow listed BSNs",
+                description = "A list of bundle symbolic names allowed to use 
loginAdministrative()."
+        )
+        String[] allowlist_bundles();
+
+        @SuppressWarnings("unused")
+        String webconsole_configurationFactory_nameHint() default 
"{allowlist.name}: [{allowlist.bundles}]";
+    }
+
     private final String name;
 
     private final Set<String> bundles;
diff --git 
a/src/main/java/org/apache/sling/jcr/base/internal/BackwardsCompatibilityConfigurationPlugin.java
 
b/src/main/java/org/apache/sling/jcr/base/internal/BackwardsCompatibilityConfigurationPlugin.java
new file mode 100644
index 0000000..72de01e
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/jcr/base/internal/BackwardsCompatibilityConfigurationPlugin.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal;
+
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationPlugin;
+import org.osgi.service.component.annotations.Component;
+
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+/**
+ * This class is updating configuration and configuration property names to use
+ * more inclusive language.
+ * See https://issues.apache.org/jira/browse/SLING-11741
+ */
+@Component(
+        service = ConfigurationPlugin.class,
+        property = {
+                ConfigurationPlugin.CM_TARGET + "=" + 
BackwardsCompatibilityConfigurationPlugin.LOGIN_ADMIN_WHITELIST_PID,
+                ConfigurationPlugin.CM_TARGET + "=" + 
BackwardsCompatibilityConfigurationPlugin.WHITELIST_FRAGMENT_PID,
+        }
+)
+public class BackwardsCompatibilityConfigurationPlugin implements 
ConfigurationPlugin {
+
+    static final String LOGIN_ADMIN_WHITELIST_PID = 
"org.apache.sling.jcr.base.internal.LoginAdminWhitelist";
+    private static final Map<String, String> 
LOGIN_ADMIN_WHITELIST_PROPS_TO_REPLACE = new HashMap<>();
+    static {
+        LOGIN_ADMIN_WHITELIST_PROPS_TO_REPLACE.put("whitelist.bypass", 
"allowlist.bypass");
+        LOGIN_ADMIN_WHITELIST_PROPS_TO_REPLACE.put("whitelist.bundles.regexp", 
"allowlist.bundles.regexp");
+    }
+
+    static final String WHITELIST_FRAGMENT_PID = 
"org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment";
+    private static final Map<String, String> FRAGMENT_PROPS_TO_REPLACE = new 
HashMap<>();
+
+    static {
+        FRAGMENT_PROPS_TO_REPLACE.put("whitelist.name", "allowlist.name");
+        FRAGMENT_PROPS_TO_REPLACE.put("whitelist.bundles", 
"allowlist.bundles");
+    }
+
+    @Override
+    public void modifyConfiguration(ServiceReference<?> serviceReference, 
Dictionary<String, Object> dictionary) {
+        FRAGMENT_PROPS_TO_REPLACE.forEach(renameKey(dictionary));
+        LOGIN_ADMIN_WHITELIST_PROPS_TO_REPLACE.forEach(renameKey(dictionary));
+    }
+
+    private static BiConsumer<? super String, ? super String> 
renameKey(Dictionary<String, Object> dictionary) {
+        return (oldKey, newKey) -> {
+            final Object value = dictionary.remove(oldKey);
+            if (value != null && dictionary.get(newKey) == null) {
+                dictionary.put(newKey, value);
+            }
+        };
+    }
+}
diff --git 
a/src/main/java/org/apache/sling/jcr/base/internal/ConfigurationUpdater.java 
b/src/main/java/org/apache/sling/jcr/base/internal/ConfigurationUpdater.java
deleted file mode 100644
index 3b32ed7..0000000
--- a/src/main/java/org/apache/sling/jcr/base/internal/ConfigurationUpdater.java
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.jcr.base.internal;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-
-import org.osgi.framework.Constants;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.service.cm.Configuration;
-import org.osgi.service.cm.ConfigurationAdmin;
-import org.osgi.service.cm.ConfigurationEvent;
-import org.osgi.service.cm.ConfigurationListener;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * This class is updating configuration and configuration property names to use
- * more inclusive language.
- * See https://issues.apache.org/jira/browse/SLING-11741
- */
-@Component(service = {ConfigurationListener.class, ConfigurationUpdater.class})
-public class ConfigurationUpdater implements ConfigurationListener {
-
-    static final String LOGIN_ADMIN_WHITELIST_PID = 
"org.apache.sling.jcr.base.internal.LoginAdminWhitelist";
-    static final String LOGIN_ADMIN_ALLOWLIST_PID = LoginAdminAllowList.PID;
-    private static final Map<String, String> 
LOGIN_ADMIN_WHITELIST_PROPS_TO_REPLACE = new HashMap<>();
-    static {
-        LOGIN_ADMIN_WHITELIST_PROPS_TO_REPLACE.put("whitelist.bypass", 
"allowlist.bypass");
-        LOGIN_ADMIN_WHITELIST_PROPS_TO_REPLACE.put("whitelist.bundles.regexp", 
"allowlist.bundles.regexp");
-    }
-
-    private static final String WHITELIST_FRAGMENT_PID = 
"org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment";
-    private static final String ALLOWLIST_FRAGMENT_PID = 
AllowListFragment.FACTORY_PID;
-    private static final Map<String, String> FRAGMENT_PROPS_TO_REPLACE = new 
HashMap<>();
-
-    static {
-        FRAGMENT_PROPS_TO_REPLACE.put("whitelist.name", "allowlist.name");
-        FRAGMENT_PROPS_TO_REPLACE.put("whitelist.bundles", 
"allowlist.bundles");
-    }
-
-    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
-
-    private final List<Updater> configurationUpdaterList = new ArrayList<>();
-
-    private final ConfigurationAdmin configurationAdmin;
-
-    @Activate
-    public ConfigurationUpdater(@Reference ConfigurationAdmin 
configurationAdmin) {
-        this.configurationAdmin = configurationAdmin;
-        configurationUpdaterList.add(new 
PidConfigurationUpdater(LOGIN_ADMIN_WHITELIST_PID, LOGIN_ADMIN_ALLOWLIST_PID, 
LOGIN_ADMIN_WHITELIST_PROPS_TO_REPLACE));
-        configurationUpdaterList.add(new 
FactoryPidConfigurationUpdater(WHITELIST_FRAGMENT_PID, ALLOWLIST_FRAGMENT_PID, 
FRAGMENT_PROPS_TO_REPLACE));
-
-        configurationUpdaterList.forEach(configurationUpdater -> 
configurationUpdater.updateProps());
-    }
-
-    @Override
-    public void configurationEvent(final ConfigurationEvent event) {
-        if ( event.getType() == ConfigurationEvent.CM_UPDATED ) {
-            configurationUpdaterList.forEach(configurationUpdater -> {
-                configurationUpdater.updateProps(event);
-            });
-        }
-    }
-
-    /**
-     * Encode the value for the ldap filter: \, *, (, and ) should be escaped.
-     */
-    private static String encode(final String value) {
-        return value.replace("\\", "\\\\")
-                .replace("*", "\\*")
-                .replace("(", "\\(")
-                .replace(")", "\\)");
-    }
-    
-    protected abstract class Updater {
-
-        protected final String oldPid;
-        protected final String newPid;
-        protected final Map<String, String> propsToReplace;
-
-        public Updater(final String oldPid, final String newPid, final 
Map<String, String> propsToReplace) {
-            this.oldPid = oldPid;
-            this.newPid = newPid;
-            this.propsToReplace = propsToReplace;
-        }
-
-        protected abstract void updateProps(ConfigurationEvent event);
-
-        protected abstract void updateProps();
-
-        protected abstract Configuration createConfiguration(String oldPid) 
throws IOException;
-
-        /**
-         * Update a configuration
-         */
-        protected void updateProps(final Configuration sourceConfig, 
ConfigurationAdmin configurationAdmin) {
-            final Dictionary<String, Object> sourceProps = 
sourceConfig.getProperties();
-            final Dictionary<String, Object> targetProps = new Hashtable<>();
-            for(final String name : Collections.list(sourceProps.keys())) {
-                targetProps.put(this.propsToReplace.getOrDefault(name, name), 
sourceProps.get(name));
-            }
-            try {
-                final Configuration cfg = 
this.createConfiguration(sourceConfig.getPid());
-                if (cfg == null) {
-                    logger.warn("Not updating configuration with PID {} to new 
configuration with PID {} as the new one already exists."+
-                        "Please update your configurations. See 
https://sling.apache.org/documentation/the-sling-engine/service-authentication.html
 for more information.", 
-                        sourceConfig.getPid(), this.newPid);
-                } else {
-                    cfg.update(targetProps);
-                    sourceConfig.delete();
-                    logger.warn("Updated configuration with PID {} to new 
configuration with PID {}. "+
-                        "Please update your configurations. See 
https://sling.apache.org/documentation/the-sling-engine/service-authentication.html
 for more information.", 
-                        sourceConfig.getPid(), cfg.getPid());    
-                }
-            } catch (final IOException e) {
-                logger.warn("Failed to update configuration with PID {}", 
sourceConfig.getPid(), e);
-            }
-        }
-    }
-
-    private class PidConfigurationUpdater extends Updater {
-
-        public PidConfigurationUpdater(final String oldPid, final String 
newPid, final Map<String, String> propsToReplace) {
-            super(oldPid, newPid, propsToReplace);
-        }
-
-        @Override
-        protected void updateProps(final ConfigurationEvent event) {
-            if (this.oldPid.equals(event.getPid())) {
-                this.updateProps();
-            }
-        }
-
-        @Override
-        protected Configuration createConfiguration(final String oldPid) 
throws IOException {
-            final String filter = String.format("(%s=%s)", 
Constants.SERVICE_PID, encode(this.newPid));
-            try {
-                final Configuration[] configs = 
configurationAdmin.listConfigurations(filter);
-                if (configs != null && configs.length > 0) {
-                    return null;
-                }
-            } catch (final IOException | InvalidSyntaxException e) {
-                // ignore
-            }
-            return configurationAdmin.getConfiguration(newPid, null);
-        }
-
-        @Override
-        protected void updateProps() {
-            final String filter = String.format("(%s=%s)", 
Constants.SERVICE_PID, encode(this.oldPid));
-            try {
-                final Configuration[] configs = 
configurationAdmin.listConfigurations(filter);
-                if (configs != null && configs.length > 0) {
-                    this.updateProps(configs[0], configurationAdmin);
-                }
-            } catch (final IOException | InvalidSyntaxException e) {
-                logger.error("Failed to retrieve configuration for PID: {}. 
Configuration is not updated to PID {}.",
-                    oldPid, newPid, e);
-            }
-        }
-    }
-
-    private class FactoryPidConfigurationUpdater extends Updater {
-
-        public FactoryPidConfigurationUpdater(final String oldPid, final 
String newPid, final Map<String, String> propsToReplace) {
-            super(oldPid, newPid, propsToReplace);
-        }
-
-        @Override
-        protected void updateProps(final ConfigurationEvent event) {
-            if (this.oldPid.equals(event.getFactoryPid())) {
-                this.updateProps();
-            }
-        }
-
-        @Override
-        protected void updateProps() {
-            final String filter = String.format("(%s=%s)", 
ConfigurationAdmin.SERVICE_FACTORYPID, encode(this.oldPid));
-            try {
-                final Configuration[] configs = 
configurationAdmin.listConfigurations(filter);
-                if ( configs != null) {
-                    for (final Configuration sourceConfig : configs) {
-                        this.updateProps(sourceConfig, configurationAdmin);
-                    }    
-                }
-            } catch (final IOException | InvalidSyntaxException e) {
-                logger.error("Failed to list configurations for filter: {}.", 
filter, e);
-            }
-        }
-
-        @Override
-        protected Configuration createConfiguration(final String oldFullPid) 
throws IOException {
-            final String prefix = this.oldPid.concat("~");
-            if (oldFullPid.startsWith(prefix)) {
-                final String name = oldFullPid.substring(prefix.length());
-                final String filter = String.format("(%s=%s~%s)", 
Constants.SERVICE_PID, encode(this.newPid), encode(name));
-                try {
-                    final Configuration[] configs = 
configurationAdmin.listConfigurations(filter);
-                    if (configs != null && configs.length > 0) {
-                        return null;
-                    }
-                } catch (final IOException | InvalidSyntaxException e) {
-                    // ignore
-                }
-                return configurationAdmin.getFactoryConfiguration(newPid, 
name, null);
-            }
-            return configurationAdmin.createFactoryConfiguration(newPid, null);
-        }
-    }
-}
diff --git 
a/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminAllowList.java 
b/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminAllowList.java
index f3aaa7b..b1e86c5 100644
--- a/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminAllowList.java
+++ b/src/main/java/org/apache/sling/jcr/base/internal/LoginAdminAllowList.java
@@ -26,6 +26,7 @@ import java.util.regex.Pattern;
 
 import org.apache.sling.jcr.api.SlingRepository;
 import org.osgi.framework.Bundle;
+import org.osgi.service.cm.ConfigurationPlugin;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Modified;
@@ -47,20 +48,17 @@ import static 
org.apache.sling.commons.osgi.PropertiesUtil.toStringArray;
  * use the loginAdministrative method.
  */
 @Component(service = LoginAdminAllowList.class, 
-    configurationPid = LoginAdminAllowList.PID,
-    reference = {
-        // ConfigurationUpdater is a required dependency to make sure that 
configurations are
-        // updated before this component is activated
-        @Reference(
-            name = "ConfigurationUpdater",
-            service = ConfigurationUpdater.class,
+    configurationPid = { LoginAdminAllowList.PID, 
BackwardsCompatibilityConfigurationPlugin.LOGIN_ADMIN_WHITELIST_PID },
+    // BackwardsCompatibilityConfigurationPlugin is a required dependency to 
make
+    // sure that configurations are adjusted when this component is activated
+    reference = @Reference(
+            name = "backwardsCompatibilityConfigurationPlugin",
+            service = ConfigurationPlugin.class,
+            target = 
"(component.name=org.apache.sling.jcr.base.internal.BackwardsCompatibilityConfigurationPlugin)",
             cardinality = ReferenceCardinality.MANDATORY
-        )
-    }
-)
-@Designate(
-        ocd = LoginAdminAllowListConfiguration.class
+    )
 )
+// @Designate(ocd = LoginAdminAllowListConfiguration.class)
 public class LoginAdminAllowList {
 
     public static final String PID = 
"org.apache.sling.jcr.base.LoginAdminAllowList";
diff --git 
a/src/test/java/org/apache/sling/jcr/base/internal/BackwardsCompatibilityConfigurationPluginTest.java
 
b/src/test/java/org/apache/sling/jcr/base/internal/BackwardsCompatibilityConfigurationPluginTest.java
new file mode 100644
index 0000000..ea8914f
--- /dev/null
+++ 
b/src/test/java/org/apache/sling/jcr/base/internal/BackwardsCompatibilityConfigurationPluginTest.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.utils.collections.DictionaryAsMap;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class BackwardsCompatibilityConfigurationPluginTest {
+
+    private final BackwardsCompatibilityConfigurationPlugin plugin =
+            new BackwardsCompatibilityConfigurationPlugin();
+
+    @Test
+    public void 
whenFragmentOldPropertiesAreProvided_thenNewPropertiesAreConfigured() {
+
+        final Dictionary<String, Object> properties = new Hashtable<>();
+        properties.put("whitelist.bypass", "whitelistBypassValue");
+        properties.put("whitelist.bundles.regexp", "whitelistBundlesValue");
+        properties.put("prop", "value");
+
+        plugin.modifyConfiguration(mock(ServiceReference.class), properties);
+
+        assertThat(new DictionaryAsMap<>(properties), Matchers.allOf(
+                Matchers.hasEntry("allowlist.bypass", "whitelistBypassValue"),
+                Matchers.hasEntry("allowlist.bundles.regexp", 
"whitelistBundlesValue"),
+                Matchers.hasEntry("prop", "value"),
+                Matchers.not(Matchers.hasKey("whitelist.bypass")),
+                Matchers.not(Matchers.hasKey("whitelist.bundles.regexp"))
+        ));
+    }
+
+    @Test
+    public void testUpdateProps_whenNewConfigExists() {
+        final Dictionary<String, Object> properties = new Hashtable<>();
+        properties.put("allowlist.bypass", "allowListBypassValue");
+        properties.put("allowlist.bundles.regexp", "allowListBundlesValue");
+        properties.put("prop", "value");
+
+        plugin.modifyConfiguration(mock(ServiceReference.class), properties);
+
+        assertThat(new DictionaryAsMap<>(properties), Matchers.allOf(
+                Matchers.hasEntry("allowlist.bypass", "allowListBypassValue"),
+                Matchers.hasEntry("allowlist.bundles.regexp", 
"allowListBundlesValue"),
+                Matchers.hasEntry("prop", "value"),
+                Matchers.not(Matchers.hasKey("whitelist.bypass")),
+                Matchers.not(Matchers.hasKey("whitelist.bundles.regexp"))
+        ));
+    }
+
+
+    @Test
+    public void preferNewConfigWhenOldAndNewConfigExists() {
+        final Dictionary<String, Object> properties = new Hashtable<>();
+        properties.put("allowlist.bypass", "allowListBypassValue");
+        properties.put("allowlist.bundles.regexp", "allowListBundlesValue");
+        properties.put("whitelist.bypass", "whitelistBypassValue");
+        properties.put("whitelist.bundles.regexp", "whitelistBundlesValue");
+        properties.put("prop", "value");
+
+        plugin.modifyConfiguration(mock(ServiceReference.class), properties);
+
+        assertThat(new DictionaryAsMap<>(properties), Matchers.allOf(
+                Matchers.hasEntry("allowlist.bypass", "allowListBypassValue"),
+                Matchers.hasEntry("allowlist.bundles.regexp", 
"allowListBundlesValue"),
+                Matchers.hasEntry("prop", "value"),
+                Matchers.not(Matchers.hasKey("whitelist.bypass")),
+                Matchers.not(Matchers.hasKey("whitelist.bundles.regexp"))
+        ));
+    }
+
+    @Test
+    public void mergePartialConfigs() {
+        final Dictionary<String, Object> properties = new Hashtable<>();
+        properties.put("allowlist.bypass", "allowListBypassValue");
+        properties.put("whitelist.bypass", "whitelistBypassValue");
+        properties.put("whitelist.bundles.regexp", "whitelistBundlesValue");
+        properties.put("prop", "value");
+
+        plugin.modifyConfiguration(mock(ServiceReference.class), properties);
+
+        assertThat(new DictionaryAsMap<>(properties), Matchers.allOf(
+                Matchers.hasEntry("allowlist.bypass", "allowListBypassValue"),
+                Matchers.hasEntry("allowlist.bundles.regexp", 
"whitelistBundlesValue"),
+                Matchers.hasEntry("prop", "value"),
+                Matchers.not(Matchers.hasKey("whitelist.bypass")),
+                Matchers.not(Matchers.hasKey("whitelist.bundles.regexp"))
+        ));
+    }
+
+
+    @Test
+    public void 
testUpdatePropsForNamedFactoryPid_whenFragmentOldPropertiesAreProvided_thenNewPropertiesAreConfigured()
 throws InvalidSyntaxException, IOException {
+        final ConfigurationAdmin mockConfigurationAdmin = 
mock(ConfigurationAdmin.class);
+        final Configuration mockSourceConfiguration = 
mock(Configuration.class);
+        final Configuration mockTargetConfiguration = 
mock(Configuration.class);
+        final Dictionary<String, Object> sourceProperties = new Hashtable<>();
+        sourceProperties.put("whitelist.name", "whitelistNameValue");
+        sourceProperties.put("whitelist.bundles", "whitelistBundleValue");
+        sourceProperties.put("prop", "value");
+        
when(mockSourceConfiguration.getProperties()).thenReturn(sourceProperties);
+        
when(mockSourceConfiguration.getFactoryPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment");
+        
when(mockSourceConfiguration.getPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment~foo");
+        
when(mockConfigurationAdmin.listConfigurations("(service.factoryPid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment)")).thenReturn(new
 Configuration[] {mockSourceConfiguration});
+        
when(mockConfigurationAdmin.getFactoryConfiguration(AllowListFragment.FACTORY_PID,
 "foo", null)).thenReturn(mockTargetConfiguration);
+
+        final Dictionary<String, Object> expectedProperties = new 
Hashtable<>();
+        expectedProperties.put("allowlist.name", "whitelistNameValue");
+        expectedProperties.put("allowlist.bundles", "whitelistBundleValue");
+        expectedProperties.put("prop", "value");
+
+   }
+
+   @Test
+   public void 
testUpdatePropsForOldFactoryPid_whenFragmentOldPropertiesAreProvided_thenNewPropertiesAreConfigured()
 throws InvalidSyntaxException, IOException {
+       final ConfigurationAdmin mockConfigurationAdmin = 
mock(ConfigurationAdmin.class);
+       final Configuration mockSourceConfiguration = mock(Configuration.class);
+       final Configuration mockTargetConfiguration = mock(Configuration.class);
+       final Dictionary<String, Object> sourceProperties = new Hashtable<>();
+       sourceProperties.put("whitelist.name", "whitelistNameValue");
+       sourceProperties.put("whitelist.bundles", "whitelistBundleValue");
+       sourceProperties.put("prop", "value");
+       
when(mockSourceConfiguration.getProperties()).thenReturn(sourceProperties);
+       
when(mockSourceConfiguration.getFactoryPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment");
+       
when(mockSourceConfiguration.getPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment-randomid");
+       
when(mockConfigurationAdmin.listConfigurations("(service.factoryPid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment)")).thenReturn(new
 Configuration[] {mockSourceConfiguration});
+       
when(mockConfigurationAdmin.createFactoryConfiguration(AllowListFragment.FACTORY_PID,
 null)).thenReturn(mockTargetConfiguration);
+
+       final Dictionary<String, Object> expectedProperties = new Hashtable<>();
+       expectedProperties.put("allowlist.name", "whitelistNameValue");
+       expectedProperties.put("allowlist.bundles", "whitelistBundleValue");
+       expectedProperties.put("prop", "value");
+    }
+}
\ No newline at end of file
diff --git 
a/src/test/java/org/apache/sling/jcr/base/internal/ConfigurationUpdaterTest.java
 
b/src/test/java/org/apache/sling/jcr/base/internal/ConfigurationUpdaterTest.java
deleted file mode 100644
index 047501a..0000000
--- 
a/src/test/java/org/apache/sling/jcr/base/internal/ConfigurationUpdaterTest.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.jcr.base.internal;
-
-import java.io.IOException;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.runners.MockitoJUnitRunner;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.service.cm.Configuration;
-import org.osgi.service.cm.ConfigurationAdmin;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-@RunWith(MockitoJUnitRunner.class)
-public class ConfigurationUpdaterTest {
-
-    @Test
-    public void 
testUpdateProps_whenFragmentOldPropertiesAreProvided_thenNewPropertiesAreConfigured()
 throws IOException, InvalidSyntaxException {
-        final ConfigurationAdmin mockConfigurationAdmin = 
mock(ConfigurationAdmin.class);
-        final Configuration mockSourceConfiguration = 
mock(Configuration.class);
-        final Configuration mockTargetConfiguration = 
mock(Configuration.class);
-        final Dictionary<String, Object> sourceProperties = new Hashtable<>();
-        sourceProperties.put("whitelist.bypass", "whitelistNameValue");
-        sourceProperties.put("whitelist.bundles.regexp", 
"whitelistBundleValue");
-        sourceProperties.put("prop", "value");
-        
when(mockSourceConfiguration.getProperties()).thenReturn(sourceProperties);
-
-        
when(mockConfigurationAdmin.listConfigurations("(service.pid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist)")).thenReturn(new
 Configuration[] {mockSourceConfiguration});
-        when(mockConfigurationAdmin.getConfiguration(LoginAdminAllowList.PID, 
null)).thenReturn(mockTargetConfiguration);
-
-        final Dictionary<String, Object> expectedProperties = new 
Hashtable<>();
-        expectedProperties.put("allowlist.bypass", "whitelistNameValue");
-        expectedProperties.put("allowlist.bundles.regexp", 
"whitelistBundleValue");
-        expectedProperties.put("prop", "value");
-
-        new ConfigurationUpdater(mockConfigurationAdmin); 
-
-        
verify(mockConfigurationAdmin).listConfigurations("(service.pid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist)");
-        
verify(mockConfigurationAdmin).getConfiguration(LoginAdminAllowList.PID, null);
-        final ArgumentCaptor<Dictionary> targetPropertiesCaptor = 
ArgumentCaptor.forClass(Dictionary.class);
-        
verify(mockTargetConfiguration).update(targetPropertiesCaptor.capture());
-        assertEquals(expectedProperties, targetPropertiesCaptor.getValue());
-        verify(mockSourceConfiguration).delete();
-    }
-
-    @Test
-    public void testUpdateProps_whenNewConfigExists() throws IOException, 
InvalidSyntaxException {
-        final ConfigurationAdmin mockConfigurationAdmin = 
mock(ConfigurationAdmin.class);
-        final Configuration mockSourceConfiguration = 
mock(Configuration.class);
-        final Configuration mockTargetConfiguration = 
mock(Configuration.class);
-        final Dictionary<String, Object> sourceProperties = new Hashtable<>();
-        sourceProperties.put("whitelist.bypass", "whitelistNameValue");
-        sourceProperties.put("whitelist.bundles.regexp", 
"whitelistBundleValue");
-        sourceProperties.put("prop", "value");
-        
when(mockSourceConfiguration.getProperties()).thenReturn(sourceProperties);
-
-        
when(mockConfigurationAdmin.listConfigurations("(service.pid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist)")).thenReturn(new
 Configuration[] {mockSourceConfiguration});
-        when(mockConfigurationAdmin.listConfigurations("(service.pid=" + 
LoginAdminAllowList.PID + ")")).thenReturn(new Configuration[] 
{mockTargetConfiguration});
-
-        new ConfigurationUpdater(mockConfigurationAdmin); 
-
-        
verify(mockConfigurationAdmin).listConfigurations("(service.pid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist)");
-        verify(mockConfigurationAdmin).listConfigurations("(service.pid=" + 
LoginAdminAllowList.PID + ")");
-        
verify(mockConfigurationAdmin).listConfigurations("(service.factoryPid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment)");
-        verifyNoMoreInteractions(mockConfigurationAdmin);
-    }
-
-    @Test
-    public void 
testUpdatePropsForNamedFactoryPid_whenFragmentOldPropertiesAreProvided_thenNewPropertiesAreConfigured()
 throws InvalidSyntaxException, IOException {
-        final ConfigurationAdmin mockConfigurationAdmin = 
mock(ConfigurationAdmin.class);
-        final Configuration mockSourceConfiguration = 
mock(Configuration.class);
-        final Configuration mockTargetConfiguration = 
mock(Configuration.class);
-        final Dictionary<String, Object> sourceProperties = new Hashtable<>();
-        sourceProperties.put("whitelist.name", "whitelistNameValue");
-        sourceProperties.put("whitelist.bundles", "whitelistBundleValue");
-        sourceProperties.put("prop", "value");
-        
when(mockSourceConfiguration.getProperties()).thenReturn(sourceProperties);
-        
when(mockSourceConfiguration.getFactoryPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment");
-        
when(mockSourceConfiguration.getPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment~foo");
-        
when(mockConfigurationAdmin.listConfigurations("(service.factoryPid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment)")).thenReturn(new
 Configuration[] {mockSourceConfiguration});
-        
when(mockConfigurationAdmin.getFactoryConfiguration(AllowListFragment.FACTORY_PID,
 "foo", null)).thenReturn(mockTargetConfiguration);
-
-        final Dictionary<String, Object> expectedProperties = new 
Hashtable<>();
-        expectedProperties.put("allowlist.name", "whitelistNameValue");
-        expectedProperties.put("allowlist.bundles", "whitelistBundleValue");
-        expectedProperties.put("prop", "value");
-
-        new ConfigurationUpdater(mockConfigurationAdmin); 
-
-        
verify(mockConfigurationAdmin).listConfigurations("(service.factoryPid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment)");
-        
verify(mockConfigurationAdmin).getFactoryConfiguration(AllowListFragment.FACTORY_PID,
 "foo", null);
-        final ArgumentCaptor<Dictionary> targetPropertiesCaptor = 
ArgumentCaptor.forClass(Dictionary.class);
-        
verify(mockTargetConfiguration).update(targetPropertiesCaptor.capture());
-        assertEquals(expectedProperties, targetPropertiesCaptor.getValue());
-        verify(mockSourceConfiguration).delete();
-   }
-
-   @Test
-   public void testUpdateNamedFactoryConfig_whenNewConfigExists() throws 
IOException, InvalidSyntaxException {
-       final ConfigurationAdmin mockConfigurationAdmin = 
mock(ConfigurationAdmin.class);
-       final Configuration mockSourceConfiguration = mock(Configuration.class);
-       final Configuration mockTargetConfiguration = mock(Configuration.class);
-       final Dictionary<String, Object> sourceProperties = new Hashtable<>();
-       sourceProperties.put("whitelist.bypass", "whitelistNameValue");
-       sourceProperties.put("whitelist.bundles.regexp", 
"whitelistBundleValue");
-       sourceProperties.put("prop", "value");
-       
when(mockSourceConfiguration.getProperties()).thenReturn(sourceProperties);
-       
when(mockSourceConfiguration.getFactoryPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment");
-       
when(mockSourceConfiguration.getPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment~foo");
-
-       
when(mockConfigurationAdmin.listConfigurations("(service.factoryPid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment)")).thenReturn(new
 Configuration[] {mockSourceConfiguration});
-       when(mockConfigurationAdmin.listConfigurations("(service.pid=" + 
AllowListFragment.FACTORY_PID + "~foo)")).thenReturn(new Configuration[] 
{mockTargetConfiguration});
-
-       new ConfigurationUpdater(mockConfigurationAdmin); 
-
-       
verify(mockConfigurationAdmin).listConfigurations("(service.pid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist)");
-       
verify(mockConfigurationAdmin).listConfigurations("(service.factoryPid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment)");
-       verify(mockConfigurationAdmin).listConfigurations("(service.pid=" + 
AllowListFragment.FACTORY_PID + "~foo)");
-       verifyNoMoreInteractions(mockConfigurationAdmin);
-   }
-
-   @Test
-   public void 
testUpdatePropsForOldFactoryPid_whenFragmentOldPropertiesAreProvided_thenNewPropertiesAreConfigured()
 throws InvalidSyntaxException, IOException {
-       final ConfigurationAdmin mockConfigurationAdmin = 
mock(ConfigurationAdmin.class);
-       final Configuration mockSourceConfiguration = mock(Configuration.class);
-       final Configuration mockTargetConfiguration = mock(Configuration.class);
-       final Dictionary<String, Object> sourceProperties = new Hashtable<>();
-       sourceProperties.put("whitelist.name", "whitelistNameValue");
-       sourceProperties.put("whitelist.bundles", "whitelistBundleValue");
-       sourceProperties.put("prop", "value");
-       
when(mockSourceConfiguration.getProperties()).thenReturn(sourceProperties);
-       
when(mockSourceConfiguration.getFactoryPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment");
-       
when(mockSourceConfiguration.getPid()).thenReturn("org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment-randomid");
-       
when(mockConfigurationAdmin.listConfigurations("(service.factoryPid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment)")).thenReturn(new
 Configuration[] {mockSourceConfiguration});
-       
when(mockConfigurationAdmin.createFactoryConfiguration(AllowListFragment.FACTORY_PID,
 null)).thenReturn(mockTargetConfiguration);
-
-       final Dictionary<String, Object> expectedProperties = new Hashtable<>();
-       expectedProperties.put("allowlist.name", "whitelistNameValue");
-       expectedProperties.put("allowlist.bundles", "whitelistBundleValue");
-       expectedProperties.put("prop", "value");
-
-       new ConfigurationUpdater(mockConfigurationAdmin); 
-
-       
verify(mockConfigurationAdmin).listConfigurations("(service.factoryPid=org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment)");
-       
verify(mockConfigurationAdmin).createFactoryConfiguration(AllowListFragment.FACTORY_PID,
 null);
-       final ArgumentCaptor<Dictionary> targetPropertiesCaptor = 
ArgumentCaptor.forClass(Dictionary.class);
-       
verify(mockTargetConfiguration).update(targetPropertiesCaptor.capture());
-       assertEquals(expectedProperties, targetPropertiesCaptor.getValue());
-       verify(mockSourceConfiguration).delete();
-    }
-}
\ No newline at end of file


Reply via email to