Now enforcing that a message endpoint must contain a handler method (INT-177).

This commit is contained in:
Mark Fisher
2008-04-12 16:02:20 +00:00
parent 3ef001545f
commit 18b096f69f
8 changed files with 79 additions and 54 deletions

View File

@@ -58,7 +58,6 @@ import org.springframework.integration.handler.MessageHandler;
import org.springframework.integration.handler.MessageHandlerChain;
import org.springframework.integration.handler.config.DefaultMessageHandlerCreator;
import org.springframework.integration.handler.config.MessageHandlerCreator;
import org.springframework.integration.message.Message;
import org.springframework.integration.router.AggregatingMessageHandler;
import org.springframework.integration.router.CompletionStrategyAdapter;
import org.springframework.integration.router.config.AggregatorMessageHandlerCreator;
@@ -120,6 +119,9 @@ public class MessageEndpointAnnotationPostProcessor implements BeanPostProcessor
}
String defaultOutputChannelName = endpointAnnotation.defaultOutput();
MessageHandlerChain handlerChain = this.createHandlerChain(bean, defaultOutputChannelName);
if (handlerChain == null) {
throw new ConfigurationException("@MessageEndpoint has no handler method");
}
DefaultMessageEndpoint endpoint = new DefaultMessageEndpoint(handlerChain);
this.configureInput(bean, beanName, endpointAnnotation, endpoint);
if (StringUtils.hasText(defaultOutputChannelName)) {
@@ -136,13 +138,6 @@ public class MessageEndpointAnnotationPostProcessor implements BeanPostProcessor
concurrencyPolicy.setQueueCapacity(concurrencyAnnotation.queueCapacity());
endpoint.setConcurrencyPolicy(concurrencyPolicy);
}
if (endpoint.getHandler() == null) {
endpoint.setHandler(new MessageHandler() {
public Message<?> handle(Message<?> message) {
return message;
}
});
}
this.configureCompletionStrategy(bean, endpoint);
this.messageBus.registerEndpoint(beanName + "-endpoint", endpoint);
return bean;

View File

@@ -238,6 +238,9 @@ public class DefaultMessageEndpoint implements MessageEndpoint, ChannelRegistryA
}
public final Message<?> handle(Message<?> message) {
if (this.handler == null) {
throw new ConfigurationException("endpoint has no 'handler'");
}
if (logger.isDebugEnabled()) {
logger.debug("endpoint '" + this + "' handling message: " + message);
}
@@ -249,21 +252,6 @@ public class DefaultMessageEndpoint implements MessageEndpoint, ChannelRegistryA
throw new MessageSelectorRejectedException(message);
}
}
if (this.handler == null) {
if (this.defaultOutputChannelName == null) {
throw new ConfigurationException(
"endpoint must have either a 'handler' or 'defaultOutputChannelName'");
}
MessageChannel outputChannel = this.channelRegistry.lookupChannel(this.defaultOutputChannelName);
if (logger.isDebugEnabled()) {
logger.debug("endpoint '" + this + "' sending to output channel '" + outputChannel + "', message: " + message);
}
if (!outputChannel.send(message, this.replyTimeout)) {
this.errorHandler.handle(new MessageDeliveryException(message,
"unable to send output message within alloted timeout of " + replyTimeout + " milliseconds"));
}
return null;
}
try {
Message<?> replyMessage = this.handler.handle(message);
if (replyMessage != null) {
@@ -317,8 +305,7 @@ public class DefaultMessageEndpoint implements MessageEndpoint, ChannelRegistryA
MessageChannel replyChannel = resolveReplyChannel(originalMessageHeader);
if (replyChannel == null) {
throw new MessageHandlingException(replyMessage,
"Unable to determine reply channel for message. " +
"Provide a 'replyChannel' or 'replyChannelName' in the message header " +
"Unable to determine reply channel for message. Provide a 'returnAddress' in the message header " +
"or a 'defaultOutputChannelName' on the message endpoint.");
}
if (logger.isDebugEnabled()) {

View File

@@ -8,9 +8,7 @@
<property name="autoStartup" value="false"/>
</bean>
<bean id="inputChannel" class="org.springframework.integration.channel.SimpleChannel"/>
<bean id="outputChannel" class="org.springframework.integration.channel.SimpleChannel"/>
<bean id="channel" class="org.springframework.integration.channel.SimpleChannel"/>
<bean id="sourceAdapter" class="org.springframework.integration.adapter.PollingSourceAdapter">
<constructor-arg>
@@ -21,7 +19,7 @@
<property name="method" value="foo"/>
</bean>
</constructor-arg>
<property name="channel" ref="inputChannel"/>
<property name="channel" ref="channel"/>
</bean>
<bean id="targetAdapter" class="org.springframework.integration.adapter.DefaultTargetAdapter">
@@ -37,20 +35,11 @@
<constructor-arg ref="targetAdapter"/>
<property name="subscription">
<bean class="org.springframework.integration.scheduling.Subscription">
<constructor-arg ref="outputChannel"/>
<constructor-arg ref="channel"/>
</bean>
</property>
</bean>
<bean id="sink" class="org.springframework.integration.adapter.TestSink"/>
<bean class="org.springframework.integration.endpoint.DefaultMessageEndpoint">
<property name="subscription">
<bean class="org.springframework.integration.scheduling.Subscription">
<constructor-arg ref="inputChannel"/>
</bean>
</property>
<property name="defaultOutputChannelName" value="outputChannel"/>
</bean>
</beans>

View File

@@ -9,15 +9,11 @@
<si:message-bus auto-startup="false"/>
<si:channel id="inputChannel"/>
<si:channel id="channel"/>
<si:channel id="outputChannel"/>
<si:source-adapter ref="source" method="foo" channel="channel" period="100"/>
<si:source-adapter ref="source" method="foo" channel="inputChannel" period="100"/>
<si:target-adapter ref="sink" method="store" channel="outputChannel"/>
<si:endpoint input-channel="inputChannel" default-output-channel="outputChannel"/>
<si:target-adapter ref="sink" method="store" channel="channel"/>
<bean id="source" class="org.springframework.integration.adapter.TestSource"/>

View File

@@ -11,6 +11,7 @@
<bean id="targetChannel" class="org.springframework.integration.channel.SimpleChannel"/>
<bean id="endpoint" class="org.springframework.integration.endpoint.DefaultMessageEndpoint">
<constructor-arg ref="handler"/>
<property name="subscription">
<bean class="org.springframework.integration.scheduling.Subscription">
<constructor-arg ref="sourceChannel"/>
@@ -18,5 +19,7 @@
</property>
<property name="defaultOutputChannelName" value="targetChannel"/>
</bean>
<bean id="handler" class="org.springframework.integration.handler.TestHandlers" factory-method="echoHandler"/>
</beans>

View File

@@ -28,6 +28,7 @@ import org.junit.Test;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.ConfigurationException;
import org.springframework.integration.annotation.Concurrency;
import org.springframework.integration.annotation.DefaultOutput;
import org.springframework.integration.annotation.Handler;
@@ -93,6 +94,7 @@ public class MessageEndpointAnnotationPostProcessorTests {
messageBus.registerChannel("testChannel", testChannel);
MessageEndpointAnnotationPostProcessor postProcessor =
new MessageEndpointAnnotationPostProcessor(messageBus);
postProcessor.afterPropertiesSet();
PolledAnnotationTestBean testBean = new PolledAnnotationTestBean();
postProcessor.postProcessAfterInitialization(testBean, "testBean");
messageBus.start();
@@ -108,6 +110,7 @@ public class MessageEndpointAnnotationPostProcessorTests {
messageBus.registerChannel("testChannel", testChannel);
MessageEndpointAnnotationPostProcessor postProcessor =
new MessageEndpointAnnotationPostProcessor(messageBus);
postProcessor.afterPropertiesSet();
CountDownLatch latch = new CountDownLatch(1);
DefaultOutputAnnotationTestBean testBean = new DefaultOutputAnnotationTestBean(latch);
postProcessor.postProcessAfterInitialization(testBean, "testBean");
@@ -124,6 +127,7 @@ public class MessageEndpointAnnotationPostProcessorTests {
MessageBus messageBus = new MessageBus();
MessageEndpointAnnotationPostProcessor postProcessor =
new MessageEndpointAnnotationPostProcessor(messageBus);
postProcessor.afterPropertiesSet();
ConcurrencyAnnotationTestBean testBean = new ConcurrencyAnnotationTestBean();
postProcessor.postProcessAfterInitialization(testBean, "testBean");
DefaultMessageEndpoint endpoint = (DefaultMessageEndpoint) messageBus.lookupEndpoint("testBean-endpoint");
@@ -144,6 +148,7 @@ public class MessageEndpointAnnotationPostProcessorTests {
MessageBus messageBus = new MessageBus();
MessageEndpointAnnotationPostProcessor postProcessor =
new MessageEndpointAnnotationPostProcessor(messageBus);
postProcessor.afterPropertiesSet();
ChannelRegistryAwareTestBean testBean = new ChannelRegistryAwareTestBean();
assertNull(testBean.getChannelRegistry());
postProcessor.postProcessAfterInitialization(testBean, "testBean");
@@ -285,6 +290,18 @@ public class MessageEndpointAnnotationPostProcessorTests {
assertNull(output.receive(500));
}
@Test(expected=ConfigurationException.class)
public void testEndpointWithNoHandlerMethod() {
MessageBus messageBus = new MessageBus();
SimpleChannel testChannel = new SimpleChannel();
messageBus.registerChannel("testChannel", testChannel);
MessageEndpointAnnotationPostProcessor postProcessor =
new MessageEndpointAnnotationPostProcessor(messageBus);
postProcessor.afterPropertiesSet();
AnnotatedEndpointWithNoHandlerMethod endpoint = new AnnotatedEndpointWithNoHandlerMethod();
postProcessor.postProcessAfterInitialization(endpoint, "endpoint");
}
@MessageEndpoint(defaultOutput="testChannel")
private static class PolledAnnotationTestBean {
@@ -293,6 +310,11 @@ public class MessageEndpointAnnotationPostProcessorTests {
public String poller() {
return "test";
}
@Handler
public Message<?> handle(Message<?> message) {
return message;
}
}
@@ -312,6 +334,11 @@ public class MessageEndpointAnnotationPostProcessorTests {
return this.messageText;
}
@Handler
public Message<?> handle(Message<?> message) {
return message;
}
@DefaultOutput
public void countdown(String input) {
this.messageText = input;
@@ -323,6 +350,11 @@ public class MessageEndpointAnnotationPostProcessorTests {
@MessageEndpoint(input="inputChannel")
@Concurrency(coreSize=17, maxSize=42, keepAliveSeconds=123, queueCapacity=11)
private static class ConcurrencyAnnotationTestBean {
@Handler
public Message<?> handle(Message<?> message) {
return null;
}
}
@@ -338,6 +370,11 @@ public class MessageEndpointAnnotationPostProcessorTests {
public ChannelRegistry getChannelRegistry() {
return this.channelRegistry;
}
@Handler
public Message<?> handle(Message<?> message) {
return null;
}
}
@@ -369,4 +406,9 @@ public class MessageEndpointAnnotationPostProcessorTests {
}
}
@MessageEndpoint(input="testChannel")
private static class AnnotatedEndpointWithNoHandlerMethod {
}
}

View File

@@ -40,6 +40,17 @@ public abstract class TestHandlers {
};
}
/**
* Create a {@link MessageHandler} that simply returns the {@link Message} it receives.
*/
public final static MessageHandler echoHandler() {
return new MessageHandler() {
public Message<?> handle(Message<?> message) {
return message;
}
};
}
/**
* Create a {@link MessageHandler} that throws a {@link MessageHandlerRejectedExecutionException}.
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* 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.
@@ -20,6 +20,7 @@ import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Random;
import org.springframework.integration.annotation.Handler;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.Polled;
@@ -30,17 +31,18 @@ import org.springframework.integration.annotation.Polled;
public class QuotePublisher {
@Polled(period=300)
public Quote getQuote() {
BigDecimal price = new BigDecimal(new Random().nextDouble() * 100);
return new Quote(generateTicker(), price.setScale(2, RoundingMode.HALF_EVEN));
}
private String generateTicker() {
public String generateTicker() {
char[] chars = new char[3];
for (int i = 0; i < 3; i++) {
chars[i] = (char) (new Random().nextInt(25) + 65);
}
return new String(chars);
return new String(chars);
}
@Handler
public Quote getQuote(String ticker) {
BigDecimal price = new BigDecimal(new Random().nextDouble() * 100);
return new Quote(ticker, price.setScale(2, RoundingMode.HALF_EVEN));
}
}