This is an automated email from the ASF dual-hosted git repository. kwin pushed a commit to branch feature/streamline-hiding-redundant-properties in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-factory-configuration.git
commit a704311dd9641e9268ab787900e5b42a3adbbe22 Author: Konrad Windszus <[email protected]> AuthorDate: Fri Nov 22 12:38:39 2024 +0100 SLING-12495 Clean up logic to hide redundant properties No longer consider metatype default values Either hide both merged/inherited and component properties from description or don't hide anything. Individual hiding may lead to false configurations due to the order of precedence. Update to Parent 62 --- pom.xml | 8 +- .../ConfigurationSerializerWebConsolePlugin.java | 234 +++++++++------------ 2 files changed, 110 insertions(+), 132 deletions(-) diff --git a/pom.xml b/pom.xml index 243282b..e5a3411 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ <parent> <groupId>org.apache.sling</groupId> <artifactId>sling-bundle-parent</artifactId> - <version>61</version> + <version>62</version> <relativePath /> </parent> @@ -78,6 +78,12 @@ <artifactId>org.osgi.service.component</artifactId> <scope>provided</scope> </dependency> + <!-- transitive dependency of DS 1.4--> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.dto</artifactId> + <scope>provided</scope> + </dependency> <dependency> <groupId>org.osgi</groupId> <artifactId>org.osgi.service.cm</artifactId> diff --git a/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigurationSerializerWebConsolePlugin.java b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigurationSerializerWebConsolePlugin.java index 7eb8ecc..e449ead 100644 --- a/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigurationSerializerWebConsolePlugin.java +++ b/src/main/java/org/apache/sling/installer/factories/configuration/impl/ConfigurationSerializerWebConsolePlugin.java @@ -27,27 +27,22 @@ import java.io.IOException; import java.io.PrintWriter; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.Dictionary; import java.util.Enumeration; +import java.util.HashMap; import java.util.Hashtable; -import java.util.stream.Collectors; +import java.util.Map; import org.apache.sling.installer.api.info.InfoProvider; import org.apache.sling.installer.api.serializer.ConfigurationSerializerFactory; import org.apache.sling.installer.api.serializer.ConfigurationSerializerFactory.Format; -import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.service.cm.Configuration; import org.osgi.service.cm.ConfigurationAdmin; -import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.runtime.ServiceComponentRuntime; import org.osgi.service.component.runtime.dto.ComponentDescriptionDTO; -import org.osgi.service.metatype.MetaTypeService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -67,9 +62,7 @@ public class ConfigurationSerializerWebConsolePlugin extends GenericServlet { private static final String RES_LOC = LABEL + "/res/ui/"; private static final String PARAMETER_PID = "pid"; private static final String PARAMETER_FORMAT = "format"; - private static final String PARAMETER_REMOVE_COMPONENT_DEFAULT_PROPERTIES = "removeComponentDefaultProps"; - private static final String PARAMETER_REMOVE_METATYPE_DEFAULT_PROPERTIES = "removeMetatypeDefaultProps"; - private static final String PARAMETER_REMOVE_MERGED_DEFAULT_PROPERTIES = "removeMergedDefaultProps"; + private static final String PARAMETER_HIDE_REDUNDANT_PROPERTIES = "hideRedundantProperties"; /** The logger */ private final Logger LOGGER = LoggerFactory.getLogger(ConfigurationSerializerWebConsolePlugin.class); @@ -77,52 +70,24 @@ public class ConfigurationSerializerWebConsolePlugin extends GenericServlet { @Reference ConfigurationAdmin configurationAdmin; - @Reference - MetaTypeService metatypeService; - @Reference private InfoProvider infoProvider; @Reference private ServiceComponentRuntime scr; - private final BundleContext bundleContext; - - @Activate - public ConfigurationSerializerWebConsolePlugin(BundleContext bundleContext) { - this.bundleContext = bundleContext; - } - @Override public void service(final ServletRequest request, final ServletResponse response) throws IOException { - final String pid = request.getParameter(PARAMETER_PID); - final String format = request.getParameter(PARAMETER_FORMAT); - // initial loading - final boolean removeMetatypeDefaultProperties; - final boolean removeComponentDefaultProperties; - final boolean removeMergedDefaultProperties; - // initial loading? - if (format == null) { - removeMetatypeDefaultProperties = true; - removeComponentDefaultProperties = true; - removeMergedDefaultProperties = true; - } else { - removeMetatypeDefaultProperties = - Boolean.parseBoolean(request.getParameter(PARAMETER_REMOVE_METATYPE_DEFAULT_PROPERTIES)); - removeComponentDefaultProperties = - Boolean.parseBoolean(request.getParameter(PARAMETER_REMOVE_COMPONENT_DEFAULT_PROPERTIES)); - removeMergedDefaultProperties = - Boolean.parseBoolean(request.getParameter(PARAMETER_REMOVE_MERGED_DEFAULT_PROPERTIES)); - } - Collection<ComponentDescriptionDTO> allComponentDescriptions; - if (removeComponentDefaultProperties) { - allComponentDescriptions = scr.getComponentDescriptionDTOs(); + final Configuration configuration; + if (pid != null && !pid.trim().isEmpty()) { + // normalize PID to detect factory configurations with "-" separator + + configuration = configurationAdmin.getConfiguration(pid, null); } else { - allComponentDescriptions = Collections.emptyList(); + configuration = null; } - - MetatypeHandler metatypeHandler = new MetatypeHandler(metatypeService, bundleContext); + final String format = request.getParameter(PARAMETER_FORMAT); ConfigurationSerializerFactory.Format serializationFormat = Format.JSON; if (format != null && !format.trim().isEmpty()) { try { @@ -131,7 +96,28 @@ public class ConfigurationSerializerWebConsolePlugin extends GenericServlet { LOGGER.warn("Illegal parameter 'format' given, falling back to default '{}'", serializationFormat, e); } } - final PrintWriter pw = response.getWriter(); + final boolean hideRedundantProperties; + if (format == null) { + hideRedundantProperties = true; + } else { + hideRedundantProperties = Boolean.parseBoolean(request.getParameter(PARAMETER_HIDE_REDUNDANT_PROPERTIES)); + } + dumpConfiguration(configuration, serializationFormat, hideRedundantProperties, response.getWriter()); + } + + private void dumpConfiguration( + Configuration configuration, + ConfigurationSerializerFactory.Format serializationFormat, + boolean hideRedundantProperties, + PrintWriter pw) { + // map with key = configuration pid and value = ComponentDescriptionDTO + Map<String, ComponentDescriptionDTO> allComponentDescriptions = new HashMap<>(); + String pid = configuration != null ? configuration.getPid() : ""; + scr.getComponentDescriptionDTOs().stream().forEach(dto -> { + for (String configPid : dto.configurationPid) { + allComponentDescriptions.put(configPid, dto); + } + }); pw.println("<script type=\"text/javascript\" src=\"" + RES_LOC + "clipboard.js\"></script>"); pw.print("<form method='get'>"); @@ -146,66 +132,67 @@ public class ConfigurationSerializerWebConsolePlugin extends GenericServlet { tdLabel(pw, "PID"); tdContent(pw); - pw.print("<input type='text' name='"); - pw.print(PARAMETER_PID); - pw.print("' value='"); - if (pid != null) { - pw.print(escapeXml(pid)); - } - - pw.println("' class='input' size='120' minlength='3'>"); + pw.printf("<input type='text' name='%s' ' value='%s' class='input' size='120' minlength='3'>", PARAMETER_PID, escapeXml(pid)); + pw.println(); + pw.println("<p>For factory PIDs use the factory PID followed by a tilde and the configuration name, e.g. 'my.factory.pid~myname'</p>"); closeTd(pw); closeTr(pw); tr(pw); - tdLabel(pw, "Remove Properties"); + + tdLabel(pw, "Hide Properties"); tdContent(pw); - pw.print("<input type='checkbox' name='"); - pw.print(PARAMETER_REMOVE_METATYPE_DEFAULT_PROPERTIES); - pw.print("'"); - if (removeMetatypeDefaultProperties) { + pw.append("<input type='checkbox' name='"); + pw.append(PARAMETER_HIDE_REDUNDANT_PROPERTIES); + pw.append("'"); + if (hideRedundantProperties) { pw.print(" checked"); } - pw.println(" id='"); - pw.print(PARAMETER_REMOVE_METATYPE_DEFAULT_PROPERTIES); - pw.println("' class='input' value='true'>"); - pw.println("<label for='"); - pw.println(PARAMETER_REMOVE_METATYPE_DEFAULT_PROPERTIES); - pw.println("'>Metatype Default Properties</label>"); - - pw.print("<input type='checkbox' name='"); - pw.print(PARAMETER_REMOVE_COMPONENT_DEFAULT_PROPERTIES); - pw.print("'"); - if (removeComponentDefaultProperties) { - pw.print(" checked"); + pw.append(" id='"); + pw.append(PARAMETER_HIDE_REDUNDANT_PROPERTIES); + pw.append("' class='input' value='true'>").println(); + pw.append("<label for='"); + pw.append(PARAMETER_HIDE_REDUNDANT_PROPERTIES); + pw.append("'>"); + + + StringBuilder sb = new StringBuilder("Redundant Properties ("); + // these configs come from inherited sources + Dictionary<String, Object> mergedProperties = + ConfigTaskCreator.getDefaultProperties(infoProvider, pid); + if (mergedProperties == null) { + mergedProperties = new Hashtable<>(); } - - pw.println(" id='"); - pw.print(PARAMETER_REMOVE_COMPONENT_DEFAULT_PROPERTIES); - pw.println("' class='input' value='true'>"); - pw.println("<label for='"); - pw.println(PARAMETER_REMOVE_COMPONENT_DEFAULT_PROPERTIES); - pw.println("'>Declarative Services Component Properties</label>"); - - if (Activator.MERGE_SCHEMES != null) { - pw.print("<input type='checkbox' name='"); - pw.print(PARAMETER_REMOVE_MERGED_DEFAULT_PROPERTIES); - pw.print("'"); - if (removeMergedDefaultProperties) { - pw.print(" checked"); + if (mergedProperties.size() > 0) { + sb.append( + "from <a href=\"https://sling.apache.org/documentation/bundles/configuration-installer-factory.html#merging-of-configurations\">Merge Schemes</a> "); + sb.append("\"").append(String.join(", ", Activator.MERGE_SCHEMES)).append("\""); + } + final String pidReferencedFromComponentDescription; + if (configuration != null) { + pidReferencedFromComponentDescription = configuration.getFactoryPid() != null ? configuration.getFactoryPid() : configuration.getPid(); + ComponentDescriptionDTO componentDescription = allComponentDescriptions.get(pidReferencedFromComponentDescription); + if (componentDescription != null) { + if (sb.length() > 0) { + sb.append(" and "); + } + sb.append(String.format( + "from component description of <a href=\"components/%s\">component \"%s\" with configuration pid(s) \"%s\"</a>", + componentDescription.name, + componentDescription.name, + String.join(", ", componentDescription.configurationPid))); } - - pw.println(" id='"); - pw.print(PARAMETER_REMOVE_MERGED_DEFAULT_PROPERTIES); - pw.println("' class='input' value='true'>"); - pw.println("<label for='"); - pw.println(PARAMETER_REMOVE_MERGED_DEFAULT_PROPERTIES); - pw.println("'>Merged Properties</label>"); + } else { + pidReferencedFromComponentDescription = ""; + } + if (sb.length() == 0) { + sb.append("no fallback sources found"); } + pw.append(sb.toString()).append(")</label>").println(); pw.println( - "<p>Selecting any of these options strips those properties which have the same name and value as one from any of the selected sources. The removed properties are very likely being redundant and therefore do not need to be added to serialized configs.</a>"); + "<p>Enabling it hides those properties which have the same name and value as any of their fallback values.</p>"); closeTd(pw); closeTr(pw); @@ -215,10 +202,10 @@ public class ConfigurationSerializerWebConsolePlugin extends GenericServlet { pw.print("<select name='"); pw.print(PARAMETER_FORMAT); pw.println("'>"); - option(pw, "JSON", "OSGi Configurator JSON", format); - option(pw, "CONFIG", "Apache Felix Config", format); - option(pw, "PROPERTIES", "Java Properties", format); - option(pw, "PROPERTIES_XML", "Java Properties (XML)", format); + option(pw, "JSON", "OSGi Configurator JSON", serializationFormat.name()); + option(pw, "CONFIG", "Apache Felix Config", serializationFormat.name()); + option(pw, "PROPERTIES", "Java Properties", serializationFormat.name()); + option(pw, "PROPERTIES_XML", "Java Properties (XML)", serializationFormat.name()); pw.println("</select>"); pw.println(" <input type='submit' value='Print' class='submit'>"); @@ -226,15 +213,11 @@ public class ConfigurationSerializerWebConsolePlugin extends GenericServlet { closeTd(pw); closeTr(pw); - if (pid != null && !pid.trim().isEmpty()) { + if (configuration != null) { tr(pw); tdLabel(pw, "Serialized Configuration Properties"); tdContent(pw); - Configuration configuration = configurationAdmin.getConfiguration(pid, null); - Dictionary<String, Object> mergedProperties = ConfigTaskCreator.getDefaultProperties(infoProvider, pid); - if (mergedProperties == null) { - mergedProperties = new Hashtable<>(); - } + Dictionary<String, Object> properties = configuration.getProperties(); if (properties == null) { pw.print("<p class='ui-state-error-text'>"); @@ -242,22 +225,16 @@ public class ConfigurationSerializerWebConsolePlugin extends GenericServlet { pw.println("</p>"); } else { properties = ConfigUtil.cleanConfiguration(properties); + if (hideRedundantProperties) { + removeComponentDefaultProperties( + allComponentDescriptions, + pidReferencedFromComponentDescription, + properties, + mergedProperties); + ConfigUtil.removeRedundantProperties(properties, mergedProperties); + } + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - if (removeMetatypeDefaultProperties) { - metatypeHandler.updateConfiguration( - configuration.getFactoryPid(), configuration.getPid(), properties, mergedProperties); - } - if (removeComponentDefaultProperties) { - removeComponentDefaultProperties( - allComponentDescriptions, - configuration.getPid(), - configuration.getFactoryPid(), - properties, - mergedProperties); - } - if (removeMergedDefaultProperties) { - ConfigUtil.removeRedundantProperties(properties, mergedProperties); - } // always emit in alphabetical order of keys ConfigurationSerializerFactory.create(serializationFormat) .serialize(new SortedDictionary<>(properties), baos); @@ -275,7 +252,6 @@ public class ConfigurationSerializerWebConsolePlugin extends GenericServlet { closeTd(pw); closeTr(pw); } - pw.println("</table>"); pw.print("</form>"); } @@ -371,25 +347,21 @@ public class ConfigurationSerializerWebConsolePlugin extends GenericServlet { * @param mergedProperties */ private void removeComponentDefaultProperties( - final Collection<ComponentDescriptionDTO> componentDescriptions, - final String pid, - final String factoryPid, + final Map<String, ComponentDescriptionDTO> componentDescriptions, + final String pidReferencedFromComponentDescription, final Dictionary<String, Object> dict, final Dictionary<String, Object> mergedProperties) { - String effectivePid = factoryPid != null ? factoryPid : pid; - Collection<ComponentDescriptionDTO> relevantComponentDescriptions = componentDescriptions.stream() - // find all with a matching pid - .filter(c -> Arrays.asList(c.configurationPid).contains(effectivePid)) - .collect(Collectors.toList()); + ComponentDescriptionDTO relevantComponentDescriptions = componentDescriptions.get(pidReferencedFromComponentDescription); final Enumeration<String> e = dict.keys(); while (e.hasMoreElements()) { final String key = e.nextElement(); final Object newValue = dict.get(key); - if (relevantComponentDescriptions.stream() - .allMatch(c -> ConfigUtil.isSameValue(newValue, c.properties.get(key)) - && mergedProperties.get(key) == null)) { - dict.remove(key); + if (relevantComponentDescriptions != null) { + Object defaultValue = relevantComponentDescriptions.properties.get(key); + if (ConfigUtil.isSameValue(newValue, defaultValue) && mergedProperties.get(key) == null) { + dict.remove(key); + } } } }
