org.springframework.cloud.stream.binder.AbstractMessageChannelBinder.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.cloud.stream.binder.AbstractMessageChannelBinder.java

Source

/*
 * Copyright 2016-2018 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.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.stream.binder;

import org.apache.commons.logging.Log;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
import org.springframework.cloud.stream.provisioning.ConsumerDestination;
import org.springframework.cloud.stream.provisioning.ProducerDestination;
import org.springframework.cloud.stream.provisioning.ProvisioningException;
import org.springframework.cloud.stream.provisioning.ProvisioningProvider;
import org.springframework.context.Lifecycle;
import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.integration.channel.PublishSubscribeChannel;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.core.MessageProducer;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.handler.AbstractMessageHandler;
import org.springframework.integration.handler.BridgeHandler;
import org.springframework.integration.handler.advice.ErrorMessageSendingRecoverer;
import org.springframework.integration.support.ErrorMessageStrategy;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.messaging.support.ChannelInterceptorAdapter;
import org.springframework.retry.RecoveryCallback;
import org.springframework.util.Assert;

/**
 * {@link AbstractBinder} that serves as base class for {@link MessageChannel} binders.
 * Implementors must implement the following methods:
 * <ul>
 * <li>{@link #createProducerMessageHandler(ProducerDestination, ProducerProperties, MessageChannel)}</li>
 * <li>{@link #createConsumerEndpoint(ConsumerDestination, String, ConsumerProperties)}
 * </li>
 * </ul>
 *
 * @param <C> the consumer properties type
 * @param <P> the producer properties type
 *
 * @author Marius Bogoevici
 * @author Ilayaperumal Gopinathan
 * @author Soby Chacko
 * @author Oleg Zhurakousky
 * @author Artem Bilan
 * @author Gary Russell
 *
 * @since 1.1
 */
public abstract class AbstractMessageChannelBinder<C extends ConsumerProperties, P extends ProducerProperties, PP extends ProvisioningProvider<C, P>>
        extends AbstractBinder<MessageChannel, C, P> implements PollableConsumerBinder<MessageHandler, C> {

    private final EmbeddedHeadersChannelInterceptor embeddedHeadersChannelInterceptor = new EmbeddedHeadersChannelInterceptor(
            this.logger);

    /**
     * {@link ProvisioningProvider} delegated by the downstream binder implementations.
     */
    protected final PP provisioningProvider;

    /**
     * Indicates which headers are to be embedded in the payload if
     * a binding requires embedding headers.
     */
    private final String[] headersToEmbed;

    public AbstractMessageChannelBinder(String[] headersToEmbed, PP provisioningProvider) {
        this.headersToEmbed = headersToEmbed == null ? new String[0] : headersToEmbed;
        this.provisioningProvider = provisioningProvider;
    }

    /**
     * @deprecated As of release 2.0. Please use other constructors.
     */
    @Deprecated
    protected AbstractMessageChannelBinder(boolean supportsHeadersNatively, String[] headersToEmbed,
            PP provisioningProvider) {
        this(headersToEmbed, provisioningProvider);
    }

    /**
     * Binds an outbound channel to a given destination. The implementation delegates to
     * {@link ProvisioningProvider#provisionProducerDestination(String, ProducerProperties)}
     * and {@link #createProducerMessageHandler(ProducerDestination, ProducerProperties, MessageChannel)}
     * for handling the middleware specific logic. If the returned producer message
     * handler is an {@link InitializingBean} then
     * {@link InitializingBean#afterPropertiesSet()} will be called on it. Similarly, if
     * the returned producer message handler endpoint is a {@link Lifecycle}, then
     * {@link Lifecycle#start()} will be called on it.
     *
     * @param destination the name of the destination
     * @param outputChannel the channel to be bound
     * @param producerProperties the {@link ProducerProperties} of the binding
     * @return the Binding for the channel
     * @throws BinderException on internal errors during binding
     */
    @Override
    public final Binding<MessageChannel> doBindProducer(final String destination, MessageChannel outputChannel,
            final P producerProperties) throws BinderException {
        Assert.isInstanceOf(SubscribableChannel.class, outputChannel,
                "Binding is supported only for SubscribableChannel instances");
        final MessageHandler producerMessageHandler;
        final ProducerDestination producerDestination;
        try {
            producerDestination = this.provisioningProvider.provisionProducerDestination(destination,
                    producerProperties);
            SubscribableChannel errorChannel = producerProperties.isErrorChannelEnabled()
                    ? registerErrorInfrastructure(producerDestination)
                    : null;
            producerMessageHandler = createProducerMessageHandler(producerDestination, producerProperties,
                    errorChannel);
            if (producerMessageHandler instanceof InitializingBean) {
                ((InitializingBean) producerMessageHandler).afterPropertiesSet();
            }
        } catch (Exception e) {
            if (e instanceof BinderException) {
                throw (BinderException) e;
            } else if (e instanceof ProvisioningException) {
                throw (ProvisioningException) e;
            } else {
                throw new BinderException("Exception thrown while building outbound endpoint", e);
            }
        }
        if (producerMessageHandler instanceof Lifecycle) {
            ((Lifecycle) producerMessageHandler).start();
        }
        postProcessOutputChannel(outputChannel, producerProperties);
        ((SubscribableChannel) outputChannel).subscribe(new SendingHandler(producerMessageHandler,
                HeaderMode.embeddedHeaders.equals(producerProperties.getHeaderMode()), this.headersToEmbed,
                producerProperties.isUseNativeEncoding()));

        return new DefaultBinding<MessageChannel>(destination, null, outputChannel,
                producerMessageHandler instanceof Lifecycle ? (Lifecycle) producerMessageHandler : null) {

            @Override
            public void afterUnbind() {
                try {
                    destroyErrorInfrastructure(producerDestination);
                    if (producerMessageHandler instanceof DisposableBean) {
                        ((DisposableBean) producerMessageHandler).destroy();
                    }
                } catch (Exception e) {
                    AbstractMessageChannelBinder.this.logger.error("Exception thrown while unbinding " + toString(),
                            e);
                }
                afterUnbindProducer(producerDestination, producerProperties);
            }
        };
    }

    /**
     * Allows subclasses to perform post processing on the channel - for example to
     * add more interceptors.
     * @param outputChannel the channel.
     * @param producerProperties the producer properties.
     */
    protected void postProcessOutputChannel(MessageChannel outputChannel, P producerProperties) {
        // default no-op
    }

    /**
     * Creates a {@link MessageHandler} with the ability to send data to the target
     * middleware. If the returned instance is also a {@link Lifecycle}, it will be
     * stopped automatically by the binder.
     * <p>
     * In order to be fully compliant, the {@link MessageHandler} of the binder must
     * observe the following headers:
     * <ul>
     * <li>{@link BinderHeaders#PARTITION_HEADER} - indicates the target partition where
     * the message must be sent</li>
     * </ul>
     * <p>
     *
     * @param destination the name of the target destination
     * @param producerProperties the producer properties
     * @param errorChannel the error channel (if enabled, otherwise null). If not null,
     * the binder must wire this channel into the producer endpoint so that errors
     * are forwarded to it.
     * @return the message handler for sending data to the target middleware
     * @throws Exception
     */
    protected abstract MessageHandler createProducerMessageHandler(ProducerDestination destination,
            P producerProperties, MessageChannel errorChannel) throws Exception;

    /**
     * Invoked after the unbinding of a producer. Subclasses may override this to provide
     * their own logic for dealing with unbinding.
     *
     * @param destination the bound destination
     * @param producerProperties the producer properties
     */
    protected void afterUnbindProducer(ProducerDestination destination, P producerProperties) {
    }

    /**
     * Binds an inbound channel to a given destination. The implementation delegates to
     * {@link ProvisioningProvider#provisionConsumerDestination(String, String, ConsumerProperties)}
     * and
     * {@link #createConsumerEndpoint(ConsumerDestination, String, ConsumerProperties)}
     * for handling middleware-specific logic. If the returned consumer endpoint is an
     * {@link InitializingBean} then {@link InitializingBean#afterPropertiesSet()} will be
     * called on it. Similarly, if the returned consumer endpoint is a {@link Lifecycle},
     * then {@link Lifecycle#start()} will be called on it.
     *
     * @param name the name of the destination
     * @param group the consumer group
     * @param inputChannel the channel to be bound
     * @param properties the {@link ConsumerProperties} of the binding
     * @return the Binding for the channel
     * @throws BinderException on internal errors during binding
     */
    @Override
    public final Binding<MessageChannel> doBindConsumer(String name, String group, MessageChannel inputChannel,
            final C properties) throws BinderException {
        MessageProducer consumerEndpoint = null;
        try {
            ConsumerDestination destination = this.provisioningProvider.provisionConsumerDestination(name, group,
                    properties);

            if (HeaderMode.embeddedHeaders.equals(properties.getHeaderMode())) {
                enhanceMessageChannel(inputChannel);
            }
            consumerEndpoint = createConsumerEndpoint(destination, group, properties);
            consumerEndpoint.setOutputChannel(inputChannel);
            if (consumerEndpoint instanceof InitializingBean) {
                ((InitializingBean) consumerEndpoint).afterPropertiesSet();
            }
            if (consumerEndpoint instanceof Lifecycle) {
                ((Lifecycle) consumerEndpoint).start();
            }

            return new DefaultBinding<MessageChannel>(name, group, inputChannel,
                    consumerEndpoint instanceof Lifecycle ? (Lifecycle) consumerEndpoint : null) {

                @Override
                protected void afterUnbind() {
                    try {
                        if (getEndpoint() instanceof DisposableBean) {
                            ((DisposableBean) getEndpoint()).destroy();
                        }
                    } catch (Exception e) {
                        AbstractMessageChannelBinder.this.logger
                                .error("Exception thrown while unbinding " + toString(), e);
                    }
                    afterUnbindConsumer(destination, this.group, properties);
                    destroyErrorInfrastructure(destination, group, properties);
                }

            };
        } catch (Exception e) {
            if (consumerEndpoint instanceof Lifecycle) {
                ((Lifecycle) consumerEndpoint).stop();
            }
            if (e instanceof BinderException) {
                throw (BinderException) e;
            } else if (e instanceof ProvisioningException) {
                throw (ProvisioningException) e;
            } else {
                throw new BinderException("Exception thrown while starting consumer: ", e);
            }
        }
    }

    @Override
    public Binding<PollableSource<MessageHandler>> bindPollableConsumer(String name, String group,
            final PollableSource<MessageHandler> inboundBindTarget, C properties) {
        Assert.isInstanceOf(DefaultPollableMessageSource.class, inboundBindTarget);
        DefaultPollableMessageSource bindingTarget = (DefaultPollableMessageSource) inboundBindTarget;
        ConsumerDestination destination = this.provisioningProvider.provisionConsumerDestination(name, group,
                properties);
        if (HeaderMode.embeddedHeaders.equals(properties.getHeaderMode())) {
            bindingTarget.addInterceptor(0, this.embeddedHeadersChannelInterceptor);
        }
        final PolledConsumerResources resources = createPolledConsumerResources(name, group, destination,
                properties);
        bindingTarget.setSource(resources.getSource());
        if (resources.getErrorInfrastructure() != null) {
            if (resources.getErrorInfrastructure().getErrorChannel() != null) {
                bindingTarget.setErrorChannel(resources.getErrorInfrastructure().getErrorChannel());
            }
            ErrorMessageStrategy ems = getErrorMessageStrategy();
            if (ems != null) {
                bindingTarget.setErrorMessageStrategy(ems);
            }
        }
        if (properties.getMaxAttempts() > 1) {
            bindingTarget.setRetryTemplate(buildRetryTemplate(properties));
            bindingTarget.setRecoveryCallback(
                    getPolledConsumerRecoveryCallback(resources.getErrorInfrastructure(), properties));
        }
        postProcessPollableSource(bindingTarget);
        if (resources.getSource() instanceof Lifecycle) {
            ((Lifecycle) resources.getSource()).start();
        }
        return new DefaultBinding<PollableSource<MessageHandler>>(name, group, inboundBindTarget,
                resources.getSource() instanceof Lifecycle ? (Lifecycle) resources.getSource() : null) {

            @Override
            public void afterUnbind() {
                afterUnbindConsumer(destination, this.group, properties);
                destroyErrorInfrastructure(destination, group, properties);
            }

        };
    }

    protected void postProcessPollableSource(DefaultPollableMessageSource bindingTarget) {
    }

    /**
     * Implementations can override the default {@link ErrorMessageSendingRecoverer}.
     * @param errorInfrastructure the infrastructure.
     * @param properties the consumer properties.
     * @return the recoverer.
     */
    protected RecoveryCallback<Object> getPolledConsumerRecoveryCallback(ErrorInfrastructure errorInfrastructure,
            C properties) {
        return errorInfrastructure.getRecoverer();
    }

    protected PolledConsumerResources createPolledConsumerResources(String name, String group,
            ConsumerDestination destination, C consumerProperties) {
        throw new UnsupportedOperationException("This binder does not support pollable consumers");
    }

    private void enhanceMessageChannel(MessageChannel inputChannel) {
        ((AbstractMessageChannel) inputChannel).addInterceptor(0, this.embeddedHeadersChannelInterceptor);
    }

    /**
     * Creates {@link MessageProducer} that receives data from the consumer destination.
     * will be started and stopped by the binder.
     *
     * @param group the consumer group
     * @param destination reference to the consumer destination
     * @param properties the consumer properties
     * @return the consumer endpoint.
     */
    protected abstract MessageProducer createConsumerEndpoint(ConsumerDestination destination, String group,
            C properties) throws Exception;

    /**
     * Invoked after the unbinding of a consumer. The binder implementation can override
     * this method to provide their own logic (e.g. for cleaning up destinations).
     *
     * @param destination the consumer destination
     * @param group the consumer group
     * @param consumerProperties the consumer properties
     */
    protected void afterUnbindConsumer(ConsumerDestination destination, String group, C consumerProperties) {
    }

    /**
     * Register an error channel for the destination when an async send error is received.
     * Bridge the channel to the global error channel (if present).
     * @param destination the destination.
     * @return the channel.
     */
    private SubscribableChannel registerErrorInfrastructure(ProducerDestination destination) {
        ConfigurableListableBeanFactory beanFactory = getApplicationContext().getBeanFactory();
        String errorChannelName = errorsBaseName(destination);
        SubscribableChannel errorChannel = null;
        if (getApplicationContext().containsBean(errorChannelName)) {
            Object errorChannelObject = getApplicationContext().getBean(errorChannelName);
            if (!(errorChannelObject instanceof SubscribableChannel)) {
                throw new IllegalStateException(
                        "Error channel '" + errorChannelName + "' must be a SubscribableChannel");
            }
            errorChannel = (SubscribableChannel) errorChannelObject;
        } else {
            errorChannel = new PublishSubscribeChannel();
            beanFactory.registerSingleton(errorChannelName, errorChannel);
            errorChannel = (PublishSubscribeChannel) beanFactory.initializeBean(errorChannel, errorChannelName);
        }
        MessageChannel defaultErrorChannel = null;
        if (getApplicationContext().containsBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME)) {
            defaultErrorChannel = getApplicationContext().getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME,
                    MessageChannel.class);
        }
        if (defaultErrorChannel != null) {
            BridgeHandler errorBridge = new BridgeHandler();
            errorBridge.setOutputChannel(defaultErrorChannel);
            errorChannel.subscribe(errorBridge);
            String errorBridgeHandlerName = getErrorBridgeName(destination);
            beanFactory.registerSingleton(errorBridgeHandlerName, errorBridge);
            beanFactory.initializeBean(errorBridge, errorBridgeHandlerName);
        }
        return errorChannel;
    }

    /**
     * Build an errorChannelRecoverer that writes to a pub/sub channel for the destination
     * when an exception is thrown to a consumer.
     * @param destination the destination.
     * @param group the group.
     * @param consumerProperties the properties.
     * @return the ErrorInfrastructure which is a holder for the error channel, the recoverer and the
     * message handler that is subscribed to the channel.
     */
    protected final ErrorInfrastructure registerErrorInfrastructure(ConsumerDestination destination, String group,
            C consumerProperties) {

        return registerErrorInfrastructure(destination, group, consumerProperties, false);
    }

    /**
     * Build an errorChannelRecoverer that writes to a pub/sub channel for the destination
     * when an exception is thrown to a consumer.
     * @param destination the destination.
     * @param group the group.
     * @param consumerProperties the properties.
     * @param true if this is for a polled consumer.
     * @return the ErrorInfrastructure which is a holder for the error channel, the recoverer and the
     * message handler that is subscribed to the channel.
     */
    protected final ErrorInfrastructure registerErrorInfrastructure(ConsumerDestination destination, String group,
            C consumerProperties, boolean polled) {

        ErrorMessageStrategy errorMessageStrategy = getErrorMessageStrategy();
        ConfigurableListableBeanFactory beanFactory = getApplicationContext().getBeanFactory();
        String errorChannelName = errorsBaseName(destination, group, consumerProperties);
        SubscribableChannel errorChannel = null;
        if (getApplicationContext().containsBean(errorChannelName)) {
            Object errorChannelObject = getApplicationContext().getBean(errorChannelName);
            if (!(errorChannelObject instanceof SubscribableChannel)) {
                throw new IllegalStateException(
                        "Error channel '" + errorChannelName + "' must be a SubscribableChannel");
            }
            errorChannel = (SubscribableChannel) errorChannelObject;
        } else {
            errorChannel = new BinderErrorChannel();
            beanFactory.registerSingleton(errorChannelName, errorChannel);
            errorChannel = (LastSubscriberAwareChannel) beanFactory.initializeBean(errorChannel, errorChannelName);
        }
        ErrorMessageSendingRecoverer recoverer;
        if (errorMessageStrategy == null) {
            recoverer = new ErrorMessageSendingRecoverer(errorChannel);
        } else {
            recoverer = new ErrorMessageSendingRecoverer(errorChannel, errorMessageStrategy);
        }
        String recovererBeanName = getErrorRecovererName(destination, group, consumerProperties);
        beanFactory.registerSingleton(recovererBeanName, recoverer);
        beanFactory.initializeBean(recoverer, recovererBeanName);
        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);
        }
        String errorMessageHandlerName = getErrorMessageHandlerName(destination, group, consumerProperties);
        if (handler != null) {
            beanFactory.registerSingleton(errorMessageHandlerName, handler);
            beanFactory.initializeBean(handler, errorMessageHandlerName);
            errorChannel.subscribe(handler);
        }
        if (defaultErrorChannel != null) {
            BridgeHandler errorBridge = new BridgeHandler();
            errorBridge.setOutputChannel(defaultErrorChannel);
            errorChannel.subscribe(errorBridge);
            String errorBridgeHandlerName = getErrorBridgeName(destination, group, consumerProperties);
            beanFactory.registerSingleton(errorBridgeHandlerName, errorBridge);
            beanFactory.initializeBean(errorBridge, errorBridgeHandlerName);
        }
        return new ErrorInfrastructure(errorChannel, recoverer, handler);
    }

    private void destroyErrorInfrastructure(ProducerDestination destination) {
        String errorChannelName = errorsBaseName(destination);
        String errorBridgeHandlerName = getErrorBridgeName(destination);
        MessageHandler bridgeHandler = null;
        if (getApplicationContext().containsBean(errorBridgeHandlerName)) {
            bridgeHandler = getApplicationContext().getBean(errorBridgeHandlerName, MessageHandler.class);
        }
        if (getApplicationContext().containsBean(errorChannelName)) {
            SubscribableChannel channel = getApplicationContext().getBean(errorChannelName,
                    SubscribableChannel.class);
            if (bridgeHandler != null) {
                channel.unsubscribe(bridgeHandler);
                ((DefaultSingletonBeanRegistry) getApplicationContext().getBeanFactory())
                        .destroySingleton(errorBridgeHandlerName);
            }
            ((DefaultSingletonBeanRegistry) getApplicationContext().getBeanFactory())
                    .destroySingleton(errorChannelName);
        }
    }

    private void destroyErrorInfrastructure(ConsumerDestination destination, String group, C properties) {
        try {
            String recoverer = getErrorRecovererName(destination, group, properties);
            if (getApplicationContext().containsBean(recoverer)) {
                ((DefaultSingletonBeanRegistry) getApplicationContext().getBeanFactory())
                        .destroySingleton(recoverer);
            }
            String errorChannelName = errorsBaseName(destination, group, properties);
            String errorMessageHandlerName = getErrorMessageHandlerName(destination, group, properties);
            String errorBridgeHandlerName = getErrorBridgeName(destination, group, properties);
            MessageHandler bridgeHandler = null;
            if (getApplicationContext().containsBean(errorBridgeHandlerName)) {
                bridgeHandler = getApplicationContext().getBean(errorBridgeHandlerName, MessageHandler.class);
            }
            MessageHandler handler = null;
            if (getApplicationContext().containsBean(errorMessageHandlerName)) {
                handler = getApplicationContext().getBean(errorMessageHandlerName, MessageHandler.class);
            }
            if (getApplicationContext().containsBean(errorChannelName)) {
                SubscribableChannel channel = getApplicationContext().getBean(errorChannelName,
                        SubscribableChannel.class);
                if (bridgeHandler != null) {
                    channel.unsubscribe(bridgeHandler);
                    ((DefaultSingletonBeanRegistry) getApplicationContext().getBeanFactory())
                            .destroySingleton(errorBridgeHandlerName);
                }
                if (handler != null) {
                    channel.unsubscribe(handler);
                    ((DefaultSingletonBeanRegistry) getApplicationContext().getBeanFactory())
                            .destroySingleton(errorMessageHandlerName);
                }
                ((DefaultSingletonBeanRegistry) getApplicationContext().getBeanFactory())
                        .destroySingleton(errorChannelName);
            }
        } catch (IllegalStateException e) {
            // context is shutting down.
        }
    }

    /**
     * Binders can return a message handler to be subscribed to the error channel.
     * Examples might be if the user wishes to (re)publish messages to a DLQ.
     * @param destination the destination.
     * @param group the group.
     * @param consumerProperties the properties.
     * @return the handler (may be null, which is the default, causing the exception to be
     * rethrown).
     */
    protected MessageHandler getErrorMessageHandler(ConsumerDestination destination, String group,
            C consumerProperties) {
        return null;
    }

    /**
     * Binders can return a message handler to be subscribed to the error channel.
     * Examples might be if the user wishes to (re)publish messages to a DLQ.
     * @param destination the destination.
     * @param group the group.
     * @param consumerProperties the properties.
     * @return the handler (may be null, which is the default, causing the exception to be
     * rethrown).
     */
    protected MessageHandler getPolledConsumerErrorMessageHandler(ConsumerDestination destination, String group,
            C consumerProperties) {
        return null;
    }

    /**
     * Return the default error message handler, which throws the error message payload to
     * the caller if there are no user handlers subscribed. The handler is ordered so it
     * runs after any user-defined handlers that are subscribed.
     * @param errorChannel the error channel.
     * @param defaultErrorChannelPresent true if the context has a default 'errorChannel'.
     * @return the handler.
     */
    protected MessageHandler getDefaultErrorMessageHandler(LastSubscriberAwareChannel errorChannel,
            boolean defaultErrorChannelPresent) {
        return new FinalRethrowingErrorMessageHandler(errorChannel, defaultErrorChannelPresent);
    }

    /**
     * Binders can return an {@link ErrorMessageStrategy} for building error messages; binder
     * implementations typically might add extra headers to the error message.
     * @return the implementation - may be null.
     */
    protected ErrorMessageStrategy getErrorMessageStrategy() {
        return null;
    }

    protected String getErrorRecovererName(ConsumerDestination destination, String group, C consumerProperties) {
        return errorsBaseName(destination, group, consumerProperties) + ".recoverer";
    }

    protected String getErrorMessageHandlerName(ConsumerDestination destination, String group,
            C consumerProperties) {
        return errorsBaseName(destination, group, consumerProperties) + ".handler";
    }

    protected String getErrorBridgeName(ConsumerDestination destination, String group, C consumerProperties) {
        return errorsBaseName(destination, group, consumerProperties) + ".bridge";
    }

    protected String errorsBaseName(ConsumerDestination destination, String group, C consumerProperties) {
        return destination.getName() + "." + group + ".errors";
    }

    protected String getErrorBridgeName(ProducerDestination destination) {
        return errorsBaseName(destination) + ".bridge";
    }

    protected String errorsBaseName(ProducerDestination destination) {
        return destination.getName() + ".errors";
    }

    private final class SendingHandler extends AbstractMessageHandler implements Lifecycle {

        private final boolean embedHeaders;

        private final String[] embeddedHeaders;

        private final MessageHandler delegate;

        private SendingHandler(MessageHandler delegate, boolean embedHeaders, String[] headersToEmbed,
                boolean useNativeEncoding) {
            this.delegate = delegate;
            setBeanFactory(AbstractMessageChannelBinder.this.getBeanFactory());
            this.embedHeaders = embedHeaders;
            this.embeddedHeaders = headersToEmbed;
            this.useNativeEncoding = useNativeEncoding;
        }

        private final boolean useNativeEncoding;

        @Override
        protected void handleMessageInternal(Message<?> message) throws Exception {
            Message<?> messageToSend = (this.useNativeEncoding) ? message
                    : serializeAndEmbedHeadersIfApplicable(message);
            this.delegate.handleMessage(messageToSend);
        }

        @SuppressWarnings("deprecation")
        private Message<?> serializeAndEmbedHeadersIfApplicable(Message<?> message) throws Exception {
            MessageValues transformed = serializePayloadIfNecessary(message);
            byte[] payload;
            if (this.embedHeaders) {
                Object contentType = transformed.get(MessageHeaders.CONTENT_TYPE);
                // transform content type headers to String, so that they can be properly
                // embedded in JSON
                if (contentType != null) {
                    transformed.put(MessageHeaders.CONTENT_TYPE, contentType.toString());
                }
                Object originalContentType = transformed.get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE);
                if (originalContentType != null) {
                    transformed.put(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE, originalContentType.toString());
                }
                payload = EmbeddedHeaderUtils.embedHeaders(transformed, this.embeddedHeaders);
            } else {
                payload = (byte[]) transformed.getPayload();
            }
            return getMessageBuilderFactory().withPayload(payload).copyHeaders(transformed.getHeaders()).build();
        }

        @Override
        public void start() {
            if (this.delegate instanceof Lifecycle) {
                ((Lifecycle) this.delegate).start();
            }
        }

        @Override
        public void stop() {
            if (this.delegate instanceof Lifecycle) {
                ((Lifecycle) this.delegate).stop();
            }
        }

        @Override
        public boolean isRunning() {
            return this.delegate instanceof Lifecycle && ((Lifecycle) this.delegate).isRunning();
        }
    }

    protected static class ErrorInfrastructure {

        private final SubscribableChannel errorChannel;

        private final ErrorMessageSendingRecoverer recoverer;

        private final MessageHandler handler;

        ErrorInfrastructure(SubscribableChannel errorChannel, ErrorMessageSendingRecoverer recoverer,
                MessageHandler handler) {
            this.errorChannel = errorChannel;
            this.recoverer = recoverer;
            this.handler = handler;
        }

        public SubscribableChannel getErrorChannel() {
            return this.errorChannel;
        }

        public ErrorMessageSendingRecoverer getRecoverer() {
            return this.recoverer;
        }

        public MessageHandler getHandler() {
            return this.handler;
        }

    }

    private static final class EmbeddedHeadersChannelInterceptor extends ChannelInterceptorAdapter {

        protected final Log logger;

        EmbeddedHeadersChannelInterceptor(Log logger) {
            this.logger = logger;
        }

        @Override
        @SuppressWarnings("unchecked")
        public Message<?> preSend(Message<?> message, MessageChannel channel) {
            if (message.getPayload() instanceof byte[]
                    && !message.getHeaders().containsKey(BinderHeaders.NATIVE_HEADERS_PRESENT)
                    && EmbeddedHeaderUtils.mayHaveEmbeddedHeaders((byte[]) message.getPayload())) {

                MessageValues messageValues;
                try {
                    messageValues = EmbeddedHeaderUtils.extractHeaders((Message<byte[]>) message, true);
                } catch (Exception e) {
                    /*
                     * debug() rather then error() since we don't know for sure that it
                     * really is a message with embedded headers, it just meets the
                     * criteria in EmbeddedHeaderUtils.mayHaveEmbeddedHeaders().
                     */
                    if (logger.isDebugEnabled()) {
                        logger.debug(EmbeddedHeaderUtils.decodeExceptionMessage(message), e);
                    }
                    messageValues = new MessageValues(message);
                }
                return messageValues.toMessage();
            }
            return message;
        }

    }

    protected static class PolledConsumerResources {

        private final MessageSource<?> source;

        private final ErrorInfrastructure errorInfrastructure;

        public PolledConsumerResources(MessageSource<?> source, ErrorInfrastructure errorInfrastructure) {
            this.source = source;
            this.errorInfrastructure = errorInfrastructure;
        }

        MessageSource<?> getSource() {
            return this.source;
        }

        ErrorInfrastructure getErrorInfrastructure() {
            return this.errorInfrastructure;
        }

    }

}