Added initial support for the MessageTransformer strategy and the @Transformer method-level annotation. Also added the PayloadTransformerAdapter implementation (INT-6).

This commit is contained in:
Mark Fisher
2008-06-05 04:48:03 +00:00
parent bd39e93257
commit 722b9c4f1c
20 changed files with 825 additions and 90 deletions

View File

@@ -0,0 +1,39 @@
/*
* 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.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates that a method is capable of transforming a message, message header,
* or message payload.
*
* @author Mark Fisher
*/
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Handler
public @interface Transformer {
}

View File

@@ -18,12 +18,16 @@ package org.springframework.integration.config.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.OrderComparator;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.integration.ConfigurationException;
import org.springframework.integration.annotation.Aggregator;
import org.springframework.integration.annotation.Concurrency;
@@ -31,6 +35,7 @@ import org.springframework.integration.annotation.Handler;
import org.springframework.integration.annotation.Polled;
import org.springframework.integration.annotation.Router;
import org.springframework.integration.annotation.Splitter;
import org.springframework.integration.annotation.Transformer;
import org.springframework.integration.bus.MessageBus;
import org.springframework.integration.channel.ChannelRegistryAware;
import org.springframework.integration.endpoint.ConcurrencyPolicy;
@@ -44,6 +49,7 @@ import org.springframework.integration.router.config.AggregatorMessageHandlerCre
import org.springframework.integration.router.config.RouterMessageHandlerCreator;
import org.springframework.integration.router.config.SplitterMessageHandlerCreator;
import org.springframework.integration.scheduling.Subscription;
import org.springframework.integration.transformer.config.TransformerMessageHandlerCreator;
import org.springframework.util.StringUtils;
/**
@@ -64,6 +70,7 @@ public class HandlerAnnotationPostProcessor extends AbstractAnnotationMethodPost
this.handlerCreators.put(Router.class, new RouterMessageHandlerCreator());
this.handlerCreators.put(Splitter.class, new SplitterMessageHandlerCreator());
this.handlerCreators.put(Aggregator.class, new AggregatorMessageHandlerCreator(messageBus));
this.handlerCreators.put(Transformer.class, new TransformerMessageHandlerCreator());
}
@@ -82,7 +89,12 @@ public class HandlerAnnotationPostProcessor extends AbstractAnnotationMethodPost
+ annotation.annotationType() + "', using DefaultMessageHandlerCreator.");
}
}
MessageHandler handler = handlerCreator.createHandler(bean, method, AnnotationUtils.getAnnotationAttributes(annotation));
Map<String, Object> attributes = AnnotationUtils.getAnnotationAttributes(annotation);
Order order = AnnotationUtils.findAnnotation(method, Order.class);
if (order != null) {
attributes.put("order", order.value());
}
MessageHandler handler = handlerCreator.createHandler(bean, method, attributes);
if (handler != null) {
if (handler instanceof ChannelRegistryAware) {
((ChannelRegistryAware) handler).setChannelRegistry(this.getMessageBus());
@@ -110,6 +122,9 @@ public class HandlerAnnotationPostProcessor extends AbstractAnnotationMethodPost
if (handlerChain.getHandlers().size() == 1) {
return handlerChain.getHandlers().get(0);
}
List<MessageHandler> handlers = new ArrayList<MessageHandler>(handlerChain.getHandlers());
Collections.sort(handlers, new OrderComparator());
handlerChain.setHandlers(handlers);
return handlerChain;
}

View File

@@ -19,20 +19,14 @@ package org.springframework.integration.handler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.integration.ConfigurationException;
import org.springframework.core.Ordered;
import org.springframework.integration.message.DefaultMessageCreator;
import org.springframework.integration.message.DefaultMessageMapper;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.MessageCreator;
import org.springframework.integration.message.MessageHandlingException;
import org.springframework.integration.message.MessageMapper;
import org.springframework.integration.util.DefaultMethodInvoker;
import org.springframework.integration.util.MethodInvoker;
import org.springframework.integration.util.NameResolvingMethodInvoker;
import org.springframework.integration.util.AbstractMethodInvokingAdapter;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@@ -50,18 +44,12 @@ import org.springframework.util.ObjectUtils;
*
* @author Mark Fisher
*/
public abstract class AbstractMessageHandlerAdapter implements MessageHandler, InitializingBean {
public abstract class AbstractMessageHandlerAdapter extends AbstractMethodInvokingAdapter implements MessageHandler {
public static final String OUTPUT_CHANNEL_NAME_KEY = "outputChannelName";
protected final Log logger = LogFactory.getLog(this.getClass());
private volatile Object object;
private volatile Method method;
private volatile String methodName;
private volatile int order;
private volatile boolean methodExpectsMessage;
@@ -69,32 +57,6 @@ public abstract class AbstractMessageHandlerAdapter implements MessageHandler, I
private volatile MessageCreator messageCreator = new DefaultMessageCreator();
protected volatile MethodInvoker invoker;
private volatile boolean initialized;
private final Object initializationMonitor = new Object();
public void setObject(Object object) {
this.object = object;
}
protected Object getObject() {
return this.object;
}
public void setMethod(Method method) {
this.method = method;
}
protected Method getMethod() {
return this.method;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public void setMethodExpectsMessage(boolean methodExpectsMessage) {
this.methodExpectsMessage = methodExpectsMessage;
@@ -110,45 +72,13 @@ public abstract class AbstractMessageHandlerAdapter implements MessageHandler, I
this.messageCreator = messageCreator;
}
public void afterPropertiesSet() {
synchronized (this.initializationMonitor) {
if (this.initialized) {
return;
}
if (this.object == null) {
throw new ConfigurationException("The target 'object' must not be null.");
}
if (this.method == null && this.methodName == null) {
throw new ConfigurationException("Either a 'method' or 'methodName' is required.");
}
if (this.method != null) {
if (this.methodName != null && !this.methodName.equals(this.method.getName())) {
throw new ConfigurationException("An ambiguity exists between the 'method' and 'methodName' properties. " +
"Note that only one of them is required, but if both are provided they must match.");
}
this.invoker = new DefaultMethodInvoker(this.object, this.method);
}
else {
this.invoker = new NameResolvingMethodInvoker(this.object, this.methodName);
}
this.initialized = true;
}
this.initialize();
}
/**
* Subclasses may override this method for custom initialization requirements.
*/
protected void initialize() {
}
public Message<?> handle(Message<?> message) {
if (!this.isInitialized()) {
this.afterPropertiesSet();
}
if (message == null) {
throw new IllegalArgumentException("message must not be null");
}
if (!this.initialized) {
this.afterPropertiesSet();
}
Object args[] = null;
Object mappingResult = (this.methodExpectsMessage) ? message : this.messageMapper.mapMessage(message);
if (mappingResult != null && mappingResult.getClass().isArray()
@@ -161,10 +91,10 @@ public abstract class AbstractMessageHandlerAdapter implements MessageHandler, I
try {
Object result = null;
try {
result = this.invoker.invokeMethod(args);
result = this.invokeMethod(args);
}
catch (NoSuchMethodException e) {
result = this.invoker.invokeMethod(message);
result = this.invokeMethod(message);
this.methodExpectsMessage = true;
}
if (result == null) {
@@ -173,12 +103,12 @@ public abstract class AbstractMessageHandlerAdapter implements MessageHandler, I
return this.handleReturnValue(result, message);
}
catch (InvocationTargetException e) {
throw new MessageHandlingException(message,
"Handler method '" + this.method + "' threw an Exception.", e.getTargetException());
throw new MessageHandlingException(message, "Handler method '"
+ this.getMethod() + "' threw an Exception.", e.getTargetException());
}
catch (Throwable e) {
throw new MessageHandlingException(message, "Failed to invoke handler method '" + this.method +
"' with arguments: " + ObjectUtils.nullSafeToString(args), e);
throw new MessageHandlingException(message, "Failed to invoke handler method '"
+ this.getMethod() + "' with arguments: " + ObjectUtils.nullSafeToString(args), e);
}
}

View File

@@ -14,9 +14,8 @@
* limitations under the License.
*/
package org.springframework.integration.transformer;
package org.springframework.integration.handler;
import org.springframework.integration.handler.MessageHandler;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.selector.MessageSelector;

View File

@@ -22,6 +22,7 @@ import java.util.Map;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.integration.ConfigurationException;
import org.springframework.integration.handler.MessageHandler;
import org.springframework.integration.util.AbstractMethodInvokingAdapter;
/**
* Base class for handler creators that generate a {@link MessageHandler}
@@ -33,6 +34,12 @@ 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) {
Object order = attributes.get("order");
if (order != null && order instanceof Integer) {
((AbstractMethodInvokingAdapter) handler).setOrder(((Integer) order).intValue());
}
}
if (handler instanceof InitializingBean) {
try {
((InitializingBean) handler).afterPropertiesSet();

View File

@@ -34,7 +34,7 @@ public class GenericMessage<T> implements Message<T> {
private final MessageHeader header = new MessageHeader();
private final T payload;
private volatile T payload;
private transient final IdGenerator defaultIdGenerator = new RandomUuidGenerator();
@@ -91,6 +91,11 @@ public class GenericMessage<T> implements Message<T> {
return this.payload;
}
public void setPayload(T payload) {
Assert.notNull(payload, "payload must not be null");
this.payload = payload;
}
public boolean isExpired() {
Date expiration = this.header.getExpiration();
return (expiration != null) ? expiration.getTime() < System.currentTimeMillis() : false;

View File

@@ -31,6 +31,8 @@ public interface Message<T> extends Serializable {
T getPayload();
void setPayload(T payload);
boolean isExpired();
void copyHeader(MessageHeader header, boolean overwriteExistingValues);

View File

@@ -0,0 +1,110 @@
/*
* 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.MessageMapper;
import org.springframework.integration.message.MessagingException;
import org.springframework.integration.util.AbstractMethodInvokingAdapter;
/**
* @author Mark Fisher
*/
public class AnnotationMethodTransformerAdapter extends AbstractMethodInvokingAdapter
implements MessageTransformer, 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();
}
@SuppressWarnings("unchecked")
public void transform(Message message) {
if (!this.isInitialized()) {
this.afterPropertiesSet();
}
if (message.getPayload() == null) {
return;
}
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 };
}
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("MessageTransformer returned a null result");
}
return;
}
if (result instanceof Properties && !(message.getPayload() instanceof Properties)) {
Properties propertiesToSet = (Properties) result;
for (Object keyObject : propertiesToSet.keySet()) {
String key = (String) keyObject;
message.getHeader().setProperty(key, propertiesToSet.getProperty(key));
}
}
else if (result instanceof Map && !(message.getPayload() instanceof Map)) {
Map<String, ?> attributesToSet = (Map) result;
for (String key : attributesToSet.keySet()) {
message.getHeader().setAttribute(key, attributesToSet.get(key));
}
}
else {
message.setPayload(result);
}
}
catch (Exception e) {
throw new MessagingException(message, "failed to transform message payload", e);
}
}
public Message<?> handle(Message<?> message) {
this.transform(message);
return message;
}
}

View File

@@ -0,0 +1,30 @@
/*
* 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 org.springframework.integration.message.Message;
/**
* Base interface for message transformers.
*
* @author Mark Fisher
*/
public interface MessageTransformer {
void transform(Message<?> message);
}

View File

@@ -0,0 +1,57 @@
/*
* 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.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.springframework.integration.message.Message;
/**
* @author Mark Fisher
*/
public class MessageTransformerChain implements MessageTransformer {
private final List<MessageTransformer> transformers = new CopyOnWriteArrayList<MessageTransformer>();
/**
* Add a transformer to the end of the chain.
*/
public void add(MessageTransformer transformer) {
this.transformers.add(transformer);
}
/**
* Add a transformer to the chain at the specified index.
*/
public void add(int index, MessageTransformer transformer) {
this.transformers.add(index, transformer);
}
public void setTransformers(List<MessageTransformer> transformers) {
this.transformers.clear();
this.transformers.addAll(transformers);
}
public void transform(Message<?> message) {
for (MessageTransformer next : transformers) {
next.transform(message);
}
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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 org.springframework.integration.message.Message;
import org.springframework.integration.message.MessagingException;
import org.springframework.integration.util.AbstractMethodInvokingAdapter;
/**
* @author Mark Fisher
*/
public class PayloadTransformerAdapter extends AbstractMethodInvokingAdapter implements MessageTransformer {
@SuppressWarnings("unchecked")
public void transform(Message message) {
try {
message.setPayload(this.invokeMethod(message.getPayload()));
} catch (Exception e) {
throw new MessagingException(message, "failed to transform message payload", e);
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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 org.springframework.integration.handler.MessageHandler;
import org.springframework.integration.message.Message;
/**
* @author Mark Fisher
*/
public class TransformerMessageHandlerAdapter implements MessageHandler {
private final MessageTransformer transformer;
public TransformerMessageHandlerAdapter(MessageTransformer transformer) {
this.transformer = transformer;
}
public Message<?> handle(Message<?> message) {
this.transformer.transform(message);
return message;
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.config;
import java.lang.reflect.Method;
import java.util.Map;
import org.springframework.integration.handler.MessageHandler;
import org.springframework.integration.handler.config.AbstractMessageHandlerCreator;
import org.springframework.integration.transformer.AnnotationMethodTransformerAdapter;
/**
* @author Mark Fisher
*/
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;
}
}

View File

@@ -0,0 +1,128 @@
/*
* 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.util;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.Ordered;
import org.springframework.integration.ConfigurationException;
/**
* An base class for adapters that invoke a specified method and target object.
* Either a {@link Method} reference or a 'methodName' may be provided, but both
* are not necessary. In fact, while preference is given to a {@link Method}
* reference if available, an Exception will be thrown if a non-matching
* 'methodName' has also been provided. Therefore, to avoid such ambiguity,
* it is recommended to provide just one or the other.
*
* @author Mark Fisher
*/
public abstract class AbstractMethodInvokingAdapter implements MethodInvoker, InitializingBean, Ordered {
protected final Log logger = LogFactory.getLog(this.getClass());
private volatile Object object;
private volatile Method method;
private volatile String methodName;
private volatile int order;
private volatile MethodInvoker invoker;
private volatile boolean initialized;
private final Object initializationMonitor = new Object();
public void setObject(Object object) {
this.object = object;
}
protected Object getObject() {
return this.object;
}
public void setMethod(Method method) {
this.method = method;
}
protected Method getMethod() {
return this.method;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public void setOrder(int order) {
this.order = order;
}
public int getOrder() {
return order;
}
protected boolean isInitialized() {
return this.initialized;
}
public void afterPropertiesSet() {
synchronized (this.initializationMonitor) {
if (this.initialized) {
return;
}
if (this.object == null) {
throw new ConfigurationException("The target 'object' must not be null.");
}
if (this.method == null && this.methodName == null) {
throw new ConfigurationException("Either a 'method' or 'methodName' is required.");
}
if (this.method != null) {
if (this.methodName != null && !this.methodName.equals(this.method.getName())) {
throw new ConfigurationException("An ambiguity exists between the 'method' and 'methodName' properties. " +
"Note that only one of them is required, but if both are provided they must match.");
}
this.invoker = new DefaultMethodInvoker(this.object, this.method);
}
else {
this.invoker = new NameResolvingMethodInvoker(this.object, this.methodName);
}
this.initialized = true;
}
this.initialize();
}
/**
* Subclasses may override this method for custom initialization requirements.
*/
protected void initialize() {
}
public Object invokeMethod(Object ... args) throws Exception {
if (!this.initialized) {
this.afterPropertiesSet();
}
return this.invoker.invokeMethod(args);
}
}

View File

@@ -34,6 +34,7 @@ import org.springframework.aop.framework.ProxyFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.integration.ConfigurationException;
import org.springframework.integration.annotation.Concurrency;
import org.springframework.integration.annotation.Handler;
@@ -42,6 +43,7 @@ import org.springframework.integration.annotation.MessageSource;
import org.springframework.integration.annotation.MessageTarget;
import org.springframework.integration.annotation.Polled;
import org.springframework.integration.annotation.Splitter;
import org.springframework.integration.annotation.Transformer;
import org.springframework.integration.bus.MessageBus;
import org.springframework.integration.channel.ChannelRegistry;
import org.springframework.integration.channel.ChannelRegistryAware;
@@ -372,6 +374,17 @@ public class MessagingAnnotationPostProcessorTests {
messageBus.stop();
}
@Test
public void testHandlerWithTransformers() {
MessageBus messageBus = new MessageBus();
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor(messageBus);
postProcessor.afterPropertiesSet();
HandlerWithTransformers testBean = new HandlerWithTransformers();
MessageHandler handler = (MessageHandler) postProcessor.postProcessAfterInitialization(testBean, "testBean");
Message<?> reply = handler.handle(new StringMessage("foo"));
assertEquals("PRE.FOO.post", reply.getPayload());
}
@MessageEndpoint(input="testChannel")
private static class TargetAnnotationTestBean {
@@ -508,7 +521,28 @@ public class MessagingAnnotationPostProcessorTests {
public String test() {
return "test";
}
}
private static class HandlerWithTransformers {
@Transformer
@Order(-1)
public String transformBefore(String input) {
return "pre." + input;
}
@Handler
@Order(0)
public String handle(String input) {
return input.toUpperCase();
}
@Transformer
@Order(1)
public String transformAfter(String input) {
return input + ".post";
}
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.integration.handler.annotation;
package org.springframework.integration.config.annotation;
import org.springframework.integration.annotation.Handler;

View File

@@ -18,6 +18,6 @@
<beans:constructor-arg ref="internal.MessageBus"/>
</beans:bean>
<beans:bean id="simpleHandler" class="org.springframework.integration.handler.annotation.SimpleHandlerTestBean"/>
<beans:bean id="simpleHandler" class="org.springframework.integration.config.annotation.SimpleHandlerTestBean"/>
</beans:beans>

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.integration.transformer;
package org.springframework.integration.handler;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -23,6 +23,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Test;
import org.springframework.integration.handler.MessageFilter;
import org.springframework.integration.handler.MessageHandler;
import org.springframework.integration.handler.MessageHandlerChain;
import org.springframework.integration.message.Message;

View File

@@ -0,0 +1,190 @@
/*
* 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import java.util.Date;
import java.util.Properties;
import org.junit.Test;
import org.springframework.integration.annotation.Transformer;
import org.springframework.integration.handler.annotation.HeaderAttribute;
import org.springframework.integration.handler.annotation.HeaderProperty;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.MessageHandlingException;
import org.springframework.integration.message.MessagingException;
import org.springframework.integration.message.StringMessage;
/**
* @author Mark Fisher
*/
public class AnnotationMethodTransformerAdapterTests {
@Test
public void testSimplePayload() throws Exception {
TestBean testBean = new TestBean();
AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter();
adapter.setObject(testBean);
adapter.setMethod(testBean.getClass().getMethod("exclaim", String.class));
Message<?> message = new StringMessage("foo");
adapter.transform(message);
assertEquals("FOO!", message.getPayload());
}
@Test
public void testTypeConversion() throws Exception {
TestBean testBean = new TestBean();
AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter();
adapter.setObject(testBean);
adapter.setMethod(testBean.getClass().getMethod("exclaim", String.class));
Message<?> message = new GenericMessage<Integer>(123);
adapter.transform(message);
assertEquals("123!", message.getPayload());
}
@Test(expected=MessagingException.class)
public void testTypeConversionFailure() throws Exception {
TestBean testBean = new TestBean();
AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter();
adapter.setObject(testBean);
adapter.setMethod(testBean.getClass().getMethod("exclaim", String.class));
Message<?> message = new GenericMessage<Date>(new Date());
adapter.transform(message);
}
@Test
public void testHeaderAttributeAnnotation() throws Exception {
TestBean testBean = new TestBean();
AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter();
adapter.setObject(testBean);
adapter.setMethod(testBean.getClass().getMethod("attributeTest", String.class, Integer.class));
Message<?> message = new StringMessage("foo");
message.getHeader().setAttribute("number", 123);
adapter.transform(message);
assertEquals("foo123", message.getPayload());
}
@Test(expected=MessageHandlingException.class)
public void testHeaderAttributeNotProvided() throws Exception {
TestBean testBean = new TestBean();
AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter();
adapter.setObject(testBean);
adapter.setMethod(testBean.getClass().getMethod("attributeTest", String.class, Integer.class));
Message<?> message = new StringMessage("foo");
message.getHeader().setAttribute("wrong", 123);
adapter.transform(message);
}
@Test
public void testHeaderPropertyAnnotation() throws Exception {
TestBean testBean = new TestBean();
AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter();
adapter.setObject(testBean);
adapter.setMethod(testBean.getClass().getMethod("propertyTest", String.class, String.class));
Message<?> message = new StringMessage("foo");
message.getHeader().setProperty("suffix", "bar");
adapter.transform(message);
assertEquals("foobar", message.getPayload());
}
@Test(expected=MessageHandlingException.class)
public void testHeaderPropertyNotAvailable() throws Exception {
TestBean testBean = new TestBean();
AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter();
adapter.setObject(testBean);
adapter.setMethod(testBean.getClass().getMethod("propertyTest", String.class, String.class));
Message<?> message = new StringMessage("foo");
message.getHeader().setProperty("wrong", "bar");
adapter.transform(message);
}
@Test
public void testPropertyEnricher() throws Exception {
TestBean testBean = new TestBean();
AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter();
adapter.setObject(testBean);
adapter.setMethod(testBean.getClass().getMethod("propertyEnricherTest", String.class));
Message<?> message = new StringMessage("test");
message.getHeader().setProperty("prop1", "bad");
message.getHeader().setProperty("prop3", "baz");
adapter.transform(message);
assertEquals("test", message.getPayload());
assertEquals("foo", message.getHeader().getProperty("prop1"));
assertEquals("bar", message.getHeader().getProperty("prop2"));
assertEquals("baz", message.getHeader().getProperty("prop3"));
}
@Test
public void testPropertyPayload() throws Exception {
TestBean testBean = new TestBean();
AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter();
adapter.setObject(testBean);
adapter.setMethod(testBean.getClass().getMethod("propertyPayloadTest", Properties.class));
Properties props = new Properties();
props.setProperty("prop1", "bad");
props.setProperty("prop3", "baz");
Message<Properties> message = new GenericMessage<Properties>(props);
adapter.transform(message);
assertEquals(Properties.class, message.getPayload().getClass());
Properties payload = message.getPayload();
assertEquals("foo", payload.getProperty("prop1"));
assertEquals("bar", payload.getProperty("prop2"));
assertEquals("baz", payload.getProperty("prop3"));
assertNull(message.getHeader().getProperty("prop1"));
assertNull(message.getHeader().getProperty("prop2"));
assertNull(message.getHeader().getProperty("prop3"));
}
private static class TestBean {
@Transformer
public String exclaim(String s) {
return s.toUpperCase() + "!";
}
@Transformer
public String attributeTest(String s, @HeaderAttribute("number") Integer num) {
return s + num;
}
@Transformer
public String propertyTest(String s, @HeaderProperty("suffix") String suffix) {
return s + suffix;
}
@Transformer
public Properties propertyEnricherTest(String s) {
Properties properties = new Properties();
properties.setProperty("prop1", "foo");
properties.setProperty("prop2", "bar");
return properties;
}
@Transformer
public Properties propertyPayloadTest(Properties properties) {
properties.setProperty("prop1", "foo");
properties.setProperty("prop2", "bar");
return properties;
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* 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 static org.junit.Assert.assertEquals;
import java.util.Date;
import org.junit.Test;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.MessagingException;
import org.springframework.integration.message.StringMessage;
/**
* @author Mark Fisher
*/
public class PayloadTransformerAdapterTests {
@Test
public void testSimpleMethod() {
PayloadTransformerAdapter adapter = new PayloadTransformerAdapter();
adapter.setObject(new TestBean());
adapter.setMethodName("exclaim");
Message<?> message = new StringMessage("foo");
adapter.transform(message);
assertEquals("FOO!", message.getPayload());
}
@Test
public void testTypeConversion() {
PayloadTransformerAdapter adapter = new PayloadTransformerAdapter();
adapter.setObject(new TestBean());
adapter.setMethodName("exclaim");
Message<?> message = new GenericMessage<Integer>(123);
adapter.transform(message);
assertEquals("123!", message.getPayload());
}
@Test(expected=MessagingException.class)
public void testTypeConversionFailure() {
PayloadTransformerAdapter adapter = new PayloadTransformerAdapter();
adapter.setObject(new TestBean());
adapter.setMethodName("exclaim");
Message<?> message = new GenericMessage<Date>(new Date());
adapter.transform(message);
}
private static class TestBean {
String exclaim(String input) {
return input.toUpperCase() + "!";
}
}
}