com.googlecode.jmxtrans.model.Query Maven / Gradle / Ivy
Show all versions of jmxtrans-core Show documentation
/**
* The MIT License
* Copyright © 2010 JmxTrans team
*
* 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 com.googlecode.jmxtrans.model;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.googlecode.jmxtrans.model.naming.typename.PrependingTypeNameValuesStringBuilder;
import com.googlecode.jmxtrans.model.naming.typename.TypeNameValuesStringBuilder;
import com.googlecode.jmxtrans.model.naming.typename.UseAllTypeNameValuesStringBuilder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.NotThreadSafe;
import javax.annotation.concurrent.ThreadSafe;
import javax.management.AttributeList;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import java.io.IOException;
import java.rmi.UnmarshalException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import static com.fasterxml.jackson.databind.annotation.JsonSerialize.Inclusion.NON_NULL;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.collect.ImmutableList.copyOf;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.newLinkedHashSet;
import static java.util.Arrays.asList;
/**
* Represents a JMX Query to ask for obj, attr and one or more keys.
*
* @author jon
*/
@JsonSerialize(include = NON_NULL)
@JsonPropertyOrder(value = {"obj", "attr", "typeNames", "resultAlias", "keys", "allowDottedKeys", "useAllTypeNames", "outputWriters"})
@ThreadSafe
@EqualsAndHashCode(exclude = {"outputWriters", "outputWriterInstances"})
@ToString(exclude = {"outputWriters", "typeNameValuesStringBuilder"})
public class Query {
private static final Logger logger = LoggerFactory.getLogger(Query.class);
/** The JMX object representation: java.lang:type=Memory */
@Nonnull @Getter private final ObjectName objectName;
@Nonnull @Getter private final ImmutableList keys;
@Nonnull @Getter private final ImmutableList attr;
/**
* The list of type names used in a JMX bean string when querying with a
* wildcard which is used to expose the actual type name value to the key
* string. e.g. for this JMX name
*
* typeName=name=PS Eden Space,type=MemoryPool
*
* If you add a typeName("name"), then it'll retrieve 'PS Eden Space' from
* the string.
*
* The order of the elements of this set matches the order provided by the
* user.
*/
@Getter private final ImmutableSet typeNames;
/**
* The alias allows you to specify what you would like the results of the
* query to go into.
*/
@Getter private final String resultAlias;
/**
* The useObjDomainAsKey property allows you to specify the use of the Domain portion of the Object Name
* as part of the output key instead of using the ClassName of the MBean which is the default behavior.
*/
@Getter private final boolean useObjDomainAsKey;
@Getter private final boolean allowDottedKeys;
@Getter private final boolean useAllTypeNames;
@Nonnull @Getter private final ImmutableList outputWriters;
@Nonnull @Getter private final Iterable outputWriterInstances;
private final TypeNameValuesStringBuilder typeNameValuesStringBuilder;
@JsonCreator
public Query(
@JsonProperty("obj") String obj,
@JsonProperty("keys") List keys,
@JsonProperty("attr") List attr,
@JsonProperty("typeNames") List typeNames,
@JsonProperty("resultAlias") String resultAlias,
@JsonProperty("useObjDomainAsKey") boolean useObjDomainAsKey,
@JsonProperty("allowDottedKeys") boolean allowDottedKeys,
@JsonProperty("useAllTypeNames") boolean useAllTypeNames,
@JsonProperty("outputWriters") List outputWriters
) {
// For typeName, note the using copyOf does not change the order of
// the elements.
this(obj, keys, attr, ImmutableSet.copyOf(firstNonNull(typeNames, Collections.emptySet())), resultAlias, useObjDomainAsKey, allowDottedKeys, useAllTypeNames,
outputWriters, ImmutableList.of());
}
public Query(
String obj,
List keys,
List attr,
Set typeNames,
String resultAlias,
boolean useObjDomainAsKey,
boolean allowDottedKeys,
boolean useAllTypeNames,
List outputWriters
) {
this(obj, keys, attr, typeNames, resultAlias, useObjDomainAsKey, allowDottedKeys, useAllTypeNames,
outputWriters, ImmutableList.of());
}
public Query(
String obj,
List keys,
List attr,
Set typeNames,
String resultAlias,
boolean useObjDomainAsKey,
boolean allowDottedKeys,
boolean useAllTypeNames,
ImmutableList outputWriters
) {
this(obj, keys, attr, typeNames, resultAlias, useObjDomainAsKey, allowDottedKeys, useAllTypeNames,
ImmutableList.of(), outputWriters);
}
private Query(
String obj,
List keys,
List attr,
Set typeNames,
String resultAlias,
boolean useObjDomainAsKey,
boolean allowDottedKeys,
boolean useAllTypeNames,
List outputWriterFactories,
List outputWriters
) {
try {
this.objectName = new ObjectName(obj);
} catch (MalformedObjectNameException e) {
throw new IllegalArgumentException("Invalid object name: " + obj, e);
}
this.attr = copyOf(firstNonNull(attr, Collections.emptyList()));
this.resultAlias = resultAlias;
this.useObjDomainAsKey = firstNonNull(useObjDomainAsKey, false);
this.keys = copyOf(firstNonNull(keys, Collections.emptyList()));
this.allowDottedKeys = allowDottedKeys;
this.useAllTypeNames = useAllTypeNames;
this.outputWriters = copyOf(firstNonNull(outputWriterFactories, ImmutableList.of()));
// We need to preserve the order of typeNames. So note that copyOf
// does not mess with the order.
this.typeNames = ImmutableSet.copyOf(firstNonNull(typeNames, Collections.emptySet()));
this.typeNameValuesStringBuilder = makeTypeNameValuesStringBuilder();
this.outputWriterInstances = copyOf(firstNonNull(outputWriters, ImmutableList.of()));
}
public String makeTypeNameValueString(List typeNames, String typeNameStr) {
return this.typeNameValuesStringBuilder.build(typeNames, typeNameStr);
}
public Iterable queryNames(MBeanServerConnection mbeanServer) throws IOException {
return mbeanServer.queryNames(objectName, null);
}
public Iterable fetchResults(MBeanServerConnection mbeanServer, ObjectName queryName) throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException {
ObjectInstance oi = mbeanServer.getObjectInstance(queryName);
List attributes;
if (attr.isEmpty()) {
attributes = new ArrayList<>();
MBeanInfo info = mbeanServer.getMBeanInfo(queryName);
for (MBeanAttributeInfo attrInfo : info.getAttributes()) {
attributes.add(attrInfo.getName());
}
} else {
attributes = attr;
}
try {
if (!attributes.isEmpty()) {
logger.debug("Executing queryName [{}] from query [{}]", queryName.getCanonicalName(), this);
AttributeList al = mbeanServer.getAttributes(queryName, attributes.toArray(new String[attributes.size()]));
return new JmxResultProcessor(this, oi, al.asList(), oi.getClassName(), queryName.getDomain()).getResults();
}
} catch (UnmarshalException ue) {
if ((ue.getCause() != null) && (ue.getCause() instanceof ClassNotFoundException)) {
logger.debug("Bad unmarshall, continuing. This is probably ok and due to something like this: "
+ "http://ehcache.org/xref/net/sf/ehcache/distribution/RMICacheManagerPeerListener.html#52", ue.getMessage());
} else {
throw ue;
}
}
return ImmutableList.of();
}
private TypeNameValuesStringBuilder makeTypeNameValuesStringBuilder() {
String separator = isAllowDottedKeys() ? "." : TypeNameValuesStringBuilder.DEFAULT_SEPARATOR;
Set typeNames = getTypeNames();
if (isUseAllTypeNames()) {
return new UseAllTypeNameValuesStringBuilder(separator);
} else if (typeNames != null && !typeNames.isEmpty()) {
return new PrependingTypeNameValuesStringBuilder(separator, new ArrayList<>(typeNames));
} else {
return new TypeNameValuesStringBuilder(separator);
}
}
public static Builder builder() {
return new Builder();
}
public static Builder builder(Query query) {
return new Builder(query);
}
public void runOutputWritersForQuery(Server server, Iterable results) throws Exception {
for (OutputWriter writer : getOutputWriterInstances()) {
writer.doWrite(server, this, results);
}
logger.debug("Finished running outputWriters for query: {}", this);
}
@NotThreadSafe
@Accessors(chain = true)
public static final class Builder {
@Setter private String obj;
private final List attr = newArrayList();
@Setter private String resultAlias;
private final List keys = newArrayList();
@Setter private boolean useObjDomainAsKey;
@Setter private boolean allowDottedKeys;
@Setter private boolean useAllTypeNames;
private final List outputWriterFactories = newArrayList();
private final List outputWriters = newArrayList();
// We need to pick an order preserving Set implementation here to
// avoid unpredictable ordering of typeNames.
private final Set typeNames = newLinkedHashSet();
private Builder() {}
/** This builder does NOT copy output writers from the given query. */
private Builder(Query query) {
this.obj = query.objectName.toString();
this.attr.addAll(query.attr);
this.resultAlias = query.resultAlias;
this.keys.addAll(query.keys);
this.useObjDomainAsKey = query.useObjDomainAsKey;
this.allowDottedKeys = query.allowDottedKeys;
this.useAllTypeNames = query.useAllTypeNames;
this.typeNames.addAll(query.typeNames);
}
public Builder addAttr(String... attr) {
this.attr.addAll(asList(attr));
return this;
}
public Builder addKey(String keys) {
return addKeys(keys);
}
public Builder addKeys(String... keys) {
this.keys.addAll(asList(keys));
return this;
}
public Builder addOutputWriterFactory(OutputWriterFactory outputWriterFactory) {
return addOutputWriterFactories(outputWriterFactory);
}
public Builder addOutputWriterFactories(OutputWriterFactory... outputWriterFactories) {
this.outputWriterFactories.addAll(asList(outputWriterFactories));
return this;
}
public Builder addOutputWriters(Collection outputWriters) {
this.outputWriters.addAll(outputWriters);
return this;
}
public Builder setTypeNames(Collection typeNames) {
this.typeNames.addAll(typeNames);
return this;
}
public Query build() {
if (!outputWriterFactories.isEmpty()) {
return new Query(
this.obj,
this.keys,
this.attr,
this.typeNames,
this.resultAlias,
this.useObjDomainAsKey,
this.allowDottedKeys,
this.useAllTypeNames,
this.outputWriterFactories
);
}
return new Query(
this.obj,
this.keys,
this.attr,
this.typeNames,
this.resultAlias,
this.useObjDomainAsKey,
this.allowDottedKeys,
this.useAllTypeNames,
copyOf(this.outputWriters)
);
}
}
}