Commit Graph

2990 Commits

Author SHA1 Message Date
Artem Bilan
f276af9fb7 Fix another LockRequestHandlerAdviceTests race condition
There is no guarantee which message would reach the service first when we use `asyncConvertSendAndReceive()`.

* Add `config.longProcessEnteredLatch` to wait before sending the second (fourth in our case) message
until the long-running process against the first (third in our case) message has been started.
2025-04-09 13:45:42 -04:00
Artem Bilan
748d78fc46 Fix race condition in the LockRequestHandlerAdviceTests
The timeout of `5 seconds` might not be enough on slow environment like GHA.

* Rework the test logic to rely on the `CountDownLatch` instead.
This way the `longer_process` would wait for it to be fulfilled,
while we count down only when another request has failed with timeout for locking.
2025-03-31 11:11:36 -04:00
Artem Bilan
c9e3de8767 GH-9931: Make consumer endpoints dependant on ChannelInitializer
Fixes: #9931
Issue link: https://github.com/spring-projects/spring-integration/issues/9931

If no message channel bean is declared explicitly, the XML parser for endpoints
register such a candidate into the global `ChannelInitializer`.
This way, the channel is created when this bean is initialized.
However, there are cases when endpoint bean could be called before `ChannelInitializer` initialization.

* Add `consumerEndpointBuilder.addDependsOn(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);`
into the `AbstractConsumerEndpointParser` to ensure that `ChannelInitializer` bean is initialized
before the called endpoint bean.
* Adjust `ChannelInitializer` logic to use `registerSingleton()` API instead of `registerBeanDefinition()`
since the last one may cause problems with beans cache when this API is called at runtime.

**Auto-cherry-pick to `6.4.x`**
2025-03-28 14:35:46 -04:00
Artem Bilan
2cc9686c84 Increase "long process" delay in the LockRequestHandlerAdviceTests 2025-03-27 13:27:10 -04:00
Artem Bilan
6a1511cb57 Fix Checkstyle violations 2025-03-24 17:32:57 -04:00
Artem Bilan
2c7f796b42 GH-8056: Add LockRequestHandlerAdvice
Fixes: https://github.com/spring-projects/spring-integration/issues/8056

* Add `LockRequestHandlerAdvice` for exclusive service access against specific `key`.
2025-03-24 16:59:28 -04:00
Artem Bilan
d73ded52c9 Some code style clean up 2025-03-20 09:27:21 -04:00
Artem Bilan
74cf59bacb GH-9890: Fix ControlBusCommandRegistry for requiresDestruction()
Fixes: #9890
Issue link: https://github.com/spring-projects/spring-integration/issues/9890

The `DestructionAwareBeanPostProcessor.requiresDestruction()` defaults to `true`.
As a result, beans that are not meant to be processed during the destroy lifecycle
are being registered for the `AbstractBeanFactory#registerDisposableBeanIfNecessary`.
for example, the `SimpleThreadScope` does not support destruction callbacks.

In other words, the `DestructionAwareBeanPostProcessor.requiresDestruction()` contract
is not merely for `DestructionAwareBeanPostProcessor` logic, but also has an effect
on the whole `BeanFactory`

* Implement `requiresDestruction()` in the `ControlBusCommandRegistry`
for same filter as `registerControlBusCommands()` does right now.
* Optimize behaviour using `MergedAnnotations` for the class instead of
`AnnotationUtils.findAnnotation()` twice, which creates the mentioned `MergedAnnotations` internally.

**Auto-cherry-pick to `6.4.x`**
2025-03-13 14:29:41 -04:00
Artem Bilan
7680e02cbb GH-9866: Fix FluxMessageChannel for race condition on destroy
Fixes: #9866
Issue link: https://github.com/spring-projects/spring-integration/issues/9866

Due to race condition between `sink.tryEmitNext()` and `sink.emitComplete()``,
there could be a situation when `onNext` signal slips after `onComplete`.
Appears on fast producers into this `FluxMessageChannel`.
That leads to error like `Spec. Rule 1.3 - onSubscribe, onNext, onError and onComplete signaled to a Subscriber MUST be signaled serially.`

* Fix `FluxMessageChannel` to check for `this.active` one more time just before `sink.tryEmitNext()` call
* Also mitigate a race condition with a `Sinks.EmitFailureHandler.busyLooping(Duration.ofSeconds(1))` in the `destroy()`
instead of `Sinks.EmitFailureHandler.FAIL_FAST`.
The `busyLooping()` would retry requested `onComplete()` signal for that specific error until success or timeout

**Auto-cherry-pick to `6.4.x` & `6.3.x`**
2025-02-26 17:47:23 -05:00
Artem Bilan
d54d55653f GH-9863: Remove deprecated PollerMetadata.sendTimeout option
Issue link: https://github.com/spring-projects/spring-integration/issues/9863

The `PollerMetadata.sendTimeout` has been deprecated in previous versions
in favor of ``

* Remove `PollerMetadata.sendTimeout` and `PollerSpec.sendTimeout`
in favor of `SourcePollingChannelAdapterSpec.sendTimeout`
2025-02-26 16:26:56 -05:00
Artem Bilan
0af3b25cd6 GH-9863: Deprecate PollerMetadata.sendTimeout option
Fixes: #9863
Issue link: https://github.com/spring-projects/spring-integration/issues/9863

The `PollerMetadata.sendTimeout` has been migrated to the `SourcePollingChannelAdapter.setSendTimeout()` long time ago.
Right now this option is not propagated anywhere

* Introduce missed `SourcePollingChannelAdapterSpec.sendTimeout`
* Deprecate (for removal) `PollerMetadata.sendTimeout`
* Verify `SourcePollingChannelAdapterSpec.sendTimeout` option in the `ReactiveStreamsTests`

**Auto-cherry-pick to `6.4.x` & `6.3.x`**
2025-02-26 16:19:14 -05:00
Artem Bilan
2296e4798e GH-9854: Special error for "too early message production"
Fixes: https://github.com/spring-projects/spring-integration/issues/9854

The well-known `Dispatcher has no subscribers` is not very informative
when a message is produced from early application context initialization phase

* Add internal `ApplicationRunningController` bean to handle early `start()` event
* Check for this bean status from the `AbstractMessageChannel.send()`
* Throw specific `MessageDispatchingException` to indicate that the message was produced from a wrong place
* Adjust `ApplicationEventListeningMessageProducer` logic for `ContextStoppedEvent` & `ContextClosedEvent`
to indicate that `AbstractMessageChannel` bean might not dispatch a message because the application context is not running
2025-02-20 17:46:27 -05:00
Artem Bilan
12fee0a9fb GH-9825: DelayerEndpointSpec: Set TaskScheduler to the handler as well
Fixes: #9825
Issue link: https://github.com/spring-projects/spring-integration/issues/9825

The `DelayerEndpointSpec` extends `ConsumerEndpointSpec` which has a `taskScheduler()` option.
However this is set only to the endpoint for this `MessageHandler`.

* Override `taskScheduler()` method on the `DelayerEndpointSpec` to set
the provided `TaskScheduler` to the `DelayHandler` as well

**Auto-cherry-pick to `6.4.x` & `6.3.x`**
2025-02-13 12:13:52 -05:00
Artem Bilan
8dab7409b5 GH-9792: Remove resource header in the StreamTransformer
Fixes: https://github.com/spring-projects/spring-integration/issues/9792

The `StreamTransformer` closes `IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE` header value,
so this becomes unusable afterward.
In addition, it may even cause some problems downstream when the message could be serialized for
subsequent network interaction.

* Add logic to the `StreamTransformer` to build a new message, but remove `IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE` header
* Verify that header is removed in the `StreamingInboundTests`.
Addition `StreamingInboundTests` clean up for better code style.
2025-02-11 11:47:33 -05:00
Artem Bilan
ad61cef7fc GH-9754: Add discardIndividuallyOnExpiry to aggregator
Fixes: https://github.com/spring-projects/spring-integration/issues/9754

Right now a correlation handler can discard messages in the expired group one by one.
In some scenarios it would be useful to have single message in discard for the whole group.

* Expose `discardIndividuallyOnExpiry` for the `AbstractCorrelatingMessageHandler`,
and `AggregatorFactoryBean`, and respective `CorrelationHandlerSpec` for DSL.
This new option takes action only if a `discardChannel` is provided,
and `sendPartialResultOnExpiry` is not set to `true`.
When `discardIndividuallyOnExpiry` is false, the messages in the expired group are packed
into a list for payload of a discarding single message.
* Test and document the new feature
2025-01-14 13:03:29 -05:00
Artem Bilan
6184c4082a GH-9745: IntEvalCtxFB: Use BeanFactory's ClassLoader
Fixes: #9745
Issue link: https://github.com/spring-projects/spring-integration/issues/9745

The `IntegrationEvaluationContextFactoryBean` does not provide a `TypeLocator` by default.
That may lead to a class-not-found problem from different `ClassLoader` in the parallel Java `Stream` executor.

* Fix `IntegrationEvaluationContextFactoryBean` to use a `StandardTypeLocator` based on the `applicationContext.getClassLoader()`

**Auto-cherry-pick to `6.4.x` & `6.3.x`**
2025-01-09 16:27:38 -05:00
Artem Bilan
8391f07728 Remove @Deprecated classes 2025-01-09 13:34:20 -05:00
Artem Bilan
89c5dfa4d6 GH-9743: Add observation to the SourcePollingChannelAdapter
Fixes: https://github.com/spring-projects/spring-integration/issues/9743

Spring Integration provides observation for the `MessageChannel`, `MessageHandler`
and `MessageProducerSupport`.
The `SourcePollingChannelAdapter` is missing, and it is that only special endpoint which
deals with `MessageSource` implementations via scheduled tasks in the poller.
Essentially, this endpoint is a start of the flow, but it still is a consumer of data from the source system.

* Add an `Observation` logic to the `SourcePollingChannelAdapter`.
* Divide it into two phases: start (and open scope) when message is received; stop (and close scope) when the whole polling task for a message is done.
We need this separation because of transaction scope for the polling task.
At the same time we don't want to emit an observation for a void polling task.
* Change `MessageReceiverContext` to accept a `handlerType`.
The `MessageHandler` contributes a `handler`.
The new support in the `SourcePollingChannelAdapter` - `message-source`.
And change `MessageProducerSupport` to contribute a `message-producer`
* Verify the single trace is supported for the whole flow (including transaction synchronization) starting from a `SourcePollingChannelAdapter` in a new `SourcePollingChannelAdapterObservationTests`
* Document this new feature
2025-01-09 13:31:02 -05:00
NaccOll
9962ee49a2 GH-5123: Add LockRegistry to AbstractMessageGroupStore
Fixes: https://github.com/spring-projects/spring-integration/issues/5123

When `RedisMessageStore`, for example, adds and removes messages, it operates on two keys separately, which may cause problems in multi-threading due to non-atomic operations.
Although using Redis to delay messages is not a good idea, the abnormal loss of messages in the logs alerted me when the number of requests was not large.
By comparing the logs, the problem that the message group representing the metadata is not consistent with the actual message.

A simple solution is to add lock like in the `SimpleMessageStore`, which is also the approach taken in this pull request.

* Add `LockRegistry` to `AbstractMessageGroupStore`
* Normalize access levels and method name about the lock of `MessageGroupStore`
* Add document about the lock of `AbstractMessageGroupStore`
2025-01-08 12:18:12 -05:00
Artem Bilan
6e9fd47426 GH-9706: Allow collection of payloads as an aggregator result
Fixes: https://github.com/spring-projects/spring-integration/issues/9706

Previously, if a `MessageGroupProcessor` returns a collection of payloads,
the `AbstractCorrelatingMessageHandler` has failed with the `IllegalArgumentException`
stating that only collection of messages is possible.
From now on such a restriction is eliminated and returned collection of payloads
is emitted as a single reply message from the aggregator.

* Add `AbstractCorrelatingMessageHandler.isResultCollectionOfMessages()`
to return `true` only if result is a collection of messages, treating them as a "partial sequence".
* Deprecate `AbstractCorrelatingMessageHandler.verifyResultCollectionConsistsOfMessages()`
since it is out of use now.
2025-01-02 16:26:37 -05:00
Artem Bilan
d9f21fef42 GH-9683: Bring back controlBus() API
Fixes: https://github.com/spring-projects/spring-integration/issues/9683

Since we don't support SpEL-based Control Bus functionality anymore,
there is no need to keep separate `controlBusOnRegistry()`

* In all DSLs Deprecate `controlBusOnRegistry()` and restore `controlBus()` which is now fully based
on the `ControlBusCommandRegistry`
* Deprecate now out of use `<control-bus use-registry="">` attribute
* Fix `ControlBusParser` to not deal with `use-registry` attribute anymore
* Remove deprecated before `ExpressionControlBusFactoryBean` and `ExpressionCommandMessageProcessor`
* Remove `use-registry` from test configs
* Fix `ControlBusChainTests` to rely on a new Control Bus functionality
2025-01-02 14:06:31 -05:00
Artem Bilan
37664fb2aa Fix Javadoc tags order in the CheckedCallable 2025-01-02 12:54:58 -05:00
Artem Bilan
ffcf5f91f1 Fix Javadoc errors in the CheckedCallable 2025-01-02 12:49:40 -05:00
Artem Bilan
8ee1a36da4 GH-9704: Properly implement CheckedCallable.unchecked()
Fixes: https://github.com/spring-projects/spring-integration/issues/9704

* Make `CheckedCallable.unchecked()` to return an expected `Callable`
* Deprecate for removal `CheckedCallable.uncheckedCallable()`
2025-01-02 12:40:41 -05:00
Artem Bilan
9d29bdf5b5 GH-9618: Remove usage of ListenableFuture
Fixes: https://github.com/spring-projects/spring-integration/issues/9618
2025-01-02 11:25:46 -05:00
Falk Hanisch
c677fc56f2 GH-9713: Add @Nullable to IntegrationEvent.getCause()
Fixes: #9713

**Auto-cherry-pick to `6.3.x`**
2024-12-12 21:33:50 -05:00
Artem Bilan
4d84220dad GH-9709: Fix IntegrationFlow for input channel resolution
Fixes: #9709
Issue link: https://github.com/spring-projects/spring-integration/issues/9709

The Java DSL loses an `inputChannel` when existing channel is referenced by its name.
Then the target IntegrationFlow does not contain a bean reference for this channel and when we call its `getInputChannel()` we got something from middle of the flow.
However, that `inputChannel` is really expected to be an input for the flow.

* Fix `IntegrationFlowBeanPostProcessor` to get a bean by `MessageChannelReference` and populate it into `targetIntegrationComponents`
as we do for newly created `DirectChannel` if there is no bean for provided `MessageChannelReference`

**Auto-cherry-pick to `6.3.x`**
2024-12-10 16:38:07 -05:00
Mitchell
7fc104ac8b GH-9705: AbstractReplyProducingMessageHandler: check for logging enabled
Fixes: #9705
Issue link: https://github.com/spring-projects/spring-integration/issues/9705

Currently when any class that implements `AbstractReplyProducingMessageHandler` doesn't produce a reply it will log the message, even if no reply is required. 
The message should only be logged if `isLoggingEnabled()` returns true as a handler that doesn't require a reply may be a normal operation that is expected, and if we've set `loggingEnabled` to false it shouldn't log the message.

* Prevent logging when no reply is provided for an `AbstractReplyProducingMessageHandler` if logging is disabled

**Auto-cherry-pick to `6.3.x`**
2024-12-10 10:02:21 -05:00
Artem Bilan
dd7dd09d00 Fix Javadoc style errors in the CheckedCallable
**Auto-cherry-pick to `6.3.x`**
2024-12-09 13:27:56 -05:00
Artem Bilan
8c22bf6430 GH-9702: Add CheckedCallable.uncheckedCallable
Fixes: #9702
Issue link: https://github.com/spring-projects/spring-integration/issues/9702

The current `CheckedCallable.unchecked()` returns `Runnable`, which is not an expectation.

* Deprecate `CheckedCallable.unchecked()` in favor of newly introduced `CheckedCallable.uncheckedCallable()`.
We cannot call it `unchecked()` as well, since `Callable` after erasure becomes similar to class signature as `Runnable`.

**Auto-cherry-pick to `6.3.x`**
2024-12-09 13:17:40 -05:00
Artem Bilan
5313c42e17 Some SimpleMessageGroupTests.testPerformance() optimization 2024-12-06 09:27:02 -05:00
Artem Bilan
6c426308f0 Adjust BaseIntegrationFlowDefinition.controlBus() deprecation
Related to: https://github.com/spring-projects/spring-integration/issues/9683.

Since we are going to restore `controlBus()` for convenience,
and only one way of Control Bus pattern, based on the `ControlBusCommandRegistry`, there is no need to have another `controlBusOnRegistry()` method

* Also fix link for `error-handling` chapter from the `special-channels.adoc`
2024-12-02 13:29:40 -05:00
Artem Bilan
da58fef3e0 More @DirtiesContext for tests in core module 2024-11-22 11:49:54 -05:00
Artem Bilan
8f4c2be0cb More @DirtiesContext for tests in core module
Additional code style cleanups in the affected classes

**Auto-cherry-pick to `6.3.x` & `6.2.x`**
2024-11-15 13:40:29 -05:00
Artem Bilan
09750f2012 Remove unnecessary Thread.sleep() from the AsyncGatewayTests
Such a `Thread.sleep()` makes tests slower, plus may cause a longer timing issue on slow CI/CD when CPU resources are limited

**Auto-cherry-pick to `6.3.x` & `6.2.x`**
2024-11-14 14:49:52 -05:00
Artem Bilan
4ccec0bae3 More @DirtiesContext in test of core module
The another attempt to mitigate out of memory error on GitHub Actions
2024-11-12 15:52:52 -05:00
Artem Bilan
a171ffd12d The EmbeddedJsonHeadersMessageMapper cleanups 2024-11-12 14:32:24 -05:00
Artem Bilan
ba57ee8a1b GH-9623: Fix ThreadStatePropagationChannelInterceptor for concurrency
Fixes: #9623
Issue link: https://github.com/spring-projects/spring-integration/issues/9623

The `ConcurrentModificationException` is thrown from the `ThreadStatePropagationChannelInterceptor.MessageWithThreadState.stateQueue`
which is a not thread-safe `LinkedList`

* Fix `ThreadStatePropagationChannelInterceptor.MessageWithThreadState.stateQueue` to be a `LinkedBlockingQueue` instead

**Auto-cherry-pick to `6.3.x` & `6.2.x`**
2024-10-31 13:23:44 -04:00
Artem Bilan
0d2595ef7c GH-9620: Use Locale.ROOT for neutral, case insensitive comparisons
Fixes: #9620
Issue link: https://github.com/spring-projects/spring-integration/issues/9620

**Auto-cherry-pick to `6.3.x` & `6.2.x`**
2024-10-31 10:59:05 -04:00
Artem Bilan
ff2b295be8 Fix version in ListenableFuture removal warning message 2024-10-30 16:28:26 -04:00
Artem Bilan
b50c40279a GH-9617: @SuppressWarnings("removal") for ListenableFuture
Fixes: #9617
Issue link: https://github.com/spring-projects/spring-integration/issues/9617

The `ListenableFuture` is marked `forRemoval` in Spring Framework.
So, fix the code base to use `@SuppressWarnings("removal")`.
Also, add a warning into logs that `ListenableFuture` support will be removed in `7.0`.

* Fix JavaDocs where `ListenableFuture` is mentioned in favor of `CompletableFuture`
2024-10-30 15:24:10 -04:00
Artem Bilan
8dad15bac2 GH-9614: SimpleJsonSerializer: escape only \
Fixes: #9614
Issue link: https://github.com/spring-projects/spring-integration/issues/9614

The `Matcher.quoteReplacement()` escapes `\` as well as `$`.
However, Jackson tries to resolve special symbol from the escaped `$` and fails as `Unrecognized character escape '$' (code 36)`

* Fix `SimpleJsonSerializer.toElement()` to escape only `\`

**Auto-cherry-pick to `6.3.x` & `6.2.x`**
2024-10-30 14:17:41 -04:00
Artem Bilan
745dde1f65 Fiux Checkstyle violation in the MessageBuilderTests 2024-10-29 16:14:16 -04:00
Artem Bilan
bd067da100 Fiux Checkstyle violation in the BaseMessageBuilder 2024-10-29 16:04:16 -04:00
Artem Bilan
f87aff3aa8 GH-9416: Extract BaseMessageBuilder for easier message extensions
Fixes: #9416
Issue link: https://github.com/spring-projects/spring-integration/issues/9416

The `MessageBuilderFactory` bean could be used a central place to provide custom `Message`
implementation into the application.
For example, the `GenericMessage.toString()` can be overridden to remove or mask sensitive
information from the payload or headers.

* Extract a `BaseMessageBuilder` from the `MessageBuilder` class to simplify
a custom `MessageBuilderFactory` implementation
* Test and document new feature and its purpose
2024-10-29 15:40:39 -04:00
NaccOll
73bb813b53 GH-9561: Make DelayedMessageWrapper JSON-serializable
Fixes: #9561
PR: https://github.com/spring-projects/spring-integration/pull/9561

The `DelayHandler.DelayedMessageWrapper` cannot be deserialized when using `RedisMessageStore` and JSON serialization:
```
org.springframework.data.redis.serializer.SerializationException: Could not read JSON:Cannot construct instance of `org.springframework.integration.handler.DelayHandler$DelayedMessageWrapper` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: UNKNOWN; byte offset: #UNKNOWN]
	at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.deserialize(GenericJackson2JsonRedisSerializer.java:311)
```

* More code clean up and refactoring in the test

**Auto-cherry-pick to `6.3.x` & `6.2.x`**
2024-10-22 12:02:59 -04:00
Artem Bilan
e1cebafa5c GH-9558: Expose BarrierSpec.discardChannel & triggerTimeout
Fixes: #9558
Issue link: https://github.com/spring-projects/spring-integration/issues/9558

**Auto-cherry-pick to `6.3.x` & `6.2.x`**
2024-10-18 16:06:06 -04:00
Artem Bilan
15e914b75b Remove Bamboo mentioning from the tests 2024-10-18 14:33:43 -04:00
NaccOll
4d08e11903 Add Redis lock periodic renewal
Although `RenewableLockRegistry` provides a renew interface, it is inconvenient for users.
Developers hope to have a lock that can be automatically renewed.
On the one hand, it can avoid subsequent failures caused by locks that will not expire when abnormal exits,
and on the other hand, it can avoid unlock failures caused by lock expired.

* Add `RenewableLockRegistry.setRenewalTaskScheduler()` and when it is set, schedule a `renew()` script periodically
when lock is acquired  from Redis with `1/3` of `expireAfter`
* Test and document the feature
2024-10-17 16:49:14 -04:00
Artem Bilan
d5ab03c0c7 GH-9524: Expose SourcePollingChannelAdapterSpec.taskScheduler
Fixes: #9524
Issue link: https://github.com/spring-projects/spring-integration/issues/9524

It is useful in some use-cases to be able to inject a custom `TaskScheduler`
(e.g. with a `TaskDecorator`) into a source polling channel adapter.

* Add `SourcePollingChannelAdapterFactoryBean.setTaskScheduler()`
 and call it from the `SourcePollingChannelAdapterSpec.taskScheduler()`
* Fix JavaDocs typos in the `ConsumerEndpointSpec`
* Test custom `TaskScheduler` usage and mention new option in the `whats-new.adoc`
2024-10-14 17:22:29 -04:00