org.jmxtrans.agent.Query Maven / Gradle / Ivy
/*
* 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