JMX Support

Spring Integration provides channel Adapters for receiving and publishing JMX Notifications.

You need to include this dependency into your project:

Maven
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-jmx</artifactId>
    <version>5.3.3.RELEASE</version>
</dependency>
Gradle
compile "org.springframework.integration:spring-integration-jmx:5.3.3.RELEASE"

An inbound channel adapter allows for polling JMX MBean attribute values, and an outbound channel adapter allows for invoking JMX MBean operations.

Notification-listening Channel Adapter

The notification-listening channel adapter requires a JMX ObjectName for the MBean that publishes notifications to which this listener should be registered. A very simple configuration might resemble the following:

<int-jmx:notification-listening-channel-adapter id="adapter"
    channel="channel"
    object-name="example.domain:name=publisher"/>
The notification-listening-channel-adapter registers with an MBeanServer at startup, and the default bean name is mbeanServer, which happens to be the same bean name generated when using Spring’s <context:mbean-server/> element. If you need to use a different name, be sure to include the mbean-server attribute.

The adapter can also accept a reference to a NotificationFilter and a “handback” object to provide some context that is passed back with each notification. Both of those attributes are optional. Extending the preceding example to include those attributes as well as an explicit MBeanServer bean name produces the following example:

<int-jmx:notification-listening-channel-adapter id="adapter"
    channel="channel"
    mbean-server="someServer"
    object-name="example.domain:name=somePublisher"
    notification-filter="notificationFilter"
    handback="myHandback"/>

The _Notification-listening channel adapter is event-driven and registered with the MBeanServer directly. It does not require any poller configuration.

For this component only, the object-name attribute can contain an object name pattern (for example, "org.something:type=MyType,name=*"). In that case, the adapter receives notifications from all MBeans with object names that match the pattern. In addition, the object-name attribute can contain a SpEL reference to a <util:list> of object name patterns, as the following example shows:

<jmx:notification-listening-channel-adapter id="manyNotificationsAdapter"
    channel="manyNotificationsChannel"
    object-name="#{patterns}"/>

<util:list id="patterns">
    <value>org.foo:type=Foo,name=*</value>
    <value>org.foo:type=Bar,name=*</value>
</util:list>

The names of the located MBean(s) are logged when DEBUG level logging is enabled.

Notification-publishing Channel Adapter

The notification-publishing channel adapter is relatively simple. It requires only a JMX object name in its configuration, as the following example shows:

<context:mbean-export/>

<int-jmx:notification-publishing-channel-adapter id="adapter"
    channel="channel"
    object-name="example.domain:name=publisher"/>

It also requires that an MBeanExporter be present in the context. That is why the <context:mbean-export/> element is also shown in the preceding example.

When messages are sent to the channel for this adapter, the notification is created from the message content. If the payload is a String, it is passed as the message text for the notification. Any other payload type is passed as the userData of the notification.

JMX notifications also have a type, and it should be a dot-delimited String. There are two ways to provide the type. Precedence is always given to a message header value associated with the JmxHeaders.NOTIFICATION_TYPE key. Alternatively, you can provide a fallback default-notification-type attribute in the configuration, as the following example shows:

<context:mbean-export/>

<int-jmx:notification-publishing-channel-adapter id="adapter"
    channel="channel"
    object-name="example.domain:name=publisher"
    default-notification-type="some.default.type"/>

Attribute-polling Channel Adapter

The attribute-polling channel adapter is useful when you need to periodically check on some value that is available through an MBean as a managed attribute. You can configured the poller in the same way as any other polling adapter in Spring Integration (or you can rely on the default poller). The object-name and the attribute-name are required. An MBeanServer reference is also required. However, by default, it automatically checks for a bean named mbeanServer, same as the notification-listening channel adapter described earlier. The following example shows how to configure an attribute-polling channel adapter with XML:

<int-jmx:attribute-polling-channel-adapter id="adapter"
    channel="channel"
    object-name="example.domain:name=someService"
    attribute-name="InvocationCount">
        <int:poller max-messages-per-poll="1" fixed-rate="5000"/>
</int-jmx:attribute-polling-channel-adapter>

Tree-polling Channel Adapter

The tree-polling channel adapter queries the JMX MBean tree and sends a message with a payload that is the graph of objects that matches the query. By default, the MBeans are mapped to primitives and simple objects, such as Map, List, and arrays. Doing so permits simple transformation to (for example) JSON. An MBeanServer reference is also required. However, by default, it automatically checks for a bean named mbeanServer, same as the notification-listening channel adapter described earlier. The following example shows how to configure an tree-polling channel adapter with XML:

<int-jmx:tree-polling-channel-adapter id="adapter"
    channel="channel"
    query-name="example.domain:type=*">
        <int:poller max-messages-per-poll="1" fixed-rate="5000"/>
</int-jmx:tree-polling-channel-adapter>

The preceding example includes all of the attributes on the selected MBeans. You can filter the attributes by providing an MBeanObjectConverter that has an appropriate filter configured. You can provide the converter as a reference to a bean definition by using the converter attribute, or you can use an inner <bean/> definition. Spring Integration provides a DefaultMBeanObjectConverter that can take a MBeanAttributeFilter in its constructor argument.

Spring Integration provides two standard filters. The NamedFieldsMBeanAttributeFilter lets you specify a list of attributes to include. The NotNamedFieldsMBeanAttributeFilter lets you specify a list of attributes to exclude. You can also implement your own filter.

Operation-invoking Channel Adapter

The operation-invoking channel adapter enables message-driven invocation of any managed operation exposed by an MBean. Each invocation requires the operation name to be invoked and the object name of the target MBean. Both of these must be explicitly provided by adapter configuration or via JmxHeaders.OBJECT_NAME and JmxHeaders.OPERATION_NAME message headers, respectively:

<int-jmx:operation-invoking-channel-adapter id="adapter"
    object-name="example.domain:name=TestBean"
    operation-name="ping"/>

Then the adapter only needs to be able to discover the mbeanServer bean. If a different bean name is required, then provide the mbean-server attribute with a reference.

The payload of the message is mapped to the parameters of the operation, if any. A Map-typed payload with String keys is treated as name/value pairs, whereas a List or array is passed as a simple argument list (with no explicit parameter names). If the operation requires a single parameter value, the payload can represent that single value. Also, if the operation requires no parameters, the payload would be ignored.

If you want to expose a channel for a single common operation to be invoked by messages that need not contain headers, that last option works well.

Operation-invoking Outbound Gateway

Similarly to the operation-invoking channel adapter, Spring Integration also provides an operation-invoking outbound gateway, which you can use when dealing with non-void operations when a return value is required. The return value is sent as the message payload to the reply-channel specified by the gateway. The following example shows how to configure an operation-invoking outbound gateway with XML:

<int-jmx:operation-invoking-outbound-gateway request-channel="requestChannel"
   reply-channel="replyChannel"
   object-name="o.s.i.jmx.config:type=TestBean,name=testBeanGateway"
   operation-name="testWithReturn"/>

If you do not provide the reply-channel attribute, the reply message is sent to the channel identified by the IntegrationMessageHeaderAccessor.REPLY_CHANNEL header. That header is typically auto-created by the entry point into a message flow, such as any gateway component. However, if the message flow was started by manually creating a Spring Integration message and sending it directly to a channel, you must specify the message header explicitly or use the reply-channel attribute.

MBean Exporter

Spring Integration components may themselves be exposed as MBeans when the IntegrationMBeanExporter is configured. To create an instance of the IntegrationMBeanExporter, define a bean and provide a reference to an MBeanServer and a domain name (if desired). You can leave out the domain, in which case the default domain is org.springframework.integration. The following example shows how to declare an instance of an IntegrationMBeanExporter and an associated MBeanServer instance:

<int-jmx:mbean-export id="integrationMBeanExporter"
            default-domain="my.company.domain" server="mbeanServer"/>

<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
    <property name="locateExistingServerIfPossible" value="true"/>
</bean>

The MBean exporter is orthogonal to the one provided in Spring core. It registers message channels and message handlers but does not register itself. You can expose the exporter itself (and certain other components in Spring Integration) by using the standard <context:mbean-export/> tag. The exporter has some metrics attached to it — for instance, a count of the number of active handlers and the number of queued messages.

It also has a useful operation, as discussed in Orderly Shutdown Managed Operation.

Spring Integration 4.0 introduced the @EnableIntegrationMBeanExport annotation to allow for convenient configuration of a default integrationMbeanExporter bean of type IntegrationMBeanExporter with several useful options at the @Configuration class level. The following example shows how to configure this bean:

@Configuration
@EnableIntegration
@EnableIntegrationMBeanExport(server = "mbeanServer", managedComponents = "input")
public class ContextConfiguration {

	@Bean
	public MBeanServerFactoryBean mbeanServer() {
		return new MBeanServerFactoryBean();
	}
}

If you need to provide more options or have several IntegrationMBeanExporter beans (such as for different MBean Servers or to avoid conflicts with the standard Spring MBeanExporter — such as through @EnableMBeanExport), you can configure an IntegrationMBeanExporter as a generic bean.

MBean Object Names

All the MessageChannel, MessageHandler, and MessageSource instances in the application are wrapped by the MBean exporter to provide management and monitoring features. The generated JMX object names for each component type are listed in the following table:

Table 1. MBean Object Names
Component Type Object Name

MessageChannel

 `o.s.i:type=MessageChannel,name=<channelName>`

MessageSource

 `o.s.i:type=MessageSource,name=<channelName>,bean=<source>`

MessageHandler

 `o.s.i:type=MessageSource,name=<channelName>,bean=<source>`

The bean attribute in the object names for sources and handlers takes one of the values in the following table:

Table 2. bean ObjectName Part
Bean Value Description

endpoint

The bean name of the enclosing endpoint (for example <service-activator>), if there is one

anonymous

An indication that the enclosing endpoint did not have a user-specified bean name, so the JMX name is the input channel name.

internal

For well known Spring Integration default components

handler/source

None of the above. Fall back to the toString() method of the object being monitored (handler or source)

You can append custom elements to the object name by providing a reference to a Properties object in the object-name-static-properties attribute.

Also, since Spring Integration 3.0, you can use a custom ObjectNamingStrategy by setting the object-naming-strategy attribute. Doing so permits greater control over the naming of the MBeans, such as grouping all integration MBeans under an 'Integration' type. The following example shows one possible custom naming strategy implementation:

public class Namer implements ObjectNamingStrategy {

	private final ObjectNamingStrategy realNamer = new KeyNamingStrategy();
	@Override
	public ObjectName getObjectName(Object managedBean, String beanKey) throws MalformedObjectNameException {
		String actualBeanKey = beanKey.replace("type=", "type=Integration,componentType=");
		return realNamer.getObjectName(managedBean, actualBeanKey);
	}

}

The beanKey argument is a String that contain the standard object name, beginning with the default-domain and including any additional static properties. The preceding example moves the standard type part to componentType and sets the type to 'Integration', enabling selection of all Integration MBeans in one query:`my.domain:type=Integration,*`. Doing so also groups the beans under one tree entry under the domain in such tools as VisualVM.

The default naming strategy is a MetadataNamingStrategy. The exporter propagates the default-domain to that object to let it generate a fallback object name if parsing of the bean key fails. If your custom naming strategy is a MetadataNamingStrategy (or a subclass of it), the exporter does not propagate the default-domain. You must configure it on your strategy bean.

Starting with version 5.1; any bean names (represented by the name key in the object name) will be quoted if they contain any characters that are not allowed in a Java identifier (or period .).

JMX Improvements

Version 4.2 introduced some important improvements, representing a fairly major overhaul to the JMX support in the framework. These resulted in a significant performance improvement of the JMX statistics collection and much more control thereof. However, it has some implications for user code in a few specific (uncommon) situations. These changes are detailed below, with a caution where necessary.

Metrics Capture

Previously, MessageSource, MessageChannel, and MessageHandler metrics were captured by wrapping the object in a JDK dynamic proxy to intercept appropriate method calls and capture the statistics. The proxy was added when an integration MBean exporter was declared in the context.

Now, the statistics are captured by the beans themselves. See Metrics and Management for more information.

This change means that you no longer automatically get an MBean or statistics for custom MessageHandler implementations, unless those custom handlers extend AbstractMessageHandler. The simplest way to resolve this is to extend AbstractMessageHandler. If you cannot do so, another work around is to implement the MessageHandlerMetrics interface. For convenience, a DefaultMessageHandlerMetrics is provided to capture and report statistics. You should invoke the beforeHandle and afterHandle at the appropriate times. Your MessageHandlerMetrics methods can then delegate to this object to obtain each statistic. Similarly, MessageSource implementations must extend AbstractMessageSource or implement MessageSourceMetrics. Message sources capture only a count, so there is no provided convenience class. You should maintain the count in an AtomicLong field.

The removal of the proxy has two additional benefits:

  • Stack traces in exceptions are reduced (when JMX is enabled) because the proxy is not on the stack

  • Cases where two MBeans were exported for the same bean now only export a single MBean with consolidated attributes and operations (see the MBean consolidation bullet, later).

Resolution

System.nanoTime() (rather than System.currentTimeMillis()) is now used to capture times . This may provide more accuracy on some JVMs, espcially when you expect durations of less than one millisecond.

Setting Initial Statistics Collection State

Previously, when JMX was enabled, all sources, channels, and handlers captured statistics. You can now control whether the statistics are enabled on an individual component. Further, you can capture simple counts on MessageChannel instances and MessageHandler instances instead of capturing the complete time-based statistics. This can have significant performance implications, because you can selectively configure where you need detailed statistics and enable and disable collection at runtime.

@IntegrationManagedResource

Similar to the @ManagedResource annotation, the @IntegrationManagedResource marks a class as being eligible to be exported as an MBean. However, it is exported only if the application context has an IntegrationMBeanExporter.

Certain Spring Integration classes (in the org.springframework.integration) package) that were previously annotated with`@ManagedResource` are now annotated with both @ManagedResource and @IntegrationManagedResource. This is for backwards compatibility (see the next item). Such MBeans are exported by any context MBeanServer or by an IntegrationMBeanExporter (but not both — if both exporters are present, the bean is exported by the integration exporter if the bean matches a managed-components pattern).

Consolidated MBeans

Certain classes within the framework (mapping routers, for example) have additional attributes and operations over and above those provided by metrics and Lifecycle. We use a Router as an example here.

Previously, beans of these types were exported as two distinct MBeans:

  • The metrics MBean (with an object name such as intDomain:type=MessageHandler,name=myRouter,bean=endpoint). This MBean had metrics attributes and metrics/Lifecycle operations.

  • A second MBean (with an object name such as ctxDomain:name=org.springframework.integration.config. RouterFactoryBean#0,type=MethodInvokingRouter`) was exported with the channel mappings attribute and operations.

    Now the attributes and operations are consolidated into a single MBean. The object name depends on the exporter. If exported by the integration MBean exporter, the object name is, for example: intDomain:type=MessageHandler,name=myRouter,bean=endpoint. If exported by another exporter, the object name is, for example: ctxDomain:name=org.springframework.integration.config. RouterFactoryBean#0,type=MethodInvokingRouter. There is no difference between these MBeans (aside from the object name), except that the statistics are not enabled (the attributes are 0) by exporters other than the integration exporter. You can enable statistics at runtime by using the JMX operations. When exported by the integration MBean exporter, the initial state can be managed as described earlier.

    If you currently use the second MBean to change, for example, channel mappings and you use the integration MBean exporter, note that the object name has changed because of the MBean consolidation. There is no change if you are not using the integration MBean exporter.
MBean Exporter Bean Name Patterns

Previously, the managed-components patterns were inclusive only. If a bean name matched one of the patterns, it would be included. Now, the pattern can be negated by prefixing it with !. For example, !thing*, things matches all bean names that do not start with thing except things. Patterns are evaluated left to right. The first match (positive or negative) wins, and then no further patterns are applied.

The addition of this syntax to the pattern causes one possible (although perhaps unlikely) problem. If you have a bean named "!thing" and you included a pattern of !thing in your MBean exporter’s managed-components patterns, it no longer matches; the pattern now matches all beans not named thing. In this case, you can escape the ! in the pattern with \. The \!thing pattern matches a bean named !thing.
IntegrationMBeanExporter changes

The IntegrationMBeanExporter no longer implements SmartLifecycle. This means that start() and stop() operations are no longer available to register and unregister MBeans. The MBeans are now registered during context initialization and unregistered when the context is destroyed.

Orderly Shutdown Managed Operation

The MBean exporter provides a JMX operation to shut down the application in an orderly manner, intended for use before terminating the JVM. The following example shows how to use it:

public void stopActiveComponents(long howLong)

Its use and operation are described in Orderly Shutdown.