This is an automated email from the ASF dual-hosted git repository. kwin pushed a commit to branch feature/SLING-11864-expose-merged-configs in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-factory-configuration.git
commit b235d6577583f9e159b21e172622a43719c913e1 Author: Konrad Windszus <[email protected]> AuthorDate: Tue May 9 19:22:25 2023 +0200 SLING-11864 Add new exported interface for filtering default properties --- pom.xml | 11 +++ .../configuration/ConfigurationMerger.java | 50 ++++++++++++ .../configuration/impl/ConfigTaskCreator.java | 58 ++------------ .../impl/ConfigurationMergerImpl.java | 91 ++++++++++++++++++++++ .../configuration/impl/ServicesListener.java | 18 ++--- .../impl/WebconsoleConfigurationHandler.java | 10 +-- .../factories/configuration/package-info.java | 2 +- 7 files changed, 172 insertions(+), 68 deletions(-) diff --git a/pom.xml b/pom.xml index 0ebfccd..7cc44df 100644 --- a/pom.xml +++ b/pom.xml @@ -118,6 +118,17 @@ <version>1.0.0</version> <scope>provided</scope> </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.service.component.annotations</artifactId> + <scope>provided</scope> + </dependency> + <!-- JetBrains annotations for null-analysis (SLING-7798), https://github.com/JetBrains/java-annotations --> + <dependency> + <groupId>org.jetbrains</groupId> + <artifactId>annotations</artifactId> + <scope>provided</scope> + </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> diff --git a/src/main/java/org/apache/sling/installer/factories/configuration/ConfigurationMerger.java b/src/main/java/org/apache/sling/installer/factories/configuration/ConfigurationMerger.java new file mode 100644 index 0000000..28b1b6e --- /dev/null +++ b/src/main/java/org/apache/sling/installer/factories/configuration/ConfigurationMerger.java @@ -0,0 +1,50 @@ +/* + * 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.installer.factories.configuration; + +import java.util.Dictionary; + +import org.jetbrains.annotations.NotNull; +import org.osgi.annotation.versioning.ProviderType; + +/** + * Exposes helper methods to identify those configuration properties which are set through installer resources having a configured merge scheme. + * Those configuration properties are referred to as default properties. + * Note though that both OSGi metatype and OSGi declarative services define default properties as well which are not meant here. + * @see <a href="https://sling.apache.org/documentation/bundles/configuration-installer-factory.html#merging-of-configurations">Merging of Configurations</a> + */ +@ProviderType +public interface ConfigurationMerger { + + /** + * Modifies the given dictionary so that all properties which have values equal to one + * of the same named property values provided by the inherited configurations are removed. + * @param pid the PID of the configuration + * @param dict the configuration properties to modify + */ + void removeDefaultProperties(@NotNull final String pid, @NotNull final Dictionary<String, Object> dict); + + /** + * Returns all properties for the given PID which are set through any of the resource with the configured merge schemes. + * @param pid the PID of the configuration + * @return the properties set through specific installer resources in a mutable dictionary + */ + Dictionary<String, Object> getDefaultProperties(@NotNull final String pid); + +} diff --git a/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigTaskCreator.java b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigTaskCreator.java index e4bd388..147101b 100644 --- a/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigTaskCreator.java +++ b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigTaskCreator.java @@ -18,10 +18,8 @@ */ package org.apache.sling.installer.factories.configuration.impl; -import java.util.ArrayList; import java.util.Arrays; import java.util.Dictionary; -import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; import java.util.List; @@ -29,9 +27,6 @@ import java.util.Map; import org.apache.sling.installer.api.InstallableResource; import org.apache.sling.installer.api.ResourceChangeListener; -import org.apache.sling.installer.api.info.InfoProvider; -import org.apache.sling.installer.api.info.Resource; -import org.apache.sling.installer.api.info.ResourceGroup; import org.apache.sling.installer.api.tasks.ChangeStateTask; import org.apache.sling.installer.api.tasks.InstallTask; import org.apache.sling.installer.api.tasks.InstallTaskFactory; @@ -42,6 +37,7 @@ import org.apache.sling.installer.api.tasks.TaskResource; import org.apache.sling.installer.api.tasks.TaskResourceGroup; import org.apache.sling.installer.api.tasks.TransformationResult; import org.apache.sling.installer.factories.configuration.ConfigurationConstants; +import org.apache.sling.installer.factories.configuration.ConfigurationMerger; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.ServiceRegistration; @@ -68,14 +64,14 @@ public class ConfigTaskCreator private final ResourceChangeListener changeListener; /** Info Provider */ - private final InfoProvider infoProvider; + private final ConfigurationMerger configMerger; public ConfigTaskCreator(final ResourceChangeListener listener, final ConfigurationAdmin configAdmin, - final InfoProvider infoProvider) { + final ConfigurationMerger configMerger) { this.changeListener = listener; this.configAdmin = configAdmin; - this.infoProvider = infoProvider; + this.configMerger = configMerger; } public ServiceRegistration<?> register(final BundleContext bundleContext) { @@ -164,7 +160,7 @@ public class ConfigTaskCreator attrs.put(ConfigurationAdmin.SERVICE_FACTORYPID, event.getFactoryPid()); } - removeDefaultProperties(this.infoProvider, event.getPid(), dict); + configMerger.removeDefaultProperties(event.getPid(), dict); this.changeListener.resourceAddedOrUpdated(InstallableResource.TYPE_CONFIG, event.getPid(), null, dict, attrs); } else { @@ -177,50 +173,6 @@ public class ConfigTaskCreator } } - public static Dictionary<String, Object> getDefaultProperties(final InfoProvider infoProvider, final String pid) { - if ( Activator.MERGE_SCHEMES != null ) { - final List<Dictionary<String, Object>> propertiesList = new ArrayList<>(); - final String entityId = InstallableResource.TYPE_CONFIG.concat(":").concat(pid); - boolean done = false; - for(final ResourceGroup group : infoProvider.getInstallationState().getInstalledResources()) { - for(final Resource rsrc : group.getResources()) { - if ( rsrc.getEntityId().equals(entityId) ) { - done = true; - if ( Activator.MERGE_SCHEMES.contains(rsrc.getScheme()) ) { - propertiesList.add(rsrc.getDictionary()); - } - } - } - if ( done ) { - break; - } - } - if ( !propertiesList.isEmpty() ) { - final Dictionary<String, Object> defaultProps = ConfigUtil.mergeReverseOrder(propertiesList); - return defaultProps; - } - } - return null; - } - - public static void removeDefaultProperties(final InfoProvider infoProvider, final String pid, final Dictionary<String, Object> dict) { - if ( Activator.MERGE_SCHEMES != null ) { - final Dictionary<String, Object> defaultProps = getDefaultProperties(infoProvider, pid); - if ( defaultProps != null ) { - final Enumeration<String> keyEnum = defaultProps.keys(); - while ( keyEnum.hasMoreElements() ) { - final String key = keyEnum.nextElement(); - final Object value = defaultProps.get(key); - - final Object newValue = dict.get(key); - if ( newValue != null && ConfigUtil.isSameValue(newValue, value)) { - dict.remove(key); - } - } - } - } - } - /** * @see org.apache.sling.installer.api.tasks.ResourceTransformer#transform(org.apache.sling.installer.api.tasks.RegisteredResource) */ diff --git a/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigurationMergerImpl.java b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigurationMergerImpl.java new file mode 100644 index 0000000..80f490e --- /dev/null +++ b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigurationMergerImpl.java @@ -0,0 +1,91 @@ +/* + * 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.installer.factories.configuration.impl; + +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.List; + +import org.apache.sling.installer.api.InstallableResource; +import org.apache.sling.installer.api.info.InfoProvider; +import org.apache.sling.installer.api.info.Resource; +import org.apache.sling.installer.api.info.ResourceGroup; +import org.apache.sling.installer.factories.configuration.ConfigurationMerger; +import org.jetbrains.annotations.NotNull; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +@Component +public class ConfigurationMergerImpl implements ConfigurationMerger { + + private final InfoProvider infoProvider; + + @Activate + public ConfigurationMergerImpl(@Reference InfoProvider infoProvider) { + this.infoProvider = infoProvider; + } + + @Override + public void removeDefaultProperties(@NotNull final String pid, @NotNull final Dictionary<String, Object> dict) { + if ( Activator.MERGE_SCHEMES != null ) { + final Dictionary<String, Object> defaultProps = getDefaultProperties(pid); + if ( defaultProps != null ) { + final Enumeration<String> keyEnum = defaultProps.keys(); + while ( keyEnum.hasMoreElements() ) { + final String key = keyEnum.nextElement(); + final Object value = defaultProps.get(key); + + final Object newValue = dict.get(key); + if ( newValue != null && ConfigUtil.isSameValue(newValue, value)) { + dict.remove(key); + } + } + } + } + } + + @Override + public Dictionary<String, Object> getDefaultProperties(@NotNull final String pid) { + if ( Activator.MERGE_SCHEMES != null ) { + final List<Dictionary<String, Object>> propertiesList = new ArrayList<>(); + final String entityId = InstallableResource.TYPE_CONFIG.concat(":").concat(pid); + boolean done = false; + for(final ResourceGroup group : infoProvider.getInstallationState().getInstalledResources()) { + for(final Resource rsrc : group.getResources()) { + if ( rsrc.getEntityId().equals(entityId) ) { + done = true; + if ( Activator.MERGE_SCHEMES.contains(rsrc.getScheme()) ) { + propertiesList.add(rsrc.getDictionary()); + } + } + } + if ( done ) { + break; + } + } + if ( !propertiesList.isEmpty() ) { + final Dictionary<String, Object> defaultProps = ConfigUtil.mergeReverseOrder(propertiesList); + return defaultProps; + } + } + return null; + } +} diff --git a/src/main/java/org/apache/sling/installer/factories/configuration/impl/ServicesListener.java b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ServicesListener.java index 139eb2f..216e41c 100644 --- a/src/main/java/org/apache/sling/installer/factories/configuration/impl/ServicesListener.java +++ b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ServicesListener.java @@ -21,7 +21,7 @@ package org.apache.sling.installer.factories.configuration.impl; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.sling.installer.api.ResourceChangeListener; -import org.apache.sling.installer.api.info.InfoProvider; +import org.apache.sling.installer.factories.configuration.ConfigurationMerger; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; @@ -53,7 +53,7 @@ public class ServicesListener { private final Listener configAdminListener; /** The listener for the installer info service. */ - private final Listener infoServiceListener; + private final Listener configMergerListener; /** Registration the service. */ private volatile ServiceRegistration<?> configTaskCreatorRegistration; @@ -69,8 +69,8 @@ public class ServicesListener { this.bundleContext = bundleContext; this.changeHandlerListener = new Listener(ResourceChangeListener.class.getName()); this.configAdminListener = new Listener(ConfigurationAdmin.class.getName()); - this.infoServiceListener = new Listener(InfoProvider.class.getName()); - this.infoServiceListener.start(); + this.configMergerListener = new Listener(ConfigurationMerger.class.getName()); + this.configMergerListener.start(); this.changeHandlerListener.start(); this.configAdminListener.start(); } @@ -79,13 +79,13 @@ public class ServicesListener { // check if all services are available final ResourceChangeListener listener = (ResourceChangeListener)this.changeHandlerListener.getService(); final ConfigurationAdmin configAdmin = (ConfigurationAdmin)this.configAdminListener.getService(); - final InfoProvider infoProvider = (InfoProvider)this.infoServiceListener.getService(); + final ConfigurationMerger configMerger = (ConfigurationMerger)this.configMergerListener.getService(); - if ( configAdmin != null && listener != null && infoProvider != null ) { + if ( configAdmin != null && listener != null && configMerger != null ) { if ( configTaskCreator == null ) { active.set(true); // start and register osgi installer service - this.configTaskCreator = new ConfigTaskCreator(listener, configAdmin, infoProvider); + this.configTaskCreator = new ConfigTaskCreator(listener, configAdmin, configMerger); final ConfigUpdateHandler handler = new ConfigUpdateHandler(configAdmin, this); configTaskCreatorRegistration = handler.register(this.bundleContext); if ( Activator.MERGE_SCHEMES != null ) { @@ -93,7 +93,7 @@ public class ServicesListener { @Override public Object getService(final Bundle bundle, final ServiceRegistration<Object> registration) { - return new WebconsoleConfigurationHandler(bundleContext, infoProvider); + return new WebconsoleConfigurationHandler(bundleContext, configMerger); } @Override @@ -138,7 +138,7 @@ public class ServicesListener { * Deactivate this listener. */ public void deactivate() { - this.infoServiceListener.deactivate(); + this.configMergerListener.deactivate(); this.changeHandlerListener.deactivate(); this.configAdminListener.deactivate(); this.stop(); diff --git a/src/main/java/org/apache/sling/installer/factories/configuration/impl/WebconsoleConfigurationHandler.java b/src/main/java/org/apache/sling/installer/factories/configuration/impl/WebconsoleConfigurationHandler.java index 3aa0161..d9ee81b 100644 --- a/src/main/java/org/apache/sling/installer/factories/configuration/impl/WebconsoleConfigurationHandler.java +++ b/src/main/java/org/apache/sling/installer/factories/configuration/impl/WebconsoleConfigurationHandler.java @@ -23,7 +23,7 @@ import java.util.Dictionary; import org.apache.felix.webconsole.spi.ConfigurationHandler; import org.apache.felix.webconsole.spi.ValidationException; -import org.apache.sling.installer.api.info.InfoProvider; +import org.apache.sling.installer.factories.configuration.ConfigurationMerger; import org.osgi.framework.BundleContext; import org.osgi.util.tracker.ServiceTracker; @@ -31,14 +31,14 @@ public class WebconsoleConfigurationHandler implements ConfigurationHandler { static final String META_TYPE_NAME = "org.osgi.service.metatype.MetaTypeService"; - private final InfoProvider infoProvider; + private final ConfigurationMerger configMerger; private final ServiceTracker<Object, Object> metatypeTracker; private final BundleContext bundleContext; - public WebconsoleConfigurationHandler(final BundleContext context, final InfoProvider infoProvider) { - this.infoProvider = infoProvider; + public WebconsoleConfigurationHandler(final BundleContext context, final ConfigurationMerger configMerger) { + this.configMerger = configMerger; this.bundleContext = context; this.metatypeTracker = new ServiceTracker<>(context, META_TYPE_NAME, null); this.metatypeTracker.open(); @@ -68,7 +68,7 @@ public class WebconsoleConfigurationHandler implements ConfigurationHandler { throws ValidationException, IOException { final Object mts = this.metatypeTracker.getService(); if ( mts != null ) { - final Dictionary<String, Object> defaultProps = ConfigTaskCreator.getDefaultProperties(infoProvider, pid); + final Dictionary<String, Object> defaultProps = configMerger.getDefaultProperties(pid); final MetatypeHandler mt = new MetatypeHandler(mts, this.bundleContext); mt.updateConfiguration(factoryPid, pid, props, defaultProps); } diff --git a/src/main/java/org/apache/sling/installer/factories/configuration/package-info.java b/src/main/java/org/apache/sling/installer/factories/configuration/package-info.java index eb71c4d..747378d 100644 --- a/src/main/java/org/apache/sling/installer/factories/configuration/package-info.java +++ b/src/main/java/org/apache/sling/installer/factories/configuration/package-info.java @@ -17,7 +17,7 @@ * under the License. */ [email protected]("1.1.2") [email protected]("1.2.0") package org.apache.sling.installer.factories.configuration;
