/* * Copyright 2002-2017 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.router; import java.util.Collection; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.integration.channel.NullChannel; import org.springframework.integration.core.MessagingTemplate; import org.springframework.integration.handler.AbstractMessageHandler; import org.springframework.integration.support.management.IntegrationManagedResource; import org.springframework.jmx.export.annotation.ManagedResource; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageDeliveryException; import org.springframework.messaging.MessagingException; import org.springframework.util.Assert; /** * Base class for all Message Routers. * * @author Mark Fisher * @author Oleg Zhurakousky * @author Gunnar Hillert * @author Soby Chacko * @author Stefan Ferstl * @author Artem Bilan */ @ManagedResource @IntegrationManagedResource public abstract class AbstractMessageRouter extends AbstractMessageHandler implements MessageRouter { private volatile MessageChannel defaultOutputChannel; private volatile String defaultOutputChannelName; private volatile boolean ignoreSendFailures; private volatile boolean applySequence; private final MessagingTemplate messagingTemplate = new MessagingTemplate(); /** * Set the default channel where Messages should be sent if channel resolution * fails to return any channels. If no default channel is provided and channel * resolution fails to return any channels, the router will throw an * {@link MessageDeliveryException}. * <p> * If messages shall be ignored (dropped) instead, please provide a {@link NullChannel}. * @param defaultOutputChannel The default output channel. */ public void setDefaultOutputChannel(MessageChannel defaultOutputChannel) { this.defaultOutputChannel = defaultOutputChannel; } /** * Get the default output channel. * @return the channel. * @since 4.3 */ @Override public MessageChannel getDefaultOutputChannel() { if (this.defaultOutputChannelName != null) { synchronized (this) { if (this.defaultOutputChannelName != null) { this.defaultOutputChannel = getChannelResolver().resolveDestination(this.defaultOutputChannelName); this.defaultOutputChannelName = null; } } } return this.defaultOutputChannel; } public void setDefaultOutputChannelName(String defaultOutputChannelName) { Assert.hasText(defaultOutputChannelName, "'defaultOutputChannelName' must not be empty"); this.defaultOutputChannelName = defaultOutputChannelName; } /** * Set the timeout for sending a message to the resolved channel. * By default, there is no timeout, meaning the send will block indefinitely. * @param timeout The timeout. * @since 4.3 */ public void setSendTimeout(long timeout) { this.messagingTemplate.setSendTimeout(timeout); } /** * Specify whether send failures for one or more of the recipients should be ignored. By default this is * <code>false</code> meaning that an Exception will be thrown whenever a send fails. To override this and suppress * Exceptions, set the value to <code>true</code>. * @param ignoreSendFailures true to ignore send failures. */ public void setIgnoreSendFailures(boolean ignoreSendFailures) { this.ignoreSendFailures = ignoreSendFailures; } /** * Specify whether to apply the sequence number and size headers to the messages prior to sending to the recipient * channels. 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 to apply sequence information. */ public void setApplySequence(boolean applySequence) { this.applySequence = applySequence; } @Override public String getComponentType() { return "router"; } /** * Provides {@link MessagingTemplate} access for subclasses * @return The messaging template. */ protected MessagingTemplate getMessagingTemplate() { return this.messagingTemplate; } protected ConversionService getRequiredConversionService() { if (this.getConversionService() == null) { synchronized (this) { if (getConversionService() == null) { setConversionService(DefaultConversionService.getSharedInstance()); } } } return getConversionService(); } @Override protected void onInit() throws Exception { super.onInit(); Assert.state(!(this.defaultOutputChannelName != null && this.defaultOutputChannel != null), "'defaultOutputChannelName' and 'defaultOutputChannel' are mutually exclusive."); if (this.getBeanFactory() != null) { this.messagingTemplate.setBeanFactory(this.getBeanFactory()); } } /** * Subclasses must implement this method to return a Collection of zero or more * MessageChannels to which the given Message should be routed. * @param message The message. * @return The collection of message channels. */ protected abstract Collection<MessageChannel> determineTargetChannels(Message<?> message); @Override protected void handleMessageInternal(Message<?> message) { boolean sent = false; Collection<MessageChannel> results = this.determineTargetChannels(message); if (results != null) { int sequenceSize = results.size(); int sequenceNumber = 1; for (MessageChannel channel : results) { final Message<?> messageToSend = !this.applySequence ? message : (this.getMessageBuilderFactory() .fromMessage(message) .pushSequenceDetails(message.getHeaders().getId(), sequenceNumber++, sequenceSize) .build()); if (channel != null) { try { this.messagingTemplate.send(channel, messageToSend); sent = true; } catch (MessagingException e) { if (!this.ignoreSendFailures) { throw e; } else if (this.logger.isDebugEnabled()) { this.logger.debug(e); } } } } } if (!sent) { getDefaultOutputChannel(); if (this.defaultOutputChannel != null) { this.messagingTemplate.send(this.defaultOutputChannel, message); } else { throw new MessageDeliveryException(message, "No channel resolved by router '" + this.getComponentName() + "' and no 'defaultOutputChannel' defined."); } } } }