Jetty JMX Webservice is a webapp providing a RESTful API to query JMX mbeans and invoke mbean operations without the hassle that comes with RMI. No more arguments with your firewall admin, just a single http port.
That alone might not be a killer feature, but Jetty JMX Webservice also aggregates multiple mbeans having the same ObjectName in the same JVM (e.g. jmx beans for multiple webapps) as well as from multiple jetty instances. That way you’ve a single REST api aggregating JMX mbeans for one or more jetty instances you can use to feed your favourite monitoring system for example.
The whole module is in an early development phase and may contain some rough edges. But we wanted to check for interest of the community early and get some early feedback.
We’ve started a very simple JQuery based webfrontend as a showcase on what the REST api can be used for.
Instance Overview:
A table showing two jetty instances.
Or a realtime memory graph gathering memory consumption from the REST api. This is an accordion like view. You see an accordion line for each node showing the current heap used. You can open each line and get a realtime graph of memory consumption. The memory used in the accordion and the graphs are updated in realtime which is hard to show in a picture:
Realtime memory graph
Pretty cool. Note that this is just a showcase on how the REST api can be used.
URLs Paths of the webservice
/ws/ – index page
/ws/nodes – aggregated basic node information
/ws/mbeans – list of all aggregated mbeans
/ws/mbeans/[mbean objectName] – detailed information about all attributes and operations this mbean offers
/ws/mbeans/[mbean objectName]/attributes – aggregate page containing the values of all attributes of the given mbean
/ws/mbeans/[mbean objectName]/attributes/[attributeName] – aggregated values of a specific attribute
/ws/mbeans/[mbean objectName]/operation/[operationName] – invoke specified operation
Examples URLs:
/ws/mbeans/java.lang:type=Memory
/ws/mbeans/java.lang:type=Memory/operations/gc
How to get it running

Here’s all you need to do to get jetty-jmx-ws running in your jetty instance and some examples how the REST api looks like and how it can be used. Should take less than 15 min.

  1. Checkout the sandbox project
    svn co https://svn.codehaus.org/jetty-contrib/sandbox/jetty-jmx-ws
  2. cd into the new directory and build the project
    cd jetty-jmx-ws && mvn clean install
  3. Make sure you got the [INFO] BUILD SUCCESS message
  4. Copy the war file you’ll find in the projects target directory into the webapps directory of your jetty instance
    cp target/jetty-jmx-ws-[version].war [pathToJetty]/webapps
  5. Access the webapp by browsing to:
    http://[jettyinstanceurl]/jetty-jmx-ws-[version]/ws/
    e.g.:
    http://localhost:8080/jetty-jmx-ws-7.4.1-SNAPSHOT/ws/
  6. You’re done. 🙂

How to use it: Starting point

As it is a RESTful api it will guide you from the base URL to more detailed pages. The base URL will return:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Index>
<mBeans>http://localhost:8080/jetty-jmx-ws-7.4.1-SNAPSHOT/ws/mbeans</mBeans>
<nodes>http://localhost:8080/jetty-jmx-ws-7.4.1-SNAPSHOT/ws/nodes</nodes>
</Index>

It shows you two URLs.
The first one will guide you through a list of mbeans which is aggregated. This means it’ll show you mbeans which do exist on ALL configured instances. mebans which exist only on a single instance will be filtered out.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<MBeans>
<MBean>
<ObjectName>JMImplementation:type=MBeanServerDelegate</ObjectName>
<URL>http://localhost:8080/jetty-jmx-ws-7.4.1-SNAPSHOT/ws/mbeans/JMImplementation:type=MBeanServerDelegate</URL>
</MBean>
<MBean>
<ObjectName>com.sun.management:type=HotSpotDiagnostic</ObjectName>
<URL>http://localhost:8080/jetty-jmx-ws-7.4.1-SNAPSHOT/ws/mbeans/com.sun.management:type=HotSpotDiagnostic</URL>
</MBean>
<MBean>
<ObjectName>java.lang:type=Memory</ObjectName>
<URL>http://localhost:8080/jetty-jmx-ws-7.4.1-SNAPSHOT/ws/mbeans/java.lang:type=Memory</URL>
</MBean>
SNIPSNAP - lots of mbeans
</MBeans>

The second URL shows you basic node information for all configured nodes:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Index>
<nodes>
<name>localhost:1099</name>
<jettyVersion>7.4.1-SNAPSHOT</jettyVersion>
<threadCount>42</threadCount>
<peakThreadCount>45</peakThreadCount>
<heapUsed>41038176</heapUsed>
<heapInit>0</heapInit>
<heapCommitted>85000192</heapCommitted>
<heapMax>129957888</heapMax>
<jmxServiceURL>service:jmx:rmi:///jndi/rmi://localhost:1099/jettyjmx</jmxServiceURL>
</nodes>
<nodes>
<name>localhost:1100</name>
<jettyVersion>7.4.1-SNAPSHOT</jettyVersion>
<threadCount>45</threadCount>
<peakThreadCount>47</peakThreadCount>
<heapUsed>73915872</heapUsed>
<heapInit>0</heapInit>
<heapCommitted>129957888</heapCommitted>
<heapMax>129957888</heapMax>
<jmxServiceURL>service:jmx:rmi:///jndi/rmi://localhost:1100/jettyjmx</jmxServiceURL>
</nodes>
</Index>

Howto query a single mbean
This example shows how to let you guide through the REST api to query a specific mbean. This example will guide you through to the memory mbean.

  1. From the base URL follow the link to the mbeans list:
    http://localhost:8080/jetty-jmx-ws-7.4.1-SNAPSHOT/ws/mbeans
  2. Search for the mbean name you’re looking for:
    <MBean>
    <ObjectName>java.lang:type=Memory</ObjectName>
    <URL>http://localhost:8080/jetty-jmx-ws-7.4.1-SNAPSHOT/ws/mbeans/java.lang:type=Memory</URL>
    </MBean>
  3. Open the link inside the URL tag
    http://localhost:8080/jetty-jmx-ws-7.4.1-SNAPSHOT/ws/mbeans/java.lang:type=Memory
  4. You’ll get a list of all operations which can be executed on that mbean and all attributes which can be queried:
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <MBean>
    <ObjectName>java.lang:type=Memory</ObjectName>
    <Operations>
    <ObjectName>java.lang:type=Memory</ObjectName>
    <Operation>
    <Name>gc</Name>
    <Description>gc</Description>
    <ReturnType>void</ReturnType>
    <URL>http://localhost:8080/jetty-jmx-ws-7.4.1-SNAPSHOT/ws/mbeans/java.lang:type=Memory/operations/gc</URL>
    </Operation>
    </Operations>
    <Attributes>
    <Attribute>
    <Name>HeapMemoryUsage</Name>
    <description>HeapMemoryUsage</description>
    <type>javax.management.openmbean.CompositeData</type>
    <isReadable>true</isReadable>
    <isWritable>false</isWritable>
    <uri>http://localhost:8080/jetty-jmx-ws-7.4.1-SNAPSHOT/ws/mbeans/java.lang:type=Memory/attributes/HeapMemoryUsage</uri>
    </Attribute>
    SNIPSNAP - lots of attributes cutted
    </Attributes>
    </MBean>
  5. Besides some information about all operations and attributes you’ll find URLs to invoke operations like:
    http://localhost:8080/jetty-jmx-ws-7.4.1-SNAPSHOT/ws/mbeans/java.lang:type=Memory/operations/gc
    which will invoke a garbage collection.And URLs to display the attributes’ values like:
    http://localhost:8080/jetty-jmx-ws-7.4.1-SNAPSHOT/ws/mbeans/java.lang:type=Memory/attributes/HeapMemoryUsage
    Will show you: 

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <mBeanAttributeValueJaxBeans>
    <Attribute>
    <AttributeName>HeapMemoryUsage</AttributeName>
    <NodeName>localhost:1099</NodeName>
    <ObjectName>java.lang:type=Memory</ObjectName>
    <Value>javax.management.openmbean.CompositeDataSupport(compositeType=javax.management.openmbean.CompositeType(name=java.lang.management.MemoryUsage,items=((itemName=committed,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=init,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=max,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=used,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)))),contents={committed=85000192, init=0, max=129957888, used=28073528})</Value>
    </Attribute>
    <Attribute>
    <AttributeName>HeapMemoryUsage</AttributeName>
    <NodeName>localhost:1100</NodeName>
    <ObjectName>java.lang:type=Memory</ObjectName>
    <Value>javax.management.openmbean.CompositeDataSupport(compositeType=javax.management.openmbean.CompositeType(name=java.lang.management.MemoryUsage,items=((itemName=committed,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=init,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=max,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=used,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)))),contents={committed=129957888, init=0, max=129957888, used=69793976})</Value>
    </Attribute>
    </mBeanAttributeValueJaxBeans>
  6. You can as well get an aggregated view of all attributes for an mbean by just adding attributes to the mbeans url: http://localhost:8080/jetty-jmx-ws-7.4.1-SNAPSHOT/ws/mbeans/java.lang:type=Memory/attributes
    will return: 

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <mBeanAttributeValueJaxBeans>
    <Attribute>
    <AttributeName>HeapMemoryUsage</AttributeName>
    <NodeName>localhost:1099</NodeName>
    <ObjectName>java.lang:type=Memory</ObjectName>
    <Value>javax.management.openmbean.CompositeDataSupport(compositeType=javax.management.openmbean.CompositeType(name=java.lang.management.MemoryUsage,items=((itemName=committed,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=init,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=max,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=used,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)))),contents={committed=85000192, init=0, max=129957888, used=30005472})</Value>
    </Attribute>
    <Attribute>
    <AttributeName>HeapMemoryUsage</AttributeName>
    <NodeName>localhost:1100</NodeName>
    <ObjectName>java.lang:type=Memory</ObjectName>
    <Value>javax.management.openmbean.CompositeDataSupport(compositeType=javax.management.openmbean.CompositeType(name=java.lang.management.MemoryUsage,items=((itemName=committed,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=init,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=max,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=used,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)))),contents={committed=129957888, init=0, max=129957888, used=68043064})</Value>
    </Attribute>
    <Attribute>
    <AttributeName>NonHeapMemoryUsage</AttributeName>
    <NodeName>localhost:1099</NodeName>
    <ObjectName>java.lang:type=Memory</ObjectName>
    <Value>javax.management.openmbean.CompositeDataSupport(compositeType=javax.management.openmbean.CompositeType(name=java.lang.management.MemoryUsage,items=((itemName=committed,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=init,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=max,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=used,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)))),contents={committed=85356544, init=24317952, max=136314880, used=52749944})</Value>
    </Attribute>
    <Attribute>
    <AttributeName>NonHeapMemoryUsage</AttributeName>
    <NodeName>localhost:1100</NodeName>
    <ObjectName>java.lang:type=Memory</ObjectName>
    <Value>javax.management.openmbean.CompositeDataSupport(compositeType=javax.management.openmbean.CompositeType(name=java.lang.management.MemoryUsage,items=((itemName=committed,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=init,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=max,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=used,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)))),contents={committed=92868608, init=24317952, max=136314880, used=78705952})</Value>
    </Attribute>
    <Attribute>
    <AttributeName>ObjectPendingFinalizationCount</AttributeName>
    <NodeName>localhost:1099</NodeName>
    <ObjectName>java.lang:type=Memory</ObjectName>
    <Value>0</Value>
    </Attribute>
    <Attribute>
    <AttributeName>ObjectPendingFinalizationCount</AttributeName>
    <NodeName>localhost:1100</NodeName>
    <ObjectName>java.lang:type=Memory</ObjectName>
    <Value>0</Value>
    </Attribute>
    <Attribute>
    <AttributeName>Verbose</AttributeName>
    <NodeName>localhost:1099</NodeName>
    <ObjectName>java.lang:type=Memory</ObjectName>
    <Value>false</Value>
    </Attribute>
    <Attribute>
    <AttributeName>Verbose</AttributeName>
    <NodeName>localhost:1100</NodeName>
    <ObjectName>java.lang:type=Memory</ObjectName>
    <Value>false</Value>
    </Attribute>
    </mBeanAttributeValueJaxBeans>

Security
It’s a webapp with some servlets. So secure it the same way you would any servlet.
What’s next?
There’s two more features which I didn’t describe. I will write a follow up soon describing how to use them.

  1. You can filter by nodes with QueryParams
  2. Invoke operations whith parameters
  3. Configuration for multiple instances
  4. Get JSON instead of XML by setting accept header

If there’s interest in this project, I will also take care to write some manuals and add them to the wiki pages.


14 Comments

Mike · 25/05/2011 at 17:10

I really don’t know anything ’bout this maven stuff, but I’m failing after ‘mvn clean install’, on both my Mac and one of my Debian servers:
–8 [Help 1]
[ERROR]
[ERROR] The project org.mortbay.jetty:jetty-jmx-ws:7.4.1-SNAPSHOT (/Users/mike/sandbox/jetty-jmx-ws/pom.xml) has 1 error
[ERROR] Non-resolvable parent POM: Could not find artifact org.mortbay.jetty:jetty-integration-project:pom:7.4.1-SNAPSHOT in maven2-repository.dev.java.net (http://download.java.net/maven/2/) and ‘parent.relativePath’ points at wrong local POM @ line 6, column 11 -> [Help 2]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/ProjectBuildingException
[ERROR] [Help 2] http://cwiki.apache.org/confluence/display/MAVEN/UnresolvableModelException
–8<–
Any hints?

Sangjin Lee · 25/05/2011 at 21:02

I’d like to try it out, but when I checked out the source and built it, it complains about not being able to download jetty-integration-project.
[ERROR] Non-resolvable parent POM: Could not find artifact org.mortbay.jetty:jetty-integration-project:pom:7.4.1-SNAPSHOT in maven2-repository.dev.java.net (http://download.java.net/maven/2/) and ‘parent.relativePath’ points at wrong local POM @ line 6, column 11 -> [Help 2]
Something wrong with my settings or is it not there? Thanks!

tbecker · 26/05/2011 at 08:26

That’s my fault. I changed the jetty version to a released version and maven should be able to download the dependency now.
Please do an svn update and try again.
Please make sure that your jetty-jmx.xml enables remote jmx via rmi and listens to localhost:1099. And that jmx is enabled in your start.ini.

Hendy Irawan · 31/05/2011 at 04:42

Hi,
Please update your feeds at Planet Eclipse to point to this blog ! 🙂
Thank you.

Steve Hermes · 08/06/2011 at 23:02

I do a good clean build SUCCESS
I go to the “http://localhost:8080/jjws/ws/nodes” url
HTTP ERROR 500
Problem accessing /jjws/ws/nodes. Reason:
java.lang.String cannot be cast to javax.management.openmbean.CompositeData
Caused by:
java.lang.ClassCastException: java.lang.String cannot be cast to javax.management.openmbean.CompositeData
at org.mortbay.jetty.jmx.ws.service.impl.AggregateServiceImpl.getMemoryByNode(AggregateServiceImpl.java:84)
at org.mortbay.jetty.jmx.ws.service.impl.AggregateServiceImpl.getNodes(AggregateServiceImpl.java:63)
at org.mortbay.jetty.jmx.ws.web.Nodes.getObjectNames(Nodes.java:42)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$TypeOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:168)
at com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:71)
at com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:280)
at com.sun.jersey.server.impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:108)
at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147)
at com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84)
at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1341)
at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1273)
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1223)
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1213)
at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:414)
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537)
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:699)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:538)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:478)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:119)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:480)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:225)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:937)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:406)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:183)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:871)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:247)
at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:149)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:110)
at org.eclipse.jetty.server.Server.handle(Server.java:346)
at org.eclipse.jetty.server.HttpConnection.handleRequest(HttpConnection.java:589)
at org.eclipse.jetty.server.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:1048)
at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:601)
at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:214)
at org.eclipse.jetty.server.HttpConnection.handle(HttpConnection.java:411)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:535)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:40)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:529)
at java.lang.Thread.run(Thread.java:662)

Steve Hermes · 09/06/2011 at 23:59

Oopsssssss
Forgot to turn jmx on.
Life is good now.

Per Weisteen · 22/06/2011 at 13:00

We’re currently using the standard RMI connector for JMX and does monitoring via HP SiteScope JMX service. In order to monitor through firewalls we have set up fixed ports and the SiteScope service uses URL’s like service:jmx:rmi://:2346/jndi/rmi://:12346/jettyjmx.
Switching to a HTTP connector would ease overall administration of ports but I wonder how an URL would have to look for a system like SiteScope ?
I also wonder if this connector would be part of standard Jetty distribution.

    tbecker · 22/06/2011 at 13:16

    Hi Per,
    the jmx-ws is as described a new RESTful api forwarding to jmx. So if you want to integrate that in your favourite monitoring tool, you would have to custom build that integration point/interface.
    As described above using this RESTful api will offer you nearly all features you have with standard jmx without the burden to use rmi or the standard jmx interfaces. You can simply do your http requests to gather information, invoke operations, etc.
    Cheers,
    Thomas

      Ravi · 19/05/2012 at 06:48

      how do I connect to remote JMX bean?
      I looked at the jmxNodes property file, It has localhost:1099
      I believe I can change that to remotehost:remoteport.
      when I connect using jconsole, i have to specify userid/password when connecting to remote JMX.How can that be done in jmxNodes.property?

        tbecker · 21/05/2012 at 07:52

        Yes, you can simply change it and also add multiple nodes if you want to monitor multiple jetty instances.
        It’s a webapp so you can secure it the same way as any other webapp. For example by defining basic-auth in web.xml.

tbecker · 22/06/2011 at 13:18

We’d be happy to receive feedback about people trying to use this webservice. If you think this is gonna be useful for you and what your experiences with jmx-ws have been if you tried it. And what you would like to have improved, etc.
So tell us. 🙂

Sebu · 15/07/2011 at 19:41

Very nice. Exploring using it as part of a distributed application project.

Dick Davies · 21/05/2012 at 19:32

Looks interesting – wondered if you were aware of Jolokia ( http://jolokia.org/ )?
Seems to fit a similar use case, but they’re a bit further along I think.

JMiniX JMX console in Jetty | Webtide Blogs · 07/06/2012 at 09:16

[…] had been working on a similar effort for restful access to JMX, but JMiniX is more advanced.  It does lack some of the features that we had been working on like […]

Comments are closed.