Removed MessageReceiver and refactored to a more unified model with MessageHandler as the central interface.

This commit is contained in:
Mark Fisher
2008-01-10 21:55:10 +00:00
parent d9d2fa4e9e
commit 7227544364
15 changed files with 286 additions and 168 deletions

View File

@@ -28,7 +28,7 @@ import org.springframework.util.Assert;
*
* @author Mark Fisher
*/
public abstract class AbstractTargetAdapter<T> implements TargetAdapter<T> {
public abstract class AbstractTargetAdapter<T> implements TargetAdapter {
private String name;
@@ -74,8 +74,9 @@ public abstract class AbstractTargetAdapter<T> implements TargetAdapter<T> {
return this.policy;
}
public final void messageReceived(Message message) {
public final Message handle(Message message) {
this.sendToTarget(this.mapper.fromMessage(message));
return null;
}
protected abstract boolean sendToTarget(T object);

View File

@@ -18,14 +18,14 @@ package org.springframework.integration.adapter;
import org.springframework.integration.bus.ConsumerPolicy;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.message.MessageReceiver;
import org.springframework.integration.handler.MessageHandler;
/**
* Base interface for target adapters.
*
* @author Mark Fisher
*/
public interface TargetAdapter<T> extends MessageReceiver<T> {
public interface TargetAdapter extends MessageHandler {
String getName();

View File

@@ -22,7 +22,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.Lifecycle;
import org.springframework.integration.handler.MessageHandler;
import org.springframework.integration.message.Message;
import org.springframework.util.Assert;
@@ -38,7 +39,7 @@ public abstract class AbstractMessageDispatcher implements MessageDispatcher {
private MessageRetriever retriever;
private List<MessageReceivingExecutor> executors = new CopyOnWriteArrayList<MessageReceivingExecutor>();
private List<MessageHandler> handlers = new CopyOnWriteArrayList<MessageHandler>();
private volatile boolean running;
@@ -50,14 +51,16 @@ public abstract class AbstractMessageDispatcher implements MessageDispatcher {
}
public void addExecutor(MessageReceivingExecutor executor) {
Assert.notNull(executor, "'executor' must not be null");
executor.start();
this.executors.add(executor);
public void addHandler(MessageHandler handler) {
Assert.notNull(handler, "'handler' must not be null");
if (this.isRunning() && handler instanceof Lifecycle) {
((Lifecycle) handler).start();
}
this.handlers.add(handler);
}
protected List<MessageReceivingExecutor> getExecutors() {
return this.executors;
protected List<MessageHandler> getHandlers() {
return this.handlers;
}
public boolean isRunning() {
@@ -67,9 +70,12 @@ public abstract class AbstractMessageDispatcher implements MessageDispatcher {
public void start() {
synchronized (this.lifecycleMonitor) {
if (!this.isRunning()) {
for (MessageReceivingExecutor executor : this.executors) {
executor.start();
for (MessageHandler handler : this.handlers) {
if (handler instanceof Lifecycle) {
((Lifecycle) handler).start();
}
}
this.running = true;
}
}
}
@@ -77,9 +83,12 @@ public abstract class AbstractMessageDispatcher implements MessageDispatcher {
public void stop() {
synchronized (this.lifecycleMonitor) {
if (this.isRunning()) {
for (MessageReceivingExecutor executor : this.executors) {
executor.stop();
for (MessageHandler handler : this.handlers) {
if (handler instanceof Lifecycle) {
((Lifecycle) handler).stop();
}
}
this.running = false;
}
}
}
@@ -90,6 +99,9 @@ public abstract class AbstractMessageDispatcher implements MessageDispatcher {
* @return the number of messages processed
*/
public int dispatch() {
if (!this.isRunning()) {
return 0;
}
int messagesProcessed = 0;
Collection<Message<?>> messages = this.retriever.retrieveMessages();
if (messages == null) {

View File

@@ -19,17 +19,18 @@ package org.springframework.integration.bus;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.RejectedExecutionException;
import org.springframework.integration.MessageDeliveryException;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.handler.MessageHandler;
import org.springframework.integration.message.Message;
import org.springframework.util.Assert;
/**
* The default implementation of {@link MessageDispatcher}. If
* {@link #broadcast} is set to <code>false</code> (the default), each message
* will be sent to a single {@link MessageReceivingExecutor}. Otherwise, each
* retrieved {@link Message} will be sent to all executors.
* will be sent to a single {@link MessageHandler}. Otherwise, each
* retrieved {@link Message} will be sent to all handlers.
*
* @author Mark Fisher
*/
@@ -74,11 +75,11 @@ public class DefaultMessageDispatcher extends AbstractMessageDispatcher {
@Override
protected boolean dispatchMessage(Message<?> message) {
int attempts = 0;
List<MessageReceivingExecutor> targets = new ArrayList<MessageReceivingExecutor>(this.getExecutors());
List<MessageHandler> targets = new ArrayList<MessageHandler>(this.getHandlers());
while (attempts < this.rejectionLimit) {
if (attempts > 0) {
if (logger.isDebugEnabled()) {
logger.debug("executor(s) rejected message after " + attempts
logger.debug("handler(s) rejected message after " + attempts
+ " attempt(s), will try again after 'retryInterval' of " + this.retryInterval
+ " milliseconds");
}
@@ -90,38 +91,41 @@ public class DefaultMessageDispatcher extends AbstractMessageDispatcher {
return false;
}
}
Iterator<MessageReceivingExecutor> iter = targets.iterator();
Iterator<MessageHandler> iter = targets.iterator();
if (!iter.hasNext()) {
if (logger.isWarnEnabled()) {
logger.warn("dispatcher has no active executors");
logger.warn("dispatcher has no active handlers");
}
return false;
}
boolean encounteredRejection = false;
boolean encounteredHandlerException = false;
while (iter.hasNext()) {
MessageReceivingExecutor executor = iter.next();
if (executor == null || !executor.isRunning()) {
if (logger.isInfoEnabled()) {
logger.info("skipping inactive executor");
}
iter.remove();
continue;
}
MessageHandler handler = iter.next();
try {
boolean accepted = executor.acceptMessage(message);
if (accepted && !this.broadcast) {
handler.handle(message);
if (!this.broadcast) {
return true;
}
iter.remove();
}
catch (RejectedExecutionException rex) {
encounteredRejection = true;
catch (MessageSelectorRejectedException e) {
if (logger.isDebugEnabled()) {
logger.debug("executor rejected task, continuing with other executors if available", rex);
logger.debug("selector rejected task, continuing with other handlers if available", e);
}
}
catch (MessageHandlerNotRunningException e) {
if (logger.isDebugEnabled()) {
logger.debug("handler not running, continuing with other handlers if available", e);
}
}
catch (MessageHandlingException e) {
encounteredHandlerException = true;
if (logger.isDebugEnabled()) {
logger.debug("handler threw exception, continuing with other handlers if available", e);
}
}
}
if (!encounteredRejection) {
if (!encounteredHandlerException) {
return true;
}
attempts++;

View File

@@ -38,8 +38,8 @@ import org.springframework.integration.channel.DefaultChannelRegistry;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.channel.SimpleChannel;
import org.springframework.integration.endpoint.MessageEndpoint;
import org.springframework.integration.handler.MessageHandler;
import org.springframework.integration.message.ErrorMessage;
import org.springframework.integration.message.MessageReceiver;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -56,7 +56,7 @@ public class MessageBus implements ChannelRegistry, ApplicationContextAware, Lif
private ChannelRegistry channelRegistry = new DefaultChannelRegistry();
private Map<String, MessageReceiver<?>> receivers = new ConcurrentHashMap<String, MessageReceiver<?>>();
private Map<String, MessageHandler> handlers = new ConcurrentHashMap<String, MessageHandler>();
private List<MessageDispatcher> dispatchers = new CopyOnWriteArrayList<MessageDispatcher>();
@@ -180,11 +180,11 @@ public class MessageBus implements ChannelRegistry, ApplicationContextAware, Lif
this.channelRegistry.registerChannel(name, channel);
}
public void registerEndpoint(String name, MessageEndpoint<?> endpoint) {
public void registerEndpoint(String name, MessageEndpoint endpoint) {
Assert.notNull(name, "'name' must not be null");
Assert.notNull(endpoint, "'endpoint' must not be null");
endpoint.setName(name);
this.receivers.put(name, endpoint);
this.handlers.put(name, endpoint);
if (logger.isInfoEnabled()) {
logger.info("registered endpoint '" + name + "'");
}
@@ -217,11 +217,11 @@ public class MessageBus implements ChannelRegistry, ApplicationContextAware, Lif
}
}
public void registerTargetAdapter(String name, TargetAdapter<?> targetAdapter) {
public void registerTargetAdapter(String name, TargetAdapter targetAdapter) {
if (targetAdapter instanceof AbstractTargetAdapter) {
AbstractTargetAdapter<?> adapter = (AbstractTargetAdapter<?>) targetAdapter;
adapter.setName(name);
this.receivers.put(name, targetAdapter);
this.handlers.put(name, targetAdapter);
MessageChannel channel = adapter.getChannel();
ConsumerPolicy policy = adapter.getConsumerPolicy();
this.doActivate(channel, adapter, policy);
@@ -233,11 +233,11 @@ public class MessageBus implements ChannelRegistry, ApplicationContextAware, Lif
public void activateSubscription(Subscription subscription) {
String channelName = subscription.getChannel();
String receiverName = subscription.getReceiver();
String handlerName = subscription.getReceiver();
ConsumerPolicy policy = subscription.getPolicy();
MessageReceiver<?> receiver = this.receivers.get(receiverName);
if (receiver == null) {
throw new MessagingException("Cannot activate subscription, unknown receiver '" + receiverName + "'");
MessageHandler handler = this.handlers.get(handlerName);
if (handler == null) {
throw new MessagingException("Cannot activate subscription, unknown handler '" + handlerName + "'");
}
MessageChannel channel = this.lookupChannel(channelName);
if (channel == null) {
@@ -251,29 +251,29 @@ public class MessageBus implements ChannelRegistry, ApplicationContextAware, Lif
channel = new SimpleChannel();
this.registerChannel(channelName, channel);
}
this.doActivate(channel, receiver, policy);
this.doActivate(channel, handler, policy);
if (logger.isInfoEnabled()) {
logger.info("activated subscription to channel '" + channelName +
"' for receiver '" + receiverName + "'");
"' for handler '" + handlerName + "'");
}
}
private void doActivate(MessageChannel channel, MessageReceiver<?> receiver, ConsumerPolicy policy) {
MessageReceivingExecutor executor = new MessageReceivingExecutor(receiver, policy.getConcurrency(), policy.getMaxConcurrency());
private void doActivate(MessageChannel channel, MessageHandler handler, ConsumerPolicy policy) {
PooledMessageHandler pooledHandler = new PooledMessageHandler(handler, policy.getConcurrency(), policy.getMaxConcurrency());
MessageRetriever retriever = new ChannelPollingMessageRetriever(channel, policy);
DefaultMessageDispatcher dispatcher = new DefaultMessageDispatcher(retriever);
dispatcher.setRejectionLimit(policy.getRejectionLimit());
dispatcher.setRetryInterval(policy.getRetryInterval());
dispatcher.addExecutor(executor);
dispatcher.addHandler(pooledHandler);
DispatcherTask dispatcherTask = new DispatcherTask(dispatcher, policy);
if (this.isRunning()) {
executor.start();
dispatcher.start();
}
this.dispatchers.add(dispatcher);
this.addDispatcherTask(dispatcherTask);
if (this.logger.isInfoEnabled()) {
logger.info("registered dispatcher task: channel='" +
channel.getName() + "' receiver='" + receiver.getName() + "'");
channel.getName() + "' handler='" + handler + "'");
}
}

View File

@@ -14,20 +14,19 @@
* limitations under the License.
*/
package org.springframework.integration.message;
package org.springframework.integration.bus;
import org.springframework.integration.MessageHandlingException;
/**
* The primary callback interface for any component capable of receiving
* messages. This includes message endpoints as well as target adapters.
* An exception indicating that a handler is not currently running.
*
* @author Mark Fisher
*/
public interface MessageReceiver<T> {
public class MessageHandlerNotRunningException extends MessageHandlingException {
String getName();
void setName(String name);
void messageReceived(Message<T> message);
public MessageHandlerNotRunningException() {
super("handler is not running");
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2002-2007 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.bus;
import org.springframework.integration.MessageHandlingException;
/**
* An exception indicating that a message was rejected by a handler; typically
* this would be the result of a thread pool executor rejecting a handler task.
*
* @author Mark Fisher
*/
public class MessageHandlerRejectedExecutionException extends MessageHandlingException {
public MessageHandlerRejectedExecutionException(Throwable cause) {
super("handler rejected execution", cause);
}
public MessageHandlerRejectedExecutionException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2002-2007 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.bus;
import org.springframework.integration.MessageHandlingException;
/**
* An exception indicating that a message was rejected by an implementation of
* {@link org.springframework.integration.message.selector.MessageSelector}.
*
* @author Mark Fisher
*/
public class MessageSelectorRejectedException extends MessageHandlingException {
public MessageSelectorRejectedException(String message) {
super(message);
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.integration.bus;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -27,8 +28,8 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.context.Lifecycle;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.handler.MessageHandler;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.MessageReceiver;
import org.springframework.integration.message.selector.MessageSelector;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.util.Assert;
@@ -38,11 +39,11 @@ import org.springframework.util.Assert;
*
* @author Mark Fisher
*/
public class MessageReceivingExecutor implements Lifecycle {
public class PooledMessageHandler implements MessageHandler, Lifecycle {
private Log logger = LogFactory.getLog(this.getClass());
private MessageReceiver receiver;
private MessageHandler handler;
private List<MessageSelector> selectors = new CopyOnWriteArrayList<MessageSelector>();
@@ -65,12 +66,12 @@ public class MessageReceivingExecutor implements Lifecycle {
private int totalErrorThreshold = -1;
public MessageReceivingExecutor(MessageReceiver receiver, int corePoolSize, int maxPoolSize) {
Assert.notNull(receiver, "'receiver' must not be null");
public PooledMessageHandler(MessageHandler handler, int corePoolSize, int maxPoolSize) {
Assert.notNull(handler, "'handler' must not be null");
Assert.isTrue(corePoolSize > 0, "'corePoolSize' must be at least 1");
Assert.isTrue(maxPoolSize > 0, "'maxPoolSize' must be at least 1");
Assert.isTrue(maxPoolSize >= corePoolSize, "'corePoolSize' cannot exceed 'maxPoolSize'");
this.receiver = receiver;
this.handler = handler;
this.corePoolSize = corePoolSize;
this.maxPoolSize = maxPoolSize;
}
@@ -95,7 +96,7 @@ public class MessageReceivingExecutor implements Lifecycle {
public void start() {
synchronized (this.lifecycleMonitor) {
if (!this.running) {
this.threadPoolExecutor = new MessageReceivingThreadPoolExecutor(this.corePoolSize, this.maxPoolSize);
this.threadPoolExecutor = new MessageHandlerThreadPoolExecutor(this.corePoolSize, this.maxPoolSize);
}
this.running = true;
}
@@ -111,17 +112,23 @@ public class MessageReceivingExecutor implements Lifecycle {
}
}
public boolean acceptMessage(Message<?> message) {
if (threadPoolExecutor == null) {
throw new MessageHandlingException("executor is not running");
public Message handle(Message<?> message) {
if (!this.isRunning()) {
throw new MessageHandlerNotRunningException();
}
for (MessageSelector selector : this.selectors) {
if (!selector.accept(message)) {
return false;
throw new MessageSelectorRejectedException("selector rejected message");
}
}
this.threadPoolExecutor.execute(new MessageReceivingTask(this.receiver, message));
return true;
try {
this.threadPoolExecutor.execute(new HandlerTask(this.handler, message));
return null;
}
catch (RejectedExecutionException e) {
throw new MessageHandlerRejectedExecutionException(
"handler executor rejected message", e);
}
}
/**
@@ -153,17 +160,17 @@ public class MessageReceivingExecutor implements Lifecycle {
}
private static class MessageReceivingTask implements Runnable {
private static class HandlerTask implements Runnable {
private MessageReceiver receiver;
private MessageHandler handler;
private Message<?> message;
private Throwable error;
MessageReceivingTask(MessageReceiver receiver, Message<?> message) {
this.receiver = receiver;
HandlerTask(MessageHandler handler, Message<?> message) {
this.handler = handler;
this.message = message;
}
@@ -173,7 +180,7 @@ public class MessageReceivingExecutor implements Lifecycle {
public void run() {
try {
this.receiver.messageReceived(this.message);
this.handler.handle(this.message);
}
catch (Throwable t) {
this.error = t;
@@ -182,18 +189,18 @@ public class MessageReceivingExecutor implements Lifecycle {
}
private class MessageReceivingThreadPoolExecutor extends ThreadPoolExecutor {
private class MessageHandlerThreadPoolExecutor extends ThreadPoolExecutor {
public MessageReceivingThreadPoolExecutor(int corePoolSize, int maximumPoolSize) {
public MessageHandlerThreadPoolExecutor(int corePoolSize, int maximumPoolSize) {
super(corePoolSize, maximumPoolSize, 0, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>());
CustomizableThreadFactory threadFactory = new CustomizableThreadFactory();
threadFactory.setThreadNamePrefix("endpoint-executor-");
threadFactory.setThreadNamePrefix("handler-pool-");
this.setThreadFactory(threadFactory);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
MessageReceivingTask task = (MessageReceivingTask) r;
HandlerTask task = (HandlerTask) r;
if (task.getError() != null) {
if (logger.isWarnEnabled()) {
logger.warn("Exception occurred during task execution", task.getError());

View File

@@ -38,7 +38,7 @@ import org.springframework.integration.message.Message;
*
* @author Mark Fisher
*/
public class GenericMessageEndpoint<T> implements MessageEndpoint<T>, BeanNameAware {
public class GenericMessageEndpoint implements MessageEndpoint, BeanNameAware {
private String name;
@@ -113,7 +113,7 @@ public class GenericMessageEndpoint<T> implements MessageEndpoint<T>, BeanNameAw
}
public void messageReceived(Message<T> message) {
public Message handle(Message<?> message) {
if (this.handler == null) {
if (this.defaultOutputChannelName == null) {
throw new MessagingConfigurationException(
@@ -121,7 +121,7 @@ public class GenericMessageEndpoint<T> implements MessageEndpoint<T>, BeanNameAw
}
MessageChannel replyChannel = this.channelRegistry.lookupChannel(this.defaultOutputChannelName);
replyChannel.send(message);
return;
return null;
}
Message<?> replyMessage = handler.handle(message);
if (replyMessage != null) {
@@ -133,6 +133,7 @@ public class GenericMessageEndpoint<T> implements MessageEndpoint<T>, BeanNameAw
}
replyChannel.send(replyMessage);
}
return null;
}
private MessageChannel resolveReplyChannel(Message<?> message) {

View File

@@ -18,14 +18,16 @@ package org.springframework.integration.endpoint;
import org.springframework.integration.bus.ConsumerPolicy;
import org.springframework.integration.channel.ChannelRegistry;
import org.springframework.integration.message.MessageReceiver;
import org.springframework.integration.handler.MessageHandler;
/**
* Base interface for message endpoints.
*
* @author Mark Fisher
*/
public interface MessageEndpoint<T> extends MessageReceiver<T> {
public interface MessageEndpoint extends MessageHandler {
void setName(String name);
void setInputChannelName(String inputChannelName);