spring-framework/framework-docs/modules/ROOT/pages/integration/jmx.adoc

1376 lines
50 KiB
Plaintext

[[jmx]]
= JMX
The JMX (Java Management Extensions) support in Spring provides features that let you
easily and transparently integrate your Spring application into a JMX infrastructure.
.JMX?
****
This chapter is not an introduction to JMX. It does not try to explain why you might want
to use JMX. If you are new to JMX, see <<jmx-resources>> at the end of this chapter.
****
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.
[[jmx-exporting]]
== Exporting Your Beans to JMX
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:
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages",chomp="-packages"]
----
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 can configure an instance of the `MBeanExporter` class in your
configuration file and pass in the bean, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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 preceding 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. You can change this behavior, as described in <<jmx-naming>>.
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 (except those inherited from the
`Object` class) are exposed as operations.
NOTE: `MBeanExporter` is a `Lifecycle` bean (see <<core.adoc#beans-factory-lifecycle-processor,
Startup and Shutdown Callbacks>>). By default, MBeans are exported as late as possible during
the application lifecycle. You can configure the `phase` at which
the export happens or disable automatic registration by setting the `autoStartup` flag.
[[jmx-exporting-mbeanserver]]
=== Creating an MBeanServer
The configuration shown in the <<jmx-exporting, preceding section>> assumes that the
application is running in an environment that has one (and only one) `MBeanServer`
already running. In this case, Spring tries to locate the running `MBeanServer` and
register your beans with that server (if any). This behavior is useful when your
application runs 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` instance's `server` property to the `MBeanServer` value returned by an
`MBeanServerFactoryBean`, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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>
----
In the preceding example, an instance of `MBeanServer` is created by the `MBeanServerFactoryBean` and is
supplied to the `MBeanExporter` through the `server` property. When you supply your own
`MBeanServer` instance, the `MBeanExporter` does not try to locate a running
`MBeanServer` and uses the supplied `MBeanServer` instance. For this to work
correctly, you must have a JMX implementation on your classpath.
[[jmx-mbean-server]]
=== Reusing an Existing `MBeanServer`
If no server is specified, the `MBeanExporter` tries to automatically detect a running
`MBeanServer`. This works in most environments, where only one `MBeanServer` instance is
used. However, when multiple instances exist, the exporter might pick the wrong server.
In such cases, you should use the `MBeanServer` `agentId` to indicate which instance to
be used, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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 or cases where the existing `MBeanServer` has a dynamic (or unknown)
`agentId` that is retrieved through lookup methods, you should use
<<core.adoc#beans-factory-class-static-factory-method, factory-method>>,
as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<beans>
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="server">
<!-- Custom MBeanServerLocator -->
<bean class="platform.package.MBeanServerLocator" factory-method="locateMBeanServer"/>
</property>
</bean>
<!-- other beans here -->
</beans>
----
[[jmx-exporting-lazy]]
=== Lazily Initialized MBeans
If you configure a bean with an `MBeanExporter` that is also configured for lazy
initialization, the `MBeanExporter` does not break this contract and avoids
instantiating the bean. Instead, it registers a proxy with the `MBeanServer` and
defers obtaining the bean from the container until the first invocation on the proxy
occurs.
[[jmx-exporting-auto]]
=== Automatic Registration of MBeans
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. You can cause MBeans
to be automatically detected by the `MBeanExporter` by setting the `autodetect`
property to `true`, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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"/>
----
In the preceding example, the bean called `spring:mbean=true` is already a valid JMX MBean
and is automatically registered by Spring. By default, a bean that is autodetected for JMX
registration has its bean name used as the `ObjectName`. You can override this behavior,
as detailed in <<jmx-naming>>.
[[jmx-exporting-registration-behavior]]
=== Controlling the Registration Behavior
Consider the scenario where a Spring `MBeanExporter` attempts to register an `MBean`
with an `MBeanServer` by 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`).
You can control 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`.
The following table summarizes these registration behaviors:
[[jmx-registration-behaviors]]
.Registration Behaviors
[cols="1,4"]
|===
| Registration behavior | Explanation
| `FAIL_ON_EXISTING`
| This is the default registration behavior. If an `MBean` instance has already been
registered under the same `ObjectName`, the `MBean` that is being registered is not
registered, and an `InstanceAlreadyExistsException` is thrown. The existing
`MBean` is unaffected.
| `IGNORE_EXISTING`
| If an `MBean` instance has already been registered under the same `ObjectName`, the
`MBean` that is being registered is not registered. The existing `MBean` is
unaffected, and no `Exception` is thrown. This is useful in settings where
multiple applications want to share a common `MBean` in a shared `MBeanServer`.
| `REPLACE_EXISTING`
| If an `MBean` instance has already been registered under the same `ObjectName`, the
existing `MBean` that was previously registered is unregistered, and the new
`MBean` is registered in its place (the new `MBean` effectively replaces the
previous instance).
|===
The values in the preceding table are defined as enums on the `RegistrationPolicy` class.
If you want to change the default registration behavior, you need to set the value of the
`registrationPolicy` property on your `MBeanExporter` definition to one of those
values.
The following example shows how to change from the default registration
behavior to the `REPLACE_EXISTING` behavior:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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="registrationPolicy" value="REPLACE_EXISTING"/>
</bean>
<bean id="testBean" class="org.springframework.jmx.JmxTestBean">
<property name="name" value="TEST"/>
<property name="age" value="100"/>
</bean>
</beans>
----
[[jmx-interface]]
== Controlling the Management Interface of Your Beans
In the example in the <<jmx-exporting-registration-behavior, 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.
[[jmx-interface-assembler]]
=== Using 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 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.
[[jmx-interface-metadata]]
=== 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 (there is no default).
To mark a bean for export to JMX, you should annotate the bean class with the
`ManagedResource` annotation. You must mark each method you wish to expose as an operation
with the `ManagedOperation` annotation and mark each property you wish to expose
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.
NOTE: A `ManagedResource`-annotated bean must be public, as must the methods exposing
an operation or an attribute.
The following example shows the annotated version of the `JmxTestBean` class that we
used in <<jmx-exporting-mbeanserver>>:
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
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();
}
}
----
In the preceding example, 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 <<jmx-interface-metadata-types>>.
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 causes both of these properties to be included in the management interface
as attributes, but the `age` attribute is read-only.
Finally, the `add(int, int)` method is marked with the `ManagedOperation` attribute,
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 following configuration shows how you can configure the `MBeanExporter` to use the
`MetadataMBeanInfoAssembler`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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>
----
In the preceding example, 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.
[[jmx-interface-metadata-types]]
=== Source-level Metadata Types
The following table describes the source-level metadata types that are available for use in Spring JMX:
[[jmx-metadata-types]]
.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 table describes the configuration parameters that are available for use on these source-level
metadata types:
[[jmx-metadata-parameters]]
.Source-level metadata parameters
[cols="1,3,1"]
|===
| 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`, or `ManagedOperationParameter`
| `currencyTimeLimit`
| Sets the value of the `currencyTimeLimit` descriptor field.
| `ManagedResource` or `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`
|===
[[jmx-interface-autodetect]]
=== Using the `AutodetectCapableMBeanInfoAssembler` Interface
To simplify configuration even further, Spring includes 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`, 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 a configuration similar to the following:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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 the preceding configuration, no beans are passed to the `MBeanExporter`.
However, the `JmxTestBean` is still 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 <<jmx-naming>>.
[[jmx-interface-java]]
=== 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:
[source,java,indent=0,subs="verbatim,quotes"]
----
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:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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.
[[jmx-interface-methodnames]]
=== 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:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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.
[[jmx-naming]]
== Controlling `ObjectName` Instances for Your Beans
Behind the scenes, the `MBeanExporter` delegates to an implementation of the
`ObjectNamingStrategy` to obtain an `ObjectName` instance for each of the beans it registers.
By default, the default implementation, `KeyNamingStrategy` uses 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` (which builds an
`ObjectName` based on the JVM identity of the bean) and the `MetadataNamingStrategy` (which
uses source-level metadata to obtain the `ObjectName`).
[[jmx-naming-properties]]
=== Reading `ObjectName` Instances from Properties
You can configure your own `KeyNamingStrategy` instance and configure it to read
`ObjectName` instances from a `Properties` instance rather than use a bean key. The
`KeyNamingStrategy` tries to locate an entry in the `Properties` with a key
that corresponds to the bean key. If no entry is found or if the `Properties` instance is
`null`, the bean key itself is used.
The following code shows a sample configuration for the `KeyNamingStrategy`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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>
----
The preceding example configures an instance of `KeyNamingStrategy` 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 is given an `ObjectName` of `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, the bean key name is used as
the `ObjectName`.
[[jmx-naming-metadata]]
=== Using `MetadataNamingStrategy`
`MetadataNamingStrategy` uses the `objectName` property of the `ManagedResource`
attribute on each bean to create the `ObjectName`. The following code shows the
configuration for the `MetadataNamingStrategy`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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, an
`ObjectName` is 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.example:type=MyClass,name=myBean`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="myBean" class="com.example.MyClass"/>
----
[[jmx-context-mbeanexport]]
=== Configuring Annotation-based MBean Export
If you prefer to use <<jmx-interface-metadata, the annotation-based approach>> to define
your management interfaces, a convenience subclass of `MBeanExporter` is available:
`AnnotationMBeanExporter`. When defining an instance of this subclass, you no longer need the
`namingStrategy`, `assembler`, and `attributeSource` configuration,
since it always uses 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,
as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Configuration
@EnableMBeanExport
public class AppConfig {
}
----
If you prefer XML-based configuration, the `<context:mbean-export/>` element serves the
same purpose and is shown in the following listing:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<context:mbean-export/>
----
If necessary, you can provide a reference to a particular MBean `server`, and the
`defaultDomain` attribute (a property of `AnnotationMBeanExporter`) accepts an alternate
value for the generated MBean `ObjectName` domains. This is used in place of the
fully qualified package name as described in the previous section on
<<jmx-naming-metadata, MetadataNamingStrategy>>, as the following example shows:
[source,java,indent=0,subs="verbatim,quotes"]
----
@EnableMBeanExport(server="myMBeanServer", defaultDomain="myDomain")
@Configuration
ContextConfiguration {
}
----
The following example shows the XML equivalent of the preceding annotation-based example:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<context:mbean-export server="myMBeanServer" default-domain="myDomain"/>
----
CAUTION: 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, you should use target-class proxies in that
case (through setting the 'proxy-target-class' flag on `<aop:config/>`,
`<tx:annotation-driven/>` and so on). Otherwise, your JMX beans might be silently ignored at
startup.
[[jmx-jsr160]]
== Using JSR-160 Connectors
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.
[[jmx-jsr160-server]]
=== Server-side Connectors
To have Spring JMX create, start, and expose a JSR-160 `JMXConnectorServer`, you can use the
following configuration:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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 the JDK
do not support JMXMP.
To specify another URL and register the `JMXConnectorServer` itself with the
`MBeanServer`, you can use the `serviceUrl` and `ObjectName` properties, respectively,
as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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 automatically registers your connector
with the `MBeanServer` under that `ObjectName`. The following example shows the full set of
parameters that you can pass to the `ConnectorServerFactoryBean` when creating a
`JMXConnector`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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 you use a RMI-based connector, you need the lookup service (`tnameserv` or
`rmiregistry`) to be started in order for the name registration to complete.
[[jmx-jsr160-client]]
=== Client-side Connectors
To create an `MBeanServerConnection` to a remote JSR-160-enabled `MBeanServer`, you can use the
`MBeanServerConnectionFactoryBean`, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<bean id="clientConnector" class="org.springframework.jmx.support.MBeanServerConnectionFactoryBean">
<property name="serviceUrl" value="service:jmx:rmi://localhost/jndi/rmi://localhost:1099/jmxrmi"/>
</bean>
----
[[jmx-jsr160-protocols]]
=== JMX over Hessian or SOAP
JSR-160 permits extensions to the way in which communication is done between the client
and the server. The examples shown in the preceding sections use 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 http://mx4j.sourceforge.net[MX4J]) you
can take advantage of protocols such as SOAP or Hessian over simple HTTP or SSL and others,
as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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 preceding example, we used MX4J 3.0.0. See the official MX4J
documentation for more information.
[[jmx-proxy]]
== Accessing MBeans through Proxies
Spring JMX lets you create proxies that re-route calls to MBeans that are 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 following code shows how to configure a
proxy for an MBean running in a local `MBeanServer`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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>
----
In the preceding example, you can see that a proxy is created for the MBean registered under the
`ObjectName` of `bean:name=testBean`. The set of interfaces that the proxy implements
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 through an
`MBeanServerConnection`. By default, the local `MBeanServer` is located and used, but
you can override this and provide an `MBeanServerConnection` that points to a remote
`MBeanServer` to cater for proxies that point to remote MBeans:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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>
----
In the preceding example, we create an `MBeanServerConnection` that points to a remote machine
that uses the `MBeanServerConnectionFactoryBean`. This `MBeanServerConnection` is then
passed to the `MBeanProxyFactoryBean` through the `server` property. The proxy that is
created forwards all invocations to the `MBeanServer` through this
`MBeanServerConnection`.
[[jmx-notifications]]
== Notifications
Spring's JMX offering includes comprehensive support for JMX notifications.
[[jmx-notifications-listeners]]
=== Registering Listeners for Notifications
Spring's JMX support makes it easy to register any number of
`NotificationListeners` with any number of MBeans (this includes MBeans exported by
Spring's `MBeanExporter` and MBeans registered through some other mechanism). For
example, consider the scenario where one would like to be informed (through a
`Notification`) each and every time an attribute of a target MBean changes. The following
example writes notifications to the console:
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
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());
}
}
----
The following example adds `ConsoleLoggingNotificationListener` (defined in the preceding
example) to `notificationListenerMappings`:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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 preceding 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 through the `notificationListenerMappings` property is
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,
as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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 you want to register a single `NotificationListener` instance for all of the beans
that the enclosing `MBeanExporter` exports, you can use the special wildcard (`{asterisk}`)
as the key for an entry in the `notificationListenerMappings` property
map, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<property name="notificationListenerMappings">
<map>
<entry key="*">
<bean class="com.example.ConsoleLoggingNotificationListener"/>
</entry>
</map>
</property>
----
If you need to do the inverse (that is, register a number of distinct listeners against
an MBean), you must instead use the `notificationListeners` list property (in
preference to the `notificationListenerMappings` property). This time, instead of
configuring a `NotificationListener` for a single MBean, we configure
`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, as the following example shows:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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 preceding example is equivalent to the first notification example. Assume, then, that
we want to be given a handback object every time a `Notification` is raised and that
we also want to filter out extraneous `Notifications` by supplying a
`NotificationFilter`. The following example accomplishes these goals:
[source,xml,indent=0,subs="verbatim,quotes"]
----
<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 the NotificationListener and NotificationFilter interfaces -->
<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>
----
(For a full discussion of what a handback object is and,
indeed, what a `NotificationFilter` is, see the section of the JMX
specification (1.2) entitled 'The JMX Notification Model'.)
[[jmx-notifications-publishing]]
=== Publishing Notifications
Spring provides support not only for registering to receive `Notifications` but also
for publishing `Notifications`.
NOTE: This section is really only relevant to Spring-managed beans that have
been exposed as MBeans through an `MBeanExporter`. Any existing user-defined MBeans should
use the standard JMX APIs for notification publication.
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 through an `MBeanExporter` instance can implement the related
`NotificationPublisherAware` interface to gain access to a `NotificationPublisher`
instance. The `NotificationPublisherAware` interface supplies an instance of a
`NotificationPublisher` to the implementing bean through a simple setter method,
which the bean can then use to publish `Notifications`.
As stated in the javadoc of the
{api-spring-framework}/jmx/export/notification/NotificationPublisher.html[`NotificationPublisher`]
interface, managed beans that publish events through the `NotificationPublisher`
mechanism are not responsible for the state management of notification listeners.
Spring's JMX support takes care of handling all the JMX infrastructure issues.
All you need to do, as an application developer, is implement the
`NotificationPublisherAware` interface and start publishing events by using the
supplied `NotificationPublisher` instance. Note that the `NotificationPublisher`
is set after the managed bean has been registered with an `MBeanServer`.
Using a `NotificationPublisher` instance is quite straightforward. You create a JMX
`Notification` instance (or an instance of an appropriate `Notification` subclass),
populate the notification with the data pertinent to the event that is to be
published, and invoke the `sendNotification(Notification)` on the
`NotificationPublisher` instance, passing in the `Notification`.
In the following example, exported instances of the `JmxTestBean` publish a
`NotificationEvent` every time the `add(int, int)` operation is invoked:
[source,java,indent=0,subs="verbatim,quotes",chomp="-packages"]
----
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.
[[jmx-resources]]
== Further Resources
This section contains links to further resources about JMX:
* The https://www.oracle.com/technetwork/java/javase/tech/javamanagement-140525.html[JMX
homepage] at Oracle.
* The https://jcp.org/aboutJava/communityprocess/final/jsr003/index3.html[JMX
specification] (JSR-000003).
* The https://jcp.org/aboutJava/communityprocess/final/jsr160/index.html[JMX Remote API
specification] (JSR-000160).
* The http://mx4j.sourceforge.net/[MX4J homepage]. (MX4J is an open-source implementation of
various JMX specs.)