All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.felix.hc.generalchecks.BundlesStartedCheck Maven / Gradle / Ivy

/*
 * 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.felix.hc.generalchecks;

import java.util.Optional;
import java.util.regex.Pattern;

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.felix.hc.core.impl.util.lang.StringUtils;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
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.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;

@HealthCheckService(name = BundlesStartedCheck.HC_NAME)
@Component(configurationPolicy = ConfigurationPolicy.REQUIRE, immediate = true)
@Designate(ocd = BundlesStartedCheck.Config.class, factory = true)
public class BundlesStartedCheck implements HealthCheck {

    private static final Logger LOG = LoggerFactory.getLogger(BundlesStartedCheck.class);

    public static final String HC_NAME = "Bundles Started";
    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 inactive bundles", description = "By default inactive bundles produce warnings, if this is set to true inactive bundles produce a CRITICAL result")
        boolean useCriticalForInactive() default false;

        @AttributeDefinition
        String webconsole_configurationFactory_nameHint() default "Bundles started includes: {includesRegex} excludes: {excludesRegex}";
    }

    private BundleContext bundleContext;
    private Pattern includesRegex;
    private Optional excludesRegex;
    boolean useCriticalForInactive;

    @Activate
    protected void activate(BundleContext bundleContext, Config config) {
        this.bundleContext = bundleContext;
        this.includesRegex = Pattern.compile(config.includesRegex());
        this.excludesRegex = StringUtils.isNotBlank(config.excludesRegex()) ? Optional.of(Pattern.compile(config.excludesRegex())) : Optional.empty();
        this.useCriticalForInactive = config.useCriticalForInactive();
        LOG.debug("Activated bundles started HC for includesRegex={} excludesRegex={}% useCriticalForInactive={}", includesRegex, excludesRegex, useCriticalForInactive);
    }


    @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 inactiveCount = 0;
        for (Bundle bundle : bundles) {
            String bundleSymbolicName = bundle.getSymbolicName();
            int bundleState = bundle.getState();

            if (!includesRegex.matcher(bundleSymbolicName).matches()) {
                LOG.debug("Bundle {} not matched by {}", bundleSymbolicName, includesRegex);
                continue;
            }

            if (excludesRegex.isPresent() && excludesRegex.get().matcher(bundleSymbolicName).matches()) {
                LOG.debug("Bundle {} excluded {}", bundleSymbolicName, excludesRegex);
                countExcluded ++;
                continue;
            }
            relevantBundlesCount++;

            boolean bundleIsLogged = false;
            if (bundleState != Bundle.ACTIVE) {
                // support lazy activation (https://www.osgi.org/developer/design/lazy-start/)
                if (bundleState == Bundle.STARTING && isLazyActivation(bundle)) {
                    LOG.debug("Ignoring lazily activated bundle {}", bundleSymbolicName);
                } else  if (StringUtils.isNotBlank(bundle.getHeaders().get(Constants.FRAGMENT_HOST))) {
                    LOG.debug("Ignoring bundle fragment: {}", bundleSymbolicName);
                } else {
                    String msg = "Inactive bundle {} {}: {}";
                    Object[] msgObjs = new Object[] {bundle.getBundleId(), bundleSymbolicName, getStateLabel(bundleState)};
                    LOG.debug(msg, msgObjs);
                    if(useCriticalForInactive) {
                        log.critical(msg, msgObjs);
                    } else {
                        if (bundleState == Bundle.RESOLVED) {
                            log.warn(msg, msgObjs);
                        } else {
                            log.temporarilyUnavailable(msg, msgObjs);
                        }
                    }
                    bundleIsLogged = true;
                    inactiveCount++;
                }
            }
            if(!bundleIsLogged) {
                log.debug("Bundle {} {}: {}", bundle.getBundleId(), bundleSymbolicName, getStateLabel(bundleState));
            }
        }

        String includeMsg = !includesRegex.pattern().equals(".*") ? " for pattern " + includesRegex.pattern(): "";
        String baseMsg = relevantBundlesCount + " bundles" + includeMsg;
        String excludedMsg = countExcluded > 0 ? " (" + countExcluded + " excluded via pattern " + excludesRegex.get().pattern() + ")" : "";
        if (inactiveCount > 0) {
            log.info("Found  " + inactiveCount + " inactive of " + baseMsg + excludedMsg);
        } else {
            log.info("All " + baseMsg + " are started" + excludedMsg);
        }

        return new Result(log);
    }

    private static boolean isLazyActivation(Bundle b) {
        return Constants.ACTIVATION_LAZY.equals(b.getHeaders().get(Constants.BUNDLE_ACTIVATIONPOLICY));
    }

    private static String getStateLabel(int state) {
        switch(state) {
        case Bundle.UNINSTALLED: return "UNINSTALLED";
        case Bundle.INSTALLED: return "INSTALLED";
        case Bundle.RESOLVED: return "RESOLVED";
        case Bundle.STARTING: return "STARTING";
        case Bundle.STOPPING: return "STOPPING";
        case Bundle.ACTIVE: return "ACTIVE";
        default: return Integer.toString(state);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy