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

org.jmxtrans.agent.Query Maven / Gradle / Ivy

There is a newer version: 1.2.11
Show newest version
/*
 * Copyright (c) 2010-2013 the original author or authors
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
package org.jmxtrans.agent;

import org.jmxtrans.agent.util.Preconditions2;
import org.jmxtrans.agent.util.collect.Iterables2;
import org.jmxtrans.agent.util.logging.Logger;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.management.*;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeType;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.*;
import java.util.logging.Level;

/**
 * @author Cyrille Le Clerc
 */
public class Query implements Collector {

    private final Logger logger = Logger.getLogger(getClass().getName());

    @Nonnull
    protected ResultNameStrategy resultNameStrategy;

    @Nonnull
    protected final ObjectName objectName;

    /**
     * The attribute(s) to retrieve ({@link MBeanServer#getAttribute(javax.management.ObjectName, String)})
     * 
     * If empty, will fetch all attributes of the MBean.
     */
    @Nonnull
    protected List attributes;
    @Nullable
    protected final String resultAlias;
    /**
     * If the MBean attribute value is a {@link CompositeData}, the key to lookup.
     *
     * @see CompositeData#get(String)
     */
    @Nullable
    protected final String key;
    /**
     * If the returned value is a {@link java.util.Collection}or an array, the position of the entry to lookup.
     */
    @Nullable
    protected final Integer position;


    /**
     * Attribute type like '{@code gauge}' or '{@code counter}'. Used by monitoring systems like Librato who require this information.
     */
    @Nullable
    private String type;

    @Nullable
    private Integer collectInterval;

    /**
     * @see #Query(String, String, String, Integer, String, String, ResultNameStrategy)
     */
    public Query(@Nonnull String objectName, @Nullable String attribute, @Nonnull ResultNameStrategy resultNameStrategy) {
        this(objectName, attribute, null, null, null, null, resultNameStrategy, null);
    }

    /**
     * @see #Query(String, String, String, Integer, String, String, ResultNameStrategy)
     */
    public Query(@Nonnull String objectName, @Nullable String attribute, int position, @Nonnull ResultNameStrategy resultNameStrategy) {
        this(objectName, attribute, null, position, null, null, resultNameStrategy, null);
    }

    /**
     * @see #Query(String, String, String, Integer, String, String, ResultNameStrategy)
     */
    public Query(@Nonnull String objectName, @Nullable String attribute, @Nullable String resultAlias, @Nonnull ResultNameStrategy resultNameStrategy) {
        this(objectName, attribute, null, null, null, resultAlias, resultNameStrategy, null);
    }

    /**
     * Creates a Query with no collectInterval overide.
     */
    public Query(@Nonnull String objectName, @Nullable String attribute, @Nullable String key, @Nullable Integer position,
                 @Nullable String type, @Nullable String resultAlias, @Nonnull ResultNameStrategy resultNameStrategy) {
        this(objectName, attribute, key, position, type, resultAlias, resultNameStrategy, null);
    }


    /**
     * @param objectName         The {@link ObjectName} to search for
     *                           ({@link MBeanServer#queryMBeans(javax.management.ObjectName, javax.management.QueryExp)}),
     *                           can contain wildcards and return several entries.
     * @param attribute          The attribute to retrieve ({@link MBeanServer#getAttribute(javax.management.ObjectName, String)})
     * @param key                if the MBean attribute value is a {@link CompositeData}, the key to lookup.
     * @param position           if the returned value is a {@link java.util.Collection} or an array, the position of the entry to lookup.
     * @param type               type of the metric ('counter', 'gauge', ...)
     * @param resultAlias
     * @param resultNameStrategy the {@link org.jmxtrans.agent.ResultNameStrategy} used during the
     *                           {@link #collectAndExport(javax.management.MBeanServer, OutputWriter)} phase.
     */
    public Query(@Nonnull String objectName, @Nullable String attribute, @Nullable String key, @Nullable Integer position,
                 @Nullable String type, @Nullable String resultAlias, @Nonnull ResultNameStrategy resultNameStrategy, @Nullable Integer collectInterval) {
        this(objectName, nullOrEmtpy(attribute) ? Collections.emptyList() : Collections.singletonList(attribute), key, position,
                type, resultAlias, resultNameStrategy, collectInterval);
    }

    private static boolean nullOrEmtpy(String attribute) {
        return attribute == null || attribute.isEmpty();
    }
    
    /**
     * Creates a query that accepts a list of attributes to collect. If the list is empty, all attributes will be collected.
     * @param collectInterval 
     * 
     * @see #Query(String, String, String, Integer, String, String, ResultNameStrategy)
     */
    public Query(@Nonnull String objectName, @Nonnull List attributes, @Nullable String key, @Nullable Integer position,
            @Nullable String type, @Nullable String resultAlias, @Nonnull ResultNameStrategy resultNameStrategy, @Nullable Integer collectInterval) {
        try {
            this.objectName = new ObjectName(Preconditions2.checkNotNull(objectName));
        } catch (MalformedObjectNameException e) {
            throw new IllegalArgumentException("Invalid objectName '" + objectName + "'", e);
        }
        this.attributes = Collections.unmodifiableList(new ArrayList(Preconditions2.checkNotNull(attributes, "attributes")));
        this.key = key;
        this.resultAlias = resultAlias;
        this.position = position;
        this.type = type;
        this.resultNameStrategy = Preconditions2.checkNotNull(resultNameStrategy, "resultNameStrategy");
        this.collectInterval = collectInterval;
    }


    public void collectAndExport(@Nonnull MBeanServer mbeanServer, @Nonnull OutputWriter outputWriter) {
        if (resultNameStrategy == null)
            throw new IllegalStateException("resultNameStrategy is not defined, query object is not properly initialized");

        Set objectNames = mbeanServer.queryNames(objectName, null);

        for (ObjectName on : objectNames) {
            collectAndExportForObjectName(mbeanServer, outputWriter, on);
        }
    }

    private void collectAndExportForObjectName(MBeanServer mbeanServer, OutputWriter outputWriter, ObjectName on) {
        for (String attribute : resolveAttributes(mbeanServer, on)) {
            collectAndExportAttribute(mbeanServer, outputWriter, on, attribute);
        }
    }


    private List resolveAttributes(MBeanServer mbeanServer, ObjectName on) {
        if (attributes.isEmpty()) {
            return findAllAttributes(mbeanServer, on);
        }
        return attributes;
    }

    private List findAllAttributes(MBeanServer mbeanServer, ObjectName on) {
        List resolvedAttributes = new ArrayList<>();
        // Null or empty attribute specified, collect all attributes
        try {
            for (MBeanAttributeInfo mBeanAttributeInfo : mbeanServer.getMBeanInfo(on).getAttributes()) {
                resolvedAttributes.add(mBeanAttributeInfo.getName());
            }
        } catch (IntrospectionException | InstanceNotFoundException | ReflectionException e) {
            logger.log(Level.WARNING, "Error when finding attributes for ObjectName " + on + ", all attributes will not be collected", e);
        }
        return resolvedAttributes;
    }

    private void collectAndExportAttribute(MBeanServer mbeanServer, OutputWriter outputWriter, ObjectName objectName, String attribute) {
        try {
            Object attributeValue = null;
            try {
                attributeValue = mbeanServer.getAttribute(objectName, attribute);
            } catch (Exception ex) {
                logger.warning("Failed to fetch attribute for '" + objectName + "'#" + attribute + ", exception: " + ex.getMessage());
                return;
            }

            Object value;
            if (attributeValue instanceof CompositeData) {
                CompositeData compositeData = (CompositeData) attributeValue;
                if (key == null) {
                    // Get for all keys
                    CompositeType compositeType = compositeData.getCompositeType();
                    for (String key : compositeType.keySet()) {
                        value = compositeData.get(key);
                        processAttributeValue(outputWriter, objectName, attribute, key, value);
                    }
                    return;
                } else {
                    value = compositeData.get(key);
                }
            } else {
                if (key == null) {
                    value = attributeValue;
                } else {
                    logger.warning("Ignore NON compositeData for specified key for '" + objectName +
                            "'#" + attribute + "#" + key + ": " + attributeValue);
                    return;
                }
            }
            if (value != null && value.getClass().isArray()) {
                List valueAsList = new ArrayList();
                for (int i = 0; i < Array.getLength(value); i++) {
                    valueAsList.add(Array.get(value, i));
                }
                value = valueAsList;
            }

            processAttributeValue(outputWriter, objectName, attribute, key, value);
        } catch (Exception e) {
            logger.log(Level.WARNING, "Exception collecting " + objectName + "#" + attribute + (key == null ? "" : "#" + key), e);
        }
    }

    /**
     *
     * @param outputWriter
     * @param objectName
     * @param attribute attribute of the MBean
     * @param compositeDataKey if the MBean value is a {@link CompositeData}, the name of the key (see {@link CompositeData#get(String)})
     * @param value value
     * @throws IOException
     */
    private void processAttributeValue(@Nonnull OutputWriter outputWriter, @Nonnull ObjectName objectName, @Nonnull String attribute,
                                       @Nullable String compositeDataKey, Object value) throws IOException {

        if (value instanceof Iterable) {
            Iterable valueAsIterable = (Iterable) value;
            if (position == null) {
                // get for all entries
                int idx = 0;
                for (Object subValue : valueAsIterable) {
                    String resultName = resultNameStrategy.getResultName(this, objectName, attribute, compositeDataKey, idx);
                    outputWriter.writeQueryResult(resultName, type, subValue);
                    idx++;
                }
            } else {
                String resultName = resultNameStrategy.getResultName(this, objectName, attribute, compositeDataKey, position);
                value = Iterables2.get((Iterable) value, position);
                outputWriter.writeQueryResult(resultName, type, value);
            }
        } else {
            String resultName = resultNameStrategy.getResultName(this, objectName, attribute, compositeDataKey, null);
            outputWriter.writeQueryResult(resultName, type, value);
        }
    }

    @Override
    public String toString() {
        return "Query{" +
                "objectName=" + objectName +
                ", resultAlias='" + resultAlias + '\'' +
                ", attributes='" + attributes + '\'' +
                ", key='" + key + '\'' +
                '}';
    }

    @Nonnull
    public ObjectName getObjectName() {
        return objectName;
    }

    @Nullable
    public String getResultAlias() {
        return resultAlias;
    }

    @Nullable
    public List getAttributes() {
        return attributes;
    }

    @Nullable
    public String getKey() {
        return key;
    }

    @Nullable
    public Integer getPosition() {
        return position;
    }

    @Nullable
    public String getType() {
        return type;
    }

    @Nullable
    public Integer getCollectIntervalOverrideOrNull() {
        return collectInterval;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy