Removed AbstractMessageHandler and DefaultMessageHandler.

This commit is contained in:
Mark Fisher
2008-09-05 21:43:58 +00:00
parent bbd28e9817
commit 4eaec4ce68
3 changed files with 59 additions and 386 deletions

View File

@@ -1,253 +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 java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
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;
import org.springframework.integration.handler.annotation.MethodArgumentMessageMapper;
import org.springframework.integration.message.Message;
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.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* A base class for any {@link MessageHandler} that may act as an adapter
* by invoking a "plain" (not Message-aware) method for a given target object.
* When used as an adapter, the target Object is mandatory and either a
* {@link Method} reference or a 'methodName' must be provided. If no Object
* and Method are provided, the handler will simply process the request
* Message's payload.
*
* @author Mark Fisher
*/
public abstract class AbstractMessageHandler implements MessageHandler, Ordered, InitializingBean {
protected static final Log logger = LogFactory.getLog(AbstractMessageHandler.class);
private volatile boolean methodExpectsMessage;
private volatile Object object;
private volatile Method method;
private volatile String methodName;
private volatile MessageMapper<Object, Object[]> methodArgumentMapper;
private volatile MethodInvoker invoker;
private volatile int order = Ordered.LOWEST_PRECEDENCE;
private volatile boolean initialized;
private final Object initializationMonitor = new Object();
public AbstractMessageHandler(Object object, Method method) {
Assert.notNull(object, "object must not be null");
this.object = object;
this.setMethod(method);
}
public AbstractMessageHandler(Object object, String methodName) {
Assert.notNull(object, "object must not be null");
this.object = object;
this.setMethodName(methodName);
}
public AbstractMessageHandler() {
}
public void setObject(Object object) {
this.object = object;
}
public void setMethod(Method method) {
Assert.notNull(method, "method must not be null");
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 0) {
throw new ConfigurationException("method must accept at least one parameter");
}
if (parameterTypes.length == 1 && Message.class.isAssignableFrom(parameterTypes[0])) {
this.methodExpectsMessage = true;
}
this.method = method;
this.methodName = method.getName();
}
public void setMethodName(String methodName) {
Assert.notNull(methodName, "methodName must not be null");
if (this.method != null && !this.method.getName().equals(methodName)) {
this.method = null;
}
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) {
return;
}
if (this.object != null) {
if (this.method == null) {
if (this.methodName == null) {
throw new IllegalStateException("either the 'method' or 'methodName' must be provided when the 'object' property has been set");
}
final List<Method> candidates = new ArrayList<Method>();
ReflectionUtils.doWithMethods(this.object.getClass(), new ReflectionUtils.MethodCallback() {
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
if (method.getName().equals(AbstractMessageHandler.this.methodName)) {
candidates.add(method);
}
}
});
if (candidates.size() == 0) {
throw new ConfigurationException("no such method '" + this.methodName
+ "' on target class [" + this.object.getClass() + "]");
}
if (candidates.size() == 1) {
this.setMethod(candidates.get(0));
}
}
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.initialized = true;
}
}
public Message<?> handle(Message<?> requestMessage) {
if (requestMessage == null || requestMessage.getPayload() == null) {
if (logger.isDebugEnabled()) {
logger.debug("message handler received a null message");
}
return null;
}
if (!this.initialized) {
this.afterPropertiesSet();
}
Object result = (this.invoker != null) ?
this.invokeHandlerMethod(requestMessage) : requestMessage.getPayload();
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 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
* @return the Message to be sent to the reply MessageTarget
*/
protected abstract Message<?> createReplyMessage(Object result, Message<?> requestMessage);
private Object invokeHandlerMethod(Message<?> message) {
Object args[] = null;
Object mappingResult = this.methodExpectsMessage ? message
: this.mapMessageToMethodArguments(message);
if (mappingResult != null && mappingResult.getClass().isArray()
&& (Object.class.isAssignableFrom(mappingResult.getClass().getComponentType()))) {
args = (Object[]) mappingResult;
}
else {
args = new Object[] { mappingResult };
}
try {
Object result = null;
try {
result = this.invoker.invokeMethod(args);
}
catch (NoSuchMethodException e) {
try {
result = this.invoker.invokeMethod(message);
this.methodExpectsMessage = true;
}
catch (NoSuchMethodException e2) {
throw new MessageHandlingException(message, "unable to resolve method for args: "
+ StringUtils.arrayToCommaDelimitedString(args));
}
}
if (result == null) {
return null;
}
return result;
}
catch (InvocationTargetException e) {
throw new MessageHandlingException(message, "Handler method '"
+ this.methodName + "' threw an Exception.", e.getTargetException());
}
catch (Throwable e) {
throw new MessageHandlingException(message, "Failed to invoke handler method '"
+ this.methodName + "' with arguments: " + ObjectUtils.nullSafeToString(args), e);
}
}
private Object[] mapMessageToMethodArguments(Message message) {
if (this.methodArgumentMapper != null) {
return this.methodArgumentMapper.mapMessage(message);
}
return new Object[] { message.getPayload() };
}
}

View File

@@ -1,48 +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.MessageBuilder;
import org.springframework.integration.message.MessageHeaders;
/**
* The default MessageHandler implementation. Creates a Message for the reply payload.
* The request Message's headers are copied and the request Message's ID is set as this
* reply Message's correlationId.
*
* @author Mark Fisher
*/
public class DefaultMessageHandler extends AbstractMessageHandler {
@Override
protected Message<?> createReplyMessage(Object result, Message<?> requestMessage) {
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

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.integration.handler;
package org.springframework.integration.message;
import static org.junit.Assert.assertEquals;
@@ -28,159 +28,133 @@ import org.springframework.integration.annotation.Header;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.MessageBuilder;
import org.springframework.integration.message.MessagingException;
import org.springframework.integration.message.MessageMappingMethodInvoker;
import org.springframework.integration.message.StringMessage;
/**
* @author Mark Fisher
*/
public class DefaultMessageHandlerTests {
public class MessageMappingMethodInvokerTests {
@Test
public void payloadAsMethodParameterAndObjectAsReturnValue() {
DefaultMessageHandler handler = new DefaultMessageHandler();
handler.setObject(new TestHandler());
handler.setMethodName("acceptPayloadAndReturnObject");
handler.afterPropertiesSet();
Message<?> result = handler.handle(new StringMessage("testing"));
assertEquals("testing-1", result.getPayload());
MessageMappingMethodInvoker invoker = new MessageMappingMethodInvoker(
new TestBean(), "acceptPayloadAndReturnObject");
Object result = invoker.invokeMethod(new StringMessage("testing"));
assertEquals("testing-1", result);
}
@Test
public void payloadAsMethodParameterAndMessageAsReturnValue() {
DefaultMessageHandler handler = new DefaultMessageHandler();
handler.setObject(new TestHandler());
handler.setMethodName("acceptPayloadAndReturnMessage");
handler.afterPropertiesSet();
Message<?> result = handler.handle(new StringMessage("testing"));
MessageMappingMethodInvoker invoker = new MessageMappingMethodInvoker(
new TestBean(), "acceptPayloadAndReturnMessage");
Message<?> result = (Message<?>) invoker.invokeMethod(new StringMessage("testing"));
assertEquals("testing-2", result.getPayload());
}
@Test
public void messageAsMethodParameterAndObjectAsReturnValue() {
DefaultMessageHandler handler = new DefaultMessageHandler();
handler.setObject(new TestHandler());
handler.setMethodName("acceptMessageAndReturnObject");
handler.afterPropertiesSet();
Message<?> result = handler.handle(new StringMessage("testing"));
assertEquals("testing-3", result.getPayload());
MessageMappingMethodInvoker invoker = new MessageMappingMethodInvoker(
new TestBean(), "acceptMessageAndReturnObject");
Object result = invoker.invokeMethod(new StringMessage("testing"));
assertEquals("testing-3", result);
}
@Test
public void messageAsMethodParameterAndMessageAsReturnValue() {
DefaultMessageHandler handler = new DefaultMessageHandler();
handler.setObject(new TestHandler());
handler.setMethodName("acceptMessageAndReturnMessage");
handler.afterPropertiesSet();
Message<?> result = handler.handle(new StringMessage("testing"));
MessageMappingMethodInvoker invoker = new MessageMappingMethodInvoker(
new TestBean(), "acceptMessageAndReturnMessage");
Message<?> result = (Message<?>) invoker.invokeMethod(new StringMessage("testing"));
assertEquals("testing-4", result.getPayload());
}
@Test
public void messageSubclassAsMethodParameterAndMessageAsReturnValue() {
DefaultMessageHandler handler = new DefaultMessageHandler();
handler.setObject(new TestHandler());
handler.setMethodName("acceptMessageSubclassAndReturnMessage");
handler.afterPropertiesSet();
Message<?> result = handler.handle(new StringMessage("testing"));
MessageMappingMethodInvoker invoker = new MessageMappingMethodInvoker(
new TestBean(), "acceptMessageSubclassAndReturnMessage");
Message<?> result = (Message<?>) invoker.invokeMethod(new StringMessage("testing"));
assertEquals("testing-5", result.getPayload());
}
@Test
public void messageSubclassAsMethodParameterAndMessageSubclassAsReturnValue() {
DefaultMessageHandler handler = new DefaultMessageHandler();
handler.setObject(new TestHandler());
handler.setMethodName("acceptMessageSubclassAndReturnMessageSubclass");
handler.afterPropertiesSet();
Message<?> result = handler.handle(new StringMessage("testing"));
MessageMappingMethodInvoker invoker = new MessageMappingMethodInvoker(
new TestBean(), "acceptMessageSubclassAndReturnMessageSubclass");
Message<?> result = (Message<?>) invoker.invokeMethod(new StringMessage("testing"));
assertEquals("testing-6", result.getPayload());
}
@Test
public void payloadAndHeaderAnnotationMethodParametersAndObjectAsReturnValue() {
DefaultMessageHandler handler = new DefaultMessageHandler();
handler.setObject(new TestHandler());
handler.setMethodName("acceptPayloadAndHeaderAndReturnObject");
handler.afterPropertiesSet();
MessageMappingMethodInvoker invoker = new MessageMappingMethodInvoker(
new TestBean(), "acceptPayloadAndHeaderAndReturnObject");
Message<?> request = MessageBuilder.fromPayload("testing")
.setHeader("number", new Integer(123)).build();
Message<?> result = handler.handle(request);
assertEquals("testing-123", result.getPayload());
Object result = invoker.invokeMethod(request);
assertEquals("testing-123", result);
}
@Test
public void messageOnlyWithAnnotatedMethod() throws Exception {
AnnotatedTestService handler = new AnnotatedTestService();
Method method = handler.getClass().getMethod("messageOnly", Message.class);
DefaultMessageHandler adapter = new DefaultMessageHandler();
adapter.setObject(handler);
adapter.setMethod(method);
Message<?> result = adapter.handle(new StringMessage("foo"));
assertEquals("foo", result.getPayload());
AnnotatedTestService service = new AnnotatedTestService();
Method method = service.getClass().getMethod("messageOnly", Message.class);
MessageMappingMethodInvoker invoker = new MessageMappingMethodInvoker(service, method);
Object result = invoker.invokeMethod(new StringMessage("foo"));
assertEquals("foo", result);
}
@Test
public void payloadWithAnnotatedMethod() throws Exception {
AnnotatedTestService handler = new AnnotatedTestService();
Method method = handler.getClass().getMethod("integerMethod", Integer.class);
DefaultMessageHandler adapter = new DefaultMessageHandler();
adapter.setObject(handler);
adapter.setMethod(method);
Message<?> result = adapter.handle(new GenericMessage<Integer>(new Integer(123)));
assertEquals(new Integer(123), result.getPayload());
AnnotatedTestService service = new AnnotatedTestService();
Method method = service.getClass().getMethod("integerMethod", Integer.class);
MessageMappingMethodInvoker invoker = new MessageMappingMethodInvoker(service, method);
Object result = invoker.invokeMethod(new GenericMessage<Integer>(new Integer(123)));
assertEquals(new Integer(123), result);
}
@Test
public void convertedPayloadWithAnnotatedMethod() throws Exception {
AnnotatedTestService handler = new AnnotatedTestService();
Method method = handler.getClass().getMethod("integerMethod", Integer.class);
DefaultMessageHandler adapter = new DefaultMessageHandler();
adapter.setObject(handler);
adapter.setMethod(method);
Message<?> result = adapter.handle(new StringMessage("456"));
assertEquals(new Integer(456), result.getPayload());
AnnotatedTestService service = new AnnotatedTestService();
Method method = service.getClass().getMethod("integerMethod", Integer.class);
MessageMappingMethodInvoker invoker = new MessageMappingMethodInvoker(service, method);
Object result = invoker.invokeMethod(new StringMessage("456"));
assertEquals(new Integer(456), result);
}
@Test(expected = MessagingException.class)
@Test(expected = IllegalArgumentException.class)
public void conversionFailureWithAnnotatedMethod() throws Exception {
AnnotatedTestService handler = new AnnotatedTestService();
Method method = handler.getClass().getMethod("integerMethod", Integer.class);
DefaultMessageHandler adapter = new DefaultMessageHandler();
adapter.setObject(handler);
adapter.setMethod(method);
Message<?> result = adapter.handle(new StringMessage("foo"));
assertEquals(new Integer(123), result.getPayload());
AnnotatedTestService service = new AnnotatedTestService();
Method method = service.getClass().getMethod("integerMethod", Integer.class);
MessageMappingMethodInvoker invoker = new MessageMappingMethodInvoker(service, method);
Object result = invoker.invokeMethod(new StringMessage("foo"));
assertEquals(new Integer(123), result);
}
@Test
public void messageAndHeaderWithAnnotatedMethod() throws Exception {
AnnotatedTestService handler = new AnnotatedTestService();
Method method = handler.getClass().getMethod("messageAndHeader", Message.class, Integer.class);
DefaultMessageHandler adapter = new DefaultMessageHandler();
adapter.setObject(handler);
adapter.setMethod(method);
AnnotatedTestService service = new AnnotatedTestService();
Method method = service.getClass().getMethod("messageAndHeader", Message.class, Integer.class);
MessageMappingMethodInvoker invoker = new MessageMappingMethodInvoker(service, method);
Message<String> message = MessageBuilder.fromPayload("foo")
.setHeader("number", 42).build();
Message<?> result = adapter.handle(message);
assertEquals("foo-42", result.getPayload());
Object result = invoker.invokeMethod(message);
assertEquals("foo-42", result);
}
@Test
public void multipleHeadersWithAnnotatedMethod() throws Exception {
AnnotatedTestService handler = new AnnotatedTestService();
Method method = handler.getClass().getMethod("twoHeaders", String.class, Integer.class);
DefaultMessageHandler adapter = new DefaultMessageHandler();
adapter.setObject(handler);
adapter.setMethod(method);
AnnotatedTestService service = new AnnotatedTestService();
Method method = service.getClass().getMethod("twoHeaders", String.class, Integer.class);
MessageMappingMethodInvoker invoker = new MessageMappingMethodInvoker(service, method);
Message<String> message = MessageBuilder.fromPayload("foo")
.setHeader("prop", "bar")
.setHeader("number", 42).build();
Message<?> result = adapter.handle(message);
assertEquals("bar-42", result.getPayload());
Object result = invoker.invokeMethod(message);
assertEquals("bar-42", result);
}
private static class TestHandler {
private static class TestBean {
public String acceptPayloadAndReturnObject(String s) {
return s + "-1";