GH-2570 Ensure consistency in error handling
Rework error handling documentation Remove dependence and usage of global errorChannel Resolves #2570
This commit is contained in:
@@ -34,12 +34,14 @@ import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
|
||||
import org.springframework.cloud.function.context.FunctionCatalog;
|
||||
import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper;
|
||||
import org.springframework.cloud.stream.config.BindingProperties;
|
||||
import org.springframework.cloud.stream.config.BindingServiceProperties;
|
||||
import org.springframework.cloud.stream.config.ConsumerEndpointCustomizer;
|
||||
import org.springframework.cloud.stream.config.ListenerContainerCustomizer;
|
||||
import org.springframework.cloud.stream.config.MessageSourceCustomizer;
|
||||
import org.springframework.cloud.stream.config.ProducerMessageHandlerCustomizer;
|
||||
import org.springframework.cloud.stream.messaging.DirectWithAttributesChannel;
|
||||
import org.springframework.cloud.stream.provisioning.ConsumerDestination;
|
||||
import org.springframework.cloud.stream.provisioning.ProducerDestination;
|
||||
import org.springframework.cloud.stream.provisioning.ProvisioningException;
|
||||
@@ -741,75 +743,63 @@ public abstract class AbstractMessageChannelBinder<C extends ConsumerProperties,
|
||||
ErrorMessageStrategy errorMessageStrategy = getErrorMessageStrategy();
|
||||
|
||||
String errorChannelName = errorsBaseName(destination, group, consumerProperties);
|
||||
SubscribableChannel errorChannel;
|
||||
if (getApplicationContext().containsBean(errorChannelName)) {
|
||||
Object errorChannelObject = getApplicationContext().getBean(errorChannelName);
|
||||
|
||||
Assert.isInstanceOf(SubscribableChannel.class, errorChannelObject,
|
||||
"Error channel '" + errorChannelName
|
||||
+ "' must be a SubscribableChannel");
|
||||
errorChannel = (SubscribableChannel) errorChannelObject;
|
||||
if (this.isSubscribable(errorChannel)) {
|
||||
this.subscribeFunctionErrorHandler(errorChannelName, consumerProperties.getBindingName());
|
||||
BindingServiceProperties bsp = this.getBindingServiceProperties();
|
||||
FunctionInvocationWrapper userErrorHandler = null;
|
||||
String errorHandlerDefinition = null;
|
||||
if (bsp != null && StringUtils.hasText(consumerProperties.getBindingName())) {
|
||||
BindingProperties bp = bsp.getBindingProperties(consumerProperties.getBindingName());
|
||||
errorHandlerDefinition = bp.getErrorHandlerDefinition();
|
||||
FunctionCatalog catalog = getApplicationContext().getBean(FunctionCatalog.class);
|
||||
if (StringUtils.hasText(errorHandlerDefinition)) {
|
||||
userErrorHandler = catalog.lookup(errorHandlerDefinition);
|
||||
if (!(userErrorHandler != null && userErrorHandler.getFunctionDefinition().equals(errorHandlerDefinition))) {
|
||||
userErrorHandler = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
BinderErrorChannel binderErrorChannel = new BinderErrorChannel();
|
||||
binderErrorChannel.setComponentName(errorChannelName);
|
||||
errorChannel = binderErrorChannel;
|
||||
|
||||
((GenericApplicationContext) getApplicationContext()).registerBean(
|
||||
errorChannelName, SubscribableChannel.class, () -> errorChannel);
|
||||
this.subscribeFunctionErrorHandler(errorChannelName, consumerProperties.getBindingName());
|
||||
}
|
||||
|
||||
ErrorMessageSendingRecoverer recoverer;
|
||||
if (errorMessageStrategy == null) {
|
||||
recoverer = new ErrorMessageSendingRecoverer(errorChannel);
|
||||
AbstractSubscribableChannel binderErrorChannel;
|
||||
if (userErrorHandler != null) {
|
||||
binderErrorChannel = new DirectWithAttributesChannel();
|
||||
}
|
||||
else {
|
||||
recoverer = new ErrorMessageSendingRecoverer(errorChannel,
|
||||
errorMessageStrategy);
|
||||
if (StringUtils.hasText(errorHandlerDefinition)) {
|
||||
logger.warn("Failed to retrieve error handling function with definition: " + errorHandlerDefinition
|
||||
+ ", for binding: " + consumerProperties.getBindingName());
|
||||
}
|
||||
binderErrorChannel = new BinderErrorChannel();
|
||||
}
|
||||
binderErrorChannel.setComponentName(errorChannelName);
|
||||
((GenericApplicationContext) getApplicationContext()).registerBean(
|
||||
errorChannelName, SubscribableChannel.class, () -> binderErrorChannel);
|
||||
this.subscribeFunctionErrorHandler(errorChannelName, consumerProperties.getBindingName());
|
||||
|
||||
String recovererBeanName = getErrorRecovererName(destination, group,
|
||||
consumerProperties);
|
||||
//
|
||||
ErrorMessageSendingRecoverer recoverer = new ErrorMessageSendingRecoverer(binderErrorChannel, errorMessageStrategy);
|
||||
String recovererBeanName = getErrorRecovererName(destination, group, consumerProperties);
|
||||
if (!getApplicationContext().containsBean(recovererBeanName)) {
|
||||
((GenericApplicationContext) getApplicationContext()).registerBean(
|
||||
recovererBeanName, ErrorMessageSendingRecoverer.class, () -> recoverer);
|
||||
}
|
||||
|
||||
MessageHandler handler;
|
||||
if (polled) {
|
||||
handler = getPolledConsumerErrorMessageHandler(destination, group,
|
||||
consumerProperties);
|
||||
}
|
||||
else {
|
||||
handler = getErrorMessageHandler(destination, group, consumerProperties);
|
||||
}
|
||||
MessageChannel defaultErrorChannel = null;
|
||||
if (getApplicationContext()
|
||||
.containsBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME)) {
|
||||
defaultErrorChannel = getApplicationContext().getBean(
|
||||
IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME,
|
||||
MessageChannel.class);
|
||||
}
|
||||
if (handler == null && errorChannel instanceof LastSubscriberAwareChannel) {
|
||||
handler = getDefaultErrorMessageHandler(
|
||||
(LastSubscriberAwareChannel) errorChannel,
|
||||
defaultErrorChannel != null);
|
||||
}
|
||||
MessageHandler binderProvidedErrorHandler = polled
|
||||
? getPolledConsumerErrorMessageHandler(destination, group, consumerProperties)
|
||||
: getErrorMessageHandler(destination, group, consumerProperties);
|
||||
|
||||
String errorMessageHandlerName = getErrorMessageHandlerName(destination, group,
|
||||
consumerProperties);
|
||||
|
||||
if (handler != null) {
|
||||
if (this.isSubscribable(errorChannel)) {
|
||||
if (binderProvidedErrorHandler != null) {
|
||||
if (this.isSubscribable(binderErrorChannel)) {
|
||||
if (!getApplicationContext().containsBean(errorMessageHandlerName)) {
|
||||
MessageHandler errorHandler = handler;
|
||||
MessageHandler h = binderProvidedErrorHandler;
|
||||
((GenericApplicationContext) getApplicationContext()).registerBean(
|
||||
errorMessageHandlerName, MessageHandler.class,
|
||||
() -> errorHandler);
|
||||
errorChannel.subscribe(handler);
|
||||
() -> h);
|
||||
binderErrorChannel.subscribe(binderProvidedErrorHandler);
|
||||
}
|
||||
else {
|
||||
binderErrorChannel.subscribe((MessageHandler) getApplicationContext().getBean(errorMessageHandlerName));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -820,29 +810,7 @@ public abstract class AbstractMessageChannelBinder<C extends ConsumerProperties,
|
||||
+ "an instance of PublishSubscribeChannel");
|
||||
}
|
||||
}
|
||||
|
||||
if (defaultErrorChannel != null) {
|
||||
if (this.isSubscribable(errorChannel)) {
|
||||
BridgeHandler errorBridge = new BridgeHandler();
|
||||
errorBridge.setOutputChannel(defaultErrorChannel);
|
||||
errorChannel.subscribe(errorBridge);
|
||||
|
||||
String errorBridgeHandlerName = getErrorBridgeName(destination, group,
|
||||
consumerProperties);
|
||||
if (getApplicationContext().containsBean(errorBridgeHandlerName)) {
|
||||
((GenericApplicationContext) getApplicationContext()).registerBean(
|
||||
errorBridgeHandlerName, BridgeHandler.class, () -> errorBridge);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.logger.warn("The provided errorChannel '" + errorChannelName
|
||||
+ "' is an instance of DirectChannel, "
|
||||
+ "so no more subscribers could be added and no error messages will be sent to global error channel. "
|
||||
+ "Resolution: Configure your own errorChannel as "
|
||||
+ "an instance of PublishSubscribeChannel");
|
||||
}
|
||||
}
|
||||
return new ErrorInfrastructure(errorChannel, recoverer, handler);
|
||||
return new ErrorInfrastructure(binderErrorChannel, recoverer, binderProvidedErrorHandler);
|
||||
}
|
||||
|
||||
private boolean isSubscribable(SubscribableChannel errorChannel) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.cloud.stream.binder.tck;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
@@ -38,6 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Oleg Zhurakousky
|
||||
*
|
||||
*/
|
||||
@Disabled
|
||||
public class ErrorHandlingTests {
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user