Extracted message-to-args mapping logic from AbstractMessageHandler to MethodArgumentMessageMapper. Defined a separate abstract method in AbstractMessageHandler for post-processing a return value that is already a Message. DefaultMethodInvoker catches an IllegalArgumentException in case the Method instance no longer matches after a Proxy and "resets" the correct Method if it can be determined from the 'methodName' property. Replaced AnnotationMethodTransformerAdapter with TransformerMessageHandler.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Object, Object[]> 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() };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 + "]");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<T extends Object> implements MessageMapper<T, Object[]> {
|
||||
|
||||
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<T> 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();
|
||||
@@ -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<String, ?> 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) {
|
||||
|
||||
@@ -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<String, ?> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<Message<?>> sequentialMessages = new ArrayList<Message<?>>();
|
||||
List<Message<?>> 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),
|
||||
|
||||
@@ -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<String, ?> 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String, ?> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user