mirror of https://github.com/apache/lucene.git
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:
parent
d29c5a8ea9
commit
bb56a01e53
|
@ -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
|
||||
----------------------
|
||||
|
||||
|
|
|
@ -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));
|
||||
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)
|
||||
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();
|
||||
else
|
||||
return val;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public AttributeList getAttributes(String[] attributes) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,35 +102,73 @@ 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")));
|
||||
Query.attr("name"), Query.value("mock")));
|
||||
assertFalse("No MBean for mock object found in MBeanServer", objects
|
||||
.isEmpty());
|
||||
.isEmpty());
|
||||
|
||||
monitoredMap.remove("mock");
|
||||
objects = mbeanServer.queryMBeans(null, Query.match(Query.attr("name"),
|
||||
Query.value("mock")));
|
||||
Query.value("mock")));
|
||||
assertTrue("MBean for mock object found in MBeanServer even after removal",
|
||||
objects.isEmpty());
|
||||
objects.isEmpty());
|
||||
|
||||
monitoredMap.put("mock", mock);
|
||||
monitoredMap.put("mock2", mock);
|
||||
objects = mbeanServer.queryMBeans(null, Query.match(Query.attr("name"),
|
||||
Query.value("mock")));
|
||||
Query.value("mock")));
|
||||
assertFalse("No MBean for mock object found in MBeanServer", objects
|
||||
.isEmpty());
|
||||
.isEmpty());
|
||||
|
||||
monitoredMap.clear();
|
||||
objects = mbeanServer.queryMBeans(null, Query.match(Query.attr("name"),
|
||||
Query.value("mock")));
|
||||
Query.value("mock")));
|
||||
assertTrue(
|
||||
"MBean for mock object found in MBeanServer even after clear has been called",
|
||||
objects.isEmpty());
|
||||
"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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue