1376 lines
50 KiB
Plaintext
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.)
|
|
|