SpoonJMX: a MBean generator

JMX is the specification for the management of Java-based applications. Initially adopted by the JavaEE community, JMX is now included in Java Platform 5.0 (ie Standard Edition). MBeans (Manageable Beans) are software components used for application configuration and monitoring. More on JMX ...

SpoonJMX is a tool for generating MBeans by annotating a POJO (ie. Plain-Old Java Object) according to the JSR 255.

JMX annotations

SpoonJMX uses the following annotations:

  • @javax.management.ManagedResource: to annotate the class to decorate as a DynamicMBean,
  • @javax.management.ManagedAttribute: to make a field as an MBean's attribute,
  • @javax.management.ManagedConstructor: to instantiate the MBean for the MBeanServer,
  • @javax.management.ManagedOperation: to make a method as a MBean's operation,
  • @javax.management.ManagedOperationParamater: to decribe the parameters of an operation or of a constructor.
  • @javax.management.ObjectNameKey: to add name-value pairs in the MBean's object name
  • @javax.management.DescriptorKey: to describe properties associated to attributes and parameters

The JMX annotations are annotated with AVal annotations to help the MBean developer to set the right annotations.

SpoonJMX generation

The SpoonJMX processor can add the following interfaces (and implemented methods) to a object class: javax.management.DynamicBean, javax.management.NotificationBroadcaster, javax.management.MBeanRegistration

Compiling SpoonJMX

Download spoon-core and spoon-aval jarfiles and install within the Maven2 local repository (mvn install:install-file) or checkout the 2 projects from the Forge and build then with Maven (mvn clean install).

Build SpoonJMX with Maven2 (mvn clean install)

Examples

The files in the ./src/examples/java directory illustrate the usage of the JMXProcessor Ant task.

When compiled, the Hello.java file (see the directory src/examples/java/spoon/jmx/example/) is genareted into a DynamicMBean (see ./target/spooned.src/ for the transformed code). The example includes a simple MBeanServer (AdvancedAgent.java), which instantiates and registers 2 MBeans.

Execute the example (mvn exec:java)

To browse the example:

  • use the JConsole 5 or 6 or the MC4J to connect the MBeanServer with this url service:jmx:rmi:///jndi/rmi://localhost:9999/server or with a local connection

  • connect to the server (with a local connection)
MBean Server Connection

MBean Server Connection

  • select the MBean named spoon.example:type=hello

  • select the tab Information

MBean information

MBean information

  • select the tab Attributes and modify the attribute counter

MBean Attributes

MBean Attributes

  • select the tab Operations and execute the sayHello method

MBean Operation

Operation

MBean Operation

Operation

  • select the tab Notification, subscribe to the notification and modify the attribute counter in the tab Attributes : the update is notified

MBean Notification history

MBean Notification history

TODOLIST

  1. see the TODOLIST.txt file in the project

References

  1. JMX Homepage
  2. JMX-related JSRs
  3. My course on JMX (mix of french and english
  4. Apache Felix MOSGi (MBeans are generated with an extension of SpoonJMX for OSGi : available soon !).
  5. Glassbox Inspector (this project combines AspectJ and JMX), https://glassbox-inspector.dev.java.net/
  6. Éamonn McManus, Jean-François Denise, Java Management Extensions (JMX) Technology Today and Tomorrow, Session TS-3523, JavaOne Conference 2006, http://java.sun.com/javaone/sf/2006.
  7. JSR 255: JavaTM Management Extensions (JMXTM) Specification, version 2.0 http://jcp.org/en/jsr/detail?id=255

Example of annotated POJO

The following POJO class Hello is annotated in order to be decorated as a DynamicMBean.


import javax.management.*;

@ManagedResource(
  objectName = "spoon.example:type=hello",
  description = "a simple mbean"
)
public class Hello {
  @ManagedAttribute(
    name = "prefix",
    description = "a read write attribute",
    notification = true,
    mode=AccessType.READWRITE
  )
  private String prefix = null;

  @ManagedAttribute(
    name = "counter",
    description = "a read only attribute",
    notification = true,
    mode=AccessType.READONLY
  )
  @Unit("Unit")
  @Range(minValue=0,maxValue=10000)
  private int count = 0;

  @ManagedAttribute(
    description = "a read only attribute"
    // notification = false,    // default for final field 
    // mode=AccessType.READONLY // default for final field 
  )
  private final int finalatt = 0;

  @ManagedAttribute(
    name = "globalCounter",
    description = "a read only attribute on a static field",
    notification = true,
    mode=AccessType.READONLY
  )
  private static int staticCount = 0;

  /**
   * Not in the MBeanInfo
   */
  private int notatt = 0;

  public Hello() {
    prefix = "Hello ";
    System.out.println("instance " + Hello.class.getName() + " with "
                       + prefix);
  }

  @MManagedConstructor(name="Hello", description="a simple constructor")
  public Hello(
      @ManagedOperationParamater(name = "prefix", description = "a prefix")
      String prefix) {
    this.prefix = prefix;
    System.out.println("instance " + Hello.class.getName() + " with "
                       + prefix);
  }

  @ManagedOperation(name = "sayHello", description = "a simple operation")
  public void sayHello(
      @ManagedOperationParamater(name = "message", description = "a message")
      String message
  ) {
    System.out.println(prefix+message);
    count++;       // triggers off a notification
    staticCount++; // triggers off a notification
  }

  @ManagedOperation(name = "reset", description = "reset the counter")
  private void reset() {
    count=0;
  }

  // this annotation should add a attribute "name" to the objectName after pre-registration
  @ObjectNameKey("name")
  private String getName() { return "hello1"; }



  /**
   * Not in the MBeanInfo
   *
   */
  public void noop() {}
}

The next code section instantiates and registeres the hello MBean with the name returned by the method preRegister of the MBeanRegistration interface. The returned objectName is the one provided by the annotation (ie. spoon.example:type=hello).

ObjectInstance objectInstance;
ObjectName objectName;
objectInstance = mbeanServer.registerMBean(
	new Hello(),
	null); // take the annotated objectName
objectName = objectInstance.getObjectName();
if(objectName==null)
	print(objectName.getCanonicalName() + " registered");
...

The next code section instanciates and registeres the hello MBean with the objectName spoon.example:type=hello2.

...
objectInstance = mbeanServer.registerMBean(
	new Hello(),
	new ObjectName("spoon.example:type=hello2"));
objectName = objectInstance.getObjectName();
if(objectName==null)
	print(objectName.getCanonicalName() + " registered");
...

Misc

Remarks and contributions are welcome !


Last modified: January 24, 2008, at 12:20 AM donsez