Controlling the Management Interface of Your Beans

In the example in the preceding section, you had little control over the management interface of your bean. All of the public properties and methods of each exported bean were 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.

Using the MBeanInfoAssembler API

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

Using Source-level Metadata: Java Annotations

By using the MetadataMBeanInfoAssembler, you can define the management interfaces for your beans by using source-level metadata. The reading of metadata is encapsulated by the org.springframework.jmx.export.metadata.JmxAttributeSource interface. Spring JMX provides a default implementation that uses Java annotations, namely org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource. You must configure the MetadataMBeanInfoAssembler with an implementation instance of the JmxAttributeSource interface for it to function correctly, since there is no default.

To mark a bean for export to JMX, you should annotate the bean class with the @ManagedResource annotation. You must annotate each method you wish to expose as an operation with the @ManagedOperation annotation and annotate each property you wish to expose with the @ManagedAttribute annotation. When annotating properties, you can omit either the annotation of the getter or the setter to create a write-only or read-only attribute, respectively.

A @ManagedResource-annotated bean must be public, as must the methods exposing operations or attributes.

The following example shows an annotated version of the JmxTestBean class that we used in Creating an MBeanServer.

package org.springframework.jmx;

@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 {

	private int age;
	private String name;

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

	@ManagedAttribute(description="The Age Attribute", currencyTimeLimit=15)
	public int getAge() {
		return this.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 this.name;
	}

	@ManagedOperation(description="Add two numbers")
	@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();
	}

}

In the preceding example, you can see that the AnnotationTestBean class is annotated with @ManagedResource and that this @ManagedResource annotation is configured with a set of attributes. These attributes can be used to configure various aspects of the MBean that is generated by the MBeanExporter and are explained in greater detail later in Spring JMX Annotations.

Both the age and name properties are annotated with @ManagedAttribute, but, in the case of the age property, only the getter method is annotated. This causes both of these properties to be included in the management interface as managed attributes, but the age attribute is read-only.

Finally, the add(int, int) method is annotated with @ManagedOperation, whereas the dontExposeMe() method is not. This causes the management interface to contain only one operation (add(int, int)) when you use the MetadataMBeanInfoAssembler.

The AnnotationTestBean class is not required to implement any Java interfaces, since the JMX management interface is derived solely from annotations.

The following configuration shows how you can configure the MBeanExporter to use the MetadataMBeanInfoAssembler:

<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>

	<!-- 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="jmxAttributeSource"
			class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>

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

</beans>

In the preceding example, a MetadataMBeanInfoAssembler bean has been configured with an instance of the AnnotationJmxAttributeSource class and passed to the MBeanExporter through the assembler property. This is all that is required to take advantage of annotation-driven management interfaces for your Spring-exposed MBeans.

Spring JMX Annotations

The following table describes the annotations that are available for use in Spring JMX:

Table 1. Spring JMX annotations
Annotation Applies to Description

@ManagedResource

Classes

Marks all instances of a Class as JMX managed resources.

@ManagedNotification

Classes

Indicates a JMX notification emitted by a managed resource.

@ManagedAttribute

Methods (only getters and setters)

Marks a getter or setter as one half of a JMX attribute.

@ManagedMetric

Methods (only getters)

Marks a getter as a JMX attribute, with added descriptor properties to indicate that it is a metric.

@ManagedOperation

Methods

Marks a method as a JMX operation.

@ManagedOperationParameter

Methods

Defines a description for an operation parameter.

The following table describes some of the common attributes that are available for use in these annotations. Consult the Javadoc for each annotation for further details.

Table 2. Spring JMX annotation attributes
Attribute Applies to Description

objectName

@ManagedResource

Used by MetadataNamingStrategy to determine the ObjectName of a managed resource.

description

@ManagedResource, @ManagedNotification, @ManagedAttribute, @ManagedMetric, @ManagedOperation, @ManagedOperationParameter

Sets the description of the resource, notification, attribute, metric, or operation.

currencyTimeLimit

@ManagedResource, @ManagedAttribute, @ManagedMetric

Sets the value of the currencyTimeLimit descriptor field.

defaultValue

@ManagedAttribute

Sets the value of the defaultValue descriptor field.

log

@ManagedResource

Sets the value of the log descriptor field.

logFile

@ManagedResource

Sets the value of the logFile descriptor field.

persistPolicy

@ManagedResource, @ManagedMetric

Sets the value of the persistPolicy descriptor field.

persistPeriod

@ManagedResource, @ManagedMetric

Sets the value of the persistPeriod descriptor field.

persistLocation

@ManagedResource

Sets the value of the persistLocation descriptor field.

persistName

@ManagedResource

Sets the value of the persistName descriptor field.

name

@ManagedOperationParameter

Sets the display name of an operation parameter.

index

@ManagedOperationParameter

Sets the index of an operation parameter.

Using the AutodetectCapableMBeanInfoAssembler Interface

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

The only implementation of the AutodetectCapableMBeanInfo interface is the MetadataMBeanInfoAssembler, which votes to include any bean that is marked with the ManagedResource attribute. The default approach in this case is to use the bean name as the ObjectName, which results in configuration similar to the following:

<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="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
		<property name="attributeSource">
			<bean class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>
		</property>
	</bean>

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

</beans>

Notice that, in the preceding configuration, no beans are passed to the MBeanExporter. However, the AnnotationTestBean is still registered, since it is annotated with @ManagedResource and the MetadataMBeanInfoAssembler detects this and votes to include it. The only downside with this approach is that the name of the AnnotationTestBean now has business meaning. You can address this issue by configuring an ObjectNamingStrategy as explained in Controlling ObjectName Instances for Your Beans. You can also see an example which uses the MetadataNamingStrategy in Using Source-level Metadata: Java Annotations.

Defining Management Interfaces by Using Java Interfaces

In addition to the MetadataMBeanInfoAssembler, Spring also includes the InterfaceBasedMBeanInfoAssembler, which lets you 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, InterfaceBasedMBeanInfoAssembler extends this functionality by removing the need for naming conventions, letting you use more than one interface and removing the need for your beans to implement the MBean interfaces.

Consider the following interface, which is used to define a management interface for the JmxTestBean class that we showed 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 are exposed as operations and attributes on the JMX MBean. The following code 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>

In the preceding example, 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 preceding case, 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 through 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, the InterfaceBasedMBeanInfoAssembler reflects on the bean and uses all of the interfaces implemented by that bean to create the management interface.

Using MethodNameBasedMBeanInfoAssembler

MethodNameBasedMBeanInfoAssembler lets you specify a list of method names that are exposed to JMX as attributes and operations. The following code shows a sample configuration:

<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>

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