Skip to content

Commit

Permalink
Allow support for null attribute or key in a query
Browse files Browse the repository at this point in the history
References jmxtrans#29
Fixes jmxtrans#34
  • Loading branch information
maheshkelkar committed May 26, 2015
1 parent b41458c commit 2fc4063
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 60 deletions.
126 changes: 79 additions & 47 deletions src/main/java/org/jmxtrans/agent/Query.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
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.ArrayList;
import java.util.List;
Expand All @@ -51,12 +51,13 @@ public class Query {

@Nonnull
protected final ObjectName objectName;

@Nullable
protected final String resultAlias;
/**
* The attribute to retrieve ({@link MBeanServer#getAttribute(javax.management.ObjectName, String)})
*/
@Nonnull
@Nullable
protected final String attribute;
/**
* If the MBean attribute value is a {@link CompositeData}, the key to lookup.
Expand All @@ -77,21 +78,21 @@ public class Query {
/**
* @see #Query(String, String, String, Integer, String, String, ResultNameStrategy)
*/
public Query(@Nonnull String objectName, @Nonnull String attribute, @Nonnull ResultNameStrategy resultNameStrategy) {
public Query(@Nonnull String objectName, @Nullable String attribute, @Nonnull ResultNameStrategy resultNameStrategy) {
this(objectName, attribute, null, null, null, attribute, resultNameStrategy);
}

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

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

Expand All @@ -107,14 +108,14 @@ public Query(@Nonnull String objectName, @Nonnull String attribute, @Nullable St
* @param resultNameStrategy the {@link org.jmxtrans.agent.ResultNameStrategy} used during the
* {@link #collectAndExport(javax.management.MBeanServer, OutputWriter)} phase.
*/
public Query(@Nonnull String objectName, @Nonnull String attribute, @Nullable String key, @Nullable Integer position,
public Query(@Nonnull String objectName, @Nullable String attribute, @Nullable String key, @Nullable Integer position,
@Nullable String type, @Nullable String resultAlias, @Nonnull ResultNameStrategy resultNameStrategy) {
try {
this.objectName = new ObjectName(Preconditions2.checkNotNull(objectName));
} catch (MalformedObjectNameException e) {
throw new IllegalArgumentException("Invalid objectName '" + objectName + "'", e);
}
this.attribute = Preconditions2.checkNotNull(attribute);
this.attribute = attribute;
this.key = key;
this.resultAlias = resultAlias;
this.position = position;
Expand All @@ -129,53 +130,84 @@ public void collectAndExport(@Nonnull MBeanServer mbeanServer, @Nonnull OutputWr
Set<ObjectName> objectNames = mbeanServer.queryNames(objectName, null);

for (ObjectName on : objectNames) {
try {
if (attribute == null || attribute.isEmpty()) {
for (MBeanAttributeInfo mBeanAttributeInfo : mbeanServer.getMBeanInfo(on).getAttributes()) {
collectAndExportAttribute(mbeanServer, outputWriter, on, mBeanAttributeInfo.getName());
}

} else {
collectAndExportAttribute(mbeanServer, outputWriter, on, attribute);
}
} catch (Exception e) {
logger.log(Level.WARNING, "Exception collecting " + on + "#" + attribute + (key == null ? "" : "#" + key), e);
}
}
}

private void collectAndExportAttribute(MBeanServer mbeanServer, OutputWriter outputWriter, ObjectName objectName, String attribute) {
try {
Object attributeValue = null;
try {
Object attributeValue = mbeanServer.getAttribute(on, attribute);

Object value;
if (attributeValue instanceof CompositeData) {
CompositeData compositeData = (CompositeData) attributeValue;
if (key == null) {
logger.warning("Ignore compositeData without key specified for '" + on + "'#" + attribute + ": " + attributeValue);
continue;
} else {
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, value, key);
}
return;
} else {
if (key == null) {
value = attributeValue;
} else {
logger.warning("Ignore NON compositeData for specified key for '" + on + "'#" + attribute + "#" + key + ": " + attributeValue);
continue;
}
value = compositeData.get(key);
}
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;
} 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;
}

String resultName = resultNameStrategy.getResultName(this, on, key);
if (value instanceof Iterable) {
Iterable iterable = (Iterable) value;
if (position == null) {
int idx = 0;
for (Object entry : iterable) {
outputWriter.writeQueryResult(resultName + "_" + idx++, type, entry);
}
} else {
value = Iterables2.get((Iterable) value, position);
outputWriter.writeQueryResult(resultName, type, value);
}
} else {
outputWriter.writeQueryResult(resultName, type, value);
processAttributeValue(outputWriter, objectName, attribute, value, key);
} catch (Exception e) {
logger.log(Level.WARNING, "Exception collecting " + objectName + "#" + attribute + (key == null ? "" : "#" + key), e);
}
}

private void processAttributeValue(OutputWriter outputWriter, ObjectName objectName, String attribute,
Object value, String key) throws IOException {
String resultName = resultNameStrategy.getResultName(this, objectName, key, attribute);
if (value instanceof Iterable) {
Iterable iterable = (Iterable) value;
if (position == null) {
int idx = 0;
for (Object entry : iterable) {
outputWriter.writeQueryResult(resultName + "_" + idx++, type, entry);
}
} catch (Exception e) {
logger.log(Level.WARNING, "Exception collecting " + on + "#" + attribute + (key == null ? "" : "#" + key), e);
} else {
value = Iterables2.get((Iterable) value, position);
outputWriter.writeQueryResult(resultName, type, value);
}
} else {
outputWriter.writeQueryResult(resultName, type, value);
}
}

Expand All @@ -199,7 +231,7 @@ public String getResultAlias() {
return resultAlias;
}

@Nonnull
@Nullable
public String getAttribute() {
return attribute;
}
Expand Down
4 changes: 1 addition & 3 deletions src/main/java/org/jmxtrans/agent/ResultNameStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,7 @@
public interface ResultNameStrategy {

@Nonnull
String getResultName(@Nonnull Query query, @Nonnull ObjectName objectName);
@Nonnull
String getResultName(@Nonnull Query query, @Nonnull ObjectName objectName, @Nullable String key);
String getResultName(@Nonnull Query query, @Nonnull ObjectName objectName, @Nullable String key, @Nullable String attribute);

void postConstruct(@Nonnull Map<String, String> settings);
}
12 changes: 3 additions & 9 deletions src/main/java/org/jmxtrans/agent/ResultNameStrategyImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,19 +102,13 @@ public class ResultNameStrategyImpl implements ResultNameStrategy {

@Nonnull
@Override
public String getResultName(@Nonnull Query query, @Nonnull ObjectName objectName) {
return getResultName(query, objectName, null);
}

@Nonnull
@Override
public String getResultName(@Nonnull Query query, @Nonnull ObjectName objectName, @Nullable String key) {
public String getResultName(@Nonnull Query query, @Nonnull ObjectName objectName, @Nullable String key, @Nonnull String attribute) {
String result;
if (query.getResultAlias() == null) {
if(key == null) {
result = escapeObjectName(objectName) + "." + query.getAttribute();
result = escapeObjectName(objectName) + "." + attribute;
} else {
result = escapeObjectName(objectName) + "." + query.getAttribute() + "." + key;
result = escapeObjectName(objectName) + "." + attribute + "." + key;
}
} else {
result = expressionLanguageEngine.resolveExpression(query.getResultAlias(), objectName);
Expand Down
54 changes: 53 additions & 1 deletion src/test/java/org/jmxtrans/agent/QueryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ public void basic_attribute_return_simple_result() throws Exception {
assertThat(actual, instanceOf(Number.class));
}

@Test
public void basic_attribute_null_result_alias_returns_simple_result() throws Exception {
Query query = new Query("test:type=Mock,name=mock", "CollectionUsageThreshold", null, resultNameStrategy);
query.collectAndExport(mbeanServer, mockOutputWriter);
Object actual = mockOutputWriter.resultsByName.get("test.name__mock.type__Mock.CollectionUsageThreshold");
assertThat(actual, notNullValue());
assertThat(actual, instanceOf(Number.class));
}

@Test
public void expression_language_substitutes_object_name_key() throws Exception {
Query query = new Query("test:type=Mock,name=mock", "CollectionUsageThreshold", "test_%type%_%name%.CollectionUsageThreshold", resultNameStrategy);
Expand Down Expand Up @@ -120,7 +129,6 @@ public void non_indexed_list_attribute_return_simple_result() throws Exception {
assertThat("Result '" + name + "' is missing", actual, notNullValue());
assertThat("Result '" + name + "' type is invalid", actual, instanceOf(Number.class));
}

}

@Test
Expand All @@ -141,6 +149,50 @@ public void indexed_integer_array_attribute_return_simple_result() throws Except
assertThat(actual, instanceOf(Number.class));
}

@Test
public void query_wildcard_objectname_domain_returns_objetname_and_attribute() throws Exception {
Query query = new Query("*:type=Mock,name=mock", "CollectionUsageThreshold", null, resultNameStrategy);
query.collectAndExport(mbeanServer, mockOutputWriter);
Object actual = mockOutputWriter.resultsByName.get("test.name__mock.type__Mock.CollectionUsageThreshold");
assertThat(actual, notNullValue());
assertThat(actual, instanceOf(Number.class));
}

@Test
public void query_wildcard_objectname_property_returns_objetname_and_attribute() throws Exception {
Query query = new Query("test:*", "CollectionUsageThreshold", null, resultNameStrategy);
query.collectAndExport(mbeanServer, mockOutputWriter);
Object actual = mockOutputWriter.resultsByName.get("test.name__mock.type__Mock.CollectionUsageThreshold");
assertThat(actual, notNullValue());
assertThat(actual, instanceOf(Number.class));
}

@Test
public void query_wildcard_objectname_domain_returns_meabn_with_resultalias() throws Exception {
Query query = new Query("*:type=Mock,name=mock", "CollectionUsageThreshold", "altTest.%name%.%type%", resultNameStrategy);
query.collectAndExport(mbeanServer, mockOutputWriter);
Object actual = mockOutputWriter.resultsByName.get("altTest.mock.Mock");
assertThat(actual, notNullValue());
assertThat(actual, instanceOf(Number.class));
}

@Test
public void query_wildcard_objectname_property_returns_mbean_with_resultalias() throws Exception {
Query query = new Query("test:*", "CollectionUsageThreshold", "altTest.%name%.%type%", resultNameStrategy);
query.collectAndExport(mbeanServer, mockOutputWriter);
Object actual = mockOutputWriter.resultsByName.get("altTest.mock.Mock");
assertThat(actual, notNullValue());
assertThat(actual, instanceOf(Number.class));
}

@Test
public void query_objectname_with_null_attribute_returns_all_attributes() throws Exception {
Query query = new Query("test:type=Mock,name=mock", null, null, resultNameStrategy);
query.collectAndExport(mbeanServer, mockOutputWriter);
Integer actualSize = mockOutputWriter.resultsByName.size();
assert(actualSize == 24);
}

public static class MockOutputWriter extends AbstractOutputWriter {

protected final boolean failOnDuplicateResult;
Expand Down

0 comments on commit 2fc4063

Please sign in to comment.