You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by dk...@apache.org on 2021/12/16 00:34:32 UTC

[sling-org-apache-sling-feature-apiregions] branch SLING-11006-regions-webconsole created (now 038e7d1)

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

dklco pushed a change to branch SLING-11006-regions-webconsole
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-apiregions.git.


      at 038e7d1  SLING-11006 - Adding a web console status printer for the API Regions

This branch includes the following new commits:

     new 038e7d1  SLING-11006 - Adding a web console status printer for the API Regions

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[sling-org-apache-sling-feature-apiregions] 01/01: SLING-11006 - Adding a web console status printer for the API Regions

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch SLING-11006-regions-webconsole
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-apiregions.git

commit 038e7d175b4158e0f54e9cfa11f187db996e245c
Author: Dan Klco <kl...@adobe.com>
AuthorDate: Wed Dec 15 19:34:21 2021 -0500

    SLING-11006 - Adding a web console status printer for the API Regions
---
 pom.xml                                            |  20 ++++
 .../sling/feature/apiregions/impl/Activator.java   |  32 +++++++
 .../feature/apiregions/impl/RegionPrinter.java     | 104 +++++++++++++++++++++
 .../feature/apiregions/impl/ActivatorTest.java     |  15 +++
 .../feature/apiregions/impl/RegionPrinterTest.java |  89 ++++++++++++++++++
 src/test/resources/printer/empty.txt               |  32 +++++++
 src/test/resources/printer/populated.txt           |  54 +++++++++++
 7 files changed, 346 insertions(+)

diff --git a/pom.xml b/pom.xml
index cdce5f3..937c3c9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -65,6 +65,7 @@
                     <excludes>
                         <exclude>*.md</exclude>
                         <exclude>src/test/resources/*</exclude>
+                        <exclude>src/test/resources/printer/*.txt</exclude>
                         <exclude>src/test/resources/props1/*</exclude>
                         <exclude>src/test/resources/props2/*</exclude>
                         <exclude>src/test/resources/props3/*</exclude>
@@ -72,6 +73,25 @@
                     </excludes>
                 </configuration>
             </plugin>
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <version>0.8.7</version>
+                <executions>
+                   <execution>
+                      <goals>
+                         <goal>prepare-agent</goal>
+                      </goals>
+                   </execution>
+                   <execution>
+                      <id>report</id>
+                      <phase>prepare-package</phase>
+                      <goals>
+                         <goal>report</goal>
+                      </goals>
+                   </execution>
+                </executions>
+             </plugin>
         </plugins>
     </build>
     <dependencies>
diff --git a/src/main/java/org/apache/sling/feature/apiregions/impl/Activator.java b/src/main/java/org/apache/sling/feature/apiregions/impl/Activator.java
index 5d0900f..1bd0aae 100644
--- a/src/main/java/org/apache/sling/feature/apiregions/impl/Activator.java
+++ b/src/main/java/org/apache/sling/feature/apiregions/impl/Activator.java
@@ -61,6 +61,7 @@ public class Activator implements BundleActivator, FrameworkListener {
 
     BundleContext bundleContext;
     ServiceRegistration<ResolverHookFactory> hookRegistration;
+    ServiceRegistration<RegionPrinter> webconsoleRegistration;
 
     RegionConfiguration configuration;
 
@@ -74,6 +75,7 @@ public class Activator implements BundleActivator, FrameworkListener {
 
         registerHook();
 
+        registerWebconsoleStatus();
         this.configAdminTracker = new ServiceTracker<>(context, CONFIG_ADMIN_CLASS_NAME, new ServiceTrackerCustomizer<Object, Object>() {
 
             @Override
@@ -100,8 +102,11 @@ public class Activator implements BundleActivator, FrameworkListener {
         this.configAdminTracker.open();
 
         context.addFrameworkListener(this);
+
+        
     }
 
+
     @Override
     public synchronized void stop(BundleContext context) throws Exception {
         // All services automatically get unregistered by the framework.
@@ -135,6 +140,23 @@ public class Activator implements BundleActivator, FrameworkListener {
         hookRegistration = bundleContext.registerService(ResolverHookFactory.class, enforcer, this.configuration.getRegistrationProperties());
     }
 
+    synchronized void registerWebconsoleStatus() {
+
+        if (webconsoleRegistration != null){
+            return; // There is already a hook, no need to re-register
+        }
+
+        LOG.info("Registering region printer");
+        RegionPrinter printer = new RegionPrinter(bundleContext,configuration);
+
+        final Dictionary<String, String> serviceProps = new Hashtable<>();
+        serviceProps.put("felix.webconsole.label", RegionPrinter.PATH);
+        serviceProps.put("felix.webconsole.title", RegionPrinter.HEADLINE);
+        serviceProps.put("felix.webconsole.configprinter.modes", "always");
+
+        webconsoleRegistration = bundleContext.registerService(RegionPrinter.class, printer, serviceProps);
+    }
+
     synchronized void unregisterHook() {
         if (hookRegistration != null) {
             hookRegistration.unregister();
@@ -142,6 +164,13 @@ public class Activator implements BundleActivator, FrameworkListener {
         }
     }
 
+    synchronized void unregisterWebconsoleStatus() {
+        if (webconsoleRegistration != null) {
+            webconsoleRegistration.unregister();
+            webconsoleRegistration = null;
+        }
+    }
+
     @Override
     public void frameworkEvent(FrameworkEvent event) {
         if (event.getType() == FrameworkEvent.STARTED) {
@@ -192,13 +221,16 @@ public class Activator implements BundleActivator, FrameworkListener {
                         Object arg = args[0];
                         if (arg == null) {
                             registerHook();
+                            registerWebconsoleStatus();
                         } else if (arg instanceof Dictionary) {
                             Dictionary<?,?> props = (Dictionary<?,?>) args[0];
                             Object disabled = props.get("disable");
                             if ("true".equals(disabled)) {
                                 unregisterHook();
+                                unregisterWebconsoleStatus();
                             } else {
                                 registerHook();
+                                registerWebconsoleStatus();
                             }
                         }
                     }
diff --git a/src/main/java/org/apache/sling/feature/apiregions/impl/RegionPrinter.java b/src/main/java/org/apache/sling/feature/apiregions/impl/RegionPrinter.java
new file mode 100644
index 0000000..9ebef16
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/apiregions/impl/RegionPrinter.java
@@ -0,0 +1,104 @@
+/*
+ * 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.feature.apiregions.impl;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Version;
+
+@SuppressWarnings("java:S3457") // adding platform specific endings makes the process harder and \n should be
+                                // interpreted correctly across all
+public class RegionPrinter {
+
+    static final String HEADLINE = "Sling Feature API Regions";
+    static final String PATH = "apiregions";
+    private RegionConfiguration config;
+    private BundleContext context;
+
+    public RegionPrinter(BundleContext context, RegionConfiguration config) {
+        this.context = context;
+        this.config = config;
+    }
+
+    private void renderPackageMappings(PrintWriter pw) {
+        Map<String, Set<String>> regionPackageMap = config.getRegionPackageMap();
+        regionPackageMap.keySet().stream().sorted().forEach(region -> {
+            pw.println(String.format("\n%s:", region));
+            regionPackageMap.get(region).stream().sorted().forEach(pkg -> pw.println(" - " + pkg));
+        });
+    }
+
+    private void renderBundleMappings(PrintWriter pw) {
+        Map<String, List<String>> featureRegions = config.getFeatureRegionMap();
+        Map<String, Set<String>> bundlesToFeatures = config.getBundleFeatureMap();
+
+        Map<String, Entry<String, Version>> bundleLocations = config.getBundleLocationConfigMap();
+        bundlesToFeatures.keySet().stream().sorted().forEach(bundle -> {
+            Set<String> regions = new HashSet<>();
+            bundlesToFeatures.get(bundle).stream()
+                    .forEach(feature -> Optional.ofNullable(featureRegions.get(feature))
+                            .ifPresent(regions::addAll));
+            String location = Optional.ofNullable(bundleLocations.get(bundle))
+                    .map(loc -> loc.getKey() + "v" + loc.getValue().toString()).orElse("null");
+            pw.println(String.format(" - %s\n\t - features: %s\n\t - regions: %s\n\t - location: %s", bundle,
+                    bundlesToFeatures.get(bundle).stream().collect(Collectors.joining(",")),
+                    regions.stream().collect(Collectors.joining(",")), location));
+        });
+    }
+
+    private void renderHeader(PrintWriter pw, String header) {
+        pw.println("\n\n" + header + "\n-------------------\n");
+    }
+
+    private void renderProperties(PrintWriter pw) {
+        String[] properties = new String[] { Activator.REGIONS_PROPERTY_NAME, RegionConstants.APIREGIONS_JOINGLOBAL,
+                RegionConstants.DEFAULT_REGIONS, RegionConstants.PROPERTIES_FILE_LOCATION };
+        Arrays.stream(properties).forEach(p -> pw.println(String.format(" - %s=%s", p, context.getProperty(p))));
+    }
+
+    /**
+     * Print out the region information
+     * 
+     * @see org.apache.felix.webconsole.ConfigurationPrinter#printConfiguration(java.io.PrintWriter)
+     */
+    public void printConfiguration(PrintWriter pw) {
+        pw.println(HEADLINE + "\n===========================");
+
+        renderHeader(pw, "Default Regions");
+        config.getDefaultRegions().stream().forEach(r -> pw.println(" - " + r));
+        renderHeader(pw, "Region Order");
+        config.getGlobalRegionOrder().stream().forEach(r -> pw.println(" - " + r));
+        renderHeader(pw, "Properties");
+        renderProperties(pw);
+        renderHeader(pw, "Packages per Region");
+        renderPackageMappings(pw);
+        renderHeader(pw, "Bundle Mappings");
+        renderBundleMappings(pw);
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/feature/apiregions/impl/ActivatorTest.java b/src/test/java/org/apache/sling/feature/apiregions/impl/ActivatorTest.java
index bace3b3..4ff300c 100644
--- a/src/test/java/org/apache/sling/feature/apiregions/impl/ActivatorTest.java
+++ b/src/test/java/org/apache/sling/feature/apiregions/impl/ActivatorTest.java
@@ -111,6 +111,15 @@ public class ActivatorTest {
                 Mockito.eq(expectedProps));
 
         Mockito.verify(bc).addFrameworkListener(a);
+
+        Dictionary<String, Object> expectedPrinterProps = new Hashtable<>();
+        expectedPrinterProps.put("felix.webconsole.label", RegionPrinter.PATH);
+        expectedPrinterProps.put("felix.webconsole.title", RegionPrinter.HEADLINE);
+        expectedPrinterProps.put("felix.webconsole.configprinter.modes", "always");
+        Mockito.verify(bc, Mockito.times(1)).registerService(
+                Mockito.eq(RegionPrinter.class),
+                Mockito.isA(RegionPrinter.class),
+                Mockito.eq(expectedPrinterProps));
     }
 
     @Test
@@ -122,6 +131,7 @@ public class ActivatorTest {
         a.registerHook();
 
         assertNull(a.hookRegistration);
+        assertNull(a.webconsoleRegistration);
     }
 
     @SuppressWarnings("unchecked")
@@ -132,9 +142,12 @@ public class ActivatorTest {
         Activator a = new Activator();
         a.bundleContext = bc;
         a.hookRegistration = Mockito.mock(ServiceRegistration.class);
+        a.webconsoleRegistration = Mockito.mock(ServiceRegistration.class);
         a.registerHook();
+        a.registerWebconsoleStatus();
 
         assertNotNull(a.hookRegistration);
+        assertNotNull(a.webconsoleRegistration);
         Mockito.verifyZeroInteractions(bc);
     }
 
@@ -142,7 +155,9 @@ public class ActivatorTest {
     public void testUnregisterHook() {
         Activator a = new Activator();
         a.unregisterHook(); // Should not throw an exception
+        a.unregisterWebconsoleStatus();
         assertNull(a.hookRegistration);
+        assertNull(a.webconsoleRegistration);
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
diff --git a/src/test/java/org/apache/sling/feature/apiregions/impl/RegionPrinterTest.java b/src/test/java/org/apache/sling/feature/apiregions/impl/RegionPrinterTest.java
new file mode 100644
index 0000000..7e4b5b3
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/apiregions/impl/RegionPrinterTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.feature.apiregions.impl;
+
+import static org.apache.sling.feature.apiregions.impl.RegionConstants.BUNDLE_FEATURE_FILENAME;
+import static org.apache.sling.feature.apiregions.impl.RegionConstants.FEATURE_REGION_FILENAME;
+import static org.apache.sling.feature.apiregions.impl.RegionConstants.IDBSNVER_FILENAME;
+import static org.apache.sling.feature.apiregions.impl.RegionConstants.PROPERTIES_RESOURCE_PREFIX;
+import static org.apache.sling.feature.apiregions.impl.RegionConstants.REGION_PACKAGE_FILENAME;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URISyntaxException;
+import java.util.stream.Collectors;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.internal.util.io.IOUtil;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+public class RegionPrinterTest {
+
+    private RegionConfiguration regionConfiguration;
+    private BundleContext bundleContext;
+    private PrintWriter pw;
+    private StringWriter sw;
+
+    @Before
+    public void setup() {
+        regionConfiguration = mock(RegionConfiguration.class);
+        bundleContext = mock(BundleContext.class);
+        sw = new StringWriter();
+        pw = new PrintWriter(sw);
+    }
+
+    private String loadResource(String name) {
+        return IOUtil.readLines(RegionPrinterTest.class.getClassLoader().getResourceAsStream(name)).stream()
+                .collect(Collectors.joining(String.format("\n")));
+    }
+
+    @Test
+    public void testBasic() {
+        RegionPrinter printer = new RegionPrinter(bundleContext, regionConfiguration);
+        printer.printConfiguration(pw);
+        assertEquals(loadResource("printer/empty.txt"), sw.toString());
+    }
+
+    @Test
+    public void testWithData() throws URISyntaxException, IOException {
+
+        String e = getClass().getResource("/empty.properties").toURI().toString();
+        String b = getClass().getResource("/bundles1.properties").toURI().toString();
+        String f = getClass().getResource("/features1.properties").toURI().toString();
+        String r = getClass().getResource("/regions1.properties").toURI().toString();
+
+        when(bundleContext.getBundle()).thenReturn(mock(Bundle.class));
+        when(bundleContext.getProperty(PROPERTIES_RESOURCE_PREFIX + IDBSNVER_FILENAME)).thenReturn(e);
+        when(bundleContext.getProperty(PROPERTIES_RESOURCE_PREFIX + BUNDLE_FEATURE_FILENAME)).thenReturn(b);
+        when(bundleContext.getProperty(PROPERTIES_RESOURCE_PREFIX + FEATURE_REGION_FILENAME)).thenReturn(f);
+        when(bundleContext.getProperty(PROPERTIES_RESOURCE_PREFIX + REGION_PACKAGE_FILENAME)).thenReturn(r);
+
+        regionConfiguration = new RegionConfiguration(bundleContext);
+
+        RegionPrinter printer = new RegionPrinter(bundleContext, regionConfiguration);
+        printer.printConfiguration(pw);
+        assertEquals(loadResource("printer/populated.txt"), sw.toString());
+    }
+}
diff --git a/src/test/resources/printer/empty.txt b/src/test/resources/printer/empty.txt
new file mode 100644
index 0000000..f8f3e98
--- /dev/null
+++ b/src/test/resources/printer/empty.txt
@@ -0,0 +1,32 @@
+Sling Feature API Regions
+===========================
+
+
+Default Regions
+-------------------
+
+
+
+Region Order
+-------------------
+
+
+
+Properties
+-------------------
+
+ - org.apache.sling.feature.apiregions.regions=null
+ - sling.feature.apiregions.joinglobal=null
+ - sling.feature.apiregions.default=null
+ - sling.feature.apiregions.location=null
+
+
+Packages per Region
+-------------------
+
+
+
+Bundle Mappings
+-------------------
+
+
diff --git a/src/test/resources/printer/populated.txt b/src/test/resources/printer/populated.txt
new file mode 100644
index 0000000..20f89d2
--- /dev/null
+++ b/src/test/resources/printer/populated.txt
@@ -0,0 +1,54 @@
+Sling Feature API Regions
+===========================
+
+
+Default Regions
+-------------------
+
+
+
+Region Order
+-------------------
+
+ - global
+ - internal
+
+
+Properties
+-------------------
+
+ - org.apache.sling.feature.apiregions.regions=null
+ - sling.feature.apiregions.joinglobal=null
+ - sling.feature.apiregions.default=null
+ - sling.feature.apiregions.location=null
+
+
+Packages per Region
+-------------------
+
+
+global:
+ - a.b.c
+ - d.e.f
+ - test
+
+internal:
+ - xyz
+
+
+Bundle Mappings
+-------------------
+
+ - org.sling:b1:1
+	 - features: org.sling:something:1.2.3:slingosgifeature:myclassifier
+	 - regions: 
+	 - location: null
+ - org.sling:b2:1
+	 - features: org.sling:something:1.2.3:slingosgifeature:myclassifier
+	 - regions: 
+	 - location: null
+ - org.sling:b3:1
+	 - features: some.other:feature:123,org.sling:something:1.2.3:slingosgifeature:myclassifier
+	 - regions: 
+	 - location: null
+