From 45b6cc85caa232264741cf7651c2e65a0a3d5efa Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Sun, 16 Dec 2007 18:45:20 +0000 Subject: [PATCH] Added @Router annotation and handler adapter --- .../handler/annotation/Router.java | 50 ++++++ .../RouterAnnotationHandlerCreator.java | 41 +++++ .../router/RouterMessageHandlerAdapter.java | 153 ++++++++++++++++++ 3 files changed, 244 insertions(+) create mode 100644 spring-integration-core/src/main/java/org/springframework/integration/handler/annotation/Router.java create mode 100644 spring-integration-core/src/main/java/org/springframework/integration/handler/annotation/RouterAnnotationHandlerCreator.java create mode 100644 spring-integration-core/src/main/java/org/springframework/integration/router/RouterMessageHandlerAdapter.java diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/annotation/Router.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/annotation/Router.java new file mode 100644 index 0000000000..3167eee982 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/annotation/Router.java @@ -0,0 +1,50 @@ +/* + * 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.handler.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that a method is capable of resolving to a channel or channel name + * based on a message, message payload, message attribute, or message property. + * + * @author Mark Fisher + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface Router { + + /** + * String value representing the name of the property that should be passed + * into the router method. This and 'attribute' should not both be provided. + */ + String property() default ""; + + /** + * String value representing the name of the attribute that should be passed + * into the router method. This and 'property' should not both be provided. + */ + String attribute() default ""; + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/annotation/RouterAnnotationHandlerCreator.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/annotation/RouterAnnotationHandlerCreator.java new file mode 100644 index 0000000000..174266302b --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/annotation/RouterAnnotationHandlerCreator.java @@ -0,0 +1,41 @@ +/* + * 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.handler.annotation; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; + +import org.springframework.integration.MessagingConfigurationException; +import org.springframework.integration.handler.MessageHandler; +import org.springframework.integration.router.RouterMessageHandlerAdapter; + +/** + * A handler creator for methods annotation with {@link Router @Router}. + * + * @author Mark Fisher + */ +public class RouterAnnotationHandlerCreator extends AbstractAnnotationHandlerCreator { + + public MessageHandler doCreateHandler(Object object, Method method, Annotation annotation) { + if (!annotation.annotationType().equals(Router.class)) { + throw new MessagingConfigurationException( + "RouterAnnotationHandlerCreator only applies to the @Router annotation"); + } + return new RouterMessageHandlerAdapter(object, method, (Router) annotation); + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/router/RouterMessageHandlerAdapter.java b/spring-integration-core/src/main/java/org/springframework/integration/router/RouterMessageHandlerAdapter.java new file mode 100644 index 0000000000..5b73dbbc6b --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/router/RouterMessageHandlerAdapter.java @@ -0,0 +1,153 @@ +/* + * 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.router; + +import java.lang.reflect.Method; +import java.util.Collection; + +import org.springframework.integration.MessagingConfigurationException; +import org.springframework.integration.channel.ChannelMapping; +import org.springframework.integration.channel.ChannelMappingAware; +import org.springframework.integration.channel.MessageChannel; +import org.springframework.integration.endpoint.SimpleMethodInvoker; +import org.springframework.integration.handler.AbstractMessageHandlerAdapter; +import org.springframework.integration.handler.annotation.Router; +import org.springframework.integration.message.Message; +import org.springframework.util.StringUtils; + +/** + * MessageHandler adapter for methods annotated with {@link Router @Router}. + * + * @author Mark Fisher + */ +public class RouterMessageHandlerAdapter extends AbstractMessageHandlerAdapter implements ChannelMappingAware { + + private Router routerAnnotation; + + private Method method; + + private ChannelMapping channelMapping; + + + public RouterMessageHandlerAdapter(Object object, Method method, Router routerAnnotation) { + this.setObject(object); + this.method = method; + this.routerAnnotation = routerAnnotation; + } + + public void setChannelMapping(ChannelMapping channelMapping) { + this.channelMapping = channelMapping; + } + + @Override + protected Object doHandle(Message message, SimpleMethodInvoker invoker) { + if (method.getParameterTypes().length != 1) { + throw new MessagingConfigurationException( + "method must accept exactly one parameter"); + } + String propertyName = routerAnnotation.property(); + String attributeName = routerAnnotation.attribute(); + Object retval = null; + if (StringUtils.hasText(propertyName)) { + if (StringUtils.hasText(attributeName)) { + throw new MessagingConfigurationException( + "cannot accept both 'property' and 'attribute'"); + } + String property = message.getHeader().getProperty(propertyName); + retval = this.invokeMethod(invoker, property); + } + else if (StringUtils.hasText(attributeName)) { + Object attribute = message.getHeader().getAttribute(attributeName); + retval = this.invokeMethod(invoker, attribute); + } + else { + Class type = method.getParameterTypes()[0]; + if (type.equals(Message.class)) { + retval = this.invokeMethod(invoker, message); + } + retval = this.invokeMethod(invoker, message.getPayload()); + } + if (retval != null) { + if (retval instanceof Collection) { + Collection channels = (Collection) retval; + for (Object channel : channels) { + if (channel instanceof MessageChannel) { + this.sendMessage(message, (MessageChannel) channel); + } + else if (channel instanceof String) { + this.sendMessage(message, (String) channel); + } + else { + throw new MessagingConfigurationException( + "router method must return type 'MessageChannel' or 'String'"); + } + } + } + else if (retval instanceof MessageChannel[]) { + for (MessageChannel channel : (MessageChannel[]) retval) { + this.sendMessage(message, channel); + } + } + else if (retval instanceof String[]) { + for (String channelName : (String[]) retval) { + this.sendMessage(message, channelName); + } + } + else if (retval instanceof MessageChannel) { + this.sendMessage(message, (MessageChannel) retval); + } + else if (retval instanceof String) { + this.sendMessage(message, (String) retval); + } + else { + throw new MessagingConfigurationException( + "router method must return type 'MessageChannel' or 'String'"); + } + } + return null; + } + + private Object invokeMethod(SimpleMethodInvoker invoker, Object parameter) { + if (this.logger.isDebugEnabled()) { + logger.debug("invoking method '" + method.getName() + "' with parameter of type '" + + parameter.getClass().getName() + "'"); + } + return invoker.invokeMethod(parameter); + } + + private boolean sendMessage(Message message, String channelName) { + MessageChannel channel = this.channelMapping.getChannel(channelName); + if (channel == null) { + if (logger.isWarnEnabled()) { + logger.warn("unable to resolve channel for name '" + channelName + "'"); + } + return false; + } + if (logger.isDebugEnabled()) { + logger.debug("sending message to channel '" + channelName + "'"); + } + return this.sendMessage(message, channel); + } + + private boolean sendMessage(Message message, MessageChannel channel) { + if (logger.isDebugEnabled()) { + logger.debug("sending message to channel '" + channel + "'"); + } + return channel.send(message); + } + +}