22.3 Controlling the management interface of your beans

In the previous example, you had little control over the management interface of your bean; all of the public properties and methods of each exported bean was exposed as JMX attributes and operations respectively. To exercise finer-grained control over exactly which properties and methods of your exported beans are actually exposed as JMX attributes and operations, Spring JMX provides a comprehensive and extensible mechanism for controlling the management interfaces of your beans.

22.3.1 The MBeanInfoAssembler Interface

Behind the scenes, the MBeanExporter delegates to an implementation of the org.springframework.jmx.export.assembler.MBeanInfoAssembler interface which is responsible for defining the management interface of each bean that is being exposed. The default implementation, org.springframework.jmx.export.assembler.SimpleReflectiveMBeanInfoAssembler, simply defines a management interface that exposes all public properties and methods (as you saw in the previous examples). Spring provides two additional implementations of the MBeanInfoAssembler interface that allow you to control the generated management interface using either source-level metadata or any arbitrary interface.

22.3.2 Using source-Level metadata

Using the MetadataMBeanInfoAssembler you can define the management interfaces for your beans using source level metadata. The reading of metadata is encapsulated by the org.springframework.jmx.export.metadata.JmxAttributeSource interface. Out of the box, Spring JMX provides support for two implementations of this interface: org.springframework.jmx.export.metadata.AttributesJmxAttributeSource for Commons Attributes and org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource for JDK 5.0 annotations. The MetadataMBeanInfoAssembler must be configured with an implementation instance of the JmxAttributeSource interface for it to function correctly (there is no default). For the following example, we will use the Commons Attributes metadata approach.

To mark a bean for export to JMX, you should annotate the bean class with the ManagedResource attribute. In the case of the Commons Attributes metadata approach this class can be found in the org.springframework.jmx.metadata package. Each method you wish to expose as an operation must be marked with the ManagedOperation attribute and each property you wish to expose must be marked with the ManagedAttribute attribute. When marking properties you can omit either the annotation of the getter or the setter to create a write-only or read-only attribute respectively.

The example below shows the JmxTestBean class that you saw earlier marked with Commons Attributes metadata:

package org.springframework.jmx;

/**
 * @@org.springframework.jmx.export.metadata.ManagedResource
 *  (description="My Managed Bean", objectName="spring:bean=test",
 *  log=true, logFile="jmx.log", currencyTimeLimit=15, persistPolicy="OnUpdate",
 *  persistPeriod=200, persistLocation="foo", persistName="bar")
 */
public class JmxTestBean implements IJmxTestBean {

  private String name;

  private int age;


  /**
   * @@org.springframework.jmx.export.metadata.ManagedAttribute
   *   (description="The Age Attribute", currencyTimeLimit=15)
   */
  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }

  /**
   * @@org.springframework.jmx.export.metadata.ManagedAttribute
   *  (description="The Name Attribute",  currencyTimeLimit=20,
   *   defaultValue="bar", persistPolicy="OnUpdate")
   */
  public void setName(String name) {
    this.name = name;
  }

  /**
   * @@org.springframework.jmx.export.metadata.ManagedAttribute
   *   (defaultValue="foo", persistPeriod=300)
   */
  public String getName() {
    return name;
  }

  /**
   * @@org.springframework.jmx.export.metadata.ManagedOperation
   *  (description="Add Two Numbers Together")
   */
  public int add(int x, int y) {
    return x + y;
  }

  public void dontExposeMe() {
    throw new RuntimeException();
  }
}

Here you can see that the JmxTestBean class is marked with the ManagedResource attribute and that this ManagedResource attribute is configured with a set of properties. These properties can be used to configure various aspects of the MBean that is generated by the MBeanExporter, and are explained in greater detail later in section entitled Section 22.3.4, “Source-Level Metadata Types”.

You will also notice that both the age and name properties are annotated with the ManagedAttribute attribute, but in the case of the age property, only the getter is marked. This will cause both of these properties to be included in the management interface as attributes, but the age attribute will be read-only.

Finally, you will notice that the add(int, int) method is marked with the ManagedOperation attribute whereas the dontExposeMe() method is not. This will cause the management interface to contain only one operation, add(int, int), when using the MetadataMBeanInfoAssembler.

The code below shows how you configure the MBeanExporter to use the MetadataMBeanInfoAssembler:

<beans>

  <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
    <property name="beans">
      <map>
        <entry key="bean:name=testBean1" value-ref="testBean"/>
      </map>
    </property>
    <property name="assembler" ref="assembler"/>
  </bean>

  <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
    <property name="name" value="TEST"/>
    <property name="age" value="100"/>
  </bean>

  <bean id="attributeSource"
        class="org.springframework.jmx.export.metadata.AttributesJmxAttributeSource">
    <property name="attributes">
      <bean class="org.springframework.metadata.commons.CommonsAttributes"/>
    </property>
  </bean>

  <bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
    <property name="attributeSource" ref="attributeSource"/>
  </bean>

</beans>

Here you can see that an MetadataMBeanInfoAssembler bean has been configured with an instance of the AttributesJmxAttributeSource class and passed to the MBeanExporter through the assembler property. This is all that is required to take advantage of metadata-driven management interfaces for your Spring-exposed MBeans.

22.3.3 Using JDK 5.0 Annotations

To enable the use of JDK 5.0 annotations for management interface definition, Spring provides a set of annotations that mirror the Commons Attribute attribute classes and an implementation of the JmxAttributeSource strategy interface, the AnnotationsJmxAttributeSource class, that allows the MBeanInfoAssembler to read them.

The example below shows a bean where the management interface is defined by the presence of JDK 5.0 annotation types:

package org.springframework.jmx;

import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedAttribute;

@ManagedResource(objectName="bean:name=testBean4", description="My Managed Bean", log=true,
    logFile="jmx.log", currencyTimeLimit=15, persistPolicy="OnUpdate", persistPeriod=200,
    persistLocation="foo", persistName="bar")
public class AnnotationTestBean implements IJmxTestBean {

  private String name;
  private int age;

  @ManagedAttribute(description="The Age Attribute", currencyTimeLimit=15)
  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }

  @ManagedAttribute(description="The Name Attribute",
      currencyTimeLimit=20,
      defaultValue="bar",
      persistPolicy="OnUpdate")
  public void setName(String name) {
    this.name = name;
  }

  @ManagedAttribute(defaultValue="foo", persistPeriod=300)
  public String getName() {
    return name;
  }

  @ManagedOperation(description="Add two numbers")
  @ManagedOperationParameters({
    @ManagedOperationParameter(name = "x", description = "The first number"),
    @ManagedOperationParameter(name = "y", description = "The second number")})
  public int add(int x, int y) {
    return x + y;
  }

  public void dontExposeMe() {
    throw new RuntimeException();
  }
}

As you can see little has changed, other than the basic syntax of the metadata definitions.

<beans>
    <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="assembler" ref="assembler"/>
        <property name="namingStrategy" ref="namingStrategy"/>
        <property name="autodetect" value="true"/>
    </bean>

    <bean id="jmxAttributeSource"
          class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>

    <!-- will create management interface using annotation metadata -->
    <bean id="assembler"
          class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
        <property name="attributeSource" ref="jmxAttributeSource"/>
    </bean>

    <!-- will pick up the ObjectName from the annotation -->
    <bean id="namingStrategy"
          class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
        <property name="attributeSource" ref="jmxAttributeSource"/>
    </bean>

    <bean id="testBean" class="org.springframework.jmx.AnnotationTestBean">
        <property name="name" value="TEST"/>
        <property name="age" value="100"/>
    </bean>
</beans>

22.3.4 Source-Level Metadata Types

The following source level metadata types are available for use in Spring JMX:

Table 22.2. Source-Level Metadata Types

PurposeCommons Attributes AttributeJDK 5.0 AnnotationAttribute / Annotation Type
Mark all instances of a Class as JMX managed resourcesManagedResource@ManagedResourceClass
Mark a method as a JMX operationManagedOperation@ManagedOperationMethod
Mark a getter or setter as one half of a JMX attributeManagedAttribute@ManagedAttributeMethod (only getters and setters)
Define descriptions for operation parametersManagedOperationParameter@ManagedOperationParameter and @ManagedOperationParametersMethod


The following configuration parameters are available for use on these source-level metadata types:

Table 22.3. Source-Level Metadata Parameters

ParameterDescriptionApplies to
ObjectNameUsed by MetadataNamingStrategy to determine the ObjectName of a managed resourceManagedResource
descriptionSets the friendly description of the resource, attribute or operationManagedResource, ManagedAttribute, ManagedOperation, ManagedOperationParameter
currencyTimeLimitSets the value of the currencyTimeLimit descriptor fieldManagedResource, ManagedAttribute
defaultValueSets the value of the defaultValue descriptor fieldManagedAttribute
logSets the value of the log descriptor fieldManagedResource
logFileSets the value of the logFile descriptor fieldManagedResource
persistPolicySets the value of the persistPolicy descriptor fieldManagedResource
persistPeriodSets the value of the persistPeriod descriptor fieldManagedResource
persistLocationSets the value of the persistLocation descriptor fieldManagedResource
persistNameSets the value of the persistName descriptor fieldManagedResource
nameSets the display name of an operation parameterManagedOperationParameter
indexSets the index of an operation parameterManagedOperationParameter


22.3.5 The AutodetectCapableMBeanInfoAssembler interface

To simplify configuration even further, Spring introduces the AutodetectCapableMBeanInfoAssembler interface which extends the MBeanInfoAssembler interface to add support for autodetection of MBean resources. If you configure the MBeanExporter with an instance of AutodetectCapableMBeanInfoAssembler then it is allowed to "vote" on the inclusion of beans for exposure to JMX.

Out of the box, the only implementation of the AutodetectCapableMBeanInfo interface is the MetadataMBeanInfoAssembler which will vote to include any bean which is marked with the ManagedResource attribute. The default approach in this case is to use the bean name as the ObjectName which results in a configuration like this:

<beans>

  <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
    <!-- notice how no 'beans' are explicitly configured here -->
    <property name="autodetect" value="true"/>
    <property name="assembler" ref="assembler"/>
  </bean>

  <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
    <property name="name" value="TEST"/>
    <property name="age" value="100"/>
  </bean>

  <!-- (for Commons Attributes-based metadata) -->
  <bean id="attributeSource"
        class="org.springframework.jmx.export.metadata.AttributesJmxAttributeSource">
    <property name="attributes">
      <bean class="org.springframework.metadata.commons.CommonsAttributes"/>
    </property>
  </bean>
  
  <!-- (for Java 5+ annotations-based metadata) -->
  <!--
  <bean id="attributeSource"
        class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>
  -->

  <bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
    <property name="attributeSource" ref="attributeSource"/>
  </bean>

</beans>

Notice that in this configuration no beans are passed to the MBeanExporter; however, the JmxTestBean will still be registered since it is marked with the ManagedResource attribute and the MetadataMBeanInfoAssembler detects this and votes to include it. The only problem with this approach is that the name of the JmxTestBean now has business meaning. You can address this issue by changing the default behavior for ObjectName creation as defined in Section 22.4, “Controlling the ObjectNames for your beans”.

22.3.6 Defining management interfaces using Java interfaces

In addition to the MetadataMBeanInfoAssembler, Spring also includes the InterfaceBasedMBeanInfoAssembler which allows you to constrain the methods and properties that are exposed based on the set of methods defined in a collection of interfaces.

Although the standard mechanism for exposing MBeans is to use interfaces and a simple naming scheme, the InterfaceBasedMBeanInfoAssembler extends this functionality by removing the need for naming conventions, allowing you to use more than one interface and removing the need for your beans to implement the MBean interfaces.

Consider this interface that is used to define a management interface for the JmxTestBean class that you saw earlier:

public interface IJmxTestBean {

  public int add(int x, int y);

  public long myOperation();

  public int getAge();

  public void setAge(int age);

  public void setName(String name);

  public String getName();
}

This interface defines the methods and properties that will be exposed as operations and attributes on the JMX MBean. The code below shows how to configure Spring JMX to use this interface as the definition for the management interface:

<beans>

  <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
    <property name="beans">
      <map>
        <entry key="bean:name=testBean5" value-ref="testBean"/>
      </map>
    </property>
    <property name="assembler">
      <bean class="org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler">
        <property name="managedInterfaces">
          <value>org.springframework.jmx.IJmxTestBean</value>
        </property>
      </bean>
    </property>
  </bean>

  <bean id="testBean" class="org.springframework.jmx.JmxTestBean">
    <property name="name" value="TEST"/>
    <property name="age" value="100"/>
  </bean>

</beans>

Here you can see that the InterfaceBasedMBeanInfoAssembler is configured to use the IJmxTestBean interface when constructing the management interface for any bean. It is important to understand that beans processed by the InterfaceBasedMBeanInfoAssembler are not required to implement the interface used to generate the JMX management interface.

In the case above, the IJmxTestBean interface is used to construct all management interfaces for all beans. In many cases this is not the desired behavior and you may want to use different interfaces for different beans. In this case, you can pass InterfaceBasedMBeanInfoAssembler a Properties instance via the interfaceMappings property, where the key of each entry is the bean name and the value of each entry is a comma-separated list of interface names to use for that bean.

If no management interface is specified through either the managedInterfaces or interfaceMappings properties, then the InterfaceBasedMBeanInfoAssembler will reflect on the bean and use all of the interfaces implemented by that bean to create the management interface.

22.3.7 Using MethodNameBasedMBeanInfoAssembler

The MethodNameBasedMBeanInfoAssembler allows you to specify a list of method names that will be exposed to JMX as attributes and operations. The code below shows a sample configuration for this:

<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
    <property name="beans">
      <map>
        <entry key="bean:name=testBean5" value-ref="testBean"/>
      </map>
    </property>
    <property name="assembler">
      <bean class="org.springframework.jmx.export.assembler.MethodNameBasedMBeanInfoAssembler">
        <property name="managedMethods">
          <value>add,myOperation,getName,setName,getAge</value>
        </property>
      </bean>
    </property>
</bean>

Here you can see that the methods add and myOperation will be exposed as JMX operations and getName(), setName(String) and getAge() will be exposed as the appropriate half of a JMX attribute. In the code above, the method mappings apply to beans that are exposed to JMX. To control method exposure on a bean-by-bean basis, use the methodMappings property of MethodNameMBeanInfoAssembler to map bean names to lists of method names.