org.hyperic.hq.product.jmx.ServiceTypeFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.hyperic.hq.product.jmx.ServiceTypeFactory.java

Source

/**
 * NOTE: This copyright does *not* cover user programs that use HQ
 * program services by normal system calls through the application
 * program interfaces provided as part of the Hyperic Plug-in Development
 * Kit or the Hyperic Client Development Kit - this is merely considered
 * normal use of the program, and does *not* fall under the heading of
 *  "derived work".
 *
 *  Copyright (C) [2009-2010], VMware, Inc.
 *  This file is part of HQ.
 *
 *  HQ is free software; you can redistribute it and/or modify
 *  it under the terms version 2 of the GNU General Public License as
 *  published by the Free Software Foundation. This program is distributed
 *  in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
 *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 *  PARTICULAR PURPOSE. See the GNU General Public License for more
 *  details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 *  USA.
 *
 */

package org.hyperic.hq.product.jmx;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.management.Descriptor;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.modelmbean.ModelMBeanAttributeInfo;
import javax.management.modelmbean.ModelMBeanInfo;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.hq.measurement.MeasurementConstants;
import org.hyperic.hq.product.MeasurementInfo;
import org.hyperic.hq.product.MeasurementInfos;
import org.hyperic.hq.product.Metric;
import org.hyperic.hq.product.ProductPlugin;
import org.hyperic.hq.product.ServerTypeInfo;
import org.hyperic.hq.product.ServiceType;
import org.hyperic.hq.product.ServiceTypeInfo;
import org.hyperic.util.config.ConfigResponse;
import org.hyperic.util.config.ConfigSchema;
import org.hyperic.util.config.StringConfigOption;
import org.hyperic.util.filter.TokenReplacer;

/**
 * Constructs a {@link ServiceType} object from a JMX {@link ModelMBeanInfo}
 *
 * @author jhickey
 *
 */
public class ServiceTypeFactory {

    private static final Set VALID_CATEGORIES = new HashSet(Arrays.asList(MeasurementConstants.VALID_CATEGORIES));

    private static final Set VALID_UNITS = new HashSet(Arrays.asList(MeasurementConstants.VALID_UNITS));

    private Log log = LogFactory.getLog(ServiceTypeFactory.class);

    private void addControlActions(final ServiceType serviceType, ModelMBeanInfo serviceInfo) {
        Set actions = new HashSet();
        final MBeanOperationInfo[] operations = serviceInfo.getOperations();
        for (int i = 0; i < operations.length; i++) {
            actions.add(operations[i].getName());
        }
        serviceType.setControlActions(actions);
    }

    private void addCustomProperties(final ServiceType serviceType, ModelMBeanInfo serviceInfo) {
        final ConfigSchema propertiesSchema = new ConfigSchema();
        final MBeanAttributeInfo[] attributes = serviceInfo.getAttributes();
        for (int i = 0; i < attributes.length; i++) {
            if (!isMetric(attributes[i])) {
                String description = attributes[i].getDescription();
                if (description == null) {
                    description = "";
                }
                propertiesSchema.addOption(new StringConfigOption(attributes[i].getName(), description));
                // custom properties are currently modified by adding a "set"
                // control action
                if (attributes[i].isWritable()) {
                    serviceType.addControlAction("set" + attributes[i].getName());
                }
            }
        }
        serviceType.setCustomProperties(propertiesSchema);
    }

    private void addFilter(String key, Properties properties, TokenReplacer replacer) {
        String val = properties.getProperty(key);
        if (val != null) {
            replacer.addFilter(key, val);
        }
    }

    private void addMeasurements(final ServiceType serviceType, final ProductPlugin productPlugin,
            ModelMBeanInfo serviceInfo) {
        MeasurementInfos measurements = new MeasurementInfos();
        final MBeanAttributeInfo[] attributes = serviceInfo.getAttributes();
        for (int i = 0; i < attributes.length; i++) {
            if (isMetric(attributes[i])) {
                measurements.addMeasurementInfo(
                        createMeasurementInfo(serviceType, productPlugin, (ModelMBeanAttributeInfo) attributes[i]));
            }
        }
        measurements.addMeasurementInfo(createAvailabilityMeasurement(serviceType, productPlugin));
        serviceType.setMeasurements(measurements);
    }

    private void addMeasurementTemplate(Properties measurementProperties, ProductPlugin productPlugin,
            ServiceType serviceType) {
        TokenReplacer replacer = new TokenReplacer();
        final String objectName = serviceType.getProperties()
                .getValue(serviceType.getInfo().getName() + ".OBJECT_NAME");
        addFilter(MeasurementInfo.ATTR_ALIAS, measurementProperties, replacer);
        addFilter(MeasurementInfo.ATTR_NAME, measurementProperties, replacer);
        replacer.addFilter("OBJECT_NAME", objectName);
        final String template = filter(productPlugin.getPluginProperty(MeasurementInfo.ATTR_TEMPLATE),
                productPlugin, replacer);
        measurementProperties.put(MeasurementInfo.ATTR_TEMPLATE, template);
    }

    private void addPlugins(final ServiceType serviceType, final ProductPlugin productPlugin) {
        final ConfigResponse pluginClasses = new ConfigResponse();
        pluginClasses.setValue("measurement", productPlugin.getPluginProperty("measurement-class"));
        pluginClasses.setValue("control", productPlugin.getPluginProperty("control-class"));
        serviceType.setPluginClasses(pluginClasses);
    }

    private void addProperties(final ServiceType serviceType, ObjectName objectName) {
        final ConfigResponse properties = new ConfigResponse();
        properties.setValue(serviceType.getInfo().getName() + ".NAME", serviceType.getServiceName());
        properties.setValue(serviceType.getInfo().getName() + ".OBJECT_NAME", getObjectNameProperty(objectName));
        serviceType.setProperties(properties);
    }

    /**
     * Creates a Set of ServiceTypes from a Set of Services, ignoring multiple
     * services of the same ServiceType (determined by fully qualified service
     * type name)
     *
     * @param productPlugin
     *            The plugin of the product containing this service type
     * @param serverType
     *            The type of service containing this service type
     * @param mServer
     * @param serviceInfo
     *            The unique info of the service type
     * @param objectNames
     *            The {@link ObjectName}s of the associated services whose
     *            metadata is to be inspected
     * @return A Set of created {@link ServiceType}s created
     * @throws InstanceNotFoundException
     * @throws IntrospectionException
     * @throws ReflectionException
     * @throws IOException
     */
    public Set create(ProductPlugin productPlugin, ServerTypeInfo serverType, MBeanServerConnection mServer,
            Set objectNames)
            throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException {
        final Set serviceTypes = new HashSet();
        for (Iterator iterator = objectNames.iterator(); iterator.hasNext();) {
            final ObjectName objectName = (ObjectName) iterator.next();
            final MBeanInfo serviceInfo = mServer.getMBeanInfo(objectName);
            if (serviceInfo instanceof ModelMBeanInfo) {
                ServiceType identityType = getServiceType(productPlugin.getName(), serverType,
                        (ModelMBeanInfo) serviceInfo, objectName);
                if (identityType != null && !serviceTypes.contains(identityType)) {
                    final ServiceType serviceType = create(productPlugin, serverType, (ModelMBeanInfo) serviceInfo,
                            objectName);
                    if (serviceType != null) {
                        serviceTypes.add(serviceType);
                    }
                }
            }
        }
        return serviceTypes;
    }

    /**
     *
     * @param productPlugin
     *            The plugin of the product containing this service type
     * @param serverType
     *            The type of service containing this service type
     * @param serviceInfo
     *            The unique info of the service type
     * @param objectName
     *            The {@link ObjectName} of the associated service whose
     *            metadata is to be inspected
     * @return The created {@link ServiceType} or null if it could not be
     *         created
     */
    public ServiceType create(ProductPlugin productPlugin, ServerTypeInfo serverType, ModelMBeanInfo serviceInfo,
            ObjectName objectName) {
        ServiceType serviceType = getServiceType(productPlugin.getName(), serverType, serviceInfo, objectName);
        if (serviceType == null) {
            return null;
        }
        addControlActions(serviceType, serviceInfo);
        addCustomProperties(serviceType, serviceInfo);
        addProperties(serviceType, objectName);
        addPlugins(serviceType, productPlugin);
        addMeasurements(serviceType, productPlugin, serviceInfo);
        return serviceType;
    }

    private MeasurementInfo createAvailabilityMeasurement(final ServiceType serviceType,
            final ProductPlugin productPlugin) {
        Properties measurementProperties = new Properties();
        measurementProperties.put(MeasurementInfo.ATTR_UNITS, MeasurementConstants.UNITS_PERCENTAGE);
        measurementProperties.put(MeasurementInfo.ATTR_NAME, Metric.ATTR_AVAIL);
        measurementProperties.put(MeasurementInfo.ATTR_ALIAS, Metric.ATTR_AVAIL);
        measurementProperties.put(MeasurementInfo.ATTR_COLLECTION_TYPE, "dynamic");
        measurementProperties.put(MeasurementInfo.ATTR_CATEGORY, MeasurementConstants.CAT_AVAILABILITY);
        measurementProperties.put(MeasurementInfo.ATTR_INDICATOR, "true");

        measurementProperties.put(MeasurementInfo.ATTR_DEFAULTON, "true");
        measurementProperties.put(MeasurementInfo.ATTR_INTERVAL, "600000");
        addMeasurementTemplate(measurementProperties, productPlugin, serviceType);
        return createMeasurementInfo(measurementProperties);
    }

    private MeasurementInfo createMeasurementInfo(Properties measurementProperties) {
        MeasurementInfo metric = new MeasurementInfo();
        try {
            metric.setAttributes(measurementProperties);
        } catch (Exception e) {
            log.warn("Error setting metric attributes.  Cause: " + e.getMessage());
        }
        metric.setCategory(metric.getCategory().toUpperCase());
        return metric;
    }

    private MeasurementInfo createMeasurementInfo(final ServiceType serviceType, final ProductPlugin productPlugin,
            ModelMBeanAttributeInfo attribute) {
        Properties measurementProperties = new Properties();

        Descriptor descriptor = attribute.getDescriptor();
        String units = (String) descriptor.getFieldValue(MeasurementInfo.ATTR_UNITS);
        if ("s".equals(units)) {
            units = MeasurementConstants.UNITS_SECONDS;
        }
        if (!VALID_UNITS.contains(units)) {
            measurementProperties.put(MeasurementInfo.ATTR_UNITS, "none");
        } else {
            measurementProperties.put(MeasurementInfo.ATTR_UNITS, units);
        }
        final String displayName = (String) descriptor.getFieldValue("displayName");
        // Not likely to be null, as JMX impl currently populates it with
        // attribute name if not set
        if (displayName == null) {
            measurementProperties.put(MeasurementInfo.ATTR_NAME, attribute.getName());
        } else {
            measurementProperties.put(MeasurementInfo.ATTR_NAME, displayName);
        }
        measurementProperties.put(MeasurementInfo.ATTR_ALIAS, attribute.getName());
        String metricType = (String) descriptor.getFieldValue("metricType");
        if (metricType == null || !("COUNTER".equals(metricType.toUpperCase()))) {
            //GAUGE
            measurementProperties.put(MeasurementInfo.ATTR_COLLECTION_TYPE, "dynamic");
            measurementProperties.put(MeasurementInfo.ATTR_INTERVAL, "300000");
        } else {
            //COUNTER
            measurementProperties.put(MeasurementInfo.ATTR_COLLECTION_TYPE, "trendsup");
            String rate = (String) descriptor.getFieldValue("rate");
            if (rate != null) {
                measurementProperties.put(MeasurementInfo.ATTR_RATE, rate);
            } else {
                measurementProperties.put(MeasurementInfo.ATTR_RATE, "none");
            }
            measurementProperties.put(MeasurementInfo.ATTR_INTERVAL, "600000");
        }
        String collectionInterval = (String) descriptor.getFieldValue("collectionInterval");
        if (collectionInterval != null) {
            try {
                Long.valueOf(collectionInterval);
                measurementProperties.put(MeasurementInfo.ATTR_INTERVAL, collectionInterval);
            } catch (NumberFormatException e) {
                log.warn("Specified collection interval " + collectionInterval
                        + " is not numeric.  Default value will be used instead.");
            }
        }

        String category = (String) descriptor.getFieldValue("metricCategory");
        if (category == null || !VALID_CATEGORIES.contains(category.toUpperCase())) {
            measurementProperties.put(MeasurementInfo.ATTR_CATEGORY, MeasurementConstants.CAT_UTILIZATION);
        } else {
            measurementProperties.put(MeasurementInfo.ATTR_CATEGORY, category.toUpperCase());
        }
        String indicator = (String) descriptor.getFieldValue("indicator");
        if (indicator == null || "true".equals(indicator.toLowerCase())) {
            //indicator is not in Spring 3.0 @ManagedMetric.  Turn measurement on and make indicator by default
            measurementProperties.put(MeasurementInfo.ATTR_INDICATOR, "true");
            measurementProperties.put(MeasurementInfo.ATTR_DEFAULTON, "true");
        } else {
            measurementProperties.put(MeasurementInfo.ATTR_INDICATOR, "false");
            measurementProperties.put(MeasurementInfo.ATTR_DEFAULTON, "false");
        }
        String defaultOn = (String) descriptor.getFieldValue("defaultOn");
        if (defaultOn != null) {
            if ("true".equals(defaultOn.toLowerCase()) || "false".equals(defaultOn.toLowerCase())) {
                measurementProperties.put(MeasurementInfo.ATTR_DEFAULTON, defaultOn.toLowerCase());
            } else {
                log.warn("Invalid value of " + defaultOn
                        + " specified for defaultOn.  Default value will be used instead.");
            }
        }
        addMeasurementTemplate(measurementProperties, productPlugin, serviceType);
        return createMeasurementInfo(measurementProperties);
    }

    private String filter(String val, ProductPlugin productPlugin, TokenReplacer replacer) {
        return replacer.replaceTokens(val);
    }

    private String getObjectNameProperty(ObjectName objectName) {
        final StringBuffer objectNameProperty = new StringBuffer(objectName.getDomain()).append(':');
        Hashtable keyProperties = objectName.getKeyPropertyList();
        for (Iterator iterator = keyProperties.entrySet().iterator(); iterator.hasNext();) {
            Map.Entry keyProperty = (Map.Entry) iterator.next();
            objectNameProperty.append(keyProperty.getKey()).append('=');
            // for now, recognize only type and subtype - replace all others
            // with variable placeholders
            if ("type".equals(keyProperty.getKey()) || "subtype".equals(keyProperty.getKey())) {
                objectNameProperty.append(keyProperty.getValue());
            } else {
                objectNameProperty.append('%').append(keyProperty.getKey()).append('%');
            }
            objectNameProperty.append(',');
        }
        objectNameProperty.deleteCharAt(objectNameProperty.length() - 1);
        return objectNameProperty.toString();
    }

    /**
     * Returns a ServiceType containing ONLY the properties needed at construction time (the ones that guarantee uniqueness)
     * @param productName The name of the product containing the service
     * @param serverType The name of the server containing the service
     * @param serviceInfo Info about the service
     * @param objectName The {@link ObjectName} of the discovered MBean representing the service instance
     * @return A ServiceType containing ONLY the properties needed at construction time (the ones that guarantee uniqueness)
     */
    public ServiceType getServiceType(String productName, ServerTypeInfo serverType, ModelMBeanInfo serviceInfo,
            ObjectName objectName) {
        String serviceTypeName = objectName.getKeyProperty("type");
        final String subType = objectName.getKeyProperty("subtype");
        if (subType != null) {
            serviceTypeName = serviceTypeName + " " + subType;
        }
        try {
            Descriptor serviceDescriptor = serviceInfo.getMBeanDescriptor();
            if ("false".equals(serviceDescriptor.getFieldValue("export"))) {
                return null;
            }
            String serviceType = (String) serviceDescriptor.getFieldValue("typeName");
            if (serviceType != null) {
                serviceTypeName = serviceType;
            }
        } catch (Exception e) {
            log.warn("Error obtaining MBeanInfo descriptor field values.  Default values will be used.  Cause: "
                    + e.getMessage());
        }
        return new ServiceType(serviceTypeName, productName, new ServiceTypeInfo(
                serverType.getName() + ' ' + serviceTypeName, serviceInfo.getDescription(), serverType));
    }

    private boolean isMetric(MBeanAttributeInfo attribute) {
        if (attribute instanceof ModelMBeanAttributeInfo) {
            String attributeType = (String) ((ModelMBeanAttributeInfo) attribute).getDescriptor()
                    .getFieldValue("attributeType");
            //attributeType not in Spring 3.0 @ManagedMetric.  If it's there from instrumented 2.5.6, process it
            if (attributeType != null) {
                if ("Metric".equals(attributeType)) {
                    return true;
                } else {
                    return false;
                }
            }
            String metricType = (String) ((ModelMBeanAttributeInfo) attribute).getDescriptor()
                    .getFieldValue("metricType");
            //this field is required for metrics - assume it's not a metric if not present
            if (metricType != null) {
                return true;
            }

        }
        return false;
    }
}