GH-3452: Warn Publisher.onError in NullChannel (#3453)

* GH-3452: Warn Publisher.onError in NullChannel

Fixes https://github.com/spring-projects/spring-integration/issues/3452

The `NullChannel` can subscribe to the `Publisher` payload fully ignoring
the possible data according the `NullChannel` nature.
However an error thrown from the reactive stream processing is also ignored.

* Log WARN message from the `Subscriber.onError()` when `NullChannel`
subscribes to the `Publisher` produced to this channel.
* Mention the logic in the `NullChannel` docs; point to the
`ReactiveRequestHandlerAdvice` for further possible error handling in
the target application

**Cherry-pick to `5.4.x`**

* * Remove unused imports

* * Fix `PseudoTransactionalMessageSourceTests` for `LogAccessor`
from `NullChannel`
This commit is contained in:
Artem Bilan
2021-01-05 15:08:30 -05:00
committed by Gary Russell
parent 5ed8c11c58
commit ebe79aedfb
3 changed files with 21 additions and 15 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,13 +18,12 @@ package org.springframework.integration.channel;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.core.log.LogAccessor;
import org.springframework.integration.IntegrationPattern;
import org.springframework.integration.IntegrationPatternType;
import org.springframework.integration.support.management.IntegrationManagedResource;
@@ -43,6 +42,8 @@ import org.springframework.messaging.PollableChannel;
* Unless the payload of a sent message is a {@link Publisher} implementation, in
* which case the {@link Publisher#subscribe(Subscriber)} is called to initiate
* the reactive stream, although the data is discarded by this channel.
* An error thrown from a reactive stream processing (see {@link Subscriber#onError(Throwable)})
* is logged under the {@code warn} level.
* Note however that the invocations are logged at debug-level.
*
* @author Mark Fisher
@@ -53,7 +54,7 @@ import org.springframework.messaging.PollableChannel;
public class NullChannel implements PollableChannel,
BeanNameAware, IntegrationManagement, IntegrationPattern {
private final Log logger = LogFactory.getLog(getClass());
private static final LogAccessor LOG = new LogAccessor(NullChannel.class);
private final ManagementOverrides managementOverrides = new ManagementOverrides();
@@ -122,8 +123,8 @@ public class NullChannel implements PollableChannel,
@Override
public boolean send(Message<?> message) {
if (this.loggingEnabled && this.logger.isDebugEnabled()) {
this.logger.debug("message sent to null channel: " + message);
if (this.loggingEnabled) {
LOG.debug(() -> "message sent to null channel: " + message);
}
Object payload = message.getPayload();
@@ -135,12 +136,12 @@ public class NullChannel implements PollableChannel,
subscription.request(Long.MAX_VALUE);
}
@Override public void onNext(Object o) {
@Override public void onNext(Object value) {
}
@Override public void onError(Throwable t) {
@Override public void onError(Throwable ex) {
LOG.warn(ex, "An error happened in a reactive stream processing");
}
@Override public void onComplete() {
@@ -173,7 +174,7 @@ public class NullChannel implements PollableChannel,
@Override
public Message<?> receive() {
if (this.loggingEnabled) {
this.logger.debug("receive called on null channel");
LOG.debug("receive called on null channel");
}
incrementReceiveCounter();
return null;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.log.LogAccessor;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.channel.NullChannel;
import org.springframework.integration.channel.QueueChannel;
@@ -97,14 +98,16 @@ public class PseudoTransactionalMessageSourceTests {
MessageChannel afterCommitChannel = new NullChannel();
syncProcessor.setAfterCommitChannel(afterCommitChannel);
Log logger = TestUtils.getPropertyValue(afterCommitChannel, "logger", Log.class);
LogAccessor logAccessor = TestUtils.getPropertyValue(afterCommitChannel, "LOG", LogAccessor.class);
Log logger = logAccessor.getLog();
logger = Mockito.spy(logger);
Mockito.when(logger.isDebugEnabled()).thenReturn(true);
DirectFieldAccessor dfa = new DirectFieldAccessor(afterCommitChannel);
dfa.setPropertyValue("logger", logger);
DirectFieldAccessor dfa = new DirectFieldAccessor(logAccessor);
dfa.setPropertyValue("log", logger);
TransactionSynchronizationManager.initSynchronization();
TransactionSynchronizationManager.setActualTransactionActive(true);
@@ -115,7 +118,7 @@ public class PseudoTransactionalMessageSourceTests {
assertThat(beforeCommitMessage).isNotNull();
assertThat(beforeCommitMessage.getPayload()).isEqualTo("qox");
Mockito.verify(logger).debug(Mockito.anyString());
Mockito.verify(logger).debug(Mockito.any(CharSequence.class));
TransactionSynchronizationUtils.triggerAfterCompletion(TransactionSynchronization.STATUS_COMMITTED);
TransactionSynchronizationManager.clearSynchronization();