The JMX support in Spring provides you with the features to easily and transparently integrate your Spring application into a JMX infrastructure.
Specifically, Spring's JMX support provides four core features:
The automatic registration of any Spring bean as a JMX MBean
A flexible mechanism for controlling the management interface of your beans
The declarative exposure of MBeans over remote, JSR-160 connectors
The simple proxying of both local and remote MBean resources
These features are designed to work without coupling your application components to either Spring or JMX interfaces and classes. Indeed, for the most part your application classes need not be aware of either Spring or JMX in order to take advantage of the Spring JMX features.
The core class in Spring's JMX framework is the
MBeanExporter. This class is responsible for taking
your Spring beans and registering them with a JMX
MBeanServer. For example, consider the following
class:
package org.springframework.jmx; public class JmxTestBean implements IJmxTestBean { private String name; private int age; private boolean isSuperman; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } public String getName() { return name; } public int add(int x, int y) { return x + y; } public void dontExposeMe() { throw new RuntimeException(); } }
To expose the properties and methods of this bean as attributes and
operations of an MBean you simply configure an instance of the
MBeanExporter class in your configuration file and
pass in the bean as shown below:
<beans> <!-- this bean must not be lazily initialized if the exporting is to happen --> <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false"> <property name="beans"> <map> <entry key="bean:name=testBean1" value-ref="testBean"/> </map> </property> </bean> <bean id="testBean" class="org.springframework.jmx.JmxTestBean"> <property name="name" value="TEST"/> <property name="age" value="100"/> </bean> </beans>
The pertinent bean definition from the above configuration snippet
is the exporter bean. The beans
property tells the MBeanExporter exactly which of
your beans must be exported to the JMX MBeanServer.
In the default configuration, the key of each entry in the
beans Map is used as the
ObjectName for the bean referenced by the
corresponding entry value. This behavior can be changed as described in
Section 24.4, “Controlling the ObjectNames for your beans”.
With this configuration the testBean bean is
exposed as an MBean under the ObjectName
bean:name=testBean1. By default, all
public properties of the bean are exposed as
attributes and all public methods (bar those
inherited from the Object class) are exposed as
operations.
The above configuration assumes that the application is running in
an environment that has one (and only one)
MBeanServer already running. In this case, Spring
will attempt to locate the running MBeanServer
and register your beans with that server (if any). This behavior is
useful when your application is running inside a container such as
Tomcat or IBM WebSphere that has its own
MBeanServer.
However, this approach is of no use in a standalone environment,
or when running inside a container that does not provide an
MBeanServer. To address this you can create an
MBeanServer instance declaratively by adding an
instance of the
org.springframework.jmx.support.MBeanServerFactoryBean
class to your configuration. You can also ensure that a specific
MBeanServer is used by setting the value of the
MBeanExporter's server
property to the MBeanServer value returned by an
MBeanServerFactoryBean; for example:
<beans> <bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean"/> <!-- this bean needs to be eagerly pre-instantiated in order for the exporting to occur; this means that it must not be marked as lazily initialized --> <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="server" ref="mbeanServer"/> </bean> <bean id="testBean" class="org.springframework.jmx.JmxTestBean"> <property name="name" value="TEST"/> <property name="age" value="100"/> </bean> </beans>
Here an instance of MBeanServer is created
by the MBeanServerFactoryBean and is supplied to
the MBeanExporter via the server property. When
you supply your own MBeanServer instance, the
MBeanExporter will not attempt to locate a
running MBeanServer and will use the supplied
MBeanServer instance. For this to work correctly,
you must (of course) have a JMX implementation on your classpath.
If no server is specified, the MBeanExporter
tries to automatically detect a running MBeanServer.
This works in most environment where only one
MBeanServer instance is used, however when multiple
instances exist, the exporter might pick the wrong server. In such
cases, one should use the MBeanServer
agentId to indicate which instance to be used:
<beans> <bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean"> <!-- indicate to first look for a server --> <property name="locateExistingServerIfPossible" value="true"/> <!-- search for the MBeanServer instance with the given agentId --> <property name="agentId" value="<MBeanServer instance agentId>"/> </bean> <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> <property name="server" ref="mbeanServer"/> ... </bean> </beans>
For platforms/cases where the existing MBeanServer
has a dynamic (or unknown) agentId which is retrieved through lookup
methods, one should use factory-method:
<beans> <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> <property name="server"> <!-- Custom MBeanServerLocator --> <bean class="platform.package.MBeanServerLocator" factory-method="locateMBeanServer"/> </property> <!-- other beans here --> </bean> </beans>
If you configure a bean with the
MBeanExporter that is also configured for lazy
initialization, then the MBeanExporter will
not break this contract and will avoid
instantiating the bean. Instead, it will register a proxy with
the MBeanServer and will defer obtaining the bean
from the container until the first invocation on the proxy occurs.
Any beans that are exported through the
MBeanExporter and are already valid MBeans are
registered as-is with the MBeanServer without
further intervention from Spring. MBeans can be automatically detected
by the MBeanExporter by setting the
autodetect property to true:
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> <property name="autodetect" value="true"/> </bean> <bean name="spring:mbean=true" class="org.springframework.jmx.export.TestDynamicMBean"/>
Here, the bean called spring:mbean=true is
already a valid JMX MBean and will be automatically registered by
Spring. By default, beans that are autodetected for JMX registration
have their bean name used as the ObjectName. This
behavior can be overridden as detailed in Section 24.4, “Controlling the ObjectNames for your beans”.
Consider the scenario where a Spring
MBeanExporter attempts to register an
MBean with an MBeanServer
using the ObjectName
'bean:name=testBean1'. If an
MBean instance has already been registered under
that same ObjectName, the default behavior is to
fail (and throw an
InstanceAlreadyExistsException).
It is possible to control the behavior of exactly what happens
when an MBean is registered with an
MBeanServer. Spring's JMX support allows for
three different registration behaviors to control the registration
behavior when the registration process finds that an
MBean has already been registered under the same
ObjectName; these registration behaviors are
summarized on the following table:
Table 24.1. Registration Behaviors
| Registration behavior | Explanation |
|---|---|
| This is the default registration behavior. If an
|
| If an This is useful in settings where multiple applications
want to share a common |
| If an |
The above values are defined as constants on the
MBeanRegistrationSupport class (the
MBeanExporter class derives from this
superclass). If you want to change the default registration behavior,
you simply need to set the value of the
registrationBehaviorName property on your
MBeanExporter definition to one of those
values.
The following example illustrates how to effect a change from the
default registration behavior to the
REGISTRATION_REPLACE_EXISTING behavior:
<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="registrationBehaviorName" value="REGISTRATION_REPLACE_EXISTING"/> </bean> <bean id="testBean" class="org.springframework.jmx.JmxTestBean"> <property name="name" value="TEST"/> <property name="age" value="100"/> </bean> </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.
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.
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. Spring JMX provides a default implementation which uses JDK 5.0 annotations, namely
org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource. The
MetadataMBeanInfoAssembler
must be configured with an implementation instance
of the JmxAttributeSource interface for it to
function correctly (there is no default).
To mark a bean for export to JMX, you should annotate the bean
class with the ManagedResource annotation. Each
method you wish to expose as an operation must be marked with the
ManagedOperation annotation and each property you
wish to expose must be marked with the
ManagedAttribute annotation. 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 annotated version of the
JmxTestBean class that you saw earlier:
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(); } }
Here you can see that the JmxTestBean class
is marked with the ManagedResource annotation and
that this ManagedResource annotation 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 24.3.3, “Source-Level Metadata Types”.
You will also notice that both the age and
name properties are annotated with the
ManagedAttribute annotation, 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 configuration below shows how you 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> <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>
Here you can see that an
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 metadata-driven
management interfaces for your Spring-exposed MBeans.
The following source level metadata types are available for use in Spring JMX:
Table 24.2. Source-Level Metadata Types
| Purpose | Annotation | Annotation Type |
|---|---|---|
Mark all instances of a Class as
JMX managed resources | @ManagedResource | Class |
| Mark a method as a JMX operation | @ManagedOperation | Method |
| Mark a getter or setter as one half of a JMX attribute | @ManagedAttribute | Method (only getters and setters) |
| Define descriptions for operation parameters | @ManagedOperationParameter and
@ManagedOperationParameters | Method |
The following configuration parameters are available for use on these source-level metadata types:
Table 24.3. Source-Level Metadata Parameters
| Parameter | Description | Applies to |
|---|---|---|
ObjectName | Used by MetadataNamingStrategy
to determine the ObjectName of a
managed resource | ManagedResource |
description | Sets the friendly description of the resource, attribute or operation | ManagedResource,
ManagedAttribute,
ManagedOperation,
ManagedOperationParameter |
currencyTimeLimit | Sets the value of the
currencyTimeLimit descriptor field | ManagedResource,
ManagedAttribute |
defaultValue | Sets the value of the defaultValue
descriptor field | ManagedAttribute |
log | Sets the value of the log descriptor
field | ManagedResource |
logFile | Sets the value of the logFile
descriptor field | ManagedResource |
persistPolicy | Sets the value of the persistPolicy
descriptor field | ManagedResource |
persistPeriod | Sets the value of the persistPeriod
descriptor field | ManagedResource |
persistLocation | Sets the value of the
persistLocation descriptor field | ManagedResource |
persistName | Sets the value of the persistName
descriptor field | ManagedResource |
name | Sets the display name of an operation parameter | ManagedOperationParameter |
index | Sets the index of an operation parameter | ManagedOperationParameter |
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> <bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler"> <property name="attributeSource"> <bean class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/> </property> </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 24.4, “Controlling the ObjectNames for your beans”.
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.
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.
Behind the scenes, the MBeanExporter
delegates to an implementation of the
ObjectNamingStrategy to obtain
ObjectNames for each of the beans it is
registering. The default implementation,
KeyNamingStrategy, will, by default, use the key of
the beans Map as the
ObjectName. In addition, the
KeyNamingStrategy can map the key of the
beans Map to an entry in a
Properties file (or files) to resolve the
ObjectName. In addition to the
KeyNamingStrategy, Spring provides two additional
ObjectNamingStrategy implementations: the
IdentityNamingStrategy that builds an
ObjectName based on the JVM identity of the bean
and the MetadataNamingStrategy that uses source
level metadata to obtain the ObjectName.
You can configure your own
KeyNamingStrategy instance and configure it to
read ObjectNames from a
Properties instance rather than use bean key. The
KeyNamingStrategy will attempt to locate an entry
in the Properties with a key corresponding to the
bean key. If no entry is found or if the
Properties instance is null
then the bean key itself is used.
The code below shows a sample configuration for the
KeyNamingStrategy:
<beans> <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> <property name="beans"> <map> <entry key="testBean" value-ref="testBean"/> </map> </property> <property name="namingStrategy" ref="namingStrategy"/> </bean> <bean id="testBean" class="org.springframework.jmx.JmxTestBean"> <property name="name" value="TEST"/> <property name="age" value="100"/> </bean> <bean id="namingStrategy" class="org.springframework.jmx.export.naming.KeyNamingStrategy"> <property name="mappings"> <props> <prop key="testBean">bean:name=testBean1</prop> </props> </property> <property name="mappingLocations"> <value>names1.properties,names2.properties</value> </property> </bean </beans>
Here an instance of KeyNamingStrategy is
configured with a Properties instance that is
merged from the Properties instance defined by
the mapping property and the properties files located in the paths
defined by the mappings property. In this configuration, the
testBean bean will be given the
ObjectName bean:name=testBean1
since this is the entry in the Properties
instance that has a key corresponding to the bean key.
If no entry in the Properties instance can
be found then the bean key name is used as the
ObjectName.
The MetadataNamingStrategy uses
the objectName property of the
ManagedResource attribute on each bean to create
the ObjectName. The code below shows the
configuration for the
MetadataNamingStrategy:
<beans> <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> <property name="beans"> <map> <entry key="testBean" value-ref="testBean"/> </map> </property> <property name="namingStrategy" ref="namingStrategy"/> </bean> <bean id="testBean" class="org.springframework.jmx.JmxTestBean"> <property name="name" value="TEST"/> <property name="age" value="100"/> </bean> <bean id="namingStrategy" class="org.springframework.jmx.export.naming.MetadataNamingStrategy"> <property name="attributeSource" ref="attributeSource"/> </bean> <bean id="attributeSource" class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/> </beans>
If no objectName has been provided for
the ManagedResource attribute, then an
ObjectName will be created with the
following format:
[fully-qualified-package-name]:type=[short-classname],name=[bean-name].
For example, the generated ObjectName for the
following bean would be: com.foo:type=MyClass,name=myBean.
<bean id="myBean" class="com.foo.MyClass"/>
If you prefer using the annotation based
approach to define your management interfaces, then a convenience subclass of
MBeanExporter is available:
AnnotationMBeanExporter.
When defining an instance of this subclass, the namingStrategy,
assembler, and attributeSource
configuration is no longer needed, since it will always use standard Java
annotation-based metadata (autodetection is always enabled as well). In fact,
rather than defining an MBeanExporter bean, an even
simpler syntax is supported by the @EnableMBeanExport
@Configuration annotation.
@Configuration @EnableMBeanExport public class AppConfig { }
If you prefer XML based configuration the 'context:mbean-export'
element serves the same purpose.
<context:mbean-export/>
You can provide a reference to a particular MBean server if
necessary, and the defaultDomain attribute
(a property of AnnotationMBeanExporter)
accepts an alternate value for the generated MBean
ObjectNames' domains. This would be used
in place of the fully qualified package name as described in the
previous section on
MetadataNamingStrategy.
@EnableMBeanExport(server="myMBeanServer", defaultDomain="myDomain") @Configuration ContextConfiguration { }
<context:mbean-export server="myMBeanServer" default-domain="myDomain"/>
![]() | Note |
|---|---|
|
Do not use interface-based AOP proxies in combination with autodetection of
JMX annotations in your bean classes. Interface-based proxies 'hide' the target class,
which also hides the JMX managed resource annotations. Hence, use target-class proxies
in that case: through setting the 'proxy-target-class' flag on |
For remote access, Spring JMX module offers two
FactoryBean implementations inside the
org.springframework.jmx.support package for creating
both server- and client-side connectors.
To have Spring JMX create, start and expose a JSR-160
JMXConnectorServer use the following
configuration:
<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean"/>
By default ConnectorServerFactoryBean creates a
JMXConnectorServer bound to
"service:jmx:jmxmp://localhost:9875". The
serverConnector bean thus exposes the local
MBeanServer to clients through the JMXMP protocol
on localhost, port 9875. Note that the JMXMP protocol is marked as
optional by the JSR 160 specification: currently, the main open-source
JMX implementation, MX4J, and the one provided with J2SE 5.0 do
not support JMXMP.
To specify another URL and register the
JMXConnectorServer itself with the
MBeanServer use the serviceUrl
and ObjectName properties respectively:
<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean"> <property name="objectName" value="connector:name=rmi"/> <property name="serviceUrl" value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/myconnector"/> </bean>
If the ObjectName property is set Spring
will automatically register your connector with the
MBeanServer under that
ObjectName. The example below shows the full set
of parameters which you can pass to the
ConnectorServerFactoryBean when creating a
JMXConnector:
<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean"> <property name="objectName" value="connector:name=iiop"/> <property name="serviceUrl" value="service:jmx:iiop://localhost/jndi/iiop://localhost:900/myconnector"/> <property name="threaded" value="true"/> <property name="daemon" value="true"/> <property name="environment"> <map> <entry key="someKey" value="someValue"/> </map> </property> </bean>
Note that when using a RMI-based connector you need the lookup service (tnameserv or rmiregistry) to be started in order for the name registration to complete. If you are using Spring to export remote services for you via RMI, then Spring will already have constructed an RMI registry. If not, you can easily start a registry using the following snippet of configuration:
<bean id="registry" class="org.springframework.remoting.rmi.RmiRegistryFactoryBean"> <property name="port" value="1099"/> </bean>
To create an MBeanServerConnection to a
remote JSR-160 enabled MBeanServer use the
MBeanServerConnectionFactoryBean as shown
below:
<bean id="clientConnector" class="org.springframework.jmx.support.MBeanServerConnectionFactoryBean"> <property name="serviceUrl" value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi"/> </bean>
JSR-160 permits extensions to the way in which communication is done between the client and the server. The examples above are using the mandatory RMI-based implementation required by the JSR-160 specification (IIOP and JRMP) and the (optional) JMXMP. By using other providers or JMX implementations (such as MX4J) you can take advantage of protocols like SOAP, Hessian, Burlap over simple HTTP or SSL and others:
<bean id="serverConnector" class="org.springframework.jmx.support.ConnectorServerFactoryBean"> <property name="objectName" value="connector:name=burlap"/> <property name="serviceUrl" value="service:jmx:burlap://localhost:9874"/> </bean>
In the case of the above example, MX4J 3.0.0 was used; see the official MX4J documentation for more information.
Spring JMX allows you to create proxies that re-route calls to
MBeans registered in a local or remote MBeanServer.
These proxies provide you with a standard Java interface through which you
can interact with your MBeans. The code below shows how to configure a
proxy for an MBean running in a local
MBeanServer:
<bean id="proxy" class="org.springframework.jmx.access.MBeanProxyFactoryBean"> <property name="objectName" value="bean:name=testBean"/> <property name="proxyInterface" value="org.springframework.jmx.IJmxTestBean"/> </bean>
Here you can see that a proxy is created for the MBean registered
under the ObjectName:
bean:name=testBean. The set of interfaces that the
proxy will implement is controlled by the
proxyInterfaces property and the rules for mapping
methods and properties on these interfaces to operations and attributes on
the MBean are the same rules used by the
InterfaceBasedMBeanInfoAssembler.
The MBeanProxyFactoryBean can create a proxy
to any MBean that is accessible via an
MBeanServerConnection. By default, the local
MBeanServer is located and used, but you can
override this and provide an MBeanServerConnection
pointing to a remote MBeanServer to cater for
proxies pointing to remote MBeans:
<bean id="clientConnector" class="org.springframework.jmx.support.MBeanServerConnectionFactoryBean"> <property name="serviceUrl" value="service:jmx:rmi://remotehost:9875"/> </bean> <bean id="proxy" class="org.springframework.jmx.access.MBeanProxyFactoryBean"> <property name="objectName" value="bean:name=testBean"/> <property name="proxyInterface" value="org.springframework.jmx.IJmxTestBean"/> <property name="server" ref="clientConnector"/> </bean>
Here you can see that we create an
MBeanServerConnection pointing to a remote machine
using the MBeanServerConnectionFactoryBean. This
MBeanServerConnection is then passed to the
MBeanProxyFactoryBean via the
server property. The proxy that is created will forward
all invocations to the MBeanServer via this
MBeanServerConnection.
Spring's JMX offering includes comprehensive support for JMX notifications.
Spring's JMX support makes it very easy to register any number of
NotificationListeners with any number of MBeans
(this includes MBeans exported by Spring's
MBeanExporter and MBeans registered via some
other mechanism). By way of an example, consider the scenario where one
would like to be informed (via a Notification)
each and every time an attribute of a target MBean changes.
package com.example; import javax.management.AttributeChangeNotification; import javax.management.Notification; import javax.management.NotificationFilter; import javax.management.NotificationListener; public class ConsoleLoggingNotificationListener implements NotificationListener, NotificationFilter { public void handleNotification(Notification notification, Object handback) { System.out.println(notification); System.out.println(handback); } public boolean isNotificationEnabled(Notification notification) { return AttributeChangeNotification.class.isAssignableFrom(notification.getClass()); } }
<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="notificationListenerMappings"> <map> <entry key="bean:name=testBean1"> <bean class="com.example.ConsoleLoggingNotificationListener"/> </entry> </map> </property> </bean> <bean id="testBean" class="org.springframework.jmx.JmxTestBean"> <property name="name" value="TEST"/> <property name="age" value="100"/> </bean> </beans>
With the above configuration in place, every time a JMX
Notification is broadcast from the target MBean
(bean:name=testBean1), the
ConsoleLoggingNotificationListener bean that was
registered as a listener via the
notificationListenerMappings property will be
notified. The ConsoleLoggingNotificationListener
bean can then take whatever action it deems appropriate in response to
the Notification.
You can also use straight bean names as the link between exported beans and listeners:
<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="notificationListenerMappings"> <map> <entry key="testBean"> <bean class="com.example.ConsoleLoggingNotificationListener"/> </entry> </map> </property> </bean> <bean id="testBean" class="org.springframework.jmx.JmxTestBean"> <property name="name" value="TEST"/> <property name="age" value="100"/> </bean> </beans>
If one wants to register a single NotificationListener
instance for all of the beans that the enclosing MBeanExporter
is exporting, one can use the special wildcard '*' (sans quotes)
as the key for an entry in the notificationListenerMappings
property map; for example:
<property name="notificationListenerMappings"> <map> <entry key="*"> <bean class="com.example.ConsoleLoggingNotificationListener"/> </entry> </map> </property>
If one needs to do the inverse (that is, register a number of distinct
listeners against an MBean), then one has to use the
notificationListeners list property instead (and in
preference to the notificationListenerMappings
property). This time, instead of configuring simply a
NotificationListener for a single MBean, one
configures NotificationListenerBean instances...
a NotificationListenerBean encapsulates a
NotificationListener and the
ObjectName (or
ObjectNames) that it is to be registered against
in an MBeanServer. The
NotificationListenerBean also encapsulates a
number of other properties such as a
NotificationFilter and an arbitrary handback
object that can be used in advanced JMX notification scenarios.
The configuration when using
NotificationListenerBean instances is not wildly
different to what was presented previously:
<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="notificationListeners"> <list> <bean class="org.springframework.jmx.export.NotificationListenerBean"> <constructor-arg> <bean class="com.example.ConsoleLoggingNotificationListener"/> </constructor-arg> <property name="mappedObjectNames"> <list> <value>bean:name=testBean1</value> </list> </property> </bean> </list> </property> </bean> <bean id="testBean" class="org.springframework.jmx.JmxTestBean"> <property name="name" value="TEST"/> <property name="age" value="100"/> </bean> </beans>
The above example is equivalent to the first notification example.
Lets assume then that we want to be given a handback object every time a
Notification is raised, and that additionally we
want to filter out extraneous Notifications by
supplying a NotificationFilter. (For a full
discussion of just what a handback object is, and indeed what a
NotificationFilter is, please do consult that
section of the JMX specification (1.2) entitled 'The JMX
Notification Model'.)
<beans> <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter"> <property name="beans"> <map> <entry key="bean:name=testBean1" value-ref="testBean1"/> <entry key="bean:name=testBean2" value-ref="testBean2"/> </map> </property> <property name="notificationListeners"> <list> <bean class="org.springframework.jmx.export.NotificationListenerBean"> <constructor-arg ref="customerNotificationListener"/> <property name="mappedObjectNames"> <list> <!-- handles notifications from two distinct MBeans --> <value>bean:name=testBean1</value> <value>bean:name=testBean2</value> </list> </property> <property name="handback"> <bean class="java.lang.String"> <constructor-arg value="This could be anything..."/> </bean> </property> <property name="notificationFilter" ref="customerNotificationListener"/> </bean> </list> </property> </bean> <!-- implements both theNotificationListenerandNotificationFilterinterfaces --> <bean id="customerNotificationListener" class="com.example.ConsoleLoggingNotificationListener"/> <bean id="testBean1" class="org.springframework.jmx.JmxTestBean"> <property name="name" value="TEST"/> <property name="age" value="100"/> </bean> <bean id="testBean2" class="org.springframework.jmx.JmxTestBean"> <property name="name" value="ANOTHER TEST"/> <property name="age" value="200"/> </bean> </beans>
Spring provides support not just for registering to receive
Notifications, but also for publishing
Notifications.
![]() | Note |
|---|---|
|
Please note that this section is really only relevant to Spring
managed beans that have been exposed as MBeans via an
|
The key interface in Spring's JMX notification publication support
is the NotificationPublisher interface (defined
in the org.springframework.jmx.export.notification
package). Any bean that is going to be exported as an MBean via an
MBeanExporter instance can implement the related
NotificationPublisherAware interface to gain
access to a NotificationPublisher instance. The
NotificationPublisherAware interface simply
supplies an instance of a NotificationPublisher
to the implementing bean via a simple setter method, which the bean can
then use to publish Notifications.
As stated in the Javadoc for the
NotificationPublisher class, managed beans that
are publishing events via the
NotificationPublisher mechanism are
not responsible for the state management of any
notification listeners and the like ... Spring's JMX support will take
care of handling all the JMX infrastructure issues. All one need do as
an application developer is implement the
NotificationPublisherAware interface and start
publishing events using the supplied
NotificationPublisher instance. Note that the
NotificationPublisher will be set
after the managed bean has been registered with an
MBeanServer.
Using a NotificationPublisher instance is
quite straightforward... one simply creates a JMX
Notification instance (or an instance of an
appropriate Notification subclass), populates
the notification with the data pertinent to the event that is to be
published, and one then invokes the
sendNotification(Notification) on the
NotificationPublisher instance, passing in the
Notification.
Find below a simple example... in this scenario, exported
instances of the JmxTestBean are going to publish
a NotificationEvent every time the
add(int, int) operation is invoked.
package org.springframework.jmx; import org.springframework.jmx.export.notification.NotificationPublisherAware; import org.springframework.jmx.export.notification.NotificationPublisher; import javax.management.Notification; public class JmxTestBean implements IJmxTestBean, NotificationPublisherAware { private String name; private int age; private boolean isSuperman; private NotificationPublisher publisher; // other getters and setters omitted for clarity public int add(int x, int y) { int answer = x + y; this.publisher.sendNotification(new Notification("add", this, 0)); return answer; } public void dontExposeMe() { throw new RuntimeException(); } public void setNotificationPublisher(NotificationPublisher notificationPublisher) { this.publisher = notificationPublisher; } }
The NotificationPublisher interface and the
machinery to get it all working is one of the nicer features of Spring's JMX support.
It does however come with the price tag of coupling your classes to both Spring and JMX; as
always, the advice here is to be pragmatic... if you need the functionality offered by the
NotificationPublisher and you can accept the coupling to both Spring
and JMX, then do so.
This section contains links to further resources about JMX.
The JMX homepage at Sun
The JMX specification (JSR-000003)
The JMX Remote API specification (JSR-000160)
The MX4J homepage (an Open Source implementation of various JMX specs)
Getting Started with JMX - an introductory article from Sun.