diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/HandlerParser.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/HandlerParser.java index 5e1d3131a9..156972f32a 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/HandlerParser.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/HandlerParser.java @@ -29,7 +29,7 @@ import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.integration.handler.DefaultMessageHandlerAdapter; +import org.springframework.integration.handler.DefaultMessageHandler; import org.springframework.integration.handler.MessageHandlerChain; import org.springframework.util.StringUtils; @@ -81,6 +81,7 @@ public class HandlerParser implements BeanDefinitionParser { return beanDefinition; } + @SuppressWarnings("unchecked") private BeanDefinition parseHandler(Element element, ParserContext parserContext, ManagedList handlers) { boolean isInnerHandler = (handlers != null); String ref = element.getAttribute("ref"); @@ -113,15 +114,15 @@ public class HandlerParser implements BeanDefinitionParser { } private BeanDefinitionHolder parseHandlerAdapter(String id, String handlerRef, String handlerMethod, ParserContext parserContext, boolean isInnerHandler) { - BeanDefinition handlerAdapterDef = new RootBeanDefinition(DefaultMessageHandlerAdapter.class); - handlerAdapterDef.getPropertyValues().addPropertyValue(OBJECT_PROPERTY, new RuntimeBeanReference(handlerRef)); - handlerAdapterDef.getPropertyValues().addPropertyValue(METHOD_NAME_PROPERTY, handlerMethod); - String adapterBeanName = (StringUtils.hasText(id)) ? id : - BeanDefinitionReaderUtils.generateBeanName(handlerAdapterDef, parserContext.getRegistry(), isInnerHandler); + BeanDefinition handlerDef = new RootBeanDefinition(DefaultMessageHandler.class); + handlerDef.getPropertyValues().addPropertyValue(OBJECT_PROPERTY, new RuntimeBeanReference(handlerRef)); + handlerDef.getPropertyValues().addPropertyValue(METHOD_NAME_PROPERTY, handlerMethod); + String handlerBeanName = (StringUtils.hasText(id)) ? id : + BeanDefinitionReaderUtils.generateBeanName(handlerDef, parserContext.getRegistry(), isInnerHandler); if (!isInnerHandler) { - parserContext.registerBeanComponent(new BeanComponentDefinition(handlerAdapterDef, adapterBeanName)); + parserContext.registerBeanComponent(new BeanComponentDefinition(handlerDef, handlerBeanName)); } - return new BeanDefinitionHolder(handlerAdapterDef, adapterBeanName); + return new BeanDefinitionHolder(handlerDef, handlerBeanName); } } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/handler/AbstractMessageHandler.java b/org.springframework.integration/src/main/java/org/springframework/integration/handler/AbstractMessageHandler.java index 48ffb48cb5..8acba71dc3 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/handler/AbstractMessageHandler.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/handler/AbstractMessageHandler.java @@ -20,22 +20,17 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; -import java.util.Map; -import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; -import org.springframework.core.GenericTypeResolver; -import org.springframework.core.LocalVariableTableParameterNameDiscoverer; -import org.springframework.core.MethodParameter; -import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.core.Ordered; import org.springframework.integration.ConfigurationException; -import org.springframework.integration.handler.annotation.Header; +import org.springframework.integration.handler.annotation.MethodArgumentMessageMapper; import org.springframework.integration.message.Message; import org.springframework.integration.message.MessageHandlingException; -import org.springframework.integration.message.MessageHeaders; +import org.springframework.integration.message.MessageMapper; import org.springframework.integration.util.DefaultMethodInvoker; import org.springframework.integration.util.MethodInvoker; import org.springframework.integration.util.NameResolvingMethodInvoker; @@ -54,9 +49,9 @@ import org.springframework.util.StringUtils; * * @author Mark Fisher */ -public abstract class AbstractMessageHandler implements MessageHandler, InitializingBean { +public abstract class AbstractMessageHandler implements MessageHandler, Ordered, InitializingBean { - private static final Log logger = LogFactory.getLog(AbstractMessageHandler.class); + protected static final Log logger = LogFactory.getLog(AbstractMessageHandler.class); private volatile boolean methodExpectsMessage; @@ -66,11 +61,11 @@ public abstract class AbstractMessageHandler implements MessageHandler, Initiali private volatile String methodName; + private volatile MessageMapper methodArgumentMapper; + private volatile MethodInvoker invoker; - private volatile MethodParameterMetadata[] parameterMetadata; - - private final ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); + private volatile int order = Ordered.LOWEST_PRECEDENCE; private volatile boolean initialized; @@ -99,10 +94,11 @@ public abstract class AbstractMessageHandler implements MessageHandler, Initiali public void setMethod(Method method) { Assert.notNull(method, "method must not be null"); - if (method.getParameterTypes().length == 0) { + Class[] parameterTypes = method.getParameterTypes(); + if (parameterTypes.length == 0) { throw new ConfigurationException("method must accept at least one parameter"); } - if (method.getParameterTypes()[0].equals(Message.class)) { + if (parameterTypes.length == 1 && Message.class.isAssignableFrom(parameterTypes[0])) { this.methodExpectsMessage = true; } this.method = method; @@ -117,6 +113,14 @@ public abstract class AbstractMessageHandler implements MessageHandler, Initiali this.methodName = methodName; } + public void setOrder(int order) { + this.order = order; + } + + public int getOrder() { + return order; + } + public void afterPropertiesSet() { synchronized (this.initializationMonitor) { if (this.initialized) { @@ -145,42 +149,17 @@ public abstract class AbstractMessageHandler implements MessageHandler, Initiali } if (this.method != null) { this.invoker = new DefaultMethodInvoker(this.object, this.method); + this.methodArgumentMapper = new MethodArgumentMessageMapper(this.method); } else { // TODO: resolve the candidate method and/or create a dynamic resolver this.invoker = new NameResolvingMethodInvoker(this.object, this.methodName); } - this.configureParameterMetadata(); } this.initialized = true; } } - private void configureParameterMetadata() { - if (this.method == null) { - return; - } - Class[] paramTypes = this.method.getParameterTypes(); - this.parameterMetadata = new MethodParameterMetadata[paramTypes.length]; - for (int i = 0; i < parameterMetadata.length; i++) { - MethodParameter methodParam = new MethodParameter(this.method, i); - methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer); - GenericTypeResolver.resolveParameterType(methodParam, this.method.getDeclaringClass()); - Object[] paramAnnotations = methodParam.getParameterAnnotations(); - String headerName = null; - for (int j = 0; j < paramAnnotations.length; j++) { - if (Header.class.isInstance(paramAnnotations[j])) { - Header headerAnnotation = (Header) paramAnnotations[j]; - headerName = this.resolveParameterNameIfNecessary(headerAnnotation.value(), methodParam); - parameterMetadata[i] = new MethodParameterMetadata(Header.class, headerName, headerAnnotation.required()); - } - } - if (headerName == null) { - parameterMetadata[i] = new MethodParameterMetadata(methodParam.getParameterType(), null, false); - } - } - } - public Message handle(Message requestMessage) { if (requestMessage == null || requestMessage.getPayload() == null) { if (logger.isDebugEnabled()) { @@ -196,11 +175,25 @@ public abstract class AbstractMessageHandler implements MessageHandler, Initiali if (result == null) { return null; } + if (result instanceof Message) { + return this.postProcessReplyMessage((Message) result, requestMessage); + } return this.createReplyMessage(result, requestMessage); } /** - * Subclasses must implement this method to generate the reply Message. + * Subclasses must implement this method to process a return value that + * is already a Message instance. + * + * @param replyMessage the Message returned from an adapter method + * @param requestMessage the original request Message + * @return the Message to be sent to the reply MessageTarget + */ + protected abstract Message postProcessReplyMessage(Message replyMessage, Message requestMessage); + + /** + * Subclasses must implement this method to generate the reply Message when + * the return value is not a Message instance. * * @param result the return value from an adapter method, or the Message payload if not acting as an adapter * @param requestMessage the original request Message @@ -210,9 +203,6 @@ public abstract class AbstractMessageHandler implements MessageHandler, Initiali private Object invokeHandlerMethod(Message message) { - if (this.invoker == null) { - throw new IllegalStateException("cannot invoke method, invoker is null"); - } Object args[] = null; Object mappingResult = this.methodExpectsMessage ? message : this.mapMessageToMethodArguments(message); @@ -253,86 +243,11 @@ public abstract class AbstractMessageHandler implements MessageHandler, Initiali } } - - private Object[] mapMessageToMethodArguments(Message message) { - if (message == null) { - return null; + private Object[] mapMessageToMethodArguments(Message message) { + if (this.methodArgumentMapper != null) { + return this.methodArgumentMapper.mapMessage(message); } - if (message.getPayload() == null) { - throw new IllegalArgumentException("Message payload must not be null."); - } - if (ObjectUtils.isEmpty(this.parameterMetadata)) { - return new Object[] { message.getPayload() }; - } - Object[] args = new Object[this.parameterMetadata.length]; - for (int i = 0; i < this.parameterMetadata.length; i++) { - MethodParameterMetadata metadata = this.parameterMetadata[i]; - Class expectedType = metadata.type; - if (expectedType.equals(Header.class)) { - Object value = message.getHeaders().get(metadata.key); - if (value == null && metadata.required) { - throw new MessageHandlingException(message, - "required header '" + metadata.key + "' not available"); - } - args[i] = value; - } - else if (expectedType.isAssignableFrom(message.getClass())) { - args[i] = message; - } - else if (expectedType.isAssignableFrom(message.getPayload().getClass())) { - args[i] = message.getPayload(); - } - else if (expectedType.equals(Map.class)) { - args[i] = message.getHeaders(); - } - else if (expectedType.equals(Properties.class)) { - args[i] = this.getStringTypedHeaders(message); - } - else { - args[i] = message.getPayload(); - } - } - return args; - } - - private Properties getStringTypedHeaders(Message message) { - Properties properties = new Properties(); - MessageHeaders headers = message.getHeaders(); - for (String key : headers.keySet()) { - Object value = headers.get(key); - if (value instanceof String) { - properties.setProperty(key, (String) value); - } - } - return properties; - } - - private String resolveParameterNameIfNecessary(String paramName, MethodParameter methodParam) { - if (!StringUtils.hasText(paramName)) { - paramName = methodParam.getParameterName(); - if (paramName == null) { - throw new IllegalStateException("No parameter name specified and not available in class file."); - } - } - return paramName; - } - - - private static class MethodParameterMetadata { - - private final Class type; - - private final String key; - - private final boolean required; - - - MethodParameterMetadata(Class type, String key, boolean required) { - this.type = type; - this.key = key; - this.required = required; - } - + return new Object[] { message.getPayload() }; } } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/handler/DefaultMessageHandler.java b/org.springframework.integration/src/main/java/org/springframework/integration/handler/DefaultMessageHandler.java index 3302845c03..e73ea47692 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/handler/DefaultMessageHandler.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/handler/DefaultMessageHandler.java @@ -18,6 +18,7 @@ package org.springframework.integration.handler; import org.springframework.integration.message.Message; import org.springframework.integration.message.MessageBuilder; +import org.springframework.integration.message.MessageHeaders; /** * The default MessageHandler implementation. Creates a Message for the reply payload. @@ -30,9 +31,17 @@ public class DefaultMessageHandler extends AbstractMessageHandler { @Override protected Message createReplyMessage(Object result, Message requestMessage) { - return MessageBuilder.fromPayload(result) - .copyHeaders(requestMessage.getHeaders()) - .setCorrelationId(requestMessage.getHeaders().getId()) + return this.prepareReplyMessage(MessageBuilder.fromPayload(result), requestMessage.getHeaders()); + } + + @Override + protected Message postProcessReplyMessage(Message replyMessage, Message requestMessage) { + return this.prepareReplyMessage(MessageBuilder.fromMessage(replyMessage), requestMessage.getHeaders()); + } + + private Message prepareReplyMessage(MessageBuilder builder, MessageHeaders requestHeaders) { + return builder.copyHeadersIfAbsent(requestHeaders) + .setHeaderIfAbsent(MessageHeaders.CORRELATION_ID, requestHeaders.getId()) .build(); } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/handler/DefaultMessageHandlerAdapter.java b/org.springframework.integration/src/main/java/org/springframework/integration/handler/DefaultMessageHandlerAdapter.java deleted file mode 100644 index 10fb1b1826..0000000000 --- a/org.springframework.integration/src/main/java/org/springframework/integration/handler/DefaultMessageHandlerAdapter.java +++ /dev/null @@ -1,35 +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.MessageCreator; - -/** - * An implementation of {@link MessageHandler} that invokes the specified method and target object. - * It will use the provided implementation of the {@link MessageCreator} strategy interface to convert - * the method invocation's return value to a reply Message. - * - * @author Mark Fisher - */ -public class DefaultMessageHandlerAdapter extends AbstractMessageHandlerAdapter { - - protected Message handleReturnValue(Object returnValue, Message originalMessage) { - return this.createReplyMessage(returnValue, originalMessage); - } - -} diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/handler/MethodInvokingTarget.java b/org.springframework.integration/src/main/java/org/springframework/integration/handler/MethodInvokingTarget.java index 417958d483..c9bf46959e 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/handler/MethodInvokingTarget.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/handler/MethodInvokingTarget.java @@ -34,11 +34,21 @@ public class MethodInvokingTarget extends AbstractMessageHandler implements Mess @Override protected Message createReplyMessage(Object result, Message requestMessage) { - if (result != null) { - throw new MessagingException(requestMessage, "The target method returned a non-null Object. " + - "MethodInvokingTarget should only be used for methods that return no value (preferably void)."); - } + this.failIfNotNull(result, requestMessage); return null; } + @Override + protected Message postProcessReplyMessage(Message replyMessage, Message requestMessage) { + this.failIfNotNull(replyMessage, requestMessage); + return null; + } + + private void failIfNotNull(Object result, Message requestMessage) { + if (result != null) { + throw new MessagingException(requestMessage, "the MethodInvokingTarget method must have a void or null return, " + + "but '" + this + "' received a non-null value: [" + result + "]"); + } + } + } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/AnnotationMethodMessageMapper.java b/org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/MethodArgumentMessageMapper.java similarity index 58% rename from org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/AnnotationMethodMessageMapper.java rename to org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/MethodArgumentMessageMapper.java index b1a0bfb917..061d38ed37 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/AnnotationMethodMessageMapper.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/MethodArgumentMessageMapper.java @@ -29,80 +29,50 @@ import org.springframework.integration.message.MessageHandlingException; import org.springframework.integration.message.MessageHeaders; import org.springframework.integration.message.MessageMapper; import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** - * A {@link MessageMapper} implementation for annotated handler methods. - * Method parameters are matched against the Message payload as well as its - * headers. If a method parameter is annotated with {@link Header @Header}, - * the annotation's value will be used as a header name. If such an annotation - * contains no value, then the parameter name will be used as long as - * the information is available in the class file (requires compilation with - * debug settings for parameter names). If neither annotation is present, then - * the parameter will typically match the Message payload. However, if a Map or - * Properties object is expected, and the paylaod is not itself assignable to - * that type, then the MessageHeaders' values will be passed in the case of - * a Map-typed parameter, or the MessageHeaders' String-based values will be - * passed in the case of a Properties-typed parameter. + * Prepares arguments for handler methods. The method parameters are matched + * against the Message payload as well as its headers. If a method parameter + * is annotated with {@link Header @Header}, the annotation's value will be + * used as a header name. If such an annotation contains no value, then the + * parameter name will be used as long as the information is available in the + * class file (requires compilation with debug settings for parameter names). + * If the {@link Header @Header} annotation is not present, then the parameter + * will typically match the Message payload. However, if a Map or Properties + * object is expected, and the paylaod is not itself assignable to that type, + * then the MessageHeaders' values will be passed in the case of a Map-typed + * parameter, or the MessageHeaders' String-based values will be passed in the + * case of a Properties-typed parameter. * * @author Mark Fisher */ -public class AnnotationMethodMessageMapper implements MessageMapper { - - private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); +public class MethodArgumentMessageMapper implements MessageMapper { private final Method method; - private MethodParameterMetadata[] parameterMetadata; + private volatile MethodParameterMetadata[] parameterMetadata; - private volatile boolean initialized; - - private final Object initializationMonitor = new Object(); + private final ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); - public AnnotationMethodMessageMapper(Method method) { + public MethodArgumentMessageMapper(Method method) { Assert.notNull(method, "method must not be null"); this.method = method; + this.initializeParameterMetadata(); } - public void initialize() { - synchronized (this.initializationMonitor) { - if (this.initialized) { - return; - } - Class[] paramTypes = this.method.getParameterTypes(); - this.parameterMetadata = new MethodParameterMetadata[paramTypes.length]; - for (int i = 0; i < parameterMetadata.length; i++) { - MethodParameter methodParam = new MethodParameter(this.method, i); - methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer); - GenericTypeResolver.resolveParameterType(methodParam, this.method.getDeclaringClass()); - Object[] paramAnnotations = methodParam.getParameterAnnotations(); - String headerName = null; - for (int j = 0; j < paramAnnotations.length; j++) { - if (Header.class.isInstance(paramAnnotations[j])) { - Header headerAnnotation = (Header) paramAnnotations[j]; - headerName = this.resolveParameterNameIfNecessary(headerAnnotation.value(), methodParam); - parameterMetadata[i] = new MethodParameterMetadata(Header.class, headerName, headerAnnotation.required()); - } - } - if (headerName == null) { - parameterMetadata[i] = new MethodParameterMetadata(methodParam.getParameterType(), null, false); - } - } - this.initialized = true; - } - } - - public Object[] mapMessage(Message message) { + public Object[] mapMessage(Message message) { if (message == null) { return null; } if (message.getPayload() == null) { throw new IllegalArgumentException("Message payload must not be null."); } - if (!this.initialized) { - this.initialize(); + if (ObjectUtils.isEmpty(this.parameterMetadata)) { + return new Object[] { message.getPayload() }; } Object[] args = new Object[this.parameterMetadata.length]; for (int i = 0; i < this.parameterMetadata.length; i++) { @@ -135,6 +105,28 @@ public class AnnotationMethodMessageMapper implements MessageMapper { return args; } + private void initializeParameterMetadata() { + Class[] paramTypes = this.method.getParameterTypes(); + this.parameterMetadata = new MethodParameterMetadata[paramTypes.length]; + for (int i = 0; i < parameterMetadata.length; i++) { + MethodParameter methodParam = new MethodParameter(this.method, i); + methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer); + GenericTypeResolver.resolveParameterType(methodParam, this.method.getDeclaringClass()); + Object[] paramAnnotations = methodParam.getParameterAnnotations(); + String headerName = null; + for (int j = 0; j < paramAnnotations.length; j++) { + if (Header.class.isInstance(paramAnnotations[j])) { + Header headerAnnotation = (Header) paramAnnotations[j]; + headerName = this.resolveParameterNameIfNecessary(headerAnnotation.value(), methodParam); + parameterMetadata[i] = new MethodParameterMetadata(Header.class, headerName, headerAnnotation.required()); + } + } + if (headerName == null) { + parameterMetadata[i] = new MethodParameterMetadata(methodParam.getParameterType(), null, false); + } + } + } + private Properties getStringTypedHeaders(Message message) { Properties properties = new Properties(); MessageHeaders headers = message.getHeaders(); diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/handler/config/AbstractMessageHandlerCreator.java b/org.springframework.integration/src/main/java/org/springframework/integration/handler/config/AbstractMessageHandlerCreator.java index 02d0b5b701..3cdd3f5e34 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/handler/config/AbstractMessageHandlerCreator.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/handler/config/AbstractMessageHandlerCreator.java @@ -21,8 +21,8 @@ import java.util.Map; import org.springframework.beans.factory.InitializingBean; import org.springframework.integration.ConfigurationException; +import org.springframework.integration.handler.AbstractMessageHandler; import org.springframework.integration.handler.MessageHandler; -import org.springframework.integration.util.AbstractMethodInvokingAdapter; /** * Base class for handler creators that generate a {@link MessageHandler} @@ -34,10 +34,10 @@ public abstract class AbstractMessageHandlerCreator implements MessageHandlerCre public final MessageHandler createHandler(Object object, Method method, Map attributes) { MessageHandler handler = this.doCreateHandler(object, method, attributes); - if (attributes != null && handler instanceof AbstractMethodInvokingAdapter) { + if (attributes != null && handler instanceof AbstractMessageHandler) { Object order = attributes.get("order"); if (order != null && order instanceof Integer) { - ((AbstractMethodInvokingAdapter) handler).setOrder(((Integer) order).intValue()); + ((AbstractMessageHandler) handler).setOrder(((Integer) order).intValue()); } } if (handler instanceof InitializingBean) { diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/handler/config/DefaultMessageHandlerCreator.java b/org.springframework.integration/src/main/java/org/springframework/integration/handler/config/DefaultMessageHandlerCreator.java index c55c29c7de..729b8520e6 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/handler/config/DefaultMessageHandlerCreator.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/handler/config/DefaultMessageHandlerCreator.java @@ -19,32 +19,23 @@ package org.springframework.integration.handler.config; import java.lang.reflect.Method; import java.util.Map; -import org.springframework.core.annotation.Order; -import org.springframework.integration.ConfigurationException; -import org.springframework.integration.handler.DefaultMessageHandlerAdapter; +import org.springframework.integration.handler.DefaultMessageHandler; import org.springframework.integration.handler.MessageHandler; -import org.springframework.integration.message.Message; /** * Default implementation of the handler creator strategy that creates a - * {@link DefaultMessageHandlerAdapter} for the provided object and method. - * This version does not even consider the attributes. It does however respect - * an {@link Order} annotation if present. + * {@link DefaultMessageHandler} for the provided object and method. * * @author Mark Fisher */ public class DefaultMessageHandlerCreator extends AbstractMessageHandlerCreator { public MessageHandler doCreateHandler(Object object, Method method, Map attributes) { - Class[] types = method.getParameterTypes(); - if (types.length != 1) { - throw new ConfigurationException("exactly one method parameter is required"); - } - DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter(); - adapter.setObject(object); - adapter.setMethodName(method.getName()); - adapter.setMethodExpectsMessage(Message.class.isAssignableFrom(types[0])); - return adapter; + DefaultMessageHandler handler = new DefaultMessageHandler(); + handler.setObject(object); + handler.setMethod(method); + handler.afterPropertiesSet(); + return handler; } } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/router/RouterMessageHandler.java b/org.springframework.integration/src/main/java/org/springframework/integration/router/RouterMessageHandler.java index c518fb1ed8..bb4c621629 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/router/RouterMessageHandler.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/router/RouterMessageHandler.java @@ -29,6 +29,7 @@ import org.springframework.integration.handler.AbstractMessageHandler; import org.springframework.integration.message.CompositeMessage; import org.springframework.integration.message.Message; import org.springframework.integration.message.MessageBuilder; +import org.springframework.integration.message.MessageHandlingException; import org.springframework.integration.message.MessageTarget; /** @@ -101,4 +102,10 @@ public class RouterMessageHandler extends AbstractMessageHandler { return new CompositeMessage(replies); } + @Override + protected Message postProcessReplyMessage(Message replyMessage, Message requestMessage) { + throw new MessageHandlingException(requestMessage, + "router method must return type 'MessageChannel' or 'String', but a Message was returned: " + replyMessage); + } + } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/splitter/SplitterMessageHandler.java b/org.springframework.integration/src/main/java/org/springframework/integration/splitter/SplitterMessageHandler.java index 24c6c12ac4..54677102dc 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/splitter/SplitterMessageHandler.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/splitter/SplitterMessageHandler.java @@ -108,6 +108,23 @@ public class SplitterMessageHandler extends AbstractMessageHandler { return new CompositeMessage(results); } + @Override + protected Message postProcessReplyMessage(Message replyMessage, Message requestMessage) { + Object requestId = requestMessage.getHeaders().getId(); + if (replyMessage instanceof CompositeMessage) { + List> sequentialMessages = new ArrayList>(); + List> replyList = ((CompositeMessage) replyMessage).getPayload(); + int sequenceSize = replyList.size(); + int sequenceNumber = 0; + for (Message message : replyList) { + sequentialMessages.add(this.setSplitMessageHeaders( + MessageBuilder.fromMessage(message), requestId, ++sequenceNumber, sequenceSize)); + } + return new CompositeMessage(sequentialMessages); + } + return this.setSplitMessageHeaders(MessageBuilder.fromMessage(replyMessage), requestId, 1, 1); + } + private Message createSplitMessage(Object item, MessageHeaders requestHeaders, int sequenceNumber, int sequenceSize) { if (item instanceof Message) { return this.setSplitMessageHeaders(MessageBuilder.fromMessage((Message) item), diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/transformer/AnnotationMethodTransformerAdapter.java b/org.springframework.integration/src/main/java/org/springframework/integration/transformer/AnnotationMethodTransformerAdapter.java deleted file mode 100644 index bafc6b5b69..0000000000 --- a/org.springframework.integration/src/main/java/org/springframework/integration/transformer/AnnotationMethodTransformerAdapter.java +++ /dev/null @@ -1,113 +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.transformer; - -import java.util.Map; -import java.util.Properties; - -import org.springframework.integration.handler.MessageHandler; -import org.springframework.integration.handler.annotation.AnnotationMethodMessageMapper; -import org.springframework.integration.message.DefaultMessageMapper; -import org.springframework.integration.message.Message; -import org.springframework.integration.message.MessageBuilder; -import org.springframework.integration.message.MessageMapper; -import org.springframework.integration.message.MessagingException; -import org.springframework.integration.util.AbstractMethodInvokingAdapter; - -/** - * @author Mark Fisher - */ -@SuppressWarnings("unchecked") -public class AnnotationMethodTransformerAdapter extends AbstractMethodInvokingAdapter implements MessageHandler { - - private volatile MessageMapper mapper; - - private volatile boolean methodExpectsMessage; - - - public void setMethodExpectsMessage(boolean methodExpectsMessage) { - this.methodExpectsMessage = methodExpectsMessage; - } - - protected void initialize() { - this.mapper = (this.getMethod() != null) - ? new AnnotationMethodMessageMapper(this.getMethod()) - : new DefaultMessageMapper(); - } - - public Message handle(Message message) { - if (!this.isInitialized()) { - this.afterPropertiesSet(); - } - if (message.getPayload() == null) { - return message; - } - Object param = (this.methodExpectsMessage) ? message : this.mapper.mapMessage(message); - try { - Object args[] = null; - if (param != null && param.getClass().isArray() - && (Object.class.isAssignableFrom(param.getClass().getComponentType()))) { - args = (Object[]) param; - } - else { - args = new Object[] { param }; - } - return this.invokeMethodAndReturnMessage(message, args); - } - catch (Exception e) { - throw new MessagingException(message, "failed to transform message payload", e); - } - } - - 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 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(); - } - } - -} diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/transformer/TransformerMessageHandler.java b/org.springframework.integration/src/main/java/org/springframework/integration/transformer/TransformerMessageHandler.java new file mode 100644 index 0000000000..b51ba05115 --- /dev/null +++ b/org.springframework.integration/src/main/java/org/springframework/integration/transformer/TransformerMessageHandler.java @@ -0,0 +1,65 @@ +/* + * 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.transformer; + +import java.util.Map; +import java.util.Properties; + +import org.springframework.integration.handler.AbstractMessageHandler; +import org.springframework.integration.message.Message; +import org.springframework.integration.message.MessageBuilder; +import org.springframework.integration.message.MessageHandlingException; + +/** + * @author Mark Fisher + */ +public class TransformerMessageHandler extends AbstractMessageHandler { + + @Override + protected Message createReplyMessage(Object result, Message requestMessage) { + if (result instanceof Properties && !(requestMessage.getPayload() instanceof Properties)) { + Properties propertiesToSet = (Properties) result; + MessageBuilder builder = MessageBuilder.fromMessage(requestMessage); + for (Object keyObject : propertiesToSet.keySet()) { + String key = (String) keyObject; + builder.setHeader(key, propertiesToSet.getProperty(key)); + } + return builder.build(); + } + else if (result instanceof Map && !(requestMessage.getPayload() instanceof Map)) { + Map attributesToSet = (Map ) result; + MessageBuilder builder = MessageBuilder.fromMessage(requestMessage); + for (Object key : attributesToSet.keySet()) { + if (!(key instanceof String)) { + throw new MessageHandlingException(requestMessage, + "Map returned from a Transformer method must have String-typed keys"); + } + builder.setHeader((String) key, attributesToSet.get(key)); + } + return builder.build(); + } + else { + return MessageBuilder.fromPayload(result).copyHeaders(requestMessage.getHeaders()).build(); + } + } + + @Override + protected Message postProcessReplyMessage(Message replyMessage, Message requestMessage) { + return replyMessage; + } + +} diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/transformer/config/TransformerMessageHandlerCreator.java b/org.springframework.integration/src/main/java/org/springframework/integration/transformer/config/TransformerMessageHandlerCreator.java index 182e8723d0..b9172fdec6 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/transformer/config/TransformerMessageHandlerCreator.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/transformer/config/TransformerMessageHandlerCreator.java @@ -21,7 +21,7 @@ import java.util.Map; import org.springframework.integration.handler.MessageHandler; import org.springframework.integration.handler.config.AbstractMessageHandlerCreator; -import org.springframework.integration.transformer.AnnotationMethodTransformerAdapter; +import org.springframework.integration.transformer.TransformerMessageHandler; /** * @author Mark Fisher @@ -29,11 +29,11 @@ import org.springframework.integration.transformer.AnnotationMethodTransformerAd public class TransformerMessageHandlerCreator extends AbstractMessageHandlerCreator { public MessageHandler doCreateHandler(Object object, Method method, Map attributes) { - AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter(); - adapter.setObject(object); - adapter.setMethodName(method.getName()); - adapter.afterPropertiesSet(); - return adapter; + TransformerMessageHandler handler = new TransformerMessageHandler(); + handler.setObject(object); + handler.setMethodName(method.getName()); + handler.afterPropertiesSet(); + return handler; } } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/util/DefaultMethodInvoker.java b/org.springframework.integration/src/main/java/org/springframework/integration/util/DefaultMethodInvoker.java index a5f0b92192..d5fcf79abd 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/util/DefaultMethodInvoker.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/util/DefaultMethodInvoker.java @@ -38,7 +38,7 @@ public class DefaultMethodInvoker implements MethodInvoker { private final Object object; - private final Method method; + private volatile Method method; private volatile TypeConverter typeConverter; @@ -85,7 +85,18 @@ public class DefaultMethodInvoker implements MethodInvoker { if (this.logger.isDebugEnabled()) { logger.debug("invoking method '" + this.method.getName() + "' with arguments " + ObjectUtils.nullSafeToString(convertedArgs)); } - return this.method.invoke(this.object, convertedArgs); + try { + return this.method.invoke(this.object, convertedArgs); + } + catch (IllegalArgumentException e) { + org.springframework.util.MethodInvoker helper = new org.springframework.util.MethodInvoker(); + helper.setTargetObject(this.object); + helper.setTargetMethod(this.method.getName()); + helper.setArguments(convertedArgs); + helper.prepare(); + this.method = helper.getPreparedMethod(); + return this.method.invoke(this.object, convertedArgs); + } } } diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/handler/CorrelationIdTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/handler/CorrelationIdTests.java index 583ddd8fc1..1e65e66e82 100644 --- a/org.springframework.integration/src/test/java/org/springframework/integration/handler/CorrelationIdTests.java +++ b/org.springframework.integration/src/test/java/org/springframework/integration/handler/CorrelationIdTests.java @@ -39,22 +39,22 @@ public class CorrelationIdTests { Object correlationId = "123-ABC"; Message message = MessageBuilder.fromPayload("test") .setCorrelationId(correlationId).build(); - DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter(); - adapter.setObject(new TestBean()); - adapter.setMethodName("upperCase"); - adapter.afterPropertiesSet(); - Message reply = adapter.handle(message); + DefaultMessageHandler handler = new DefaultMessageHandler(); + handler.setObject(new TestBean()); + handler.setMethodName("upperCase"); + handler.afterPropertiesSet(); + Message reply = handler.handle(message); assertEquals(correlationId, reply.getHeaders().getCorrelationId()); } @Test public void testCorrelationIdCopiedFromMessageIdByDefault() { Message message = MessageBuilder.fromPayload("test").build(); - DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter(); - adapter.setObject(new TestBean()); - adapter.setMethodName("upperCase"); - adapter.afterPropertiesSet(); - Message reply = adapter.handle(message); + DefaultMessageHandler handler = new DefaultMessageHandler(); + handler.setObject(new TestBean()); + handler.setMethodName("upperCase"); + handler.afterPropertiesSet(); + Message reply = handler.handle(message); assertEquals(message.getHeaders().getId(), reply.getHeaders().getCorrelationId()); } @@ -62,11 +62,11 @@ public class CorrelationIdTests { public void testCorrelationIdCopiedFromMessageCorrelationIdIfAvailable() { Message message = MessageBuilder.fromPayload("test") .setCorrelationId("correlationId").build(); - DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter(); - adapter.setObject(new TestBean()); - adapter.setMethodName("upperCase"); - adapter.afterPropertiesSet(); - Message reply = adapter.handle(message); + DefaultMessageHandler handler = new DefaultMessageHandler(); + handler.setObject(new TestBean()); + handler.setMethodName("upperCase"); + handler.afterPropertiesSet(); + Message reply = handler.handle(message); assertEquals(message.getHeaders().getCorrelationId(), reply.getHeaders().getCorrelationId()); assertTrue(message.getHeaders().getCorrelationId().equals(reply.getHeaders().getCorrelationId())); } diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/handler/DefaultMessageHandlerAdapterTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/handler/DefaultMessageHandlerAdapterTests.java deleted file mode 100644 index bd75b4b61b..0000000000 --- a/org.springframework.integration/src/test/java/org/springframework/integration/handler/DefaultMessageHandlerAdapterTests.java +++ /dev/null @@ -1,123 +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 static org.junit.Assert.assertEquals; - -import org.junit.Test; - -import org.springframework.integration.message.Message; -import org.springframework.integration.message.StringMessage; - -/** - * @author Mark Fisher - */ -public class DefaultMessageHandlerAdapterTests { - - @Test - public void testPayloadAsMethodParameterAndObjectAsReturnValue() { - DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter(); - adapter.setObject(new TestHandler()); - adapter.setMethodName("acceptPayloadAndReturnObject"); - adapter.afterPropertiesSet(); - Message result = adapter.handle(new StringMessage("testing")); - assertEquals("testing-1", result.getPayload()); - } - - @Test - public void testPayloadAsMethodParameterAndMessageAsReturnValue() { - DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter(); - adapter.setObject(new TestHandler()); - adapter.setMethodName("acceptPayloadAndReturnMessage"); - adapter.afterPropertiesSet(); - Message result = adapter.handle(new StringMessage("testing")); - assertEquals("testing-2", result.getPayload()); - } - - @Test - public void testMessageAsMethodParameterAndObjectAsReturnValue() { - DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter(); - adapter.setMethodExpectsMessage(true); - adapter.setObject(new TestHandler()); - adapter.setMethodName("acceptMessageAndReturnObject"); - adapter.afterPropertiesSet(); - Message result = adapter.handle(new StringMessage("testing")); - assertEquals("testing-3", result.getPayload()); - } - - @Test - public void testMessageAsMethodParameterAndMessageAsReturnValue() { - DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter(); - adapter.setMethodExpectsMessage(true); - adapter.setObject(new TestHandler()); - adapter.setMethodName("acceptMessageAndReturnMessage"); - adapter.afterPropertiesSet(); - Message result = adapter.handle(new StringMessage("testing")); - assertEquals("testing-4", result.getPayload()); - } - - @Test - public void testMessageSubclassAsMethodParameterAndMessageAsReturnValue() { - DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter(); - adapter.setMethodExpectsMessage(true); - adapter.setObject(new TestHandler()); - adapter.setMethodName("acceptMessageSubclassAndReturnMessage"); - adapter.afterPropertiesSet(); - Message result = adapter.handle(new StringMessage("testing")); - assertEquals("testing-5", result.getPayload()); - } - - @Test - public void testMessageSubclassAsMethodParameterAndMessageSubclassAsReturnValue() { - DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter(); - adapter.setMethodExpectsMessage(true); - adapter.setObject(new TestHandler()); - adapter.setMethodName("acceptMessageSubclassAndReturnMessageSubclass"); - adapter.afterPropertiesSet(); - Message result = adapter.handle(new StringMessage("testing")); - assertEquals("testing-6", result.getPayload()); - } - - - private static class TestHandler { - - public String acceptPayloadAndReturnObject(String s) { - return s + "-1"; - } - - public Message acceptPayloadAndReturnMessage(String s) { - return new StringMessage(s + "-2"); - } - - public String acceptMessageAndReturnObject(Message m) { - return m.getPayload() + "-3"; - } - - public Message acceptMessageAndReturnMessage(Message m) { - return new StringMessage(m.getPayload() + "-4"); - } - - public Message acceptMessageSubclassAndReturnMessage(StringMessage m) { - return new StringMessage(m.getPayload() + "-5"); - } - - public StringMessage acceptMessageSubclassAndReturnMessageSubclass(StringMessage m) { - return new StringMessage(m.getPayload() + "-6"); - } - } - -} diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/handler/DefaultMessageHandlerTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/handler/DefaultMessageHandlerTests.java new file mode 100644 index 0000000000..4b96914d1a --- /dev/null +++ b/org.springframework.integration/src/test/java/org/springframework/integration/handler/DefaultMessageHandlerTests.java @@ -0,0 +1,264 @@ +/* + * 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 static org.junit.Assert.assertEquals; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Properties; + +import org.junit.Test; + +import org.springframework.integration.annotation.Handler; +import org.springframework.integration.handler.annotation.Header; +import org.springframework.integration.message.GenericMessage; +import org.springframework.integration.message.Message; +import org.springframework.integration.message.MessageBuilder; +import org.springframework.integration.message.MessagingException; +import org.springframework.integration.message.StringMessage; + +/** + * @author Mark Fisher + */ +public class DefaultMessageHandlerTests { + + @Test + public void payloadAsMethodParameterAndObjectAsReturnValue() { + DefaultMessageHandler handler = new DefaultMessageHandler(); + handler.setObject(new TestHandler()); + handler.setMethodName("acceptPayloadAndReturnObject"); + handler.afterPropertiesSet(); + Message result = handler.handle(new StringMessage("testing")); + assertEquals("testing-1", result.getPayload()); + } + + @Test + public void payloadAsMethodParameterAndMessageAsReturnValue() { + DefaultMessageHandler handler = new DefaultMessageHandler(); + handler.setObject(new TestHandler()); + handler.setMethodName("acceptPayloadAndReturnMessage"); + handler.afterPropertiesSet(); + Message result = handler.handle(new StringMessage("testing")); + assertEquals("testing-2", result.getPayload()); + } + + @Test + public void messageAsMethodParameterAndObjectAsReturnValue() { + DefaultMessageHandler handler = new DefaultMessageHandler(); + handler.setObject(new TestHandler()); + handler.setMethodName("acceptMessageAndReturnObject"); + handler.afterPropertiesSet(); + Message result = handler.handle(new StringMessage("testing")); + assertEquals("testing-3", result.getPayload()); + } + + @Test + public void messageAsMethodParameterAndMessageAsReturnValue() { + DefaultMessageHandler handler = new DefaultMessageHandler(); + handler.setObject(new TestHandler()); + handler.setMethodName("acceptMessageAndReturnMessage"); + handler.afterPropertiesSet(); + Message result = handler.handle(new StringMessage("testing")); + assertEquals("testing-4", result.getPayload()); + } + + @Test + public void messageSubclassAsMethodParameterAndMessageAsReturnValue() { + DefaultMessageHandler handler = new DefaultMessageHandler(); + handler.setObject(new TestHandler()); + handler.setMethodName("acceptMessageSubclassAndReturnMessage"); + handler.afterPropertiesSet(); + Message result = handler.handle(new StringMessage("testing")); + assertEquals("testing-5", result.getPayload()); + } + + @Test + public void messageSubclassAsMethodParameterAndMessageSubclassAsReturnValue() { + DefaultMessageHandler handler = new DefaultMessageHandler(); + handler.setObject(new TestHandler()); + handler.setMethodName("acceptMessageSubclassAndReturnMessageSubclass"); + handler.afterPropertiesSet(); + Message result = handler.handle(new StringMessage("testing")); + assertEquals("testing-6", result.getPayload()); + } + + @Test + public void payloadAndHeaderAnnotationMethodParametersAndObjectAsReturnValue() { + DefaultMessageHandler handler = new DefaultMessageHandler(); + handler.setObject(new TestHandler()); + handler.setMethodName("acceptPayloadAndHeaderAndReturnObject"); + handler.afterPropertiesSet(); + Message request = MessageBuilder.fromPayload("testing") + .setHeader("number", new Integer(123)).build(); + Message result = handler.handle(request); + assertEquals("testing-123", result.getPayload()); + } + + @Test + public void messageOnlyWithAnnotatedMethod() throws Exception { + AnnotatedTestHandler handler = new AnnotatedTestHandler(); + Method method = handler.getClass().getMethod("messageOnly", Message.class); + DefaultMessageHandler adapter = new DefaultMessageHandler(); + adapter.setObject(handler); + adapter.setMethod(method); + Message result = adapter.handle(new StringMessage("foo")); + assertEquals("foo", result.getPayload()); + } + + @Test + public void payloadWithAnnotatedMethod() throws Exception { + AnnotatedTestHandler handler = new AnnotatedTestHandler(); + Method method = handler.getClass().getMethod("integerMethod", Integer.class); + DefaultMessageHandler adapter = new DefaultMessageHandler(); + adapter.setObject(handler); + adapter.setMethod(method); + Message result = adapter.handle(new GenericMessage(new Integer(123))); + assertEquals(new Integer(123), result.getPayload()); + } + + @Test + public void convertedPayloadWithAnnotatedMethod() throws Exception { + AnnotatedTestHandler handler = new AnnotatedTestHandler(); + Method method = handler.getClass().getMethod("integerMethod", Integer.class); + DefaultMessageHandler adapter = new DefaultMessageHandler(); + adapter.setObject(handler); + adapter.setMethod(method); + Message result = adapter.handle(new StringMessage("456")); + assertEquals(new Integer(456), result.getPayload()); + } + + @Test(expected = MessagingException.class) + public void conversionFailureWithAnnotatedMethod() throws Exception { + AnnotatedTestHandler handler = new AnnotatedTestHandler(); + Method method = handler.getClass().getMethod("integerMethod", Integer.class); + DefaultMessageHandler adapter = new DefaultMessageHandler(); + adapter.setObject(handler); + adapter.setMethod(method); + Message result = adapter.handle(new StringMessage("foo")); + assertEquals(new Integer(123), result.getPayload()); + } + + @Test + public void messageAndHeaderWithAnnotatedMethod() throws Exception { + AnnotatedTestHandler handler = new AnnotatedTestHandler(); + Method method = handler.getClass().getMethod("messageAndHeader", Message.class, Integer.class); + DefaultMessageHandler adapter = new DefaultMessageHandler(); + adapter.setObject(handler); + adapter.setMethod(method); + Message message = MessageBuilder.fromPayload("foo") + .setHeader("number", 42).build(); + Message result = adapter.handle(message); + assertEquals("foo-42", result.getPayload()); + } + + @Test + public void multipleHeadersWithAnnotatedMethod() throws Exception { + AnnotatedTestHandler handler = new AnnotatedTestHandler(); + Method method = handler.getClass().getMethod("twoHeaders", String.class, Integer.class); + DefaultMessageHandler adapter = new DefaultMessageHandler(); + adapter.setObject(handler); + adapter.setMethod(method); + Message message = MessageBuilder.fromPayload("foo") + .setHeader("prop", "bar") + .setHeader("number", 42).build(); + Message result = adapter.handle(message); + assertEquals("bar-42", result.getPayload()); + } + + + private static class TestHandler { + + public String acceptPayloadAndReturnObject(String s) { + return s + "-1"; + } + + public Message acceptPayloadAndReturnMessage(String s) { + return new StringMessage(s + "-2"); + } + + public String acceptMessageAndReturnObject(Message m) { + return m.getPayload() + "-3"; + } + + public Message acceptMessageAndReturnMessage(Message m) { + return new StringMessage(m.getPayload() + "-4"); + } + + public Message acceptMessageSubclassAndReturnMessage(StringMessage m) { + return new StringMessage(m.getPayload() + "-5"); + } + + public StringMessage acceptMessageSubclassAndReturnMessageSubclass(StringMessage m) { + return new StringMessage(m.getPayload() + "-6"); + } + + public String acceptPayloadAndHeaderAndReturnObject(String s, @Header("number") Integer n) { + return s + "-" + n; + } + } + + private static class AnnotatedTestHandler { + + @Handler + public String messageOnly(Message message) { + return (String) message.getPayload(); + } + + @Handler + public String messageAndHeader(Message message, @Header("number") Integer num) { + return (String) message.getPayload() + "-" + num.toString(); + } + + @Handler + public String twoHeaders(@Header String prop, @Header("number") Integer num) { + return prop + "-" + num.toString(); + } + + @Handler + public Integer optionalHeader(@Header(required=false) Integer num) { + return num; + } + + @Handler + public Integer requiredHeader(@Header(value="num", required=true) Integer num) { + return num; + } + + @Handler + public String optionalAndRequiredHeader(@Header(required=false) String prop, @Header(value="num", required=true) Integer num) { + return prop + num; + } + + @Handler + public Properties propertiesMethod(Properties properties) { + return properties; + } + + @Handler + @SuppressWarnings("unchecked") + public Map mapMethod(Map map) { + return map; + } + + @Handler + public Integer integerMethod(Integer i) { + return i; + } + } + +} diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/handler/annotation/AnnotationMethodMessageMapperTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/handler/annotation/MethodArgumentMessageMapperTests.java similarity index 59% rename from org.springframework.integration/src/test/java/org/springframework/integration/handler/annotation/AnnotationMethodMessageMapperTests.java rename to org.springframework.integration/src/test/java/org/springframework/integration/handler/annotation/MethodArgumentMessageMapperTests.java index 8e8073c960..3440beab19 100644 --- a/org.springframework.integration/src/test/java/org/springframework/integration/handler/annotation/AnnotationMethodMessageMapperTests.java +++ b/org.springframework.integration/src/test/java/org/springframework/integration/handler/annotation/MethodArgumentMessageMapperTests.java @@ -27,23 +27,20 @@ import java.util.Properties; import org.junit.Test; import org.springframework.integration.annotation.Handler; -import org.springframework.integration.handler.DefaultMessageHandlerAdapter; -import org.springframework.integration.message.GenericMessage; import org.springframework.integration.message.Message; import org.springframework.integration.message.MessageBuilder; import org.springframework.integration.message.MessageHandlingException; -import org.springframework.integration.message.MessagingException; import org.springframework.integration.message.StringMessage; /** * @author Mark Fisher */ -public class AnnotationMethodMessageMapperTests { +public class MethodArgumentMessageMapperTests { @Test public void testOptionalHeader() throws Exception { Method method = TestHandler.class.getMethod("optionalHeader", Integer.class); - AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); + MethodArgumentMessageMapper mapper = new MethodArgumentMessageMapper(method); Object[] args = (Object[]) mapper.mapMessage(new StringMessage("foo")); assertEquals(1, args.length); assertNull(args[0]); @@ -52,14 +49,14 @@ public class AnnotationMethodMessageMapperTests { @Test(expected=MessageHandlingException.class) public void testRequiredHeaderNotProvided() throws Exception { Method method = TestHandler.class.getMethod("requiredHeader", Integer.class); - AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); + MethodArgumentMessageMapper mapper = new MethodArgumentMessageMapper(method); mapper.mapMessage(new StringMessage("foo")); } @Test public void testRequiredHeaderProvided() throws Exception { Method method = TestHandler.class.getMethod("requiredHeader", Integer.class); - AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); + MethodArgumentMessageMapper mapper = new MethodArgumentMessageMapper(method); Message message = MessageBuilder.fromPayload("foo") .setHeader("num", new Integer(123)).build(); Object[] args = (Object[]) mapper.mapMessage(message); @@ -70,7 +67,7 @@ public class AnnotationMethodMessageMapperTests { @Test(expected=MessageHandlingException.class) public void testOptionalAndRequiredHeaderWithOnlyOptionalHeaderProvided() throws Exception { Method method = TestHandler.class.getMethod("optionalAndRequiredHeader", String.class, Integer.class); - AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); + MethodArgumentMessageMapper mapper = new MethodArgumentMessageMapper(method); Message message = MessageBuilder.fromPayload("foo") .setHeader("prop", "bar").build(); mapper.mapMessage(message); @@ -79,7 +76,7 @@ public class AnnotationMethodMessageMapperTests { @Test public void testOptionalAndRequiredHeaderWithOnlyRequiredHeaderProvided() throws Exception { Method method = TestHandler.class.getMethod("optionalAndRequiredHeader", String.class, Integer.class); - AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); + MethodArgumentMessageMapper mapper = new MethodArgumentMessageMapper(method); Message message = MessageBuilder.fromPayload("foo") .setHeader("num", new Integer(123)).build(); Object[] args = (Object[]) mapper.mapMessage(message); @@ -91,7 +88,7 @@ public class AnnotationMethodMessageMapperTests { @Test public void testOptionalAndRequiredHeaderWithBothHeadersProvided() throws Exception { Method method = TestHandler.class.getMethod("optionalAndRequiredHeader", String.class, Integer.class); - AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); + MethodArgumentMessageMapper mapper = new MethodArgumentMessageMapper(method); Message message = MessageBuilder.fromPayload("foo") .setHeader("num", new Integer(123)) .setHeader("prop", "bar") @@ -105,7 +102,7 @@ public class AnnotationMethodMessageMapperTests { @Test public void testPropertiesMethodWithNonPropertiesPayload() throws Exception { Method method = TestHandler.class.getMethod("propertiesMethod", Properties.class); - AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); + MethodArgumentMessageMapper mapper = new MethodArgumentMessageMapper(method); Message message = MessageBuilder.fromPayload("test") .setHeader("prop1", "foo").setHeader("prop2", "bar").build(); Object[] args = (Object[]) mapper.mapMessage(message); @@ -118,7 +115,7 @@ public class AnnotationMethodMessageMapperTests { @Test public void testPropertiesMethodWithPropertiesPayload() throws Exception { Method method = TestHandler.class.getMethod("propertiesMethod", Properties.class); - AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); + MethodArgumentMessageMapper mapper = new MethodArgumentMessageMapper(method); Properties payload = new Properties(); payload.setProperty("prop1", "foo"); payload.setProperty("prop2", "bar"); @@ -135,7 +132,7 @@ public class AnnotationMethodMessageMapperTests { @SuppressWarnings("unchecked") public void testMapMethodWithNonMapPayload() throws Exception { Method method = TestHandler.class.getMethod("mapMethod", Map.class); - AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); + MethodArgumentMessageMapper mapper = new MethodArgumentMessageMapper(method); Message message = MessageBuilder.fromPayload("test") .setHeader("attrib1", new Integer(123)) .setHeader("attrib2", new Integer(456)).build(); @@ -149,7 +146,7 @@ public class AnnotationMethodMessageMapperTests { @SuppressWarnings("unchecked") public void testMapMethodWithMapPayload() throws Exception { Method method = TestHandler.class.getMethod("mapMethod", Map.class); - AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); + MethodArgumentMessageMapper> mapper = new MethodArgumentMessageMapper>(method); Map payload = new HashMap(); payload.put("attrib1", new Integer(123)); payload.put("attrib2", new Integer(456)); @@ -163,89 +160,6 @@ public class AnnotationMethodMessageMapperTests { assertEquals(new Integer(456), result.get("attrib2")); } - @Test - public void testMessageOnlyWithAdapter() throws Exception { - TestHandler handler = new TestHandler(); - Method method = handler.getClass().getMethod("messageOnly", Message.class); - AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); - DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter(); - adapter.setObject(handler); - adapter.setMethod(method); - adapter.setMessageMapper(mapper); - Message result = adapter.handle(new StringMessage("foo")); - assertEquals("foo", result.getPayload()); - } - - @Test - public void testPayloadWithAdapter() throws Exception { - TestHandler handler = new TestHandler(); - Method method = handler.getClass().getMethod("integerMethod", Integer.class); - AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); - DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter(); - adapter.setObject(handler); - adapter.setMethod(method); - adapter.setMessageMapper(mapper); - Message result = adapter.handle(new GenericMessage(new Integer(123))); - assertEquals(new Integer(123), result.getPayload()); - } - - @Test - public void testConvertedPayloadWithAdapter() throws Exception { - TestHandler handler = new TestHandler(); - Method method = handler.getClass().getMethod("integerMethod", Integer.class); - AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); - DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter(); - adapter.setObject(handler); - adapter.setMethod(method); - adapter.setMessageMapper(mapper); - Message result = adapter.handle(new StringMessage("456")); - assertEquals(new Integer(456), result.getPayload()); - } - - @Test(expected=MessagingException.class) - public void testConversionFailureWithAdapter() throws Exception { - TestHandler handler = new TestHandler(); - Method method = handler.getClass().getMethod("integerMethod", Integer.class); - AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); - DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter(); - adapter.setObject(handler); - adapter.setMethod(method); - adapter.setMessageMapper(mapper); - Message result = adapter.handle(new StringMessage("foo")); - assertEquals(new Integer(123), result.getPayload()); - } - - @Test - public void testMessageAndHeaderWithAdapter() throws Exception { - TestHandler handler = new TestHandler(); - Method method = handler.getClass().getMethod("messageAndHeader", Message.class, Integer.class); - AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); - DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter(); - adapter.setObject(handler); - adapter.setMethod(method); - adapter.setMessageMapper(mapper); - Message message = MessageBuilder.fromPayload("foo") - .setHeader("number", 42).build(); - Message result = adapter.handle(message); - assertEquals("foo-42", result.getPayload()); - } - - @Test - public void testMultipleHeadersWithAdapter() throws Exception { - TestHandler handler = new TestHandler(); - Method method = handler.getClass().getMethod("twoHeaders", String.class, Integer.class); - AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); - DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter(); - adapter.setObject(handler); - adapter.setMethod(method); - adapter.setMessageMapper(mapper); - Message message = MessageBuilder.fromPayload("foo") - .setHeader("prop", "bar") - .setHeader("number", 42).build(); - Message result = adapter.handle(message); - assertEquals("bar-42", result.getPayload()); - } - private static class TestHandler { @@ -294,7 +208,7 @@ public class AnnotationMethodMessageMapperTests { public Integer integerMethod(Integer i) { return i; } - } + } diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/transformer/AnnotationMethodTransformerAdapterTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/transformer/TransformerMessageHandlerTests.java similarity index 61% rename from org.springframework.integration/src/test/java/org/springframework/integration/transformer/AnnotationMethodTransformerAdapterTests.java rename to org.springframework.integration/src/test/java/org/springframework/integration/transformer/TransformerMessageHandlerTests.java index cc106ad838..ff4af0c648 100644 --- a/org.springframework.integration/src/test/java/org/springframework/integration/transformer/AnnotationMethodTransformerAdapterTests.java +++ b/org.springframework.integration/src/test/java/org/springframework/integration/transformer/TransformerMessageHandlerTests.java @@ -36,84 +36,95 @@ import org.springframework.integration.message.StringMessage; /** * @author Mark Fisher */ -public class AnnotationMethodTransformerAdapterTests { +public class TransformerMessageHandlerTests { @Test - public void testSimplePayload() throws Exception { + public void simplePayload() throws Exception { TestBean testBean = new TestBean(); - AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter(); - adapter.setObject(testBean); - adapter.setMethod(testBean.getClass().getMethod("exclaim", String.class)); + TransformerMessageHandler handler = new TransformerMessageHandler(); + handler.setObject(testBean); + handler.setMethod(testBean.getClass().getMethod("exclaim", String.class)); Message message = new StringMessage("foo"); - Message result = adapter.handle(message); + Message result = handler.handle(message); assertEquals("FOO!", result.getPayload()); } @Test - public void testTypeConversion() throws Exception { + public void typeConversion() throws Exception { TestBean testBean = new TestBean(); - AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter(); - adapter.setObject(testBean); - adapter.setMethod(testBean.getClass().getMethod("exclaim", String.class)); + TransformerMessageHandler handler = new TransformerMessageHandler(); + handler.setObject(testBean); + handler.setMethod(testBean.getClass().getMethod("exclaim", String.class)); Message message = new GenericMessage(123); - Message result = adapter.handle(message); + Message result = handler.handle(message); assertEquals("123!", result.getPayload()); } - @Test(expected=MessagingException.class) - public void testTypeConversionFailure() throws Exception { + @Test(expected = MessagingException.class) + public void typeConversionFailure() throws Exception { TestBean testBean = new TestBean(); - AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter(); - adapter.setObject(testBean); - adapter.setMethod(testBean.getClass().getMethod("exclaim", String.class)); + TransformerMessageHandler handler = new TransformerMessageHandler(); + handler.setObject(testBean); + handler.setMethod(testBean.getClass().getMethod("exclaim", String.class)); Message message = new GenericMessage(new Date()); - adapter.handle(message); + handler.handle(message); } @Test - public void testHeaderAnnotation() throws Exception { + public void headerAnnotation() throws Exception { TestBean testBean = new TestBean(); - AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter(); - adapter.setObject(testBean); - adapter.setMethod(testBean.getClass().getMethod("headerTest", String.class, Integer.class)); + TransformerMessageHandler handler = new TransformerMessageHandler(); + handler.setObject(testBean); + handler.setMethod(testBean.getClass().getMethod("headerTest", String.class, Integer.class)); Message message = MessageBuilder.fromPayload("foo") .setHeader("number", 123).build(); - Message result = adapter.handle(message); + Message result = handler.handle(message); assertEquals("foo123", result.getPayload()); } - @Test(expected=MessageHandlingException.class) - public void testHeaderValueNotProvided() throws Exception { + @Test(expected = MessageHandlingException.class) + public void headerValueNotProvided() throws Exception { TestBean testBean = new TestBean(); - AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter(); - adapter.setObject(testBean); - adapter.setMethod(testBean.getClass().getMethod("headerTest", String.class, Integer.class)); + TransformerMessageHandler handler = new TransformerMessageHandler(); + handler.setObject(testBean); + handler.setMethod(testBean.getClass().getMethod("headerTest", String.class, Integer.class)); Message message = MessageBuilder.fromPayload("foo") .setHeader("wrong", 123).build(); - adapter.handle(message); + handler.handle(message); } @Test - public void testOptionalHeaderAnnotation() throws Exception { + public void optionalHeaderAnnotation() throws Exception { TestBean testBean = new TestBean(); - AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter(); - adapter.setObject(testBean); - adapter.setMethod(testBean.getClass().getMethod("optionalHeaderTest", String.class, Integer.class)); + TransformerMessageHandler handler = new TransformerMessageHandler(); + handler.setObject(testBean); + handler.setMethod(testBean.getClass().getMethod("optionalHeaderTest", String.class, Integer.class)); + Message message = MessageBuilder.fromPayload("foo").setHeader("number", 99).build(); + Message result = handler.handle(message); + assertEquals("foo99", result.getPayload()); + } + + @Test + public void optionalHeaderValueNotProvided() throws Exception { + TestBean testBean = new TestBean(); + TransformerMessageHandler handler = new TransformerMessageHandler(); + handler.setObject(testBean); + handler.setMethod(testBean.getClass().getMethod("optionalHeaderTest", String.class, Integer.class)); Message message = MessageBuilder.fromPayload("foo").build(); - Message result = adapter.handle(message); + Message result = handler.handle(message); assertEquals("foonull", result.getPayload()); } @Test - public void testHeaderEnricher() throws Exception { + public void headerEnricher() throws Exception { TestBean testBean = new TestBean(); - AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter(); - adapter.setObject(testBean); - adapter.setMethod(testBean.getClass().getMethod("propertyEnricherTest", String.class)); + TransformerMessageHandler handler = new TransformerMessageHandler(); + handler.setObject(testBean); + handler.setMethod(testBean.getClass().getMethod("propertyEnricherTest", String.class)); Message message = MessageBuilder.fromPayload("test") .setHeader("prop1", "bad") .setHeader("prop3", "baz").build(); - Message result = adapter.handle(message); + Message result = handler.handle(message); assertEquals("test", result.getPayload()); assertEquals("foo", result.getHeaders().get("prop1")); assertEquals("bar", result.getHeaders().get("prop2")); @@ -122,16 +133,16 @@ public class AnnotationMethodTransformerAdapterTests { @Test @SuppressWarnings("unchecked") - public void testPropertiesPayload() throws Exception { + public void propertiesPayload() throws Exception { TestBean testBean = new TestBean(); - AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter(); - adapter.setObject(testBean); - adapter.setMethod(testBean.getClass().getMethod("propertyPayloadTest", Properties.class)); + TransformerMessageHandler handler = new TransformerMessageHandler(); + handler.setObject(testBean); + handler.setMethod(testBean.getClass().getMethod("propertyPayloadTest", Properties.class)); Properties props = new Properties(); props.setProperty("prop1", "bad"); props.setProperty("prop3", "baz"); Message message = new GenericMessage(props); - Message result = (Message) adapter.handle(message); + Message result = (Message) handler.handle(message); assertEquals(Properties.class, result.getPayload().getClass()); Properties payload = result.getPayload(); assertEquals("foo", payload.getProperty("prop1"));