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); } }