/* * 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.installer.hc; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Property; import org.apache.felix.scr.annotations.Reference; import org.apache.sling.commons.osgi.PropertiesUtil; import org.apache.sling.hc.annotations.SlingHealthCheck; import org.apache.sling.hc.api.HealthCheck; import org.apache.sling.hc.api.Result; import org.apache.sling.hc.util.FormattingResultLog; import org.apache.sling.installer.api.InstallableResource; import org.apache.sling.installer.api.info.InfoProvider; import org.apache.sling.installer.api.info.InstallationState; import org.apache.sling.installer.api.info.Resource; import org.apache.sling.installer.api.info.ResourceGroup; import org.osgi.service.cm.ConfigurationAdmin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @SlingHealthCheck( name = OsgiInstallerHealthCheck.HC_NAME, description = "Checks that all OSGi configurations/bundles are successfully installed by the OSGi Installer (and are not skipped for some reason).", tags = { "installer", "osgi" } ) public class OsgiInstallerHealthCheck implements HealthCheck { protected static final String HC_NAME = "OSGi Installer Health Check"; @Reference private InfoProvider infoProvider; private static final Logger LOG = LoggerFactory.getLogger(OsgiInstallerHealthCheck.class); private static final String DEFAULT_URL_PREFIX = "jcrinstall:/apps/"; @Property(label = "URL Prefixes to consider", description = "Only those OSGi configurations/bundles whose location are starting with one of the given URL prefixes are checked (whether they are installed correctly). Open /system/console/osgi-installer for a list of valid prefixes.", cardinality = 1, value = DEFAULT_URL_PREFIX) static final String PROP_URL_PREFIXES = "urlPrefixes"; @Property(label = "Check Bundles", description = "If enabled bundles are checked (restricted to the ones matching one of the prefixes)", boolValue = true) static final String PROP_CHECK_BUNDLES = "checkBundles"; @Property(label = "Check Configurations", description = "If enabled configurations are checked (restricted to the ones matching one of the prefixes)", boolValue = true) static final String PROP_CHECK_CONFIGURATIONS = "checkConfigurations"; private String[] urlPrefixes; private boolean checkBundles; private boolean checkConfigurations; private final static String DOCUMENTATION_URL = "https://sling.apache.org/documentation/bundles/osgi-installer.html#health-check"; @Reference private ConfigurationAdmin configurationAdmin; @Activate public void activate(Map<String, ?> properties) { urlPrefixes = PropertiesUtil.toStringArray(properties.get(PROP_URL_PREFIXES), new String[] { DEFAULT_URL_PREFIX }); checkBundles = PropertiesUtil.toBoolean(properties.get(PROP_CHECK_BUNDLES), true); checkConfigurations = PropertiesUtil.toBoolean(properties.get(PROP_CHECK_CONFIGURATIONS), true); } @Override public Result execute() { InstallationState installationState = infoProvider.getInstallationState(); FormattingResultLog hcLog = new FormattingResultLog(); int numCheckedConfigurations = 0; int numCheckedBundles = 0; // go through all resource groups of the OSGi Installer for (final ResourceGroup group : installationState.getInstalledResources()) { String type = evaluateGroup(group, hcLog); switch (type) { case InstallableResource.TYPE_CONFIG: numCheckedConfigurations++; break; case InstallableResource.TYPE_BUNDLE: numCheckedBundles++; break; } } hcLog.info("Checked {} OSGi bundles and {} configurations.", numCheckedBundles, numCheckedConfigurations); if (hcLog.getAggregateStatus().ordinal() >= Result.Status.WARN.ordinal()) { hcLog.info("Refer to the OSGi installer's documentation page at {} for further details on how to fix those issues.", DOCUMENTATION_URL); } return new Result(hcLog); } /** * @param group * the resource group to evaluate * @param hcLog * the log to fill during the health check * @return the type of resources in this group ("bundle" or "config") or empty string, if the group was not * considered by this health check */ private String evaluateGroup(ResourceGroup group, FormattingResultLog hcLog) { Resource invalidResource = null; String resourceType = ""; // go through all resources within the given group for (Resource resource : group.getResources()) { // check for the correct type resourceType = resource.getType(); switch (resourceType) { case InstallableResource.TYPE_CONFIG: if (!checkConfigurations) { LOG.debug("Skip resource '{}', configuration checks are disabled", resource.getEntityId()); return ""; } break; case InstallableResource.TYPE_BUNDLE: if (!checkBundles) { LOG.debug("Skip resource '{}', bundle checks are disabled", resource.getEntityId()); return ""; } break; default: LOG.debug("Skip resource '{}' as it is neither a bundle nor a configuration but a {}", resource.getEntityId(), resourceType); return ""; } if (StringUtils.startsWithAny(resource.getURL(), urlPrefixes)) { switch (resource.getState()) { case IGNORED: // means a considered resource was found and it is invalid // still the other resources need to be evaluated case INSTALL: if (invalidResource == null) { invalidResource = resource; } break; default: // means a considered resource was found and it is valid // no need to evaluate other resources from this group return resourceType; } } else { LOG.debug("Skipping resource '{}' as its URL is not starting with any of these prefixes'{}'", resource, StringUtils.join(urlPrefixes, ",")); } } if (invalidResource != null) { if (resourceType.equals(InstallableResource.TYPE_CONFIG)) { hcLog.critical( "The installer state of the OSGi configuration resource '{}' is {}, config might have been manually overwritten!", invalidResource, invalidResource.getState()); } else { hcLog.critical( "The installer state of the OSGi bundle resource '{}' is {}, probably because a later or the same version of that bundle is already installed!", invalidResource, invalidResource.getState()); } return resourceType; } else { return ""; // do not count this group, as only non-considered resources have been in there } } }