GH-9198: Fix MessageChannel observation for ErrorMessage

Fixes: #9198

When observation is enabled on the `MessageChannel`, the message to send is converted to a `MutableMessage`.
In case of `ErrorMessage` this causes a loss of `originalMessage` and may lead to `ClassCastException`
in the target error handler.

* Check for `ErrorMessage` in the `AbstractMessageChannel.sendWithObservation()` and create a new one
as a copy of original request, but including observation headers before performing `sendInternal()`
* Modify `IntegrationObservabilityZipkinTests` to verify an observation with an `ErrorMessage` and its handler.

**Auto-cherry-pick to `6.3.x` & `6.2.x`**
This commit is contained in:
Artem Bilan
2024-06-03 14:49:33 -04:00
parent 1358718268
commit b0cbacfafb
2 changed files with 45 additions and 8 deletions

View File

@@ -30,6 +30,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.beans.factory.BeanFactory;
@@ -59,6 +60,7 @@ import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageDeliveryException;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.ErrorMessage;
import org.springframework.messaging.support.InterceptableChannel;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -335,12 +337,22 @@ public abstract class AbstractMessageChannel extends IntegrationObjectSupport
private boolean sendWithObservation(Message<?> message, long timeout) {
MutableMessage<?> messageToSend = MutableMessage.of(message);
return IntegrationObservation.PRODUCER.observation(
this.observationConvention,
DefaultMessageSenderObservationConvention.INSTANCE,
() -> new MessageSenderContext(messageToSend, getComponentName()),
this.observationRegistry)
.observe(() -> sendInternal(messageToSend, timeout)); // NOSONAR - never null
Observation observation = IntegrationObservation.PRODUCER.observation(
this.observationConvention,
DefaultMessageSenderObservationConvention.INSTANCE,
() -> new MessageSenderContext(messageToSend, getComponentName()),
this.observationRegistry);
Boolean observe = observation.observe(() -> {
Message<?> messageToSendInternal = messageToSend;
if (message instanceof ErrorMessage errorMessage) {
messageToSendInternal =
new ErrorMessage(errorMessage.getPayload(),
messageToSend.getHeaders(),
errorMessage.getOriginalMessage());
}
return sendInternal(messageToSendInternal, timeout);
});
return Boolean.TRUE.equals(observe);
}
private boolean sendWithMetrics(Message<?> message, long timeout) {