PageRenderTime 125ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/spring-integration-core/src/main/java/org/springframework/integration/dispatcher/UnicastingDispatcher.java

https://github.com/dturanski/spring-integration
Java | 170 lines | 98 code | 17 blank | 55 comment | 16 complexity | a61083dc6031007c5e8ad1bf6c45e0f2 MD5 | raw file
  1. /* Copyright 2002-2013 the original author or authors.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. package org.springframework.integration.dispatcher;
  16. import java.util.ArrayList;
  17. import java.util.Iterator;
  18. import java.util.List;
  19. import java.util.concurrent.Executor;
  20. import java.util.concurrent.locks.Lock;
  21. import java.util.concurrent.locks.ReadWriteLock;
  22. import java.util.concurrent.locks.ReentrantReadWriteLock;
  23. import org.springframework.integration.Message;
  24. import org.springframework.integration.MessageDeliveryException;
  25. import org.springframework.integration.MessageDispatchingException;
  26. import org.springframework.integration.MessagingException;
  27. import org.springframework.integration.core.MessageHandler;
  28. /**
  29. * Implementation of {@link MessageDispatcher} that will attempt to send a
  30. * {@link Message} to at most one of its handlers. The handlers will be tried
  31. * as determined by the {@link LoadBalancingStrategy} if one is configured. As
  32. * soon as <em>one</em> of the handlers accepts the Message, the dispatcher will
  33. * return <code>true</code> and ignore the rest of its handlers.
  34. * <p>
  35. * If the dispatcher has no handlers, a {@link MessageDeliveryException} will be
  36. * thrown. If all handlers throw Exceptions, the dispatcher will throw an
  37. * {@link AggregateMessageDeliveryException}.
  38. * <p>
  39. * A load-balancing strategy may be provided to this class to control the order in
  40. * which the handlers will be tried.
  41. *
  42. * @author Iwein Fuld
  43. * @author Mark Fisher
  44. * @author Gary Russell
  45. * @author Oleg Zhurakousky
  46. * @since 1.0.2
  47. */
  48. public class UnicastingDispatcher extends AbstractDispatcher {
  49. private volatile boolean failover = true;
  50. private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
  51. private volatile LoadBalancingStrategy loadBalancingStrategy;
  52. private final Executor executor;
  53. public UnicastingDispatcher() {
  54. this.executor = null;
  55. }
  56. public UnicastingDispatcher(Executor executor) {
  57. this.executor = executor;
  58. }
  59. /**
  60. * Specify whether this dispatcher should failover when a single
  61. * {@link MessageHandler} throws an Exception. The default value is
  62. * <code>true</code>.
  63. */
  64. public void setFailover(boolean failover) {
  65. this.failover = failover;
  66. }
  67. /**
  68. * Provide a {@link LoadBalancingStrategy} for this dispatcher.
  69. */
  70. public void setLoadBalancingStrategy(LoadBalancingStrategy loadBalancingStrategy) {
  71. Lock lock = rwLock.writeLock();
  72. lock.lock();
  73. try {
  74. this.loadBalancingStrategy = loadBalancingStrategy;
  75. }
  76. finally {
  77. lock.unlock();
  78. }
  79. }
  80. public final boolean dispatch(final Message<?> message) {
  81. if (this.executor != null) {
  82. this.executor.execute(new Runnable() {
  83. public void run() {
  84. doDispatch(message);
  85. }
  86. });
  87. return true;
  88. }
  89. return this.doDispatch(message);
  90. }
  91. private boolean doDispatch(Message<?> message) {
  92. boolean success = false;
  93. Iterator<MessageHandler> handlerIterator = this.getHandlerIterator(message);
  94. if (!handlerIterator.hasNext()) {
  95. throw new MessageDispatchingException(message, "Dispatcher has no subscribers");
  96. }
  97. List<RuntimeException> exceptions = new ArrayList<RuntimeException>();
  98. while (success == false && handlerIterator.hasNext()) {
  99. MessageHandler handler = handlerIterator.next();
  100. try {
  101. handler.handleMessage(message);
  102. success = true; // we have a winner.
  103. }
  104. catch (Exception e) {
  105. RuntimeException runtimeException = (e instanceof RuntimeException)
  106. ? (RuntimeException) e
  107. : new MessageDeliveryException(message,
  108. "Dispatcher failed to deliver Message.", e);
  109. if (e instanceof MessagingException &&
  110. ((MessagingException) e).getFailedMessage() == null) {
  111. ((MessagingException) e).setFailedMessage(message);
  112. }
  113. exceptions.add(runtimeException);
  114. this.handleExceptions(exceptions, message, !handlerIterator.hasNext());
  115. }
  116. }
  117. return success;
  118. }
  119. /**
  120. * Returns the iterator that will be used to loop over the handlers.
  121. * Delegates to a {@link LoadBalancingStrategy} if available. Otherwise,
  122. * it simply returns the Iterator for the existing handler List.
  123. */
  124. private Iterator<MessageHandler> getHandlerIterator(Message<?> message) {
  125. Lock lock = rwLock.readLock();
  126. lock.lock();
  127. try {
  128. if (this.loadBalancingStrategy != null) {
  129. return this.loadBalancingStrategy.getHandlerIterator(message, this.getHandlers());
  130. }
  131. } finally {
  132. lock.unlock();
  133. }
  134. return this.getHandlers().iterator();
  135. }
  136. /**
  137. * Handles Exceptions that occur while dispatching. If this dispatcher has
  138. * failover enabled, it will only throw an Exception when the handler list
  139. * is exhausted. The 'isLast' flag will be <em>true</em> if the
  140. * Exception occurred during the final iteration of the MessageHandlers.
  141. * If failover is disabled for this dispatcher, it will re-throw any
  142. * Exception immediately.
  143. */
  144. private void handleExceptions(List<RuntimeException> allExceptions, Message<?> message, boolean isLast) {
  145. if (isLast || !this.failover) {
  146. if (allExceptions != null && allExceptions.size() == 1) {
  147. throw allExceptions.get(0);
  148. }
  149. throw new AggregateMessageDeliveryException(message,
  150. "All attempts to deliver Message to MessageHandlers failed.", allExceptions);
  151. }
  152. }
  153. }