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

org.apache.felix.hc.generalchecks.JmxAttributeCheck 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.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.management.MBeanServer;
import javax.management.ObjectName;

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.api.ResultLog;
import org.apache.felix.hc.core.impl.util.lang.StringUtils;
import org.apache.felix.hc.generalchecks.util.SimpleConstraintChecker;
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;

/** {@link HealthCheck} that checks one (or multiple) JMX attribute(s). */
@Component(service = HealthCheck.class, configurationPolicy = ConfigurationPolicy.REQUIRE, immediate = true)
@Designate(ocd = JmxAttributeCheck.Config.class, factory = true)
public class JmxAttributeCheck implements HealthCheck {

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

    public static final String HC_NAME = "JMX Attribute";
    public static final String HC_LABEL = "Health Check: " + HC_NAME;

    private Result.Status statusForFailedContraint;

    @ObjectClassDefinition(name = HC_LABEL, description = "Checks the value of a single JMX attribute.")
    @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 = "MBean Name", description = "The name of the MBean to retrieve the attribute to be checked from.")
        String mbean_name() default "";

        @AttributeDefinition(name = "Attribute Name", description = "The name of the MBean attribute to check against the constraint.")
        String attribute_name() default "";

        @AttributeDefinition(name = "Attribute Constraint", description = "Constraint on the MBean attribute value. If simple value, uses equals. For strings constraints like 'CONTAINS mystr', 'STARTS_WITH mystr' or 'ENDS_WITH mystr' can be used, for numbers constraints like '> 4', '= 7', '< 9' or 'between 3 and 7' work.")
        String attribute_value_constraint() default "";

        @AttributeDefinition(name = "Status for failed constraint", description = "Status to fail with if the constraint check fails")
        Result.Status statusForFailedContraint() default Result.Status.WARN;

        @AttributeDefinition
        String webconsole_configurationFactory_nameHint() default "JMX MBean {mbean.name} Attribute '{attribute.name}' constraint: {attribute.value.constraint}";
    }

    private List attributeConstraintConfigs;

    @Activate
    protected void activate(final Config config, final Map rawConfig) {
        statusForFailedContraint = config.statusForFailedContraint();
        attributeConstraintConfigs = AttributeConstraintConfig.load(config, rawConfig);

        if(LOG.isDebugEnabled()) {
            LOG.debug("Activated JMX Attribute HC with statusForFailedContraint={} and attribute constraint config(s):", statusForFailedContraint);
            for (AttributeConstraintConfig attributeConstraintConfig : attributeConstraintConfigs) {
                LOG.debug(attributeConstraintConfig.toString());
            }
        }
    }


    @Override
    public Result execute() {
        FormattingResultLog resultLog = new FormattingResultLog();
        for (AttributeConstraintConfig attributeConstraintConfig : attributeConstraintConfigs) {
            checkAttributeConstraint(resultLog, attributeConstraintConfig);
        }
        return new Result(resultLog);
    }

    private void checkAttributeConstraint(final FormattingResultLog resultLog, AttributeConstraintConfig attributeConstraintConfig) {
        resultLog.debug("Checking {}", attributeConstraintConfig);
        try {
            final MBeanServer jmxServer = ManagementFactory.getPlatformMBeanServer();
            final ObjectName objectName = new ObjectName(attributeConstraintConfig.mbeanName);
            if (jmxServer.queryNames(objectName, null).size() == 0) {
                resultLog.warn("MBean not found: {}", objectName);
            } else {
                final Object value = jmxServer.getAttribute(objectName, attributeConstraintConfig.attributeName);
                resultLog.debug("{} {} returns {}", attributeConstraintConfig.mbeanName, attributeConstraintConfig.attributeName, value);
                boolean matches = new SimpleConstraintChecker().check(value, attributeConstraintConfig.attributeValueConstraint);
                String baseMsg = "JMX attribute "+attributeConstraintConfig.mbeanName+" -> '"+attributeConstraintConfig.attributeName+"': Value [" + value + "] ";
                if (matches) {
                    resultLog.add(new ResultLog.Entry(Result.Status.OK, baseMsg+"matches constraint [" + attributeConstraintConfig.attributeValueConstraint + "]"));
                } else {
                    resultLog.add(new ResultLog.Entry( statusForFailedContraint, baseMsg+"does not match constraint [" + attributeConstraintConfig.attributeValueConstraint + "]"));
                }
            }
        } catch (Exception e) {
            LOG.warn("JMX check {} failed: {}", attributeConstraintConfig, e.getMessage(), e);
            resultLog.healthCheckError("JMX attribute check failed: {}", attributeConstraintConfig, e);
        }
    }


    private static class AttributeConstraintConfig {

        public static final String PROP_MBEAN = "mbean";
        public static final String PROP_ATTRIBUTE = "attribute";

        public static final String SUFFIX_NAME = ".name";
        public static final String SUFFIX_VALUE_CONSTRAINT = ".value.constraint";

        private static List load(final Config config, final Map rawConfig) {
            List attributeConstraintConfigs = new ArrayList();

            // first attribute via metatype
            attributeConstraintConfigs.add(new AttributeConstraintConfig(config.mbean_name(), config.attribute_name(),config.attribute_value_constraint()));

            // additional attributes possible via naming scheme "mbean2.name" / "attribute2.name" ...
            int attributeCounter = 2;
            while(AttributeConstraintConfig.hasConfig(rawConfig, attributeCounter)) {
                attributeConstraintConfigs.add(new AttributeConstraintConfig(rawConfig, attributeCounter));
                attributeCounter++;
            }
            return attributeConstraintConfigs;
        }

        private static String getAttributePropName(int attributeCounter) {
            return PROP_ATTRIBUTE + attributeCounter + SUFFIX_NAME;
        }

        private static boolean hasConfig(Map rawConfig, int attributeCounter) {
            return rawConfig.containsKey(getAttributePropName(attributeCounter));
        }

        final String mbeanName;

        final String attributeName;
        final String attributeValueConstraint;

        public AttributeConstraintConfig(String mbeanName, String attributeName, String attributeValueConstraint) {
            this.mbeanName = mbeanName;
            this.attributeName = attributeName;
            this.attributeValueConstraint = attributeValueConstraint;
        }

        public AttributeConstraintConfig(Map rawConfig, int attributeCounter) {
            String propNameAttribute = getAttributePropName(attributeCounter);
            String defaultMBeanName = (String) rawConfig.get(PROP_MBEAN + SUFFIX_NAME);
            String mBeanName = (String) rawConfig.get(PROP_MBEAN + attributeCounter + SUFFIX_NAME);
            this.mbeanName = StringUtils.defaultIfBlank(mBeanName, defaultMBeanName);
            this.attributeName = (String) rawConfig.get(propNameAttribute);
            this.attributeValueConstraint = (String) rawConfig.get(PROP_ATTRIBUTE + attributeCounter + SUFFIX_VALUE_CONSTRAINT);
            if(StringUtils.isBlank(mbeanName) || StringUtils.isBlank(attributeName) || StringUtils.isBlank(attributeValueConstraint)) {
                throw new IllegalArgumentException("Invalid JmxAttributeCheck config for property "+mbeanName+" -> "+propNameAttribute+": "+toString());
            }
        }

        @Override
        public String toString() {
            return "JMX attribute "+mbeanName+" -> '"+attributeName+"': Constraint: "+attributeValueConstraint;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy