You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by en...@apache.org on 2020/09/16 04:13:26 UTC

[sling-org-apache-sling-jcr-contentloader] branch master updated: SLING-9734 Add a health check component that monitors bundle content loading

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

enorman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-contentloader.git


The following commit(s) were added to refs/heads/master by this push:
     new 21892ab  SLING-9734 Add a health check component that monitors bundle content loading
21892ab is described below

commit 21892ab229ea9aa6355722bb105c714a90107c8c
Author: Eric Norman <en...@apache.org>
AuthorDate: Tue Sep 15 21:13:17 2020 -0700

    SLING-9734 Add a health check component that monitors bundle content
    loading
---
 bnd.bnd                                            |   4 +
 pom.xml                                            |  14 ++
 .../contentloader/hc/BundleContentLoadedCheck.java | 209 +++++++++++++++++++++
 3 files changed, 227 insertions(+)

diff --git a/bnd.bnd b/bnd.bnd
index 9e22f40..f673c81 100644
--- a/bnd.bnd
+++ b/bnd.bnd
@@ -6,3 +6,7 @@ Bundle-Category:\
 -includeresource:\
   @kxml2-*.jar,\
   @org.apache.sling.jcr.contentparser-*.jar!/org/apache/sling/jcr/contentparser/impl/JsonTicksConverter.class
+
+# healthcheck support is optional
+ Import-Package: org.apache.felix.hc.api;resolution:=optional,\
+  *
diff --git a/pom.xml b/pom.xml
index fb3aa58..dafa4aa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -210,6 +210,20 @@
             <version>2.4</version>
             <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>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.healthcheck.annotation</artifactId>
+            <version>2.0.0</version>
+            <scope>provided</scope>
+        </dependency>
+                
       <!-- Apache Felix -->
       <dependency>
         <groupId>org.apache.felix</groupId>
diff --git a/src/main/java/org/apache/sling/jcr/contentloader/hc/BundleContentLoadedCheck.java b/src/main/java/org/apache/sling/jcr/contentloader/hc/BundleContentLoadedCheck.java
new file mode 100644
index 0000000..954b3b0
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/contentloader/hc/BundleContentLoadedCheck.java
@@ -0,0 +1,209 @@
+/*
+ * 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 SF 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.jcr.contentloader.hc;
+
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.felix.hc.annotation.HealthCheckService;
+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.jcr.api.SlingRepository;
+import org.apache.sling.jcr.contentloader.internal.BundleHelper;
+import org.apache.sling.jcr.contentloader.internal.ContentLoaderService;
+import org.apache.sling.jcr.contentloader.internal.PathEntry;
+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.ConfigurationPolicy;
+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;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Health check component that monitors the bundle content
+ * loading progress for all of the deployed bundles.
+ */
+@HealthCheckService(name = BundleContentLoadedCheck.HC_NAME)
+@Component(configurationPolicy = ConfigurationPolicy.REQUIRE)
+@Designate(ocd = BundleContentLoadedCheck.Config.class, factory = true)
+public class BundleContentLoadedCheck implements HealthCheck {
+
+    private static final Logger LOG = LoggerFactory.getLogger(BundleContentLoadedCheck.class);
+
+    public static final String HC_NAME = "Bundle Content Loaded";
+    public static final String HC_LABEL = "Health Check: " + HC_NAME;
+
+    @ObjectClassDefinition(name = HC_LABEL, description = "Checks the configured path(s) against the given thresholds")
+    public @interface Config {
+        @AttributeDefinition(name = "Name", description = "Name of this health check")
+        String hc_name() default HC_NAME;
+
+        @AttributeDefinition(name = "Tags", description = "List of tags for this health check, used to select subsets of health checks for execution e.g. by a composite health check.")
+        String[] hc_tags() default {};
+
+        @AttributeDefinition(name = "Includes RegEx", description = "RegEx to select all relevant bundles for this check. The RegEx is matched against the symbolic name of the bundle.")
+        String includesRegex() default ".*";
+
+        @AttributeDefinition(name = "Excludes RegEx", description = "Optional RegEx to exclude bundles from this check (matched against symbolic name). Allows to exclude specific bundles from selected set as produced by 'Includes RegEx'.")
+        String excludesRegex() default "";
+
+        @AttributeDefinition(name = "CRITICAL for not loaded bundles", description = "By default not loaded bundles produce warnings, if this is set to true not loaded bundles produce a CRITICAL result")
+        boolean useCriticalForNotLoaded() default false;
+        
+        @AttributeDefinition
+        String webconsole_configurationFactory_nameHint() default "Bundle content loaded includes: {includesRegex} excludes: {excludesRegex}";
+    }
+
+    private BundleContext bundleContext;
+    private Pattern includesRegex;
+    private Pattern excludesRegex;
+    boolean useCriticalForNotLoaded;
+
+    /**
+     * The JCR Repository we access to resolve resources
+     */
+    @Reference
+    private SlingRepository repository;
+
+//    @Reference
+//    private ContentLoaderService bundleHelper = null;
+
+    @Activate
+    protected void activate(BundleContext bundleContext, Config config) {
+        this.bundleContext = bundleContext;
+        this.includesRegex = Pattern.compile(config.includesRegex());
+        String excludesRegex2 = config.excludesRegex();
+		this.excludesRegex = (excludesRegex2 != null && !excludesRegex2.isEmpty()) ? Pattern.compile(excludesRegex2) : null;
+        this.useCriticalForNotLoaded = config.useCriticalForNotLoaded();
+        LOG.debug("Activated bundle content loaded HC for includesRegex={} excludesRegex={}% useCriticalForNotLoaded={}", includesRegex, excludesRegex, useCriticalForNotLoaded);
+    }
+
+    
+    @Override
+    public Result execute() {
+        FormattingResultLog log = new FormattingResultLog();
+
+        Bundle[] bundles = this.bundleContext.getBundles();
+        log.debug("Framwork has {} bundles in total", bundles.length);
+ 
+        int countExcluded = 0;
+        int relevantBundlesCount = 0;
+        int notLoadedCount = 0;
+
+        Session metadataSession = null;
+        try {
+        	metadataSession = repository.loginAdministrative(null);
+        	
+        	BundleHelper bundleHelper = new ContentLoaderService();
+        	
+            for (Bundle bundle : bundles) {
+            	String bundleSymbolicName = bundle.getSymbolicName();
+                if (!includesRegex.matcher(bundleSymbolicName).matches()) {
+                    LOG.debug("Bundle {} not matched by {}", bundleSymbolicName, includesRegex);
+                    continue;
+                }
+
+                if (excludesRegex!=null && excludesRegex.matcher(bundleSymbolicName).matches()) {
+                    LOG.debug("Bundle {} excluded {}", bundleSymbolicName, excludesRegex);
+                    countExcluded ++;
+                    continue;
+                }
+            	
+                // check if bundle has initial content
+                final Iterator<PathEntry> pathIter = PathEntry.getContentPaths(bundle);
+                if (pathIter == null) {
+                    log.debug("Bundle {} has no initial content", bundleSymbolicName);
+                } else {
+                	relevantBundlesCount++;
+                	
+                    // check if the content has already been loaded
+                    final Map<String, Object> bundleContentInfo = bundleHelper.getBundleContentInfo(metadataSession, bundle, false);
+
+                    // if we don't get an info, someone else is currently loading
+                    if (bundleContentInfo == null) {
+                    	notLoadedCount++;
+                        String msg = "Not loaded bundle {} {}";
+                        Object[] msgObjs = new Object[] {bundle.getBundleId(), bundleSymbolicName};
+                        LOG.debug(msg, msgObjs);
+                        if (useCriticalForNotLoaded) {
+                            log.critical(msg, msgObjs);
+                        } else {
+                            log.warn(msg, msgObjs);
+                        }
+                    } else {
+                    	try {
+                            final boolean contentAlreadyLoaded = ((Boolean) bundleContentInfo.get(ContentLoaderService.PROPERTY_CONTENT_LOADED)).booleanValue();
+                            boolean isBundleUpdated = false;
+                            Calendar lastLoadedAt = (Calendar) bundleContentInfo.get(ContentLoaderService.PROPERTY_CONTENT_LOADED_AT);
+                            if (lastLoadedAt != null && lastLoadedAt.getTimeInMillis() < bundle.getLastModified()) {
+                                isBundleUpdated = true;
+                            }
+                            if (!isBundleUpdated && contentAlreadyLoaded) {
+                                log.debug("Content of bundle is already loaded {} {}.", bundle.getBundleId(), bundleSymbolicName);
+                            } else {
+                                notLoadedCount++;
+                                String msg = "Not loaded bundle {} {}";
+                                Object[] msgObjs = new Object[] {bundle.getBundleId(), bundleSymbolicName};
+                                LOG.debug(msg, msgObjs);
+                                if (useCriticalForNotLoaded) {
+                                    log.critical(msg, msgObjs);
+                                } else {
+                                    log.warn(msg, msgObjs);
+                                }
+                            }                    	
+                    	} finally {
+                            bundleHelper.unlockBundleContentInfo(metadataSession, bundle, false, null);
+                    	}
+                    }
+                }
+            }
+        } catch (RepositoryException t) {
+            LOG.error("Unexpected error: " + t.getMessage(), t);
+        } finally {
+            if (metadataSession != null) {
+                try {
+                	metadataSession.logout();
+                } catch (Exception t) {
+                    LOG.error("Unable to log out of session: " + t.getMessage(), t);
+                }
+            }
+        }
+                
+        String baseMsg = relevantBundlesCount + " bundles" + (!includesRegex.pattern().equals(".*") ? " for pattern " + includesRegex.pattern() : "");
+        String excludedMsg = countExcluded > 0 ? " (" + countExcluded + " excluded via pattern "+excludesRegex.pattern()+")" : "";
+        if (notLoadedCount > 0) {
+            log.info("Found " + notLoadedCount + " not content loaded of " + baseMsg + excludedMsg);
+        } else {
+            log.info("All " + baseMsg + " are content loaded" + excludedMsg);
+        }
+
+        return new Result(log);
+    }
+
+}
\ No newline at end of file