This is an automated email from the ASF dual-hosted git repository. joerghoh pushed a commit to branch SLING-12417 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-servlets-resolver.git
commit 8c4b260133a7e977caadc92b427daa6958c2344d Author: Joerg Hoh <[email protected]> AuthorDate: Thu Nov 7 16:30:58 2024 +0100 SLING-12417 add healthcheck --- pom.xml | 6 ++ .../internal/bundle/BundledScriptTracker.java | 67 +++++++++++++++++++++- .../internal/bundle/BundledScriptTrackerTest.java | 38 ++++++------ .../resolver/it/ServletResolverTestSupport.java | 6 ++ 4 files changed, 95 insertions(+), 22 deletions(-) diff --git a/pom.xml b/pom.xml index 31a313e..f2d17ee 100644 --- a/pom.xml +++ b/pom.xml @@ -279,6 +279,12 @@ <artifactId>osgi.cmpn</artifactId> <scope>provided</scope> </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.healthcheck.api</artifactId> + <version>2.0.4</version> + <scope>provided</scope> + </dependency> <!-- Testing --> <dependency> <groupId>org.apache.sling</groupId> 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 70bb82b..0753ca5 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,6 +52,9 @@ import javax.servlet.http.HttpServletResponse; 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; @@ -85,6 +88,9 @@ 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; @@ -93,12 +99,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Component( - service = {} + service = {HealthCheck.class} ) @Capability(namespace = ExtenderNamespace.EXTENDER_NAMESPACE, name = BundledScriptTracker.NS_SLING_SCRIPTING_EXTENDER, version = "1.0.0") -public class BundledScriptTracker implements BundleTrackerCustomizer<List<ServiceRegistration<Servlet>>> { +@Designate(ocd=BundledScriptTracker.BundledScriptTrackerConfig.class) +public class BundledScriptTracker implements BundleTrackerCustomizer<List<ServiceRegistration<Servlet>>>, HealthCheck { static final String NS_SLING_SCRIPTING_EXTENDER = "sling.scripting"; private static final Logger LOGGER = LoggerFactory.getLogger(BundledScriptTracker.class); @@ -120,14 +127,25 @@ public class BundledScriptTracker implements BundleTrackerCustomizer<List<Servic private final AtomicReference<Map<Set<String>, ServiceRegistration<Servlet>>> dispatchers = new AtomicReference<>(); private volatile List<String> searchPaths; + + private Set<String> registeredBundles = new HashSet<>(); + private Set<String> expectedBundles = new HashSet<>(); + + private ServiceRegistration<HealthCheck> healthCheckRegistration = null; @Activate - protected void activate(BundleContext context) { + protected void activate(BundleContext context, BundledScriptTrackerConfig config) { 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())); + healthCheckRegistration = registerHealthCheck(config.tags()); + LOGGER.info("Healthcheck configured with mandatory bundles {} for tags {}", + Arrays.toString(config.mandatoryBundles()), Arrays.toString(config.tags())); + } } @Deactivate @@ -136,10 +154,23 @@ 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, "Sightly BundledScriptTracker Healthceck"); + 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; @@ -188,6 +219,7 @@ public class BundledScriptTracker implements BundleTrackerCustomizer<List<Servic refreshDispatcher(serviceRegistrations); long duration = Duration.between(registerStart, Instant.now()).toMillis(); LOGGER.info("Took {}ms to register {} servlets from bundle {}.", duration, serviceRegistrations.size(), bundle.getSymbolicName()); + registeredBundles.add(bundle.getSymbolicName()); return serviceRegistrations; } else { return Collections.emptyList(); @@ -557,6 +589,23 @@ public class BundledScriptTracker implements BundleTrackerCustomizer<List<Servic LOGGER.debug("Bundle {} removed", bundle.getSymbolicName()); regs.forEach(ServiceRegistration::unregister); refreshDispatcher(Collections.emptyList()); + registeredBundles.remove(bundle.getSymbolicName()); + } + + @Override + public Result execute() { + + if (expectedBundles == null) { + return new Result(Result.Status.OK,"Healthcheck not configured"); + } + + if (registeredBundles.containsAll(expectedBundles)) { + return new Result(Result.Status.OK,"all expected bundles registered"); + } else { + FormattingResultLog log = new FormattingResultLog(); + log.warn("Expected bundles : {}, present bundles: {}", expectedBundles, registeredBundles); + return new Result(log); + } } private class DispatcherServlet extends GenericServlet { @@ -756,4 +805,16 @@ public class BundledScriptTracker implements BundleTrackerCustomizer<List<Servic newSet.addAll(originalCapabilities); return newSet; } + + + @ObjectClassDefinition + public @interface BundledScriptTrackerConfig { + + @AttributeDefinition(name="Mandatory Bundles", description="for all of the provided symbolic bundle names the " + + "registration process must have been completed successfully for the healthcheck to report ok") + String[] mandatoryBundles(); + + @AttributeDefinition(name="healthcheck tags", description="the tags under which the healthcheck should be registered") + String[] 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/BundledScriptTrackerTest.java index 13e19c8..47f7329 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 @@ -1,21 +1,21 @@ -/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ~ 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. - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* + * 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 static org.mockito.Mockito.mock; @@ -37,7 +37,7 @@ public class BundledScriptTrackerTest { @Test public void removedBundle() { BundledScriptTracker tracker = new BundledScriptTracker(); - tracker.activate(mock(BundleContext.class)); + tracker.activate(mock(BundleContext.class),mock(BundledScriptTracker.BundledScriptTrackerConfig.class)); List<ServiceRegistration<Servlet>> registrations = new ArrayList<>(); @SuppressWarnings("unchecked") ServiceRegistration<Servlet> registration = mock(ServiceRegistration.class); diff --git a/src/test/java/org/apache/sling/servlets/resolver/it/ServletResolverTestSupport.java b/src/test/java/org/apache/sling/servlets/resolver/it/ServletResolverTestSupport.java index 939198e..8919f44 100644 --- a/src/test/java/org/apache/sling/servlets/resolver/it/ServletResolverTestSupport.java +++ b/src/test/java/org/apache/sling/servlets/resolver/it/ServletResolverTestSupport.java @@ -124,6 +124,9 @@ public class ServletResolverTestSupport extends TestSupport { // mavenBundle().groupId("commons-codec").artifactId("commons-codec").version("1.15"), // + mavenBundle().groupId("org.apache.sling").artifactId("org.apache.sling.hc.api").version("1.0.4"), + mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.healthcheck.api").versionAsInProject(), + // junitBundles(), newConfiguration("org.apache.felix.http") .put("org.osgi.service.http.port", httpPort) @@ -137,6 +140,9 @@ public class ServletResolverTestSupport extends TestSupport { ), newConfiguration("org.apache.sling.jcr.base.internal.LoginAdminWhitelist") .put("whitelist.bundles.regexp", "^PAXEXAM.*$") + .asOption(), + newConfiguration("org.apache.sling.servlets.resolver.internal.bundle.BundledScriptTracker") + .put("mandatoryBundles", "testBundle") .asOption() ) );
