diff --git a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/jmx/JMXJsonServlet.java b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/jmx/JMXJsonServlet.java
index 3dbf7c3214dc..7e3a79df9d3f 100644
--- a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/jmx/JMXJsonServlet.java
+++ b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/jmx/JMXJsonServlet.java
@@ -67,6 +67,21 @@
* will return the cluster id of the namenode mxbean.
*
*
+ * If we are not sure on the exact attribute and we want to get all the attributes that match one or
+ * more given pattern then the format is
+ * http://.../jmx?get=MXBeanName::*[RegExp1],*[RegExp2]
+ *
+ *
+ * For example
+ *
+ *
+ * http://../jmx?get=Hadoop:service=HBase,name=RegionServer,sub=Tables::[a-zA-z_0-9]*memStoreSize
+ *
+ *
+ * http://../jmx?get=Hadoop:service=HBase,name=RegionServer,sub=Tables::[a-zA-z_0-9]*memStoreSize,[a-zA-z_0-9]*storeFileSize
+ *
+ *
+ *
* If the qry
or the get
parameter is not formatted
* correctly then a 400 BAD REQUEST http response code will be returned.
*
diff --git a/hbase-http/src/main/java/org/apache/hadoop/hbase/util/JSONBean.java b/hbase-http/src/main/java/org/apache/hadoop/hbase/util/JSONBean.java
index c9b18e3bf81a..0dbe0fdf78cd 100644
--- a/hbase-http/src/main/java/org/apache/hadoop/hbase/util/JSONBean.java
+++ b/hbase-http/src/main/java/org/apache/hadoop/hbase/util/JSONBean.java
@@ -25,6 +25,8 @@
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;
+import java.util.regex.Pattern;
+
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
@@ -40,18 +42,20 @@
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.TabularData;
-import org.apache.yetus.audience.InterfaceAudience;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.apache.hbase.thirdparty.com.google.gson.Gson;
import org.apache.hbase.thirdparty.com.google.gson.stream.JsonWriter;
+import org.apache.yetus.audience.InterfaceAudience;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Utility for doing JSON and MBeans.
*/
@InterfaceAudience.Private
public class JSONBean {
+ private static final String COMMA = ",";
+ private static final String ASTERICK = "*";
private static final Logger LOG = LoggerFactory.getLogger(JSONBean.class);
private static final Gson GSON = GsonUtil.createGson().create();
@@ -125,11 +129,12 @@ public int write(MBeanServer mBeanServer, ObjectName qry, String attribute,
*/
private static int write(JsonWriter writer, MBeanServer mBeanServer, ObjectName qry,
String attribute, boolean description) throws IOException {
- LOG.trace("Listing beans for " + qry);
+ LOG.debug("Listing beans for {}", qry);
Set names = null;
names = mBeanServer.queryNames(qry, null);
writer.name("beans").beginArray();
Iterator it = names.iterator();
+ Pattern[] matchingPattern = null;
while (it.hasNext()) {
ObjectName oname = it.next();
MBeanInfo minfo;
@@ -149,8 +154,24 @@ private static int write(JsonWriter writer, MBeanServer mBeanServer, ObjectName
code = (String) mBeanServer.getAttribute(oname, prs);
}
if (attribute != null) {
- prs = attribute;
- attributeinfo = mBeanServer.getAttribute(oname, prs);
+ String[] patternAttr = null;
+ if (attribute.contains(ASTERICK)) {
+ if (attribute.contains(COMMA)) {
+ patternAttr = attribute.split(COMMA);
+ } else {
+ patternAttr = new String[1];
+ patternAttr[0] = attribute;
+ }
+ matchingPattern = new Pattern[patternAttr.length];
+ for (int i = 0; i < patternAttr.length; i++) {
+ matchingPattern[i] = Pattern.compile(patternAttr[i]);
+ }
+ // nullify the attribute
+ attribute = null;
+ } else {
+ prs = attribute;
+ attributeinfo = mBeanServer.getAttribute(oname, prs);
+ }
}
} catch (RuntimeMBeanException e) {
// UnsupportedOperationExceptions happen in the normal course of business,
@@ -216,7 +237,7 @@ private static int write(JsonWriter writer, MBeanServer mBeanServer, ObjectName
} else {
MBeanAttributeInfo[] attrs = minfo.getAttributes();
for (int i = 0; i < attrs.length; i++) {
- writeAttribute(writer, mBeanServer, oname, description, attrs[i]);
+ writeAttribute(writer, mBeanServer, oname, description, matchingPattern, attrs[i]);
}
}
writer.endObject();
@@ -226,7 +247,7 @@ private static int write(JsonWriter writer, MBeanServer mBeanServer, ObjectName
}
private static void writeAttribute(JsonWriter writer, MBeanServer mBeanServer, ObjectName oname,
- boolean description, MBeanAttributeInfo attr) throws IOException {
+ boolean description, Pattern pattern[], MBeanAttributeInfo attr) throws IOException {
if (!attr.isReadable()) {
return;
}
@@ -237,6 +258,21 @@ private static void writeAttribute(JsonWriter writer, MBeanServer mBeanServer, O
if (attName.indexOf("=") >= 0 || attName.indexOf(":") >= 0 || attName.indexOf(" ") >= 0) {
return;
}
+
+ if (pattern != null) {
+ boolean matchFound = false;
+ for (Pattern compile : pattern) {
+ // check if we have any match
+ if (compile.matcher(attName).find()) {
+ matchFound = true;
+ break;
+ }
+ }
+ if (!matchFound) {
+ return;
+ }
+ }
+
String descriptionStr = description ? attr.getDescription() : null;
Object value = null;
try {
diff --git a/hbase-http/src/test/java/org/apache/hadoop/hbase/http/jmx/TestJMXJsonServlet.java b/hbase-http/src/test/java/org/apache/hadoop/hbase/http/jmx/TestJMXJsonServlet.java
index 9de8b2eb910f..e907a3260b0b 100644
--- a/hbase-http/src/test/java/org/apache/hadoop/hbase/http/jmx/TestJMXJsonServlet.java
+++ b/hbase-http/src/test/java/org/apache/hadoop/hbase/http/jmx/TestJMXJsonServlet.java
@@ -66,6 +66,12 @@ public static void assertReFind(String re, String value) {
assertTrue("'"+p+"' does not match "+value, m.find());
}
+ public static void assertNotFind(String re, String value) {
+ Pattern p = Pattern.compile(re);
+ Matcher m = p.matcher(value);
+ assertFalse("'"+p+"' should not match "+value, m.find());
+ }
+
@Test public void testQuery() throws Exception {
String result = readOutput(new URL(baseUrl, "/jmx?qry=java.lang:type=Runtime"));
LOG.info("/jmx?qry=java.lang:type=Runtime RESULT: "+result);
@@ -116,7 +122,39 @@ public static void assertReFind(String re, String value) {
assertReFind("\"name\"\\s*:\\s*\"java.lang:type=Memory\"", result);
assertReFind("\"committed\"\\s*:", result);
assertReFind("\\}\\);$", result);
+ }
+
+ @Test
+ public void testGetPattern() throws Exception {
+ // test to get an attribute of a mbean as JSONP
+ String result = readOutput(
+ new URL(baseUrl, "/jmx?get=java.lang:type=Memory::[a-zA-z_]*NonHeapMemoryUsage"));
+ LOG.info("/jmx RESULT: " + result);
+ assertReFind("\"name\"\\s*:\\s*\"java.lang:type=Memory\"", result);
+ assertReFind("\"committed\"\\s*:", result);
+ assertReFind("\"NonHeapMemoryUsage\"\\s*:", result);
+ assertNotFind("\"HeapMemoryUsage\"\\s*:", result);
+ result =
+ readOutput(new URL(baseUrl, "/jmx?get=java.lang:type=Memory::[^Non]*HeapMemoryUsage"));
+ LOG.info("/jmx RESULT: " + result);
+ assertReFind("\"name\"\\s*:\\s*\"java.lang:type=Memory\"", result);
+ assertReFind("\"committed\"\\s*:", result);
+ assertReFind("\"HeapMemoryUsage\"\\s*:", result);
+ assertNotFind("\"NonHeapHeapMemoryUsage\"\\s*:", result);
+
+ result = readOutput(new URL(baseUrl,
+ "/jmx?get=java.lang:type=Memory::[a-zA-z_]*HeapMemoryUsage,[a-zA-z_]*NonHeapMemoryUsage"));
+ LOG.info("/jmx RESULT: " + result);
+ assertReFind("\"name\"\\s*:\\s*\"java.lang:type=Memory\"", result);
+ assertReFind("\"committed\"\\s*:", result);
+ }
+
+ @Test
+ public void testPatternMatching() throws Exception {
+ assertReFind("[a-zA-z_]*Table1[a-zA-z_]*memStoreSize",
+ "Namespace_default_table_Table1_metric_memStoreSize");
+ assertReFind("[a-zA-z_]*memStoreSize", "Namespace_default_table_Table1_metric_memStoreSize");
}
@Test