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:

  • Java

  • XML

@Configuration
@EnableIntegration
@EnableMessageHistory
<int:message-history/>

Now every named component (that has an 'id' defined) is tracked. The framework sets the 'history' header in your message. Its value a List<Properties>.

Consider the following configuration example:

  • Java

  • XML

@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;
}
<int:gateway id="sampleGateway"
    service-interface="org.springframework.integration.history.sample.SampleGateway"
    default-request-channel="bridgeInChannel"/>

<int:header-enricher id="sampleEnricher" input-channel="enricherChannel" output-channel="filterChannel">
    <int:header name="baz" value="baz"/>
</int:header-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<Properties> 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 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:

  • Java

  • XML

@Configuration
@EnableIntegration
@EnableMessageHistory("*Gateway", "sample*", "aName")
<int:message-history tracked-components="*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), 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 <domain>:name=messageHistoryConfigurer,type=MessageHistoryConfigurer.

Only one @EnableMessageHistory (or <message-history/>) must be declared in the application context as single source for components tracking configuration. Do not use a generic bean definition for the MessageHistoryConfigurer.
Prior to version 6.3, the message history header was immutable (you cannot re-write history): every single track created not only new instance of the MessageHistory, but a fully new message copy. Now it works in append-only mode: the first track creates a new message with a new MessageHistory container. All the rest MessageHistory.write() calls add new entries to existing header - and no new message created. This significantly improves the application performance. All the components in the framework, where same message can be sent to several consumers (PublishSubscribeChannel, AbstractMessageRouter, WireTap etc.), or splitter produces several outputs based on the input message, are now cloning an existing MessageHistory header into those new messages. For any other multi-producing use-cases, outside the framework scope, the AbstractIntegrationMessageBuilder.cloneMessageHistoryIfAny() API is recommended to ensure that parallel downstream sub-flows contribute their own message history traces.