# System Management ## System Management ### Metrics and Management This section describes how to capture metrics for Spring Integration. In recent versions, we have relied more on Micrometer (see [https://micrometer.io](https://micrometer.io)), and we plan to use Micrometer even more in future releases. #### Legacy Metrics Legacy metrics were removed in Version 5.4; see Micrometer Integration below. #### Disabling Logging in High Volume Environments You can control debug logging in the main message flow. In very high volume applications, calls to `isDebugEnabled()` can be quite expensive with some logging subsystems. You can disable all such logging to avoid this overhead. Exception logging (debug or otherwise) is not affected by this setting. The following listing shows the available options for controlling logging: Java ``` @Configuration @EnableIntegration @EnableIntegrationManagement( defaultLoggingEnabled = "true" <1>) public static class ContextConfiguration { ... } ``` XML ``` (1) ``` |**1**|Set to `false` to disable all logging in the main message flow, regardless of the log system category settings.
Set to 'true' to enable debug logging (if also enabled by the logging subsystem).
Only applied if you have not explicitly configured the setting in a bean definition.
The default is `true`.| |-----|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | |`defaultLoggingEnabled` is applied only if you have not explicitly configured the corresponding setting in a bean definition.| |---|-----------------------------------------------------------------------------------------------------------------------------| #### Micrometer Integration ##### Overview Starting with version 5.0.3, the presence of a [Micrometer](https://micrometer.io/) `MeterRegistry` in the application context triggers support for Micrometer metrics. To use Micrometer, add one of the `MeterRegistry` beans to the application context. For each `MessageHandler` and `MessageChannel`, timers are registered. For each `MessageSource`, a counter is registered. This only applies to objects that extend `AbstractMessageHandler`, `AbstractMessageChannel`, and `AbstractMessageSource` (which is the case for most framework components). The `Timer` Meters for send operations on message channels have the following names or tags: * `name`: `spring.integration.send` * `tag`: `type:channel` * `tag`: `name:` * `tag`: `result:(success|failure)` * `tag`: `exception:(none|exception simple class name)` * `description`: `Send processing time` (A `failure` result with a `none` exception means the channel’s `send()` operation returned `false`.) The `Counter` Meters for receive operations on pollable message channels have the following names or tags: * `name`: `spring.integration.receive` * `tag`: `type:channel` * `tag`: `name:` * `tag`: `result:(success|failure)` * `tag`: `exception:(none|exception simple class name)` * `description`: `Messages received` The `Timer` Meters for operations on message handlers have the following names or tags: * `name`: `spring.integration.send` * `tag`: `type:handler` * `tag`: `name:` * `tag`: `result:(success|failure)` * `tag`: `exception:(none|exception simple class name)` * `description`: `Send processing time` The `Counter` meters for message sources have the following names/tags: * `name`: `spring.integration.receive` * `tag`: `type:source` * `tag`: `name:` * `tag`: `result:success` * `tag`: `exception:none` * `description`: `Messages received` In addition, there are three `Gauge` Meters: * `spring.integration.channels`: The number of `MessageChannels` in the application. * `spring.integration.handlers`: The number of `MessageHandlers` in the application. * `spring.integration.sources`: The number of `MessageSources` in the application. It is possible to customize the names and tags of `Meters` created by integration components by providing a subclass of `MicrometerMetricsCaptor`. The [MicrometerCustomMetricsTests](https://github.com/spring-projects/spring-integration/blob/main/spring-integration-core/src/test/java/org/springframework/integration/support/management/micrometer/MicrometerCustomMetricsTests.java) test case shows a simple example of how to do that. You can also further customize the meters by overloading the `build()` methods on builder subclasses. Starting with version 5.1.13, the `QueueChannel` exposes Micrometer gauges for queue size and remaining capacity: * `name`: `spring.integration.channel.queue.size` * `tag`: `type:channel` * `tag`: `name:` * `description`: `The size of the queue channel` and * `name`: `spring.integration.channel.queue.remaining.capacity` * `tag`: `type:channel` * `tag`: `name:` * `description`: `The remaining capacity of the queue channel` ##### Disabling Meters With the [Legacy Metrics](#legacy-metrics) (which have now been removed), you could specify which integration components would collect metrics. By default, all meters are registered when first used. Now, with Micrometer, you can add `MeterFilter` s to the `MeterRegistry` to prevent some or all from being registered. You can filter out (deny) meters by any of the properties provided, `name`, `tag`, etc. See [Meter Filters](https://micrometer.io/docs/concepts#_meter_filters) in the Micrometer documentation for more information. For example, given: ``` @Bean public QueueChannel noMeters() { return new QueueChannel(10); } ``` You can suppress registration of meters for just this channel with: ``` registry.config().meterFilter(MeterFilter.deny(id -> "channel".equals(id.getTag("type")) && "noMeters".equals(id.getTag("name")))); ``` #### Spring Integration JMX Support Also see [JMX Support](./jmx.html#jmx). ### Message History The key benefit of a messaging architecture is loose coupling such that participating components do not maintain any awareness about one another. This fact alone makes an application extremely flexible, letting you change components without affecting the rest of the flow, change messaging routes, change message consuming styles (polling versus event driven), and so on. However, this unassuming style of architecture could prove to be difficult when things go wrong. When debugging, you probably want as much information (its origin, the channels it has traversed, and other details) about the message as you can get. Message history is one of those patterns that helps by giving you an option to maintain some level of awareness of a message path either for debugging purposes or for maintaining an audit trail. Spring integration provides a simple way to configure your message flows to maintain the message history by adding a header to the message and updating that header every time a message passes through a tracked component. #### Message History Configuration To enable message history, you need only define the `message-history` element (or `@EnableMessageHistory`) in your configuration, as shown in the following example: ``` @Configuration @EnableIntegration @EnableMessageHistory ``` ``` ``` Now every named component (component that has an 'id' defined) is tracked. The framework sets the 'history' header in your message. Its value a `List`. Consider the following configuration example: ``` @MessagingGateway(defaultRequestChannel = "bridgeInChannel") public interface SampleGateway { ... } @Bean @Transformer(inputChannel = "enricherChannel", outputChannel="filterChannel") HeaderEnricher sampleEnricher() { HeaderEnricher enricher = new HeaderEnricher(Collections.singletonMap("baz", new StaticHeaderValueMessageProcessor("baz"))); return enricher; } ``` ``` ``` The preceding configuration produces a simple message history structure, with output similar to the following: ``` [{name=sampleGateway, type=gateway, timestamp=1283281668091}, {name=sampleEnricher, type=header-enricher, timestamp=1283281668094}] ``` To get access to message history, you need only access the `MessageHistory` header. The following example shows how to do so: ``` Iterator historyIterator = message.getHeaders().get(MessageHistory.HEADER_NAME, MessageHistory.class).iterator(); assertTrue(historyIterator.hasNext()); Properties gatewayHistory = historyIterator.next(); assertEquals("sampleGateway", gatewayHistory.get("name")); assertTrue(historyIterator.hasNext()); Properties chainHistory = historyIterator.next(); assertEquals("sampleChain", chainHistory.get("name")); ``` You might not want to track all of the components. To limit the history to certain components based on their names, you can provide the `tracked-components` attribute and specify a comma-delimited list of component names and patterns that match the components you want to track. The following example shows how to do so: ``` @Configuration @EnableIntegration @EnableMessageHistory("*Gateway", "sample*", "aName") ``` ``` ``` In the preceding example, message history is maintained only for the components that end with 'Gateway', start with 'sample', or match the name, 'aName', exactly. In addition, the `MessageHistoryConfigurer` bean is now exposed as a JMX MBean by the `IntegrationMBeanExporter` (see [MBean Exporter](./jmx.html#jmx-mbean-exporter)), letting you change the patterns at runtime. Note, however, that the bean must be stopped (turning off message history) in order to change the patterns. This feature might be useful to temporarily turn on history to analyze a system. The MBean’s object name is `:name=messageHistoryConfigurer,type=MessageHistoryConfigurer`. | |Only one `@EnableMessageHistory` (or ``) must be declared in the application context as single source for components tracking configuration.
Do not use a generic bean definition for the `MessageHistoryConfigurer`.| |---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | |By definition, the message history header is immutable (you cannot re-write history).
Therefore, when writing message history values, the components either create new messages (when the component is an origin) or they copy the history from a request message, modifying it and setting the new list on a reply message.
In either case, the values can be appended even if the message itself is crossing thread boundaries.
That means that the history values can greatly simplify debugging in an asynchronous message flow.| |---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ### Message Store The [*Enterprise Integration Patterns*](https://www.enterpriseintegrationpatterns.com/) (EIP) book identifies several patterns that have the ability to buffer messages. For example, an aggregator buffers messages until they can be released, and a `QueueChannel` buffers messages until consumers explicitly receive those messages from that channel. Because of the failures that can occur at any point within your message flow, EIP components that buffer messages also introduce a point where messages could be lost. To mitigate the risk of losing messages, EIP defines the [message store](https://www.enterpriseintegrationpatterns.com/MessageStore.html) pattern, which lets EIP components store messages, typically in some type of persistent store (such as an RDBMS). Spring Integration provides support for the message store pattern by: * Defining an `org.springframework.integration.store.MessageStore` strategy interface * Providing several implementations of this interface * Exposing a `message-store` attribute on all components that have the capability to buffer messages so that you can inject any instance that implements the `MessageStore` interface. Details on how to configure a specific message store implementation and how to inject a `MessageStore` implementation into a specific buffering component are described throughout the manual (see the specific component, such as [QueueChannel](./channel.html#channel-configuration-queuechannel), [Aggregator](./aggregator.html#aggregator), [Delayer](./delayer.html#delayer), and others). The following pair of examples show how to add a reference to a message store for a `QueueChannel` and for an aggregator: Example 1. QueueChannel ``` ``` Example 2. Aggregator ``` ``` By default, messages are stored in-memory by using `o.s.i.store.SimpleMessageStore`, an implementation of `MessageStore`. That might be fine for development or simple low-volume environments where the potential loss of non-persistent messages is not a concern. However, the typical production application needs a more robust option, not only to mitigate the risk of message loss but also to avoid potential out-of-memory errors. Therefore, we also provide `MessageStore` implementations for a variety of data-stores. The following is a complete list of supported implementations: * [JDBC Message Store](./jdbc.html#jdbc-message-store): Uses an RDBMS to store messages * [Redis Message Store](./redis.html#redis-message-store): Uses a Redis key/value datastore to store messages * [MongoDB Message Store](./mongodb.html#mongodb-message-store): Uses a MongoDB document store to store messages * [Gemfire Message Store](./gemfire.html#gemfire-message-store): Uses a Gemfire distributed cache to store messages | |However, be aware of some limitations while using persistent implementations of the `MessageStore`.

The Message data (payload and headers) is serialized and deserialized by using different serialization strategies, depending on the implementation of the `MessageStore`.
For example, when using `JdbcMessageStore`, only `Serializable` data is persisted by default.
In this case, non-Serializable headers are removed before serialization occurs.
Also, be aware of the protocol-specific headers that are injected by transport adapters (such as FTP, HTTP, JMS, and others).
For example, `` maps HTTP headers into message headers, and one of them is an `ArrayList` of non-serializable `org.springframework.http.MediaType` instances.
However, you can inject your own implementation of the `Serializer` and `Deserializer` strategy interfaces into some `MessageStore` implementations (such as `JdbcMessageStore`) to change the behavior of serialization and deserialization.

Pay special attention to the headers that represent certain types of data.
For example, if one of the headers contains an instance of some Spring bean, upon deserialization, you may end up with a different instance of that bean, which directly affects some of the implicit headers created by the framework (such as `REPLY_CHANNEL` or `ERROR_CHANNEL`).
Currently, they are not serializable, but, even if they were, the deserialized channel would not represent the expected instance.

Beginning with Spring Integration version 3.0, you can resolve this issue with a header enricher configured to replace these headers with a name after registering the channel with the `HeaderChannelRegistry`.

Also, consider what happens when you configure a message-flow as follows: gateway → queue-channel (backed by a persistent Message Store) → service-activator.
That gateway creates a temporary reply channel, which is lost by the time the service-activator’s poller reads from the queue.
Again, you can use the header enricher to replace the headers with a `String` representation.

For more information, see [Header Enricher](./content-enrichment.html#header-enricher).| |---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| Spring Integration 4.0 introduced two new interfaces: * `ChannelMessageStore`: To implement operations specific for `QueueChannel` instances * `PriorityCapableChannelMessageStore`: To mark `MessageStore` implementations to be used for `PriorityChannel` instances and to provide priority order for persisted messages. The real behavior depends on the implementation. The framework provides the following implementations, which can be used as a persistent `MessageStore` for `QueueChannel` and `PriorityChannel`: * [Redis Channel Message Stores](./redis.html#redis-cms) * [MongoDB Channel Message Store](./mongodb.html#mongodb-priority-channel-message-store) * [Backing Message Channels](./jdbc.html#jdbc-message-store-channels) | |Caution about `SimpleMessageStore`

Starting with version 4.1, the `SimpleMessageStore` no longer copies the message group when calling `getMessageGroup()`.
For large message groups, this was a significant performance problem.
4.0.1 introduced a boolean `copyOnGet` property that lets you control this behavior.
When used internally by the aggregator, this property was set to `false` to improve performance.
It is now `false` by default.

Users accessing the group store outside of components such as aggregators now get a direct reference to the group being used by the aggregator instead of a copy.
Manipulation of the group outside of the aggregator may cause unpredictable results.

For this reason, you should either not perform such manipulation or set the `copyOnGet` property to `true`.| |---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| #### Using `MessageGroupFactory` Starting with version 4.3, some `MessageGroupStore` implementations can be injected with a custom `MessageGroupFactory` strategy to create and customize the `MessageGroup` instances used by the `MessageGroupStore`. This defaults to a `SimpleMessageGroupFactory`, which produces `SimpleMessageGroup` instances based on the `GroupType.HASH_SET` (`LinkedHashSet`) internal collection. Other possible options are `SYNCHRONISED_SET` and `BLOCKING_QUEUE`, where the last one can be used to reinstate the previous `SimpleMessageGroup` behavior. Also the `PERSISTENT` option is available. See the next section for more information. Starting with version 5.0.1, the `LIST` option is also available for when the order and uniqueness of messages in the group does not matter. #### Persistent `MessageGroupStore` and Lazy-load Starting with version 4.3, all persistent `MessageGroupStore` instances retrieve `MessageGroup` instances and their `messages` from the store in the lazy-load manner. In most cases, it is useful for the correlation `MessageHandler` instances (see [Aggregator](./aggregator.html#aggregator) and [Resequencer](./resequencer.html#resequencer)), when it would add overhead to load entire the `MessageGroup` from the store on each correlation operation. You can use the `AbstractMessageGroupStore.setLazyLoadMessageGroups(false)` option to switch off the lazy-load behavior from the configuration. Our performance tests for lazy-load on MongoDB `MessageStore` ([MongoDB Message Store](./mongodb.html#mongodb-message-store)) and `` ([Aggregator](./aggregator.html#aggregator)) use a custom `release-strategy` similar to the following: ``` ``` It produces results similar to the following for 1000 simple messages: ``` ... StopWatch 'Lazy-Load Performance': running time (millis) = 38918 ----------------------------------------- ms % Task name ----------------------------------------- 02652 007% Lazy-Load 36266 093% Eager ... ``` However starting with version 5.5, all the persistent `MessageGroupStore` implementations provide a `streamMessagesForGroup(Object groupId)` contract based on the target database streaming API. This improves resources utilization when groups are very big in the store. Internally in the framework this new API is used in the [Delayer](./delayer.html#delayer) (for example) when it reschedules persisted messages on startup. A returned `Stream>` must be closed in the end of processing, e.g. via auto-close by the `try-with-resources`. Whenever a `PersistentMessageGroup` is used, its `streamMessages()` delegates to the `MessageGroupStore.streamMessagesForGroup()`. #### Message Group Condition Starting with version 5.5, the `MessageGroup` abstraction provides a `condition` string option. The value of this option can be anything that could be parsed later on for any reason to make a decision for the group. For example a `ReleaseStrategy` from a [correlation message handler](./aggregator.html#aggregator-api) may consult this property from the group instead of iterating all the messages in the group. The `MessageGroupStore` exposes a `setGroupCondition(Object groupId, String condition)` API. For this purpose a `setGroupConditionSupplier(BiFunction, String, String>)` option has been added to the `AbstractCorrelatingMessageHandler`. This function is evaluated against each message after it has been added to the group as well as the existing condition of the group. The implementation may decide to return a new value, the existing value, or reset the target condition to `null`. The value for a `condition` can be a JSON, SpEL expression, number or anything what can be serialized as a string and parsed afterwards. For example, the `FileMarkerReleaseStrategy` from the [File Aggregator](./file.html#file-aggregator) component, populates a condition into a group from the `FileHeaders.LINE_COUNT` header of the `FileSplitter.FileMarker.Mark.END` message and consults with it from its `canRelease()` comparing a group size with the value in this condition. This way it doesn’t iterate all the messages in group to find a `FileSplitter.FileMarker.Mark.END` message with the `FileHeaders.LINE_COUNT` header. It also allows the end marker to arrive at the aggregator before all the other records; for example when processing a file in a multi-threaded environment. In addition, for configuration convenience, a `GroupConditionProvider` contract has been introduced. The `AbstractCorrelatingMessageHandler` checks if the provided `ReleaseStrategy` implements this interface and extracts a `conditionSupplier` for group condition evaluation logic. ### Metadata Store Many external systems, services, or resources are not transactional (Twitter, RSS, file systems, and so on), and there is not any ability to mark the data as read. Also, sometimes, you may need to implement the Enterprise Integration Pattern [idempotent receiver](https://www.enterpriseintegrationpatterns.com/IdempotentReceiver.html) in some integration solutions. To achieve this goal and store some previous state of the endpoint before the next interaction with external system or to deal with the next message, Spring Integration provides the metadata store component as an an implementation of the `org.springframework.integration.metadata.MetadataStore` interface with a general key-value contract. The metadata store is designed to store various types of generic metadata (for example, the published date of the last feed entry that has been processed) to help components such as the feed adapter deal with duplicates. If a component is not directly provided with a reference to a `MetadataStore`, the algorithm for locating a metadata store is as follows: First, look for a bean with a `metadataStore` ID in the application context. If one is found, use it. Otherwise, create a new instance of `SimpleMetadataStore`, which is an in-memory implementation that persists only metadata within the lifecycle of the currently running application context. This means that, upon restart, you may end up with duplicate entries. If you need to persist metadata between application context restarts, the framework provides the following persistent `MetadataStores`: * `PropertiesPersistingMetadataStore` * [Gemfire Metadata Store](./gemfire.html#gemfire-metadata-store) * [JDBC Metadata Store](./jdbc.html#jdbc-metadata-store) * [MongoDB Metadata Store](./mongodb.html#mongodb-metadata-store) * [Redis Metadata Store](./redis.html#redis-metadata-store) * [Zookeeper Metadata Store](./zookeeper.html#zk-metadata-store) The `PropertiesPersistingMetadataStore` is backed by a properties file and a [`PropertiesPersister`](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/PropertiesPersister.html). By default, it persists only the state when the application context is closed normally. It implements `Flushable` so that you can persist the state at will, by invoking `flush()`. The following example shows how to configure a 'PropertiesPersistingMetadataStore' with XML: ``` ``` Alternatively, you can provide your own implementation of the `MetadataStore` interface (for example, `JdbcMetadataStore`) and configure it as a bean in the application context. Starting with version 4.0, `SimpleMetadataStore`, `PropertiesPersistingMetadataStore`, and `RedisMetadataStore` implement `ConcurrentMetadataStore`. These provide for atomic updates and can be used across multiple component or application instances. #### Idempotent Receiver and Metadata Store The metadata store is useful for implementing the EIP [idempotent receiver](https://www.enterpriseintegrationpatterns.com/IdempotentReceiver.html) pattern when there is need to filter an incoming message if it has already been processed and you can discard it or perform some other logic on discarding. The following configuration shows an example of how to do so: ``` ``` The `value` of the idempotent entry may be an expiration date, after which that entry should be removed from metadata store by some scheduled reaper. See also [Idempotent Receiver Enterprise Integration Pattern](./handler-advice.html#idempotent-receiver). #### `MetadataStoreListener` Some metadata stores (currently only zookeeper) support registering a listener to receive events when items change, as the following example shows: ``` public interface MetadataStoreListener { void onAdd(String key, String value); void onRemove(String key, String oldValue); void onUpdate(String key, String newValue); } ``` See the [Javadoc](https://docs.spring.io/spring-integration/api/org/springframework/integration/metadata/MetadataStoreListenerAdapter.html) for more information. The `MetadataStoreListenerAdapter` can be subclassed if you are interested only in a subset of events. ### Control Bus As described in the [*Enterprise Integration Patterns*](https://www.enterpriseintegrationpatterns.com/) (EIP) book, the idea behind the control bus is that the same messaging system can be used for monitoring and managing the components within the framework as is used for “application-level” messaging. In Spring Integration, we build upon the adapters described above so that you can send messages as a means of invoking exposed operations. The following example shows how to configure a control bus with XML: ``` ``` The control bus has an input channel that can be accessed for invoking operations on the beans in the application context. It also has all the common properties of a service activating endpoint. For example, you can specify an output channel if the result of the operation has a return value that you want to send on to a downstream channel. The control bus runs messages on the input channel as Spring Expression Language (SpEL) expressions. It takes a message, compiles the body to an expression, adds some context, and then runs it. The default context supports any method that has been annotated with `@ManagedAttribute` or `@ManagedOperation`. It also supports the methods on Spring’s `Lifecycle` interface (and its `Pausable` extension since version 5.2), and it supports methods that are used to configure several of Spring’s `TaskExecutor` and `TaskScheduler` implementations. The simplest way to ensure that your own methods are available to the control bus is to use the `@ManagedAttribute` or `@ManagedOperation` annotations. Since those annotations are also used for exposing methods to a JMX MBean registry, they offer a convenient by-product: Often, the same types of operations you want to expose to the control bus are reasonable for exposing through JMX). Resolution of any particular instance within the application context is achieved in the typical SpEL syntax. To do so, provide the bean name with the SpEL prefix for beans (`@`). For example, to execute a method on a Spring Bean, a client could send a message to the operation channel as follows: ``` Message operation = MessageBuilder.withPayload("@myServiceBean.shutdown()").build(); operationChannel.send(operation) ``` The root of the context for the expression is the `Message` itself, so you also have access to the `payload` and `headers` as variables within your expression. This is consistent with all the other expression support in Spring Integration endpoints. With Java annotations, you can configured the control bus as follows: ``` @Bean @ServiceActivator(inputChannel = "operationChannel") public ExpressionControlBusFactoryBean controlBus() { return new ExpressionControlBusFactoryBean(); } ``` Similarly, you can configure Java DSL flow definitions as follows: ``` @Bean public IntegrationFlow controlBusFlow() { return IntegrationFlows.from("controlBus") .controlBus() .get(); } ``` If you prefer to use lambdas with automatic `DirectChannel` creation, you can create a control bus as follows: ``` @Bean public IntegrationFlow controlBus() { return IntegrationFlowDefinition::controlBus; } ``` In this case, the channel is named `controlBus.input`. ### Orderly Shutdown As described in "[MBean Exporter](./jmx.html#jmx-mbean-exporter)", the MBean exporter provides a JMX operation called `stopActiveComponents`, which is used to stop the application in an orderly manner. The operation has a single `Long` parameter. The parameter indicates how long (in milliseconds) the operation waits to allow in-flight messages to complete. The operation works as follows: 1. Call `beforeShutdown()` on all beans that implement `OrderlyShutdownCapable`. Doing so lets such components prepare for shutdown. Examples of components that implement this interface and what they do with this call include JMS and AMQP message-driven adapters that stop their listener containers, TCP server connection factories that stop accepting new connections (while keeping existing connections open), TCP inbound endpoints that drop (log) any new messages received, and HTTP inbound endpoints that return `503 - Service Unavailable` for any new requests. 2. Stop any active channels, such as JMS- or AMQP-backed channels. 3. Stop all `MessageSource` instances. 4. Stop all inbound `MessageProducer` s (that are not `OrderlyShutdownCapable`). 5. Wait for any remaining time left, as defined by the value of the `Long` parameter passed in to the operation. Doing so lets any in-flight messages complete their journeys. It is therefore important to select an appropriate timeout when invoking this operation. 6. Call `afterShutdown()` on all `OrderlyShutdownCapable` components. Doing so lets such components perform final shutdown tasks (closing all open sockets, for example). As discussed in [Orderly Shutdown Managed Operation](./jmx.html#jmx-mbean-shutdown), this operation can be invoked by using JMX. If you wish to programmatically invoke the method, you need to inject or otherwise get a reference to the `IntegrationMBeanExporter`. If no `id` attribute is provided on the `` definition, the bean has a generated name. This name contains a random component to avoid `ObjectName` collisions if multiple Spring Integration contexts exist in the same JVM (`MBeanServer`). For this reason, if you wish to invoke the method programmatically, we recommend that you provide the exporter with an `id` attribute so that you can easily access it in the application context. Finally, the operation can be invoked by using the `` element. See the [monitoring Spring Integration sample application](https://github.com/spring-projects/spring-integration-samples/tree/main/intermediate/monitoring) for details. | |The algorithm described earlier was improved in version 4.1.
Previously, all task executors and schedulers were stopped.
This could cause mid-flow messages in `QueueChannel` instances to remain.
Now the shutdown leaves pollers running, to let these messages be drained and processed.| |---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ### Integration Graph Starting with version 4.3, Spring Integration provides access to an application’s runtime object model, which can, optionally, include component metrics. It is exposed as a graph, which may be used to visualize the current state of the integration application. The `o.s.i.support.management.graph` package contains all the required classes to collect, build, and render the runtime state of Spring Integration components as a single tree-like `Graph` object. The `IntegrationGraphServer` should be declared as a bean to build, retrieve, and refresh the `Graph` object. The resulting `Graph` object can be serialized to any format, although JSON is flexible and convenient to parse and represent on the client side. A Spring Integration application with only the default components would expose a graph as follows: ``` { "contentDescriptor" : { "providerVersion" : "5.5.9", "providerFormatVersion" : 1.2, "provider" : "spring-integration", "name" : "myAppName:1.0" }, "nodes" : [ { "nodeId" : 1, "componentType" : "null-channel", "integrationPatternType" : "null_channel", "integrationPatternCategory" : "messaging_channel", "properties" : { }, "sendTimers" : { "successes" : { "count" : 1, "mean" : 0.0, "max" : 0.0 }, "failures" : { "count" : 0, "mean" : 0.0, "max" : 0.0 } }, "receiveCounters" : { "successes" : 0, "failures" : 0 }, "name" : "nullChannel" }, { "nodeId" : 2, "componentType" : "publish-subscribe-channel", "integrationPatternType" : "publish_subscribe_channel", "integrationPatternCategory" : "messaging_channel", "properties" : { }, "sendTimers" : { "successes" : { "count" : 1, "mean" : 7.807002, "max" : 7.807002 }, "failures" : { "count" : 0, "mean" : 0.0, "max" : 0.0 } }, "name" : "errorChannel" }, { "nodeId" : 3, "componentType" : "logging-channel-adapter", "integrationPatternType" : "outbound_channel_adapter", "integrationPatternCategory" : "messaging_endpoint", "properties" : { }, "output" : null, "input" : "errorChannel", "sendTimers" : { "successes" : { "count" : 1, "mean" : 6.742722, "max" : 6.742722 }, "failures" : { "count" : 0, "mean" : 0.0, "max" : 0.0 } }, "name" : "errorLogger" } ], "links" : [ { "from" : 2, "to" : 3, "type" : "input" } ] } ``` | |Version 5.2 deprecated the legacy metrics in favor of Micrometer meters as discussed [Metrics Management](./metrics.html#metrics-management).
The legacy metrics were removed in Version 5.4 and will no longer appear in the graph.| |---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| In the preceding example, the graph consists of three top-level elements. The `contentDescriptor` graph element contains general information about the application providing the data. The `name` can be customized on the `IntegrationGraphServer` bean or in the `spring.application.name` application context environment property. Other properties are provided by the framework and let you distinguish a similar model from other sources. The `links` graph element represents connections between nodes from the `nodes` graph element and, therefore, between integration components in the source Spring Integration application. For example, from a `MessageChannel` to an `EventDrivenConsumer` with some `MessageHandler`or from an `AbstractReplyProducingMessageHandler` to a `MessageChannel`. For convenience and to let you determine a link’s purpose, the model includes the `type` attribute. The possible types are: * `input`: Identifies the direction from `MessageChannel` to the endpoint, `inputChannel`, or `requestChannel` property * `output`: The direction from the `MessageHandler`, `MessageProducer`, or `SourcePollingChannelAdapter` to the `MessageChannel` through an `outputChannel` or `replyChannel` property * `error`: From `MessageHandler` on `PollingConsumer` or `MessageProducer` or `SourcePollingChannelAdapter` to the `MessageChannel` through an `errorChannel` property; * `discard`: From `DiscardingMessageHandler` (such as `MessageFilter`) to the `MessageChannel` through an `errorChannel` property. * `route`: From `AbstractMappingMessageRouter` (such as `HeaderValueRouter`) to the `MessageChannel`. Similar to `output` but determined at run-time. May be a configured channel mapping or a dynamically resolved channel. Routers typically retain only up to 100 dynamic routes for this purpose, but you can modify this value by setting the `dynamicChannelLimit` property. The information from this element can be used by a visualization tool to render connections between nodes from the `nodes` graph element, where the `from` and `to` numbers represent the value from the `nodeId` property of the linked nodes. For example, the `link` element can be used to determine the proper `port` on the target node. The following “text image” shows the relationships between the types: ``` +---(discard) | +----o----+ | | | | | | (input)--o o---(output) | | | | | | +----o----+ | +---(error) ``` The `nodes` graph element is perhaps the most interesting, because its elements contain not only the runtime components with their `componentType` instances and `name` values but can also optionally contain metrics exposed by the component. Node elements contain various properties that are generally self-explanatory. For example, expression-based components include the `expression` property that contains the primary expression string for the component. To enable the metrics, add an `@EnableIntegrationManagement` to a `@Configuration` class or add an `` element to your XML configuration. See [Metrics and Management](./metrics.html#metrics-management) for complete information. The `nodeId` represents a unique incremental identifier to let you distinguish one component from another. It is also used in the `links` element to represent a relationship (connection) of this component to others, if any. The `input` and `output` attributes are for the `inputChannel` and `outputChannel` properties of the `AbstractEndpoint`, `MessageHandler`, `SourcePollingChannelAdapter`, or `MessageProducerSupport`. See the next section for more information. Starting with version 5.1, the `IntegrationGraphServer` accepts a `Function> additionalPropertiesCallback` for population of additional properties on the `IntegrationNode` for a particular `NamedComponent`. For example you can expose the `SmartLifecycle` `autoStartup` and `running` properties into the target graph: ``` server.setAdditionalPropertiesCallback(namedComponent -> { Map properties = null; if (namedComponent instanceof SmartLifecycle) { SmartLifecycle smartLifecycle = (SmartLifecycle) namedComponent; properties = new HashMap<>(); properties.put("auto-startup", smartLifecycle.isAutoStartup()); properties.put("running", smartLifecycle.isRunning()); } return properties; }); ``` #### Graph Runtime Model Spring Integration components have various levels of complexity. For example, any polled `MessageSource` also has a `SourcePollingChannelAdapter` and a `MessageChannel` to which to periodically send messages from the source data. Other components might be middleware request-reply components (such as `JmsOutboundGateway`) with a consuming `AbstractEndpoint` to subscribe to (or poll) the `requestChannel` (`input`) for messages, and a `replyChannel` (`output`) to produce a reply message to send downstream. Meanwhile, any `MessageProducerSupport` implementation (such as `ApplicationEventListeningMessageProducer`) wraps some source protocol listening logic and sends messages to the `outputChannel`. Within the graph, Spring Integration components are represented by using the `IntegrationNode` class hierarchy, which you can find in the `o.s.i.support.management.graph` package. For example, you can use the `ErrorCapableDiscardingMessageHandlerNode` for the `AggregatingMessageHandler` (because it has a `discardChannel` option) and can produce errors when consuming from a `PollableChannel` by using a `PollingConsumer`. Another example is `CompositeMessageHandlerNode` — for a `MessageHandlerChain` when subscribed to a `SubscribableChannel` by using an `EventDrivenConsumer`. | |The `@MessagingGateway` (see [Messaging Gateways](./gateway.html#gateway)) provides nodes for each of its method, where the `name` attribute is based on the gateway’s bean name and the short method signature.
Consider the following example of a gateway:| |---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ``` @MessagingGateway(defaultRequestChannel = "four") public interface Gate { void foo(String foo); void foo(Integer foo); void bar(String bar); } ``` The preceding gateway produces nodes similar to the following: ``` { "nodeId" : 10, "name" : "gate.bar(class java.lang.String)", "stats" : null, "componentType" : "gateway", "integrationPatternType" : "gateway", "integrationPatternCategory" : "messaging_endpoint", "output" : "four", "errors" : null }, { "nodeId" : 11, "name" : "gate.foo(class java.lang.String)", "stats" : null, "componentType" : "gateway", "integrationPatternType" : "gateway", "integrationPatternCategory" : "messaging_endpoint", "output" : "four", "errors" : null }, { "nodeId" : 12, "name" : "gate.foo(class java.lang.Integer)", "stats" : null, "componentType" : "gateway", "integrationPatternType" : "gateway", "integrationPatternCategory" : "messaging_endpoint", "output" : "four", "errors" : null } ``` You can use this `IntegrationNode` hierarchy for parsing the graph model on the client side as well as to understand the general Spring Integration runtime behavior. See also [Programming Tips and Tricks](./overview.html#programming-tips) for more information. Version 5.3 introduced an `IntegrationPattern` abstraction and all out-of-the-box components, which represent an Enterprise Integration Pattern (EIP), implement this abstraction and provide an `IntegrationPatternType` enum value. This information can be useful for some categorizing logic in the target application or, being exposed into the graph node, it can be used by a UI to determine how to draw the component. ### Integration Graph Controller If your application is web-based (or built on top of Spring Boot with an embedded web container) and the Spring Integration HTTP or WebFlux module (see [HTTP Support](./http.html#http) and [WebFlux Support](./webflux.html#webflux), respectively) is present on the classpath, you can use a `IntegrationGraphController` to expose the `IntegrationGraphServer` functionality as a REST service. For this purpose, the `@EnableIntegrationGraphController` and `@Configuration` class annotations and the `` XML element are available in the HTTP module. Together with the `@EnableWebMvc` annotation (or `` for XML definitions), this configuration registers an `IntegrationGraphController` `@RestController` where its `@RequestMapping.path` can be configured on the `@EnableIntegrationGraphController` annotation or `` element. The default path is `/integration`. The `IntegrationGraphController` `@RestController` provides the following services: * `@GetMapping(name = "getGraph")`: To retrieve the state of the Spring Integration components since the last `IntegrationGraphServer` refresh. The `o.s.i.support.management.graph.Graph` is returned as a `@ResponseBody` of the REST service. * `@GetMapping(path = "/refresh", name = "refreshGraph")`: To refresh the current `Graph` for the actual runtime state and return it as a REST response. It is not necessary to refresh the graph for metrics. They are provided in real-time when the graph is retrieved. Refresh can be called if the application context has been modified since the graph was last retrieved. In that case, the graph is completely rebuilt. You can set security and cross-origin restrictions for the `IntegrationGraphController` with the standard configuration options and components provided by the Spring Security and Spring MVC projects. The following example achieves those goals: ``` ``` The following example shows how to do the same thing with Java configuration: ``` @Configuration @EnableWebMvc // or @EnableWebFlux @EnableWebSecurity // or @EnableWebFluxSecurity @EnableIntegration @EnableIntegrationGraphController(path = "/testIntegration", allowedOrigins="http://localhost:9090") public class IntegrationConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/testIntegration/**").hasRole("ADMIN") // ... .formLogin(); } //... } ``` Note that, for convenience, the `@EnableIntegrationGraphController` annotation provides an `allowedOrigins` attribute. This provides `GET` access to the `path`. For more sophistication, you can configure the CORS mappings by using standard Spring MVC mechanisms.