blob: f1eaa9511763de1bea5f0a4135ec2036cc3ad4ef [file] [log] [blame]
/*
* 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 = "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. The bundles/configs with those prefixes are asserted to be successfully installed under all circumstances!", 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 '{}' as it is a configuration", resource.getEntityId());
return "";
}
break;
case InstallableResource.TYPE_BUNDLE:
if (!checkBundles) {
LOG.debug("Skip resource '{}' as it is a bundle", 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
}
}
}