org.springframework.integration.channel.PublishSubscribeChannel.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.integration.channel.PublishSubscribeChannel.java

Source

/*
 * Copyright 2002-2019 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
 *
 *      https://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.integration.channel;

import java.util.concurrent.Executor;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.integration.context.IntegrationProperties;
import org.springframework.integration.dispatcher.BroadcastingDispatcher;
import org.springframework.integration.util.ErrorHandlingTaskExecutor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ErrorHandler;

/**
 * A channel that sends Messages to each of its subscribers.
 *
 * @author Mark Fisher
 * @author Oleg Zhurakousky
 * @author Gary Russell
 * @author Artem Bilan
 */
public class PublishSubscribeChannel extends AbstractExecutorChannel {

    private ErrorHandler errorHandler;

    private boolean ignoreFailures;

    private boolean applySequence;

    private int minSubscribers;

    /**
     * Create a PublishSubscribeChannel that will use an {@link Executor}
     * to invoke the handlers. If this is null, each invocation will occur in
     * the message sender's thread.
     * @param executor The executor.
     */
    public PublishSubscribeChannel(@Nullable Executor executor) {
        super(executor);
        this.dispatcher = new BroadcastingDispatcher(executor);
    }

    /**
     * Create a PublishSubscribeChannel that will invoke the handlers in the
     * message sender's thread.
     */
    public PublishSubscribeChannel() {
        this(null);
    }

    @Override
    public String getComponentType() {
        return "publish-subscribe-channel";
    }

    /**
     * Provide an {@link ErrorHandler} strategy for handling Exceptions that
     * occur downstream from this channel. This will <i>only</i> be applied if
     * an Executor has been configured to dispatch the Messages for this
     * channel. Otherwise, Exceptions will be thrown directly within the
     * sending Thread. If no ErrorHandler is provided, and this channel does
     * delegate its dispatching to an Executor, the default strategy is
     * a {@link MessagePublishingErrorHandler} that sends error messages to
     * the failed request Message's error channel header if available or to
     * the default 'errorChannel' otherwise.
     * @param errorHandler The error handler.
     * @see #PublishSubscribeChannel(Executor)
     */
    public void setErrorHandler(ErrorHandler errorHandler) {
        this.errorHandler = errorHandler;
    }

    /**
     * Specify whether failures for one or more of the handlers should be
     * ignored. By default this is <code>false</code> meaning that an Exception
     * will be thrown whenever a handler fails. To override this and suppress
     * Exceptions, set the value to <code>true</code>.
     * @param ignoreFailures true if failures should be ignored.
     */
    public void setIgnoreFailures(boolean ignoreFailures) {
        this.ignoreFailures = ignoreFailures;
        getDispatcher().setIgnoreFailures(ignoreFailures);
    }

    /**
     * Specify whether to apply the sequence number and size headers to the
     * messages prior to invoking the subscribed handlers. By default, this
     * value is <code>false</code> meaning that sequence headers will
     * <em>not</em> be applied. If planning to use an Aggregator downstream
     * with the default correlation and completion strategies, you should set
     * this flag to <code>true</code>.
     * @param applySequence true if the sequence information should be applied.
     */
    public void setApplySequence(boolean applySequence) {
        this.applySequence = applySequence;
        getDispatcher().setApplySequence(applySequence);
    }

    /**
     * If at least this number of subscribers receive the message,
     * {@link #send(org.springframework.messaging.Message)}
     * will return true. Default: 0.
     *
     * @param minSubscribers The minimum number of subscribers.
     */
    public void setMinSubscribers(int minSubscribers) {
        this.minSubscribers = minSubscribers;
        getDispatcher().setMinSubscribers(minSubscribers);
    }

    /**
     * Callback method for initialization.
     */
    @Override
    public final void onInit() {
        super.onInit();
        BeanFactory beanFactory = this.getBeanFactory();
        BroadcastingDispatcher dispatcherToUse = getDispatcher();
        if (this.executor != null) {
            Assert.state(dispatcherToUse.getHandlerCount() == 0,
                    "When providing an Executor, you cannot subscribe() until the channel "
                            + "bean is fully initialized by the framework. Do not subscribe in a @Bean definition");
            if (!(this.executor instanceof ErrorHandlingTaskExecutor)) {
                if (this.errorHandler == null) {
                    this.errorHandler = ChannelUtils.getErrorHandler(beanFactory);
                }
                this.executor = new ErrorHandlingTaskExecutor(this.executor, this.errorHandler);
            }
            dispatcherToUse = new BroadcastingDispatcher(this.executor);
            dispatcherToUse.setIgnoreFailures(this.ignoreFailures);
            dispatcherToUse.setApplySequence(this.applySequence);
            dispatcherToUse.setMinSubscribers(this.minSubscribers);
            this.dispatcher = dispatcherToUse;
        } else if (this.errorHandler != null) {
            if (this.logger.isWarnEnabled()) {
                this.logger.warn("The 'errorHandler' is ignored for the '" + getComponentName()
                        + "' (an 'executor' is not provided) and exceptions will be thrown "
                        + "directly within the sending Thread");
            }
        }

        if (this.maxSubscribers == null) {
            Integer maxSubscribers = getIntegrationProperty(
                    IntegrationProperties.CHANNELS_MAX_BROADCAST_SUBSCRIBERS, Integer.class);
            setMaxSubscribers(maxSubscribers);
        }
        dispatcherToUse.setBeanFactory(beanFactory);

        dispatcherToUse.setMessageHandlingTaskDecorator(task -> {
            if (PublishSubscribeChannel.this.executorInterceptorsSize > 0) {
                return new MessageHandlingTask(task);
            } else {
                return task;
            }
        });
    }

    @Override
    protected BroadcastingDispatcher getDispatcher() {
        return (BroadcastingDispatcher) this.dispatcher;
    }

}