diff --git a/common/CHANGES.txt b/common/CHANGES.txt
index 86275563fe..5873f58794 100644
--- a/common/CHANGES.txt
+++ b/common/CHANGES.txt
@@ -49,7 +49,10 @@ Trunk (unreleased changes)
HADOOP-7206. Support Snappy compression. (Issei Yoshida and
Alejandro Abdelnur via eli)
-
+
+ HADOOP-7329. Add the capability of getting invividual attribute of a mbean
+ using JMXProxyServlet. (tanping)
+
IMPROVEMENTS
HADOOP-7042. Updates to test-patch.sh to include failed test names and
diff --git a/common/src/java/org/apache/hadoop/jmx/JMXJsonServlet.java b/common/src/java/org/apache/hadoop/jmx/JMXJsonServlet.java
index be59f5a301..2c8f797214 100644
--- a/common/src/java/org/apache/hadoop/jmx/JMXJsonServlet.java
+++ b/common/src/java/org/apache/hadoop/jmx/JMXJsonServlet.java
@@ -67,8 +67,20 @@
* For example http://.../jmx?qry=Hadoop:*
will return
* all hadoop metrics exposed through JMX.
*
- * If the qry
parameter is not formatted correctly then a
- * 400 BAD REQUEST http response code will be returned.
+ * The optional get
parameter is used to query an specific
+ * attribute of a JMX bean. The format of the URL is
+ * http://.../jmx?get=MXBeanName::AttributeName
+ *
+ * For example
+ *
+ * http://../jmx?get=Hadoop:service=NameNode,name=NameNodeInfo::ClusterId
+ *
will return the cluster id of the namenode mxbean.
+ *
+ * If the qry
or the get
parameter is not formatted
+ * correctly then a 400 BAD REQUEST http response code will be returned.
+ *
+ * If a resouce such as a mbean or attribute can not be found,
+ * a 404 SC_NOT_FOUND http response code will be returned.
*
* The return format is JSON and in the form
*
@@ -150,25 +162,49 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) {
jg.writeStringField("result", "ERROR");
jg.writeStringField("message", "No MBeanServer could be found");
jg.close();
+ LOG.error("No MBeanServer could be found.");
+ response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
+
+ // query per mbean attribute
+ String getmethod = request.getParameter("get");
+ if (getmethod != null) {
+ String[] splitStrings = getmethod.split("\\:\\:");
+ if (splitStrings.length != 2) {
+ jg.writeStringField("result", "ERROR");
+ jg.writeStringField("message", "query format is not as expected.");
+ jg.close();
+ response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ listBeans(jg, new ObjectName(splitStrings[0]), splitStrings[1],
+ response);
+ jg.close();
+ return;
+ }
+
+ // query per mbean
String qry = request.getParameter("qry");
if (qry == null) {
qry = "*:*";
}
- listBeans(jg, new ObjectName(qry));
+ listBeans(jg, new ObjectName(qry), null, response);
jg.close();
- } catch (IOException e) {
+
+ } catch ( IOException e ) {
LOG.error("Caught an exception while processing JMX request", e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- } catch (MalformedObjectNameException e) {
+ } catch ( MalformedObjectNameException e ) {
LOG.error("Caught an exception while processing JMX request", e);
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
}
// --------------------------------------------------------- Private Methods
- private void listBeans(JsonGenerator jg, ObjectName qry) throws IOException {
+ private void listBeans(JsonGenerator jg, ObjectName qry, String attribute,
+ HttpServletResponse response)
+ throws IOException {
LOG.debug("Listing beans for "+qry);
Set names = null;
names = mBeanServer.queryNames(qry, null);
@@ -178,62 +214,89 @@ private void listBeans(JsonGenerator jg, ObjectName qry) throws IOException {
while (it.hasNext()) {
ObjectName oname = it.next();
MBeanInfo minfo;
- String code;
+ String code = "";
+ Object attributeinfo = null;
try {
minfo = mBeanServer.getMBeanInfo(oname);
code = minfo.getClassName();
+ String prs = "";
try {
if ("org.apache.commons.modeler.BaseModelMBean".equals(code)) {
- code = (String) mBeanServer.getAttribute(oname, "modelerType");
+ prs = "modelerType";
+ code = (String) mBeanServer.getAttribute(oname, prs);
+ }
+ if (attribute!=null) {
+ prs = attribute;
+ attributeinfo = mBeanServer.getAttribute(oname, prs);
}
} catch (AttributeNotFoundException e) {
- //Ignored the modelerType attribute was not found, so use the class name instead.
+ // If the modelerType attribute was not found, the class name is used
+ // instead.
+ LOG.error("getting attribute " + prs + " of " + oname
+ + " threw an exception", e);
} catch (MBeanException e) {
- //The code inside the attribute getter threw an exception so log it, and
- // fall back on the class name
- LOG.error("getting attribute modelerType of "+oname+" threw an exception", e);
+ // The code inside the attribute getter threw an exception so log it,
+ // and fall back on the class name
+ LOG.error("getting attribute " + prs + " of " + oname
+ + " threw an exception", e);
} catch (RuntimeException e) {
- //For some reason even with an MBeanException available to them Runtime exceptions
- //can still find their way through, so treat them the same as MBeanException
- LOG.error("getting attribute modelerType of "+oname+" threw an exception", e);
- } catch (ReflectionException e) {
- //This happens when the code inside the JMX bean (setter?? from the java docs)
- //threw an exception, so log it and fall back on the class name
- LOG.error("getting attribute modelerType of "+oname+" threw an exception", e);
+ // For some reason even with an MBeanException available to them
+ // Runtime exceptionscan still find their way through, so treat them
+ // the same as MBeanException
+ LOG.error("getting attribute " + prs + " of " + oname
+ + " threw an exception", e);
+ } catch ( ReflectionException e ) {
+ // This happens when the code inside the JMX bean (setter?? from the
+ // java docs) threw an exception, so log it and fall back on the
+ // class name
+ LOG.error("getting attribute " + prs + " of " + oname
+ + " threw an exception", e);
}
} catch (InstanceNotFoundException e) {
//Ignored for some reason the bean was not found so don't output it
continue;
- } catch (IntrospectionException e) {
- //This is an internal error, something odd happened with reflection so log it and
- //don't output the bean.
- LOG.error("Problem while trying to process JMX query: "+qry+" with MBean "+oname, e);
+ } catch ( IntrospectionException e ) {
+ // This is an internal error, something odd happened with reflection so
+ // log it and don't output the bean.
+ LOG.error("Problem while trying to process JMX query: " + qry
+ + " with MBean " + oname, e);
continue;
- } catch (ReflectionException e) {
- //This happens when the code inside the JMX bean threw an exception, so log it and
- //don't output the bean.
- LOG.error("Problem while trying to process JMX query: "+qry+" with MBean "+oname, e);
+ } catch ( ReflectionException e ) {
+ // This happens when the code inside the JMX bean threw an exception, so
+ // log it and don't output the bean.
+ LOG.error("Problem while trying to process JMX query: " + qry
+ + " with MBean " + oname, e);
continue;
}
jg.writeStartObject();
jg.writeStringField("name", oname.toString());
- // can't be null - I think
-
+
jg.writeStringField("modelerType", code);
-
- MBeanAttributeInfo attrs[] = minfo.getAttributes();
- for (int i = 0; i < attrs.length; i++) {
- writeAttribute(jg, oname, attrs[i]);
+ if ((attribute != null) && (attributeinfo == null)) {
+ jg.writeStringField("result", "ERROR");
+ jg.writeStringField("message", "No attribute with name " + attribute
+ + " was found.");
+ jg.writeEndObject();
+ jg.writeEndArray();
+ jg.close();
+ response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+
+ if (attribute != null) {
+ writeAttribute(jg, attribute, attributeinfo);
+ } else {
+ MBeanAttributeInfo attrs[] = minfo.getAttributes();
+ for (int i = 0; i < attrs.length; i++) {
+ writeAttribute(jg, oname, attrs[i]);
+ }
}
- // LOG.error("Caught Error writing value ",t);
- // ExceptionUtils.handleThrowable(t);
- //}
jg.writeEndObject();
}
jg.writeEndArray();
}
-
+
private void writeAttribute(JsonGenerator jg, ObjectName oname, MBeanAttributeInfo attr) throws IOException {
if (!attr.isReadable()) {
return;
diff --git a/common/src/test/core/org/apache/hadoop/jmx/TestJMXJsonServlet.java b/common/src/test/core/org/apache/hadoop/jmx/TestJMXJsonServlet.java
index b1feaf3ae8..62a2af9555 100644
--- a/common/src/test/core/org/apache/hadoop/jmx/TestJMXJsonServlet.java
+++ b/common/src/test/core/org/apache/hadoop/jmx/TestJMXJsonServlet.java
@@ -65,5 +65,18 @@ public static void assertReFind(String re, String value) {
result = readOutput(new URL(baseUrl, "/jmx"));
LOG.info("/jmx RESULT: "+result);
assertReFind("\"name\"\\s*:\\s*\"java.lang:type=Memory\"", result);
+
+ // test to get an attribute of a mbean
+ result = readOutput(new URL(baseUrl,
+ "/jmx?get=java.lang:type=Memory::HeapMemoryUsage"));
+ LOG.info("/jmx RESULT: "+result);
+ assertReFind("\"name\"\\s*:\\s*\"java.lang:type=Memory\"", result);
+ assertReFind("\"committed\"\\s*:", result);
+
+ // negative test to get an attribute of a mbean
+ result = readOutput(new URL(baseUrl,
+ "/jmx?get=java.lang:type=Memory::"));
+ LOG.info("/jmx RESULT: "+result);
+ assertReFind("\"ERROR\"", result);
}
}