/* * Copyright 2002-2016 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.integration.channel; import java.util.concurrent.Executor; import org.springframework.integration.context.IntegrationProperties; import org.springframework.integration.dispatcher.LoadBalancingStrategy; import org.springframework.integration.dispatcher.RoundRobinLoadBalancingStrategy; import org.springframework.integration.dispatcher.UnicastingDispatcher; import org.springframework.integration.support.channel.BeanFactoryChannelResolver; import org.springframework.integration.util.ErrorHandlingTaskExecutor; import org.springframework.messaging.MessageChannel; import org.springframework.util.Assert; import org.springframework.util.ErrorHandler; /** * An implementation of {@link MessageChannel} that delegates to an instance of * {@link UnicastingDispatcher} which in turn delegates all dispatching * invocations to an {@link Executor}. * <p> * <em><b>NOTE</b>: unlike DirectChannel, the ExecutorChannel does not support a * shared transactional context between sender and handler, because the * {@link Executor} typically does not block the sender's Thread since it * uses another Thread for the dispatch.</em> (SyncTaskExecutor is an * exception but would provide no value for this channel. If synchronous * dispatching is required, a DirectChannel should be used instead). * * @author Mark Fisher * @author Gary Russell * @author Artem Bilan * @since 1.0.3 */ public class ExecutorChannel extends AbstractExecutorChannel { private volatile boolean failover = true; private volatile LoadBalancingStrategy loadBalancingStrategy; /** * Create an ExecutorChannel that delegates to the provided * {@link Executor} when dispatching Messages. * <p> * The Executor must not be null. * @param executor The executor. */ public ExecutorChannel(Executor executor) { this(executor, new RoundRobinLoadBalancingStrategy()); } /** * Create an ExecutorChannel with a {@link LoadBalancingStrategy} that * delegates to the provided {@link Executor} when dispatching Messages. * <p> * The Executor must not be null. * @param executor The executor. * @param loadBalancingStrategy The load balancing strategy implementation. */ public ExecutorChannel(Executor executor, LoadBalancingStrategy loadBalancingStrategy) { super(executor); Assert.notNull(executor, "executor must not be null"); UnicastingDispatcher unicastingDispatcher = new UnicastingDispatcher(executor); if (loadBalancingStrategy != null) { this.loadBalancingStrategy = loadBalancingStrategy; unicastingDispatcher.setLoadBalancingStrategy(loadBalancingStrategy); } this.dispatcher = unicastingDispatcher; } /** * Specify whether the channel's dispatcher should have failover enabled. * By default, it will. Set this value to 'false' to disable it. * @param failover The failover boolean. */ public void setFailover(boolean failover) { this.failover = failover; getDispatcher().setFailover(failover); } @Override protected UnicastingDispatcher getDispatcher() { return (UnicastingDispatcher) this.dispatcher; } @Override public final void onInit() throws Exception { Assert.state(getDispatcher().getHandlerCount() == 0, "You cannot subscribe() until the channel " + "bean is fully initialized by the framework. Do not subscribe in a @Bean definition"); super.onInit(); if (!(this.executor instanceof ErrorHandlingTaskExecutor)) { ErrorHandler errorHandler = new MessagePublishingErrorHandler( new BeanFactoryChannelResolver(this.getBeanFactory())); this.executor = new ErrorHandlingTaskExecutor(this.executor, errorHandler); } UnicastingDispatcher unicastingDispatcher = new UnicastingDispatcher(this.executor); unicastingDispatcher.setFailover(this.failover); if (this.maxSubscribers == null) { this.maxSubscribers = getIntegrationProperty(IntegrationProperties.CHANNELS_MAX_UNICAST_SUBSCRIBERS, Integer.class); } unicastingDispatcher.setMaxSubscribers(this.maxSubscribers); if (this.loadBalancingStrategy != null) { unicastingDispatcher.setLoadBalancingStrategy(this.loadBalancingStrategy); } unicastingDispatcher.setMessageHandlingTaskDecorator(task -> { if (ExecutorChannel.this.executorInterceptorsSize > 0) { return new MessageHandlingTask(task); } else { return task; } }); this.dispatcher = unicastingDispatcher; } }