Skip to content

Spring Integration 4.3 to 5.0 Migration Guide

Artem Bilan edited this page May 22, 2017 · 36 revisions

IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE value typo

The closableResource typo in the IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE constant value has been fixed to the proper closeableResource. If application doesn't use IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE to access to an appropriate header, it is recommended to review any closableResource typo usage.

JMS Default Connection Factory Bean Name

JMS components can be configured using XML with no connection factory property; the framework uses a default bean name. Prior to version 5.0, this default bean name was connectionFactory. In order to align with Spring Boot's auto configuration, which configures a bean called jmsConnectionFacrory, Spring Integration now uses that bean name as the default. If your application relied on the previous behavior, you will need to rename your bean to jmsConnectionFactory or change the component definitions to explicitly reference your bean.

Reactor 3.1 support

The Reactor 2.0 isn't supported any more. The Messaging Gateway Promise return type from Reactor 2.0 has been replaced with the Mono type from Reactor 3.1. For all the Reactive Streams changes for new Mono type, please, refer to the Reactor Project Site.

Java DSL

The Spring Integration Java DSL has been merged to the Core project with Java 8 code base. The old project remains for the previous Spring Integration versions and isn't compatible with version 5.0. Although the project has been generally merged, some changes have happened:

The following classes have been moved :

  • All the classes from org.springframework.integration.dsl.core to org.springframework.integration.dsl.

  • From org.springframework.integration.dsl.support:

  • the Java 8 functions (Consumer, Function etc.) have been removed in favor of java.util.function package classes
  • Transformers to the org.springframework.integration.dsl;
  • BeanNameMessageProcessor to the org.springframework.integration.handler;
  • FunctionExpression to the org.springframework.integration.expression;
  • GenericHandler to the org.springframework.integration.handler;
  • MapBuilder, StringStringMapBuilder and PropertiesBuilder to the org.springframework.integration.support;
  • MessageProcessorMessageSource to the org.springframework.integration.endpoint;
  • org.springframework.integration.dsl.support.tuple classes have been replaced for usage for similar from the package reactor.util.function, which is now mandatory dependency Reactor 3.0.

  • Classes DslIntegrationConfigurationInitializer and IntegrationFlowBeanPostProcessor from the package org.springframework.integration.dsl.config have been moved to the org.springframework.integration.config.dsl.

  • Classes TransactionHandleMessageAdvice and TransactionInterceptorBuilder from the package org.springframework.integration.dsl.transaction have been moved to the org.springframework.integration.transaction.

All the protocol-specific Java DSL components, e.g. Jms and Jpa factories, have been moved to the appropriate Spring Integration Modules with straightforward package rename. For example classes from the org.springframework.integration.dsl.mail package are now in the spring-integration-mail module and in the package org.springframework.integration.mail.dsl. The org.springframework.integration.dsl.kafka content is now located in the Spring Integration Kafka extension project, in version 3.0 in the package org.springframework.integration.kafka.dsl.

Java DSL breaking changes

  • The protocol-specific factory methods in the Channels (e.g. amqp(ConnectionFactory connectionFactory)) have been removed in favor of appropriate factory methods in the target DSL components factory. For example jmsPollable() is now like Jms.pollableChannel().

  • The protocol-specific factory methods in the Transformers (e.g. fromMail()) have been removed in favor of appropriate factory methods in the target DSL components factory. For example fileToString() is now like Files.toStringTransformer().

  • The IntegrationFlowDefinition.handleWithAdapter(), together with its Adapters factory, has been removed to avoid modules tangle. Now you have to use target DSL components factory directly, for example:

.handle(Files.outboundGateway(m -> m.getHeaders().get("directory")))

instead of:

.handleWithAdapter(a -> a.fileGateway(m -> m.getHeaders().get("directory")))
  • The EnricherSpec now extends ConsumerEndpointSpec instead of MessageHandlerSpec and, therefore,
IntegrationFlowDefintion.enrich(Consumer<EnricherSpec> enricherConfigurer, 
              Consumer<GenericEndpointSpec<ContentEnricher>> endpointConfigurer)

method has been removed, since all GenericEndpointSpec options are now supplied via EnricherSpec directly

  • The AbstractRouterSpec now extends ConsumerEndpointSpec instead of MessageHandlerSpec and, therefore, methods in the IntegrationFlowDefintion like:
route(Object service, String methodName, 
       Consumer<RouterSpec<Object, MethodInvokingRouter>> routerConfigurer, 
       Consumer<GenericEndpointSpec<MethodInvokingRouter>> endpointConfigurer)
...
route(String expression, 
       Consumer<RouterSpec<T, ExpressionEvaluatingRouter>> routerConfigurer, 
       Consumer<GenericEndpointSpec<ExpressionEvaluatingRouter>> endpointConfigurer)
...
route(Function<S, T> router, 
       Consumer<RouterSpec<T, MethodInvokingRouter>> routerConfigurer, 
       Consumer<GenericEndpointSpec<MethodInvokingRouter>> endpointConfigurer)
...
route(Class<P> payloadType, Function<P, T> router, 
       Consumer<RouterSpec<T, MethodInvokingRouter>> routerConfigurer, 
       Consumer<GenericEndpointSpec<MethodInvokingRouter>> endpointConfigurer)
...
route(MessageProcessorSpec<?> messageProcessorSpec,
       Consumer<RouterSpec<Object, MethodInvokingRouter>> routerConfigurer,
       Consumer<GenericEndpointSpec<MethodInvokingRouter>> endpointConfigurer)
...
route(R router, Consumer<RouterSpec<K, R>> routerConfigurer,
       Consumer<GenericEndpointSpec<R>> endpointConfigurer)
...
routeToRecipients(Consumer<RecipientListRouterSpec> routerConfigurer,
       Consumer<GenericEndpointSpec<RecipientListRouter>> endpointConfigurer)

have been removed in favor of those methods without the Consumer<GenericEndpointSpec<?>> since all its options are now supported by the AbstractRouterSpec` directly.

  • The HeaderEnricherSpec now extends ConsumerEndpointSpec instead of IntegrationComponentSpec and, therefore,
IntegrationFlowDefintion.enrichHeaders(Consumer<HeaderEnricherSpec> headerEnricherConfigurer,
       Consumer<GenericEndpointSpec<MessageTransformingHandler>> endpointConfigurer)

method has been removed, since all GenericEndpointSpec options are now supplied via HeaderEnricherSpec directly

  • The 'Amqp' factory methods for inbound adapters and gateways can now accept either a SimpleMessageListenerContainer or the new DirectMessageListenerContainer from Spring AMQP 2.0.

There is a breaking change in that the container properties now must be set via a .configureContainer(...) call, instead of on the endpoint spec itself.

Previously:

.from(Amqp.inboundGateway(rabbitConnectionFactory, amqpTemplate, queue())
		.id("amqpInboundGateway")
		.recoveryInterval(5000)
		.concurrentConsumers(2)
		.defaultReplyTo(defaultReplyTo().getName()))

Now:

.from(Amqp.inboundGateway(rabbitConnectionFactory, amqpTemplate, queue())
		.id("amqpInboundGateway")
		.configureContainer(c -> c
			.recoveryInterval(5000)
			.concurrentConsumers(2))
		.defaultReplyTo(defaultReplyTo().getName()))

STOMP Support and Reactor 3.0

The Reactor2TcpStompSessionManager has been renamed to the ReactorNettyTcpStompSessionManager and it is based on a new ReactorNettyTcpStompClient from Spring Framework 5.0. Reactor 2.x components aren't supported any more.

AMQP Header Mapping

The DefaultAmqpHeaderMapper now maps the AmqpHeaders.CORRELATION_ID (amqp_correlationId) to/from String. Previously, it mapped to/from byte[].

FileWritingMessageHandler Flush Predicates

The FlushPredicate and MessageFlushPredicate have an addition parameter firstWrite - the time a new (or previously closed) file was first written to.

AMQP-Backed (Pollable) Channel

The pollable channel now blocks the poller thread for the specified receiveTimeout (default 1 second). Previously, unlike other PollableChannels, the thread returned immediately to the scheduler if no message was available, regardless of the receive timeout. Blocking is a little more expensive than just using a basicGet() to retrieve a message (with no timeout) because a consumer has to be created to receive each message. To restore the previous behavior, set the poller receiveTimeout to 0.

ExpressionEvaluatingReleaseStrategy

To provide better performance for typical expressions for ReleaseStrategy (e.g. size() == 10) during aggregator functionality, and to avoid extra overhead to load messages from the persistence message store, the ExpressionEvaluatingReleaseStrategy has been changed to use the entire MessageGroup as the root evaluation context object, instead of MessageGroup.getMessages() as before. So, if your expressions were based on the message collection, you should now enhance them by adding the messages. property reference, for example:

release-strategy-expression="^[payload gt 5] != null"

must be changed to:

release-strategy-expression="messages.^[payload gt 5] != null"

HTTP Outbound and payload for methods

In addition to the GET HTTP method, the AbstractHttpRequestExecutingMessageHandler now doesn't include payload as a request body for the HEAD and TRACE HTTP methods as well.

RequestReplyExchanger contract

The messaging gateway has always had logic to extract the target exception from the downstream flow, when the top-level exception is a MessagingException, containing the failedMessage property. Since RequestReplyExchanger has a full duplex messaging contract, it has been changed to throw that MessagingException as is without unwrapping. If you still are using RequestReplyExchanger directly and wish to unwrapn the target exception, you always can either analyze the cause of the MessagingException or use a custom interface for gateway instead of RequestReplyExchanger (with a similar contract but no throws clause).

Testing Support

An existing spring-integration-test module has been renamed to the spring-integration-test-support with the same structure. And it still doesn't have dependencies from the Spring Integration Core. At the same time spring-integration-test module provides a new Spring Integration Test Framework with the org.springframework.integration.test.context and org.springframework.integration.test.mock packages. The spring-integration-test-support is a transitive dependency of this module. So, if you use spring-integration-test in your project, nothing is changed from the classpath perspective and existing utilities and matchers are loaded transparently for you.

RedisLockRegistry

Previously, having the local optimization, the RedisLockRegistry must be used like obtain() very close for the lock() to have as fresh state in the store as possible. When it is used for the LockRegistryLeaderInitiator, the scenario doesn't work because that one uses only tryLock() repeatedly and the local optimization doesn't allow to refresh state in the store on lock re-entrance. With all those flaws the RedisLockRegistry has been reworked to perform key refreshing in the store on each lock re-entrance. To be sure that only one instance gets access to the key, a unique clientId property has been added to the RedisLockRegistry. The value structure in the Redis now is changed to only clientId property. The RedisLock object representation in the store does not make sense because it can be deserialized only by the clientId, which is different from instance to instance of RedisLockRegistry.

RedisStoreWritingMessageHandler

A ZADD Redis command INCR option is now evaluated to false by default, to align with the redis default. Also, previously, it could only be configured using the RedisHeaders.ZSET_INCREMENT_SCORE message header. Now, the RedisStoreWritingMessageHandler provides the setZsetIncrementExpression() option which can use any expression that evaluates to a boolean.