diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/RouterFactoryBean.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/RouterFactoryBean.java new file mode 100644 index 0000000000..205ca179b6 --- /dev/null +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/RouterFactoryBean.java @@ -0,0 +1,73 @@ +/* + * 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.config; + +import org.springframework.integration.channel.ChannelResolver; +import org.springframework.integration.core.MessageChannel; +import org.springframework.integration.message.MessageConsumer; +import org.springframework.integration.router.AbstractMessageRouter; +import org.springframework.integration.router.MethodInvokingRouter; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Factory bean for creating a Message Router. + * + * @author Mark Fisher + */ +public class RouterFactoryBean extends AbstractConsumerFactoryBean { + + private volatile ChannelResolver channelResolver; + + private volatile MessageChannel defaultOutputChannel; + + + public void setChannelResolver(ChannelResolver channelResolver) { + this.channelResolver = channelResolver; + } + + public void setDefaultOutputChannel(MessageChannel defaultOutputChannel) { + this.defaultOutputChannel = defaultOutputChannel; + } + + @Override + protected MessageConsumer createConsumer(Object targetObject, String targetMethodName) { + Assert.notNull(targetObject, "target object must not be null"); + AbstractMessageRouter router = this.createRouter(targetObject, targetMethodName); + if (this.defaultOutputChannel != null) { + router.setDefaultOutputChannel(this.defaultOutputChannel); + } + return router; + } + + private AbstractMessageRouter createRouter(Object targetObject, String targetMethodName) { + if (targetObject instanceof AbstractMessageRouter) { + Assert.isTrue(!StringUtils.hasText(targetMethodName), + "target method should not be provided when the target " + + "object is an implementation of AbstractMessageRouter"); + return (AbstractMessageRouter) targetObject; + } + MethodInvokingRouter router = (StringUtils.hasText(targetMethodName)) + ? new MethodInvokingRouter(targetObject, targetMethodName) + : new MethodInvokingRouter(targetObject); + if (this.channelResolver != null) { + router.setChannelResolver(this.channelResolver); + } + return router; + } + +} diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/RouterParser.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/RouterParser.java index dda917d424..02d1145f89 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/RouterParser.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/xml/RouterParser.java @@ -22,7 +22,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.channel.BeanFactoryChannelResolver; -import org.springframework.integration.router.MethodInvokingRouter; +import org.springframework.integration.config.RouterFactoryBean; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -37,11 +37,11 @@ public class RouterParser extends AbstractConsumerEndpointParser { protected BeanDefinitionBuilder parseConsumer(Element element, ParserContext parserContext) { String ref = element.getAttribute(REF_ATTRIBUTE); Assert.hasText(ref, "The '" + REF_ATTRIBUTE + "' attribute is required."); - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MethodInvokingRouter.class); - builder.addConstructorArgReference(ref); + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(RouterFactoryBean.class); + builder.addPropertyReference("targetObject", ref); if (StringUtils.hasText(element.getAttribute(METHOD_ATTRIBUTE))) { String method = element.getAttribute(METHOD_ATTRIBUTE); - builder.addConstructorArgValue(method); + builder.addPropertyValue("targetMethodName", method); } BeanDefinitionBuilder resolverBuilder = BeanDefinitionBuilder.genericBeanDefinition(BeanFactoryChannelResolver.class); String resolverBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName( diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/router/MethodInvokingRouter.java b/org.springframework.integration/src/main/java/org/springframework/integration/router/MethodInvokingRouter.java index 03b77b92c6..b03d0647ec 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/router/MethodInvokingRouter.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/router/MethodInvokingRouter.java @@ -23,11 +23,14 @@ import java.util.Collection; import java.util.List; import org.springframework.beans.factory.InitializingBean; +import org.springframework.integration.annotation.Router; import org.springframework.integration.channel.ChannelResolver; import org.springframework.integration.core.Message; import org.springframework.integration.core.MessageChannel; import org.springframework.integration.core.MessagingException; import org.springframework.integration.message.MessageMappingMethodInvoker; +import org.springframework.integration.util.DefaultMethodResolver; +import org.springframework.integration.util.MethodResolver; import org.springframework.util.Assert; /** @@ -41,6 +44,8 @@ import org.springframework.util.Assert; */ public class MethodInvokingRouter extends AbstractMessageRouter implements InitializingBean { + private final MethodResolver methodResolver = new DefaultMethodResolver(Router.class); + private final MessageMappingMethodInvoker invoker; private volatile ChannelResolver channelResolver; @@ -54,6 +59,14 @@ public class MethodInvokingRouter extends AbstractMessageRouter implements Initi this.invoker = new MessageMappingMethodInvoker(object, methodName); } + public MethodInvokingRouter(Object object) { + Assert.notNull(object, "object must not be null"); + Method method = this.methodResolver.findMethod(object); + Assert.notNull(method, "unable to resolve Router method on target class [" + + object.getClass() + "]"); + this.invoker = new MessageMappingMethodInvoker(object, method); + } + /** * Provide the {@link ChannelResolver} strategy to use for methods that diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/router/config/RouterParserTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/router/config/RouterParserTests.java index 846fb0b006..ab31cc56c1 100644 --- a/org.springframework.integration/src/test/java/org/springframework/integration/router/config/RouterParserTests.java +++ b/org.springframework.integration/src/test/java/org/springframework/integration/router/config/RouterParserTests.java @@ -17,15 +17,21 @@ package org.springframework.integration.router.config; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import java.util.Collection; +import java.util.Collections; + import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.annotation.Router; import org.springframework.integration.channel.PollableChannel; import org.springframework.integration.core.Message; import org.springframework.integration.core.MessageChannel; import org.springframework.integration.message.StringMessage; +import org.springframework.integration.router.AbstractMessageRouter; /** * @author Mark Fisher @@ -66,4 +72,60 @@ public class RouterParserTests { assertEquals("99", result.getPayload()); } + @Test + public void refOnlyForAbstractMessageRouterImplementation() { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( + "routerParserTests.xml", this.getClass()); + context.start(); + MessageChannel input = (MessageChannel) context.getBean("inputForAbstractMessageRouterImplementation"); + PollableChannel output = (PollableChannel) context.getBean("output3"); + input.send(new StringMessage("test-implementation")); + Message result = output.receive(0); + assertNotNull(result); + assertEquals("test-implementation", result.getPayload()); + } + + @Test + public void refOnlyForAnnotatedObject() { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( + "routerParserTests.xml", this.getClass()); + context.start(); + MessageChannel input = (MessageChannel) context.getBean("inputForAnnotatedRouter"); + PollableChannel output = (PollableChannel) context.getBean("output4"); + input.send(new StringMessage("test-annotation")); + Message result = output.receive(0); + assertNotNull(result); + assertEquals("test-annotation", result.getPayload()); + } + + + public static class TestRouterImplementation extends AbstractMessageRouter { + + private final MessageChannel channel; + + public TestRouterImplementation(MessageChannel channel) { + this.channel = channel; + } + + @Override + protected Collection determineTargetChannels(Message message) { + return Collections.singletonList(this.channel); + } + } + + + public static class AnnotatedTestRouterBean { + + private final MessageChannel channel; + + public AnnotatedTestRouterBean(MessageChannel channel) { + this.channel = channel; + } + + @Router + public MessageChannel test(String payload) { + return this.channel; + } + } + } diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/router/config/routerParserTests.xml b/org.springframework.integration/src/test/java/org/springframework/integration/router/config/routerParserTests.xml index 8d42c4566c..ef6aa6788a 100644 --- a/org.springframework.integration/src/test/java/org/springframework/integration/router/config/routerParserTests.xml +++ b/org.springframework.integration/src/test/java/org/springframework/integration/router/config/routerParserTests.xml @@ -33,4 +33,28 @@ + + + + + + + + + + + + + + + + + + + + + + + +