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