Skip to content

Commit c0334ee

Browse files
committed
Fix race condition for ApplicationEventListeningMessageProducer
The `ContextStoppedEvent` and `ContextClosedEvent` are emitted when the application context is not in an active state anymore. Therefore, dispatching those events into beans (`MessageChannel` in our case) is not what possible to handle because endpoints are stopped (or destroyed) * Fix `ApplicationEventListeningMessageProducer` to ignore `ContextStoppedEvent` and `ContextClosedEvent` altogether for output channel of `AbstractMessageChannel`. The warning we emit in that case should be enough to indicate what is going on and what could be done to mitigate situation with custom `MessageChannel` implementation. * Improve error message for the `MessageDispatchingException` in the `AbstractMessageChannel.assertApplicationRunning()` to indicate what exactly channel is in the problem.
1 parent 4dccefd commit c0334ee

File tree

3 files changed

+20
-15
lines changed

3 files changed

+20
-15
lines changed

spring-integration-core/src/main/java/org/springframework/integration/channel/AbstractMessageChannel.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,11 +371,12 @@ private void assertApplicationRunning(Message<?> message) {
371371
if (!this.applicationRunning) {
372372
throw new MessageDispatchingException(message,
373373
"""
374-
The application context is not ready to dispatch messages. \
374+
The application context is not ready (or stopped) to dispatch messages to '%s' channel. \
375375
It has to be refreshed or started first. \
376376
Also, messages must not be emitted from initialization phase, \
377377
like 'afterPropertiesSet()', '@PostConstruct' or bean definition methods. \
378-
Consider to use 'SmartLifecycle.start()' instead.""");
378+
Consider to use 'SmartLifecycle.start()' instead."""
379+
.formatted(getComponentName()));
379380
}
380381
}
381382

spring-integration-core/src/test/java/org/springframework/integration/bus/ApplicationContextMessageBusTests.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public void endpointRegistrationWithInputChannelReference() throws ClassNotFound
7676
ClassUtils.forName(
7777
IntegrationContextUtils.BASE_PACKAGE + ".config.ApplicationRunningController", null)));
7878
QueueChannel sourceChannel = new QueueChannel();
79+
sourceChannel.setBeanName("sourceChannel");
7980
sourceChannel.setApplicationContext(this.context);
8081
QueueChannel targetChannel = new QueueChannel();
8182
this.context.registerChannel("sourceChannel", sourceChannel);
@@ -84,7 +85,8 @@ public void endpointRegistrationWithInputChannelReference() throws ClassNotFound
8485
.setReplyChannelName("targetChannel").build();
8586
assertThatExceptionOfType(MessageDispatchingException.class)
8687
.isThrownBy(() -> sourceChannel.send(message))
87-
.withMessageStartingWith("The application context is not ready to dispatch messages.");
88+
.withMessageStartingWith(
89+
"The application context is not ready (or stopped) to dispatch messages to 'sourceChannel'");
8890
AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
8991

9092
@Override
@@ -106,7 +108,8 @@ public Object handleRequestMessage(Message<?> message) {
106108

107109
assertThatExceptionOfType(MessageDispatchingException.class)
108110
.isThrownBy(() -> sourceChannel.send(message))
109-
.withMessageStartingWith("The application context is not ready to dispatch messages.");
111+
.withMessageStartingWith(
112+
"The application context is not ready (or stopped) to dispatch messages to 'sourceChannel'");
110113
}
111114

112115
@Test

spring-integration-event/src/main/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducer.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -114,23 +114,24 @@ public void onApplicationEvent(ApplicationEvent event) {
114114

115115
if (contextFinished && getRequiredOutputChannel() instanceof AbstractMessageChannel) {
116116
logger.warn("Messages for 'ContextStoppedEvent' or 'ContextClosedEvent' cannot be dispatched " +
117-
"via 'AbstractMessageChannel' beans: the application context is in the finished state." +
117+
"via 'AbstractMessageChannel' beans: the application context is in the finished state. " +
118118
"Consider to use custom 'MessageChannel' implementation without dispatching logic.");
119119
}
120-
121-
if (event.getSource() instanceof Message<?> message) {
122-
sendMessage(message);
123-
}
124120
else {
125-
Message<?> message;
126-
Object result = extractObjectToSend(event);
127-
if (result instanceof Message) {
128-
message = (Message<?>) result;
121+
if (event.getSource() instanceof Message<?> message) {
122+
sendMessage(message);
129123
}
130124
else {
131-
message = getMessageBuilderFactory().withPayload(result).build();
125+
Message<?> message;
126+
Object result = extractObjectToSend(event);
127+
if (result instanceof Message) {
128+
message = (Message<?>) result;
129+
}
130+
else {
131+
message = getMessageBuilderFactory().withPayload(result).build();
132+
}
133+
sendMessage(message);
132134
}
133-
sendMessage(message);
134135
}
135136
}
136137
}

0 commit comments

Comments
 (0)