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