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:
Mark Fisher
2008-08-13 23:31:18 +00:00
parent 0f93cab8cc
commit 0d49689821
19 changed files with 580 additions and 644 deletions

View File

@@ -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);
}
}

View File

@@ -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() };
}
}

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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 + "]");
}
}
}

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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),

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}