This is an automated email from the ASF dual-hosted git repository. jsedding pushed a commit to branch resolver-2.x-backports in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-servlets-resolver.git
commit d3092c909fa69d06e6b4b9ca02818125211050b7 Author: Carsten Ziegeler <[email protected]> AuthorDate: Sat Mar 29 17:32:49 2025 +0100 SLING-12599 : Make dependency to Apache Felix HC optional (cherry picked from commit 6d6309b797fe1741cc1f7bd23cbdf6b5ab06d2e9) --- bnd.bnd | 2 + .../internal/bundle/BundledScriptTracker.java | 103 ++-------------- .../internal/bundle/BundledScriptTrackerHC.java | 135 +++++++++++++++++++++ ...erTest.java => BundledScriptTrackerHCTest.java} | 24 +--- .../internal/bundle/BundledScriptTrackerTest.java | 31 +---- 5 files changed, 148 insertions(+), 147 deletions(-) diff --git a/bnd.bnd b/bnd.bnd index 3facbe4..275685d 100644 --- a/bnd.bnd +++ b/bnd.bnd @@ -1,6 +1,8 @@ Provide-Capability:\ osgi.extender;osgi.extender="org.apache.sling.servlets.resolver";version:Version="1.1" +Import-Package: org.apache.felix.hc.api;resolution:=optional, * + -plugin:\ org.apache.sling.bnd.plugin.headers.parameters.remove.Plugin;\ 'Require-Capability'='osgi.service;filter:="(objectClass=org.apache.sling.servlets.resolver.internal.resolution.ResolutionCache)";effective:=active',\ diff --git a/src/main/java/org/apache/sling/servlets/resolver/internal/bundle/BundledScriptTracker.java b/src/main/java/org/apache/sling/servlets/resolver/internal/bundle/BundledScriptTracker.java index cbab0ad..ae08e27 100644 --- a/src/main/java/org/apache/sling/servlets/resolver/internal/bundle/BundledScriptTracker.java +++ b/src/main/java/org/apache/sling/servlets/resolver/internal/bundle/BundledScriptTracker.java @@ -52,9 +52,6 @@ import java.util.stream.Stream; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.felix.hc.api.FormattingResultLog; -import org.apache.felix.hc.api.HealthCheck; -import org.apache.felix.hc.api.Result; import org.apache.sling.api.SlingConstants; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.SlingHttpServletResponse; @@ -88,9 +85,6 @@ import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferencePolicy; -import org.osgi.service.metatype.annotations.AttributeDefinition; -import org.osgi.service.metatype.annotations.Designate; -import org.osgi.service.metatype.annotations.ObjectClassDefinition; import org.osgi.util.converter.Converter; import org.osgi.util.converter.Converters; import org.osgi.util.tracker.BundleTracker; @@ -98,16 +92,17 @@ import org.osgi.util.tracker.BundleTrackerCustomizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@Component(service = {HealthCheck.class}) +@Component(immediate = true, service = BundledScriptTracker.class) +// component needs to be immediate as this is registered as a internal service +// which is picked up by the optional BundledScriptTrackerHC @Capability( namespace = ExtenderNamespace.EXTENDER_NAMESPACE, name = BundledScriptTracker.NS_SLING_SCRIPTING_EXTENDER, version = "1.0.0") -@Designate(ocd = BundledScriptTracker.BundledScriptTrackerConfig.class) -public class BundledScriptTracker implements BundleTrackerCustomizer<List<ServiceRegistration<Servlet>>>, HealthCheck { +public class BundledScriptTracker implements BundleTrackerCustomizer<List<ServiceRegistration<Servlet>>> { static final String NS_SLING_SCRIPTING_EXTENDER = "sling.scripting"; - private static final Logger LOGGER = LoggerFactory.getLogger(BundledScriptTracker.class); + static final Logger LOGGER = LoggerFactory.getLogger(BundledScriptTracker.class); private static final String REGISTERING_BUNDLE = "BundledScriptTracker.registering_bundle"; public static final String NS_SLING_SERVLET = "sling.servlet"; public static final String AT_VERSION = "version"; @@ -128,28 +123,14 @@ public class BundledScriptTracker implements BundleTrackerCustomizer<List<Servic private volatile List<String> searchPaths; private Set<String> registeredBundles = new HashSet<>(); - private Set<String> expectedBundles = new HashSet<>(); - private boolean ignoreNonExistingBundles = false; - - private ServiceRegistration<HealthCheck> healthCheckRegistration = null; @Activate - protected void activate(BundleContext context, BundledScriptTrackerConfig config) { + protected void activate(BundleContext context) { bundleContext.set(context); dispatchers.set(new HashMap<>()); BundleTracker<List<ServiceRegistration<Servlet>>> bt = new BundleTracker<>(context, Bundle.ACTIVE, this); tracker.set(bt); bt.open(); - if (config.mandatoryBundles() != null) { - expectedBundles.addAll(Arrays.asList(config.mandatoryBundles())); - ignoreNonExistingBundles = config.ignoreNonExistingBundles(); - healthCheckRegistration = registerHealthCheck(config.tags()); - LOGGER.info( - "Healthcheck configured with mandatory bundles {} for tags {}, ignoreNonExistingBundles = {}", - Arrays.toString(config.mandatoryBundles()), - Arrays.toString(config.tags()), - ignoreNonExistingBundles); - } } @Deactivate @@ -158,22 +139,10 @@ public class BundledScriptTracker implements BundleTrackerCustomizer<List<Servic if (bt != null) { bt.close(); } - if (healthCheckRegistration != null) { - healthCheckRegistration.unregister(); - healthCheckRegistration = null; - } bundleContext.set(null); dispatchers.set(null); } - @SuppressWarnings({"rawtypes", "unchecked"}) - ServiceRegistration<HealthCheck> registerHealthCheck(String[] tags) { - Dictionary props = new Hashtable(); - props.put(HealthCheck.NAME, "BundledScriptTracker Healthcheck"); - props.put(HealthCheck.TAGS, tags); - return bundleContext.get().registerService(HealthCheck.class, this, props); - } - @Reference(policy = ReferencePolicy.DYNAMIC, updated = "bindSearchPathProvider") protected void bindSearchPathProvider(final SearchPathProvider searchPathProvider) { final boolean reconfiguration = this.searchPaths != null; @@ -635,42 +604,8 @@ public class BundledScriptTracker implements BundleTrackerCustomizer<List<Servic registeredBundles.remove(bundle.getSymbolicName()); } - @Override - public Result execute() { - - if (expectedBundles == null) { - return new Result(Result.Status.OK, "Health check is not configured."); - } - - Set<String> mandatoryAvailableBundles; - if (ignoreNonExistingBundles) { - // Filter the provided symbolic names if a bundle with that name actually exists - mandatoryAvailableBundles = filterForExistingBundles(bundleContext.get(), expectedBundles); - } else { - mandatoryAvailableBundles = expectedBundles; - } - - if (registeredBundles.containsAll(mandatoryAvailableBundles)) { - return new Result(Result.Status.OK, "All expected bundles have registered their scripts."); - } else { - FormattingResultLog log = new FormattingResultLog(); - log.warn("Expected bundles : {}, registered bundles: {}", mandatoryAvailableBundles, registeredBundles); - return new Result(log); - } - } - - /** - * Return the symbolic names of bundles which are provided via {{code expectedBundles}} and present - * @param bundleContext a bundleContext - * @param expectedBundles the symbolic names of bundles to check for - * @return the symbolic names of present bundles - */ - protected static Set<String> filterForExistingBundles(BundleContext bundleContext, Set<String> expectedBundles) { - List<Bundle> allBundles = Arrays.asList(bundleContext.getBundles()); - return allBundles.stream() - .map(Bundle::getSymbolicName) - .filter(s -> expectedBundles.contains(s)) - .collect(Collectors.toSet()); + public Set<String> getRegisteredBundles() { + return Collections.unmodifiableSet(registeredBundles); } private class DispatcherServlet extends GenericServlet { @@ -896,26 +831,4 @@ public class BundledScriptTracker implements BundleTrackerCustomizer<List<Servic newSet.addAll(originalCapabilities); return newSet; } - - @ObjectClassDefinition - public @interface BundledScriptTrackerConfig { - - @AttributeDefinition( - name = "Mandatory Bundles", - description = - "A list of symbolic bundle names for which the " - + "script registration process must have been successfully completed for the health check to report ok.") - String[] mandatoryBundles(); - - @AttributeDefinition( - name = "Check for bundle presence", - description = - "If disabled, bundles listed as mandatory are ignored if no bundle with that symbolic name is present") - boolean ignoreNonExistingBundles() default false; - - @AttributeDefinition( - name = "healthcheck tags", - description = "the tags under which the healthcheck should be registered") - String[] tags() default "systemready"; - } } diff --git a/src/main/java/org/apache/sling/servlets/resolver/internal/bundle/BundledScriptTrackerHC.java b/src/main/java/org/apache/sling/servlets/resolver/internal/bundle/BundledScriptTrackerHC.java new file mode 100644 index 0000000..9a7d3a4 --- /dev/null +++ b/src/main/java/org/apache/sling/servlets/resolver/internal/bundle/BundledScriptTrackerHC.java @@ -0,0 +1,135 @@ +/* + * 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.servlets.resolver.internal.bundle; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.felix.hc.api.FormattingResultLog; +import org.apache.felix.hc.api.HealthCheck; +import org.apache.felix.hc.api.Result; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +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.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.Designate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +@Component( + property = { + "felix.healthcheck.name=BundledScriptTracker", + }, + service = {HealthCheck.class}) +@Designate(ocd = BundledScriptTrackerHC.BundledScriptTrackerHCConfig.class) +public class BundledScriptTrackerHC implements HealthCheck { + + private final BundledScriptTracker tracker; + + private final BundleContext bundleContext; + + private final Set<String> expectedBundles = new HashSet<>(); + + private final boolean ignoreNonExistingBundles; + + @Activate + public BundledScriptTrackerHC( + final BundleContext context, + final @Reference BundledScriptTracker tracker, + final BundledScriptTrackerHCConfig config) { + this.tracker = tracker; + this.bundleContext = context; + this.ignoreNonExistingBundles = config.ignoreNonExistingBundles(); + if (config.mandatoryBundles() != null) { + expectedBundles.addAll(Arrays.asList(config.mandatoryBundles())); + BundledScriptTracker.LOGGER.info( + "Healthcheck configured with mandatory bundles {} for tags {}, ignoreNonExistingBundles = {}", + Arrays.toString(config.mandatoryBundles()), + Arrays.toString(config.hc_tags()), + ignoreNonExistingBundles); + } + } + + @Override + public Result execute() { + if (this.expectedBundles.isEmpty()) { + return new Result(Result.Status.OK, "Health check is not configured."); + } + + final Set<String> mandatoryAvailableBundles; + if (this.ignoreNonExistingBundles) { + // Filter the provided symbolic names if a bundle with that name actually exists + mandatoryAvailableBundles = filterForExistingBundles(this.bundleContext, this.expectedBundles); + } else { + mandatoryAvailableBundles = this.expectedBundles; + } + + if (this.tracker.getRegisteredBundles().containsAll(mandatoryAvailableBundles)) { + return new Result(Result.Status.OK, "All expected bundles have registered their scripts."); + } else { + FormattingResultLog log = new FormattingResultLog(); + log.warn( + "Expected bundles : {}, registered bundles: {}", + mandatoryAvailableBundles, + this.tracker.getRegisteredBundles()); + return new Result(log); + } + } + + /** + * Return the symbolic names of bundles which are provided via {{code expectedBundles}} and present + * @param bundleContext a bundleContext + * @param expectedBundles the symbolic names of bundles to check for + * @return the symbolic names of present bundles + */ + protected static Set<String> filterForExistingBundles( + final BundleContext bundleContext, final Set<String> expectedBundles) { + final List<Bundle> allBundles = Arrays.asList(bundleContext.getBundles()); + return allBundles.stream() + .map(Bundle::getSymbolicName) + .filter(s -> expectedBundles.contains(s)) + .collect(Collectors.toSet()); + } + + @ObjectClassDefinition + public @interface BundledScriptTrackerHCConfig { + + @AttributeDefinition( + name = "Mandatory Bundles", + description = + "A list of symbolic bundle names for which the " + + "script registration process must have been successfully completed for the health check to report ok.") + String[] mandatoryBundles(); + + @AttributeDefinition( + name = "Check for bundle presence", + description = + "If disabled, bundles listed as mandatory are ignored if no bundle with that symbolic name is present") + boolean ignoreNonExistingBundles() default false; + + @AttributeDefinition( + name = "healthcheck tags", + description = "the tags under which the healthcheck should be registered") + String[] hc_tags() default "systemready"; + } +} diff --git a/src/test/java/org/apache/sling/servlets/resolver/internal/bundle/BundledScriptTrackerTest.java b/src/test/java/org/apache/sling/servlets/resolver/internal/bundle/BundledScriptTrackerHCTest.java similarity index 66% copy from src/test/java/org/apache/sling/servlets/resolver/internal/bundle/BundledScriptTrackerTest.java copy to src/test/java/org/apache/sling/servlets/resolver/internal/bundle/BundledScriptTrackerHCTest.java index 7c9a226..727bdd3 100644 --- a/src/test/java/org/apache/sling/servlets/resolver/internal/bundle/BundledScriptTrackerTest.java +++ b/src/test/java/org/apache/sling/servlets/resolver/internal/bundle/BundledScriptTrackerHCTest.java @@ -18,10 +18,6 @@ */ package org.apache.sling.servlets.resolver.internal.bundle; -import javax.servlet.Servlet; - -import java.util.ArrayList; -import java.util.List; import java.util.Set; import org.junit.Test; @@ -29,28 +25,12 @@ import org.mockito.Mockito; import org.mockito.internal.util.collections.Sets; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; -import org.osgi.framework.BundleEvent; -import org.osgi.framework.ServiceRegistration; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -public class BundledScriptTrackerTest { - @Test - public void removedBundle() { - BundledScriptTracker tracker = new BundledScriptTracker(); - tracker.activate(mock(BundleContext.class), mock(BundledScriptTracker.BundledScriptTrackerConfig.class)); - List<ServiceRegistration<Servlet>> registrations = new ArrayList<>(); - @SuppressWarnings("unchecked") - ServiceRegistration<Servlet> registration = mock(ServiceRegistration.class); - registrations.add(registration); - tracker.removedBundle(mock(Bundle.class), mock(BundleEvent.class), registrations); - verify(registration).unregister(); - } +public class BundledScriptTrackerHCTest { @Test public void test_filterForExistingBundles() { @@ -62,7 +42,7 @@ public class BundledScriptTrackerTest { .getBundles(); Set<String> expectedSymbolicNames = Sets.newSet("a", "b", "z"); - Set<String> res = BundledScriptTracker.filterForExistingBundles(bc, expectedSymbolicNames); + Set<String> res = BundledScriptTrackerHC.filterForExistingBundles(bc, expectedSymbolicNames); assertNotNull(res); assertEquals(2, res.size()); assertTrue(res.contains("a")); diff --git a/src/test/java/org/apache/sling/servlets/resolver/internal/bundle/BundledScriptTrackerTest.java b/src/test/java/org/apache/sling/servlets/resolver/internal/bundle/BundledScriptTrackerTest.java index 7c9a226..6e3216c 100644 --- a/src/test/java/org/apache/sling/servlets/resolver/internal/bundle/BundledScriptTrackerTest.java +++ b/src/test/java/org/apache/sling/servlets/resolver/internal/bundle/BundledScriptTrackerTest.java @@ -22,19 +22,13 @@ import javax.servlet.Servlet; import java.util.ArrayList; import java.util.List; -import java.util.Set; import org.junit.Test; -import org.mockito.Mockito; -import org.mockito.internal.util.collections.Sets; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.framework.ServiceRegistration; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -43,7 +37,7 @@ public class BundledScriptTrackerTest { @Test public void removedBundle() { BundledScriptTracker tracker = new BundledScriptTracker(); - tracker.activate(mock(BundleContext.class), mock(BundledScriptTracker.BundledScriptTrackerConfig.class)); + tracker.activate(mock(BundleContext.class)); List<ServiceRegistration<Servlet>> registrations = new ArrayList<>(); @SuppressWarnings("unchecked") ServiceRegistration<Servlet> registration = mock(ServiceRegistration.class); @@ -51,27 +45,4 @@ public class BundledScriptTrackerTest { tracker.removedBundle(mock(Bundle.class), mock(BundleEvent.class), registrations); verify(registration).unregister(); } - - @Test - public void test_filterForExistingBundles() { - BundleContext bc = Mockito.mock(BundleContext.class); - Mockito.doReturn(new Bundle[] { - mockBundle("a"), mockBundle("b"), mockBundle("c"), mockBundle("d"), mockBundle("e") - }) - .when(bc) - .getBundles(); - - Set<String> expectedSymbolicNames = Sets.newSet("a", "b", "z"); - Set<String> res = BundledScriptTracker.filterForExistingBundles(bc, expectedSymbolicNames); - assertNotNull(res); - assertEquals(2, res.size()); - assertTrue(res.contains("a")); - assertTrue(res.contains("b")); - } - - private static Bundle mockBundle(String symbolicName) { - Bundle b = Mockito.mock(Bundle.class); - Mockito.when(b.getSymbolicName()).thenReturn(symbolicName); - return b; - } }
