SOLR-3083: JMX beans now report Numbers as numeric values rather then String

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1335147 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Ryan McKinley 2012-05-07 17:37:47 +00:00
parent d29c5a8ea9
commit bb56a01e53
4 changed files with 115 additions and 22 deletions

View File

@ -551,6 +551,10 @@ Other Changes
paths have been fixed so that they are resolved against the data dir
instead of the CWD of the java process. (hossman)
* SOLR-3083: JMX beans now report Numbers as numeric values rather then String
(Tagged Siteops, Greg Bowyer via ryan)
Documentation
----------------------

View File

@ -19,16 +19,20 @@ package org.apache.solr.core;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.SolrConfig.JmxConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.management.*;
import javax.management.openmbean.OpenMBeanAttributeInfoSupport;
import javax.management.openmbean.OpenType;
import javax.management.openmbean.SimpleType;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>
@ -228,14 +232,23 @@ public class JmxMonitoredMap<K, V> extends
if (dynamicStats != null) {
for (int i = 0; i < dynamicStats.size(); i++) {
String name = dynamicStats.getName(i);
if (!staticStats.contains(name))
if (staticStats.contains(name)) {
continue;
}
Class type = dynamicStats.get(name).getClass();
OpenType typeBox = determineType(type);
if (type.equals(String.class) || typeBox == null) {
attrInfoList.add(new MBeanAttributeInfo(dynamicStats.getName(i),
String.class.getName(), null, true, false, false));
} else {
attrInfoList.add(new OpenMBeanAttributeInfoSupport(
dynamicStats.getName(i), dynamicStats.getName(i), typeBox,
true, false, false));
}
}
}
} catch (Exception e) {
LOG.warn( "Could not getStatistics on info bean "
+ infoBean.getName(), e);
LOG.warn("Could not getStatistics on info bean {}", infoBean.getName(), e);
}
MBeanAttributeInfo[] attrInfoArr = attrInfoList
@ -244,6 +257,22 @@ public class JmxMonitoredMap<K, V> extends
.getDescription(), attrInfoArr, null, null, null);
}
private OpenType determineType(Class type) {
try {
for (Field field : SimpleType.class.getFields()) {
if (field.getType().equals(SimpleType.class)) {
SimpleType candidate = (SimpleType) field.get(SimpleType.class);
if (candidate.getTypeName().equals(type.getName())) {
return candidate;
}
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
public Object getAttribute(String attribute)
throws AttributeNotFoundException, MBeanException, ReflectionException {
Object val;
@ -264,11 +293,18 @@ public class JmxMonitoredMap<K, V> extends
val = list.get(attribute);
}
if (val != null)
return val.toString();
else
if (val != null) {
// Its String or one of the simple types, just return it as JMX suggests direct support for such types
for (String simpleTypeName : SimpleType.ALLOWED_CLASSNAMES_LIST) {
if (val.getClass().getName().equals(simpleTypeName)) {
return val;
}
}
// Its an arbitrary object which could be something complex and odd, return its toString, assuming that is
// a workable representation of the object
return val.toString();
}
return null;
}
public AttributeList getAttributes(String[] attributes) {

View File

@ -101,10 +101,10 @@ public class TestJmxIntegration extends AbstractSolrTestCase {
assertFalse("No mbean found for SolrIndexSearcher", mbeanServer.queryMBeans(searcher, null).isEmpty());
int oldNumDocs = Integer.valueOf((String) mbeanServer.getAttribute(searcher, "numDocs"));
int oldNumDocs = (Integer)mbeanServer.getAttribute(searcher, "numDocs");
assertU(adoc("id", "1"));
assertU("commit", commit());
int numDocs = Integer.valueOf((String) mbeanServer.getAttribute(searcher, "numDocs"));
int numDocs = (Integer)mbeanServer.getAttribute(searcher, "numDocs");
assertTrue("New numDocs is same as old numDocs as reported by JMX",
numDocs > oldNumDocs);
}

View File

@ -25,6 +25,7 @@ import org.junit.Test;
import javax.management.MBeanServerConnection;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.Query;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
@ -35,6 +36,10 @@ import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.util.Set;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
/**
* Test for JmxMonitoredMap
*
@ -54,7 +59,9 @@ public class TestJmxMonitoredMap extends LuceneTestCase {
@Override
@Before
public void setUp() throws Exception {
super.setUp();
int retries = 5;
for (int i = 0; i < retries; i++) {
try {
@ -95,11 +102,48 @@ public class TestJmxMonitoredMap extends LuceneTestCase {
super.tearDown();
}
@Test
public void testTypeName() throws Exception{
MockInfoMBean mock = new MockInfoMBean();
monitoredMap.put("mock", mock);
NamedList dynamicStats = mock.getStatistics();
assertTrue(dynamicStats.size() != 0);
assertTrue(dynamicStats.get("Integer") instanceof Integer);
assertTrue(dynamicStats.get("Double") instanceof Double);
assertTrue(dynamicStats.get("Long") instanceof Long);
assertTrue(dynamicStats.get("Short") instanceof Short);
assertTrue(dynamicStats.get("Byte") instanceof Byte);
assertTrue(dynamicStats.get("Float") instanceof Float);
assertTrue(dynamicStats.get("String") instanceof String);
Set<ObjectInstance> objects = mbeanServer.queryMBeans(null, Query.match(
Query.attr("name"), Query.value("mock")));
ObjectName name = objects.iterator().next().getObjectName();
assertMBeanTypeAndValue(name, "Integer", Integer.class, 123);
assertMBeanTypeAndValue(name, "Double", Double.class, 567.534);
assertMBeanTypeAndValue(name, "Long", Long.class, 32352463l);
assertMBeanTypeAndValue(name, "Short", Short.class, (short) 32768);
assertMBeanTypeAndValue(name, "Byte", Byte.class, (byte) 254);
assertMBeanTypeAndValue(name, "Float", Float.class, 3.456f);
assertMBeanTypeAndValue(name, "String",String.class, "testing");
}
@SuppressWarnings("unchecked")
public void assertMBeanTypeAndValue(ObjectName name, String attr, Class type, Object value) throws Exception {
assertThat(mbeanServer.getAttribute(name, attr),
allOf(instanceOf(type), equalTo(value))
);
}
@Test
public void testPutRemoveClear() throws Exception {
MockInfoMBean mock = new MockInfoMBean();
monitoredMap.put("mock", mock);
Set<ObjectInstance> objects = mbeanServer.queryMBeans(null, Query.match(
Query.attr("name"), Query.value("mock")));
assertFalse("No MBean for mock object found in MBeanServer", objects
@ -124,6 +168,7 @@ public class TestJmxMonitoredMap extends LuceneTestCase {
assertTrue(
"MBean for mock object found in MBeanServer even after clear has been called",
objects.isEmpty());
}
private class MockInfoMBean implements SolrInfoMBean {
@ -154,7 +199,15 @@ public class TestJmxMonitoredMap extends LuceneTestCase {
@SuppressWarnings("unchecked")
public NamedList getStatistics() {
return null;
NamedList myList = new NamedList<Integer>();
myList.add("Integer", 123);
myList.add("Double",567.534);
myList.add("Long", 32352463l);
myList.add("Short", (short) 32768);
myList.add("Byte", (byte) 254);
myList.add("Float", 3.456f);
myList.add("String","testing");
return myList;
}
}