Refactored SimpleDispatcher and added test cases in SimpleDispatcherTests. Also removed MessageHandlerNotRunningException.

This commit is contained in:
Mark Fisher
2008-07-18 23:00:03 +00:00
parent ce88e75725
commit 0aa8aeb129
8 changed files with 251 additions and 131 deletions

View File

@@ -18,17 +18,17 @@ package org.springframework.integration.dispatcher;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.springframework.integration.handler.MessageHandlerNotRunningException;
import org.springframework.integration.handler.MessageHandlerRejectedExecutionException;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.MessageDeliveryException;
import org.springframework.integration.message.MessageHandlingException;
import org.springframework.integration.message.MessageRejectedException;
import org.springframework.integration.message.MessageTarget;
import org.springframework.util.Assert;
/**
* Basic implementation of {@link MessageDispatcher}.
* Basic implementation of {@link MessageDispatcher} that will attempt
* to send a {@link Message} to one of its targets (the first that accepts).
*
* @author Mark Fisher
*/
@@ -71,66 +71,81 @@ public class SimpleDispatcher extends AbstractDispatcher {
}
public boolean send(Message<?> message) {
if (this.targets.size() == 0) {
if (logger.isWarnEnabled()) {
logger.warn("Dispatcher has no targets.");
}
return false;
}
int attempts = 0;
List<MessageTarget> targetList = new ArrayList<MessageTarget>(this.targets);
MessageHandlingException lastException = null;
while (attempts < this.rejectionLimit) {
Iterator<MessageTarget> iter = new ArrayList<MessageTarget>(this.targets).iterator();
if (!iter.hasNext()) {
return false;
}
if (attempts > 0) {
if (logger.isDebugEnabled()) {
logger.debug("target(s) rejected message after " + attempts +
" attempt(s), will try again after 'retryInterval' of " +
this.retryInterval + " milliseconds");
}
try {
Thread.sleep(this.retryInterval);
this.waitBetweenAttempts(attempts);
}
catch (InterruptedException iex) {
Thread.currentThread().interrupt();
return false;
}
}
Iterator<MessageTarget> iter = targetList.iterator();
if (!iter.hasNext()) {
if (logger.isWarnEnabled()) {
logger.warn("no active targets");
}
return false;
}
boolean rejected = false;
while (iter.hasNext()) {
MessageTarget target = iter.next();
try {
if (this.sendMessageToTarget(message, target)) {
return true;
}
if (logger.isDebugEnabled()) {
logger.debug("target rejected message, continuing with other targets if available");
}
iter.remove();
}
catch (MessageHandlerNotRunningException e) {
if (logger.isDebugEnabled()) {
logger.debug("target is not running, continuing with other targets if available", e);
}
}
catch (MessageHandlerRejectedExecutionException e) {
rejected = true;
if (logger.isDebugEnabled()) {
logger.debug("target '" + target + "' is busy, continuing with other targets if available", e);
}
}
}
if (!rejected) {
lastException = sendMessageToFirstAcceptingTarget(message, iter);
if (lastException == null) {
return true;
}
attempts++;
}
if (this.shouldFailOnRejectionLimit) {
throw new MessageDeliveryException(message, "Dispatcher reached rejection limit of "
+ this.rejectionLimit
+ ". Consider increasing the target's concurrency and/or "
+ "the dispatcherPolicy's 'rejectionLimit'.");
throw new MessageDeliveryException(message, "Dispatcher reached rejection limit of " + this.rejectionLimit, lastException);
}
return false;
}
private MessageHandlingException sendMessageToFirstAcceptingTarget(Message<?> message, Iterator<MessageTarget> iter) {
MessageHandlingException lastException = null;
int count = 0;
int rejectedExceptionCount = 0;
while (iter.hasNext()) {
count++;
MessageTarget target = iter.next();
try {
if (this.sendMessageToTarget(message, target)) {
return null;
}
if (logger.isDebugEnabled()) {
logger.debug("Failed to send message to target, continuing with other targets if available.");
}
}
catch (MessageRejectedException e) {
rejectedExceptionCount++;
if (logger.isDebugEnabled()) {
logger.debug("Target '" + target + "' rejected Message, continuing with other targets if available.", e);
}
}
catch (MessageHandlingException e) {
lastException = e;
if (logger.isDebugEnabled()) {
logger.debug("Target '" + target + "' threw an exception, continuing with other targets if available.", e);
}
}
}
if (rejectedExceptionCount == count) {
throw new MessageRejectedException(message, "All of dispatcher's targets rejected Message.");
}
return lastException;
}
private void waitBetweenAttempts(int attempts) throws InterruptedException {
if (logger.isDebugEnabled()) {
logger.debug("target(s) unable to handle message after " + attempts +
" attempt(s), will try again after 'retryInterval' of " +
this.retryInterval + " milliseconds");
}
Thread.sleep(this.retryInterval);
}
}

View File

@@ -30,14 +30,12 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.Lifecycle;
import org.springframework.core.task.TaskExecutor;
import org.springframework.integration.channel.ChannelRegistry;
import org.springframework.integration.channel.ChannelRegistryAware;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.endpoint.ConcurrencyPolicy;
import org.springframework.integration.endpoint.EndpointInterceptor;
import org.springframework.integration.handler.MessageHandlerNotRunningException;
import org.springframework.integration.handler.MessageHandlerRejectedExecutionException;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.MessageTarget;
@@ -121,9 +119,6 @@ public class ConcurrencyInterceptor extends EndpointInterceptorAdapter
@Override
public boolean aroundSend(final Message<?> message, final MessageTarget endpoint) {
if (endpoint instanceof Lifecycle && !((Lifecycle) endpoint).isRunning()) {
throw new MessageHandlerNotRunningException(message);
}
try {
this.executor.execute(new Runnable() {
public void run() {

View File

@@ -1,34 +0,0 @@
/*
* Copyright 2002-2008 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.handler;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.MessageHandlingException;
/**
* An exception indicating that a handler is not currently running.
*
* @author Mark Fisher
*/
@SuppressWarnings("serial")
public class MessageHandlerNotRunningException extends MessageHandlingException {
public MessageHandlerNotRunningException(Message<?> message) {
super(message, "handler is not running");
}
}

View File

@@ -32,4 +32,8 @@ public class MessageDeliveryException extends MessagingException {
super(undeliveredMessage, description);
}
public MessageDeliveryException(Message<?> undeliveredMessage, String description, Throwable cause) {
super(undeliveredMessage, description, cause);
}
}

View File

@@ -109,8 +109,8 @@ public final class MessageHeaders implements Map<String, Object>, Serializable {
return null;
}
if (!type.isAssignableFrom(value.getClass())) {
throw new MessagingException("Type mismatch for header '" + key + "'. Expected ["
+ type + "] but actual type is [" + value.getClass() + "]");
throw new IllegalArgumentException("Incorrect type specified for header '" + key
+ "'. Expected [" + type + "] but actual type is [" + value.getClass() + "]");
}
return (T) value;
}

View File

@@ -31,6 +31,7 @@ import org.springframework.integration.util.AbstractMethodInvokingAdapter;
/**
* @author Mark Fisher
*/
@SuppressWarnings("unchecked")
public class AnnotationMethodTransformerAdapter extends AbstractMethodInvokingAdapter implements MessageHandler {
private volatile MessageMapper mapper;
@@ -48,8 +49,7 @@ public class AnnotationMethodTransformerAdapter extends AbstractMethodInvokingAd
: new DefaultMessageMapper();
}
@SuppressWarnings("unchecked")
public Message<?> transform(Message<?> message) {
public Message<?> handle(Message<?> message) {
if (!this.isInitialized()) {
this.afterPropertiesSet();
}
@@ -66,48 +66,48 @@ public class AnnotationMethodTransformerAdapter extends AbstractMethodInvokingAd
else {
args = new Object[] { param };
}
Object result = null;
try {
result = this.invokeMethod(args);
}
catch (NoSuchMethodException e) {
result = this.invokeMethod(message);
this.methodExpectsMessage = true;
}
if (result == null) {
if (logger.isDebugEnabled()) {
logger.debug("MessageTransformer returned a null result");
}
return null;
}
if (result instanceof Properties && !(message.getPayload() instanceof Properties)) {
Properties propertiesToSet = (Properties) result;
MessageBuilder builder = MessageBuilder.fromMessage(message);
for (Object keyObject : propertiesToSet.keySet()) {
String key = (String) keyObject;
builder.setHeader(key, propertiesToSet.getProperty(key));
}
return builder.build();
}
else if (result instanceof Map && !(message.getPayload() instanceof Map)) {
Map<String, ?> attributesToSet = (Map) result;
MessageBuilder builder = MessageBuilder.fromMessage(message);
for (String key : attributesToSet.keySet()) {
builder.setHeader(key, attributesToSet.get(key));
}
return builder.build();
}
else {
return MessageBuilder.fromPayload(result).copyHeaders(message.getHeaders()).build();
}
return this.invokeMethodAndReturnMessage(message, args);
}
catch (Exception e) {
throw new MessagingException(message, "failed to transform message payload", e);
}
}
public Message<?> handle(Message<?> message) {
return this.transform(message);
private Message<?> invokeMethodAndReturnMessage(Message<?> message, Object[] args) throws Exception {
Object result = null;
try {
result = this.invokeMethod(args);
}
catch (NoSuchMethodException e) {
result = this.invokeMethod(message);
this.methodExpectsMessage = true;
}
if (result == null) {
if (logger.isDebugEnabled()) {
logger.debug("handler invocation returned a null result");
}
return null;
}
if (result instanceof Properties && !(message.getPayload() instanceof Properties)) {
Properties propertiesToSet = (Properties) result;
MessageBuilder builder = MessageBuilder.fromMessage(message);
for (Object keyObject : propertiesToSet.keySet()) {
String key = (String) keyObject;
builder.setHeader(key, propertiesToSet.getProperty(key));
}
return builder.build();
}
else if (result instanceof Map && !(message.getPayload() instanceof Map)) {
Map<String, ?> attributesToSet = (Map) result;
MessageBuilder builder = MessageBuilder.fromMessage(message);
for (String key : attributesToSet.keySet()) {
builder.setHeader(key, attributesToSet.get(key));
}
return builder.build();
}
else {
return MessageBuilder.fromPayload(result).copyHeaders(message.getHeaders()).build();
}
}
}