Added header-copying constructor to GenericMessage (INT-123). Also simplified the AbstractMessageHandlerAdapter.

This commit is contained in:
Mark Fisher
2008-02-22 19:34:08 +00:00
parent 094a131b1a
commit 51d9c8e42c
9 changed files with 104 additions and 58 deletions

View File

@@ -21,9 +21,10 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.Ordered;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.MessageHeader;
import org.springframework.integration.message.MessageMapper;
import org.springframework.integration.message.SimplePayloadMessageMapper;
import org.springframework.integration.util.SimpleMethodInvoker;
import org.springframework.util.Assert;
@@ -45,8 +46,6 @@ public abstract class AbstractMessageHandlerAdapter<T> implements MessageHandler
private volatile String methodName;
private volatile MessageMapper mapper = new SimplePayloadMessageMapper();
private volatile SimpleMethodInvoker<T> invoker;
private volatile int order = Integer.MAX_VALUE;
@@ -61,28 +60,11 @@ public abstract class AbstractMessageHandlerAdapter<T> implements MessageHandler
this.object = object;
}
protected Object getObject() {
return this.object;
}
public void setMethodName(String methodName) {
Assert.notNull(methodName, "'methodName' must not be null");
this.methodName = methodName;
}
public String getMethodName() {
return this.methodName;
}
public void setMapper(MessageMapper mapper) {
Assert.notNull(mapper, "'mapper' must not be null");
this.mapper = mapper;
}
protected MessageMapper getMapper() {
return this.mapper;
}
public void setOrder(int order) {
this.order = order;
}
@@ -94,22 +76,22 @@ public abstract class AbstractMessageHandlerAdapter<T> implements MessageHandler
public final void afterPropertiesSet() {
this.validate();
synchronized (this.lifecycleMonitor) {
if (this.initialized) {
return;
}
this.invoker = new SimpleMethodInvoker<T>(this.object, this.methodName);
this.initialized = true;
}
}
public final boolean isInitialized() {
synchronized (this.lifecycleMonitor) {
return this.initialized;
}
}
public final Message<?> handle(Message<?> message) {
if (!this.initialized) {
this.afterPropertiesSet();
}
Object result = this.doHandle(message, invoker);
if (result != null) {
Message<?> reply = (result instanceof Message) ? (Message<?>) result :
this.mapper.toMessage(result);
this.createReplyMessage(result, message.getHeader());
Object correlationId = reply.getHeader().getCorrelationId();
if (correlationId == null) {
Object orginalCorrelationId = message.getHeader().getCorrelationId();
@@ -127,6 +109,10 @@ public abstract class AbstractMessageHandlerAdapter<T> implements MessageHandler
protected void validate() {
}
protected Message<?> createReplyMessage(Object payload, MessageHeader originalMessageHeader) {
return new GenericMessage(payload, originalMessageHeader);
}
/**
* Subclasses must implement this method. The invoker has been created for
* the provided target object and method. May return an object of type

View File

@@ -33,24 +33,23 @@ import org.springframework.integration.util.SimpleMethodInvoker;
*/
public class DefaultMessageHandlerAdapter<T> extends AbstractMessageHandlerAdapter<T> implements Ordered {
private boolean shouldUseMapperOnInvocation = true;
private boolean expectsMessage = false;
/**
* Specify whether the handler method should use the
* {@link org.springframework.integration.message.MessageMapper} when
* invoking the target method. Default is <code>true</code>. To force
* passing the {@link Message} directly, set this to <code>false</code>.
* Specify whether the handler should pass the message when invoking the
* target method. The default is <code>false</code> indicating that the
* message's <em>payload</em> should be passed as the argument. To force
* passing the {@link Message} directly, set this to <code>true</code>.
*/
public void setShouldUseMapperOnInvocation(boolean shouldUseMapperOnInvocation) {
this.shouldUseMapperOnInvocation = shouldUseMapperOnInvocation;
public void setExpectsMessage(boolean expectsMessage) {
this.expectsMessage = expectsMessage;
}
public Object doHandle(Message message, SimpleMethodInvoker invoker) {
if (this.shouldUseMapperOnInvocation) {
return invoker.invokeMethod(this.getMapper().fromMessage(message));
if (this.expectsMessage) {
return invoker.invokeMethod(message);
}
return invoker.invokeMethod(message);
return invoker.invokeMethod(message.getPayload());
}
}

View File

@@ -41,8 +41,7 @@ public class DefaultMessageHandlerCreator extends AbstractMessageHandlerCreator
throw new MessagingConfigurationException("exactly one method parameter is required");
}
DefaultMessageHandlerAdapter<Object> adapter = new DefaultMessageHandlerAdapter<Object>();
boolean expectsMessage = (Message.class.isAssignableFrom(types[0]));
adapter.setShouldUseMapperOnInvocation(!expectsMessage);
adapter.setExpectsMessage(Message.class.isAssignableFrom(types[0]));
return adapter;
}

View File

@@ -17,9 +17,10 @@
package org.springframework.integration.message;
import java.util.Date;
import java.util.Set;
import org.springframework.integration.util.RandomUuidGenerator;
import org.springframework.integration.util.IdGenerator;
import org.springframework.integration.util.RandomUuidGenerator;
import org.springframework.util.Assert;
/**
@@ -63,6 +64,11 @@ public class GenericMessage<T> implements Message<T> {
this.payload = payload;
}
public GenericMessage(T payload, MessageHeader headerToCopy) {
this(payload);
this.copyHeader(headerToCopy);
}
public Object getId() {
return this.id;
@@ -85,4 +91,16 @@ public class GenericMessage<T> implements Message<T> {
return "[ID=" + this.id + "][Header=" + this.header + "][Payload='" + this.payload + "']";
}
private void copyHeader(final MessageHeader headerToCopy) {
Set<String> propertyNames = headerToCopy.getPropertyNames();
for (String key : propertyNames) {
this.header.setProperty(key, headerToCopy.getProperty(key));
}
Set<String> attributeNames = headerToCopy.getAttributeNames();
for (String key : attributeNames) {
this.header.setAttribute(key, headerToCopy.getAttribute(key));
}
this.header.setReturnAddress(headerToCopy.getReturnAddress());
}
}

View File

@@ -18,10 +18,10 @@ package org.springframework.integration.message;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* A holder for Message metadata. This includes information that may be used by
@@ -48,7 +48,7 @@ public class MessageHeader {
private final Properties properties = new Properties();
private final Map<String, Object> attributes = new ConcurrentHashMap<String, Object>();
private final ConcurrentMap<String, Object> attributes = new ConcurrentHashMap<String, Object>();
/**
@@ -110,8 +110,8 @@ public class MessageHeader {
return this.properties.getProperty(key);
}
public void setProperty(String key, String value) {
this.properties.setProperty(key, value);
public String setProperty(String key, String value) {
return (String) this.properties.setProperty(key, value);
}
public Set<String> getPropertyNames() {
@@ -126,8 +126,12 @@ public class MessageHeader {
return this.attributes.get(key);
}
public void setAttribute(String key, Object value) {
this.attributes.put(key, value);
public Object setAttribute(String key, Object value) {
return this.attributes.put(key, value);
}
public Object setAttributeIfAbsent(String key, Object value) {
return this.attributes.putIfAbsent(key, value);
}
public Set<String> getAttributeNames() {

View File

@@ -67,9 +67,6 @@ public class RouterMessageHandlerAdapter extends AbstractMessageHandlerAdapter i
@Override
protected Object doHandle(Message message, SimpleMethodInvoker invoker) {
if (!this.isInitialized()) {
this.afterPropertiesSet();
}
if (method.getParameterTypes().length != 1) {
throw new MessagingConfigurationException(
"method must accept exactly one parameter");

View File

@@ -51,7 +51,7 @@ public class DefaultMessageHandlerAdapterTests {
@Test
public void testMessageAsMethodParameterAndObjectAsReturnValue() {
DefaultMessageHandlerAdapter<TestHandler> adapter = new DefaultMessageHandlerAdapter<TestHandler>();
adapter.setShouldUseMapperOnInvocation(false);
adapter.setExpectsMessage(true);
adapter.setObject(new TestHandler());
adapter.setMethodName("acceptMessageAndReturnObject");
adapter.afterPropertiesSet();
@@ -62,7 +62,7 @@ public class DefaultMessageHandlerAdapterTests {
@Test
public void testMessageAsMethodParameterAndMessageAsReturnValue() {
DefaultMessageHandlerAdapter<TestHandler> adapter = new DefaultMessageHandlerAdapter<TestHandler>();
adapter.setShouldUseMapperOnInvocation(false);
adapter.setExpectsMessage(true);
adapter.setObject(new TestHandler());
adapter.setMethodName("acceptMessageAndReturnMessage");
adapter.afterPropertiesSet();
@@ -73,7 +73,7 @@ public class DefaultMessageHandlerAdapterTests {
@Test
public void testMessageSubclassAsMethodParameterAndMessageAsReturnValue() {
DefaultMessageHandlerAdapter<TestHandler> adapter = new DefaultMessageHandlerAdapter<TestHandler>();
adapter.setShouldUseMapperOnInvocation(false);
adapter.setExpectsMessage(true);
adapter.setObject(new TestHandler());
adapter.setMethodName("acceptMessageSubclassAndReturnMessage");
adapter.afterPropertiesSet();
@@ -84,7 +84,7 @@ public class DefaultMessageHandlerAdapterTests {
@Test
public void testMessageSubclassAsMethodParameterAndMessageSubclassAsReturnValue() {
DefaultMessageHandlerAdapter<TestHandler> adapter = new DefaultMessageHandlerAdapter<TestHandler>();
adapter.setShouldUseMapperOnInvocation(false);
adapter.setExpectsMessage(true);
adapter.setObject(new TestHandler());
adapter.setMethodName("acceptMessageSubclassAndReturnMessageSubclass");
adapter.afterPropertiesSet();

View File

@@ -16,6 +16,7 @@
package org.springframework.integration.message;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -55,4 +56,14 @@ public class GenericMessageTests {
assertFalse(message.isExpired());
}
@Test
public void testMessageHeaderCopied() {
MessageHeader header = new MessageHeader();
header.setAttribute("testAttribute", new Integer(123));
header.setProperty("testProperty", "foo");
GenericMessage<String> message = new GenericMessage<String>("test", header);
assertEquals(new Integer(123), message.getHeader().getAttribute("testAttribute"));
assertEquals("foo", message.getHeader().getProperty("testProperty"));
}
}

View File

@@ -17,6 +17,7 @@
package org.springframework.integration.message;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -39,24 +40,55 @@ public class MessageHeaderTests {
@Test
public void testAttributes() {
MessageHeader header = new MessageHeader();
Integer integer = new Integer(123);
header.setAttribute("test", integer);
assertEquals(integer, header.getAttribute("test"));
Integer value = new Integer(123);
Object previousValue = header.setAttribute("test", value);
assertNull(previousValue);
assertEquals(value, header.getAttribute("test"));
assertNull(header.getAttribute("nosuchattribute"));
Set<String> names = header.getAttributeNames();
assertEquals(1, names.size());
assertTrue(names.contains("test"));
Integer newValue = new Integer(456);
previousValue = header.setAttribute("test", newValue);
assertEquals(value, previousValue);
assertEquals(newValue, header.getAttribute("test"));
}
@Test
public void testProperties() {
MessageHeader header = new MessageHeader();
header.setProperty("foo", "bar");
String previousValue = header.setProperty("foo", "bar");
assertNull(previousValue);
assertEquals("bar", header.getProperty("foo"));
assertNull(header.getProperty("nosuchproperty"));
Set<String> names = header.getPropertyNames();
assertEquals(1, names.size());
assertTrue(names.contains("foo"));
previousValue = header.setProperty("foo", "baz");
assertEquals("bar", previousValue);
assertEquals("baz", header.getProperty("foo"));
}
@Test
public void testSetAttributeIfAbsent() {
MessageHeader header = new MessageHeader();
Integer integer = new Integer(123);
assertNull(header.getAttribute("test"));
assertFalse(header.getAttributeNames().contains("test"));
Object existingValue = header.setAttributeIfAbsent("test", integer);
assertNull(existingValue);
assertEquals(integer, header.getAttribute("test"));
assertTrue(header.getAttributeNames().contains("test"));
}
@Test
public void testSetAttributeIfAbsentDoesNotOverride() {
MessageHeader header = new MessageHeader();
Integer originalValue = new Integer(123);
header.setAttributeIfAbsent("test", originalValue);
Object existingValue = header.setAttributeIfAbsent("test", new Integer(456));
assertEquals(originalValue, header.getAttribute("test"));
assertEquals(originalValue, existingValue);
}
}