Adds AggregatorAdapter and support for the 'method' attribute in <aggregtor> elements (INT-137). Target methods must conform with a signature pattern as follows: ReturnType methodName (Collection<CollectionType> args).

If CollectionType is Message or any of its descendants (including parametrized types), the aggregator will pass the collection of messages to be aggregated 'as is', leaving to the target method to extract the payloads. If CollectionType is non-Message, the payloads will be extracted and passed to the target collection. 
If the result of the method call is not a Message, it will be wrapped in a GenericMessage.
This commit is contained in:
Marius Bogoevici
2008-03-03 23:21:39 +00:00
parent 1e19a96a38
commit 03f4c8e1ea
9 changed files with 370 additions and 9 deletions

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2002-2007 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.config;
import java.util.Collection;
/**
* @author Marius Bogoevici
*/
public class Adder {
public Long add(Collection<Long> results) {
long total = 0;
for (long partialResult: results) {
total += partialResult;
}
return total;
}
}

View File

@@ -24,9 +24,11 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.StringMessage;
import org.springframework.integration.router.AggregatingMessageHandler;
@@ -41,7 +43,6 @@ public class AggregatorParserTests {
private ApplicationContext context;
@Before
public void setUp() {
context = new ClassPathXmlApplicationContext("aggregatorParserTests.xml", this.getClass());
@@ -100,10 +101,29 @@ public class AggregatorParserTests {
99, getPropertyValue(completeAggregatingMessageHandler, "trackedCorrelationIdCapacity", int.class));
}
@Test
public void testSimpleJavaBeanAggregator() {
AggregatingMessageHandler addingAggregator = (AggregatingMessageHandler) context.getBean("aggregatorWithReferenceAndMethod");
List<Message<?>> outboundMessages = new ArrayList<Message<?>>();
outboundMessages.add(createMessage(1l, "id1", 3, 1, null));
outboundMessages.add(createMessage(2l, "id1", 3, 3, null));
outboundMessages.add(createMessage(3l, "id1", 3, 2, null));
for (Message<?> message : outboundMessages) {
addingAggregator.handle(message);
}
MessageChannel replyChannel = (MessageChannel) context.getBean("replyChannel");
Message response = replyChannel.receive();
Assert.assertEquals(6l, response.getPayload());
}
@Test(expected=BeanCreationException.class)
public void testMissingMethodOnAggregator() {
context = new ClassPathXmlApplicationContext("invalidMethodNameAggregator.xml", this.getClass());
}
private static Message<?> createMessage(String payload, Object correlationId, int sequenceSize, int sequenceNumber,
private static <T> Message<T> createMessage(T payload, Object correlationId, int sequenceSize, int sequenceNumber,
MessageChannel replyChannel) {
StringMessage message = new StringMessage(payload);
GenericMessage<T> message = new GenericMessage<T>(payload);
message.getHeader().setCorrelationId(correlationId);
message.getHeader().setSequenceSize(sequenceSize);
message.getHeader().setSequenceNumber(sequenceNumber);

View File

@@ -17,6 +17,7 @@
package org.springframework.integration.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@@ -34,16 +35,19 @@ public class TestAggregator implements Aggregator {
private final ConcurrentMap<Object, Message<?>> aggregatedMessages = new ConcurrentHashMap<Object, Message<?>>();
public Message<?> aggregate(List<Message<?>> messages) {
public Message<?> aggregate(Collection<Message<?>> messages) {
List<Message<?>> sortableList = new ArrayList<Message<?>>(messages);
Collections.sort(sortableList, new MessageSequenceComparator());
StringBuffer buffer = new StringBuffer();
Object correlationId = null;
for (Message<?> message : sortableList) {
buffer.append(message.getPayload().toString());
if (null == correlationId) {
correlationId = message.getHeader().getCorrelationId();
}
}
Message<?> returnedMessage = new StringMessage(buffer.toString());
aggregatedMessages.put(messages.get(0).getHeader().getCorrelationId(), returnedMessage);
aggregatedMessages.put(correlationId, returnedMessage);
return returnedMessage;
}

View File

@@ -18,12 +18,16 @@
send-partial-result-on-timeout="true"
reaper-interval="135"
tracked-correlation-id-capacity="99"/>
<aggregator id="aggregatorWithReferenceAndMethod" ref="adderBean" method="add" default-reply-channel="replyChannel"/>
<channel id="replyChannel"/>
<channel id="discardChannel"/>
<beans:bean id="aggregatorBean" class="org.springframework.integration.config.TestAggregator"/>
<beans:bean id="adderBean" class="org.springframework.integration.config.Adder"/>
<beans:bean id="completionStrategy" class="org.springframework.integration.config.TestCompletionStrategy"/>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-core-1.0.xsd">
<aggregator id="aggregatorWithReferenceAndMethod" ref="adderBean" method="substract" default-reply-channel="replyChannel"/>
<channel id="replyChannel"/>
<beans:bean id="adderBean" class="org.springframework.integration.config.Adder"/>
</beans:beans>

View File

@@ -21,6 +21,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -207,7 +208,7 @@ public class AggregatingMessageHandlerTests {
private static class TestAggregator implements Aggregator {
public Message<?> aggregate(List<Message<?>> messages) {
public Message<?> aggregate(Collection<Message<?>> messages) {
List<Message<?>> sortableList = new ArrayList<Message<?>>(messages);
Collections.sort(sortableList, new MessageSequenceComparator());
StringBuffer buffer = new StringBuffer();

View File

@@ -0,0 +1,161 @@
/*
* Copyright 2002-2007 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.router;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.integration.MessagingConfigurationException;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.message.Message;
/**
* @author Marius Bogoevici
*/
public class AggregatorAdapterTests {
private SimpleAggregator simpleAggregator;
@Before
public void setUp() {
simpleAggregator = new SimpleAggregator();
}
@Test
public void testAdapterWithMessageCollectionBasedMethod() {
Aggregator aggregator = new AggregatorAdapter(simpleAggregator, "doAggregationOnCollectionOfMessages");
Collection<Message<?>> messages = createCollectionOfMessages();
Message<?> returnedMessge = aggregator.aggregate(messages);
Assert.assertTrue(simpleAggregator.isAggregationPerformed());
Assert.assertEquals("123456789", returnedMessge.getPayload());
}
@Test
public void testAdapterWithWildcardParametrizedMessageBasedMethod() {
Aggregator aggregator = new AggregatorAdapter(simpleAggregator, "doAggregationOnCollectionOfMessagesParametrizedWithWildcard");
Collection<Message<?>> messages = createCollectionOfMessages();
Message<?> returnedMessge = aggregator.aggregate(messages);
Assert.assertTrue(simpleAggregator.isAggregationPerformed());
Assert.assertEquals("123456789", returnedMessge.getPayload());
}
@Test
public void testAdapterWithTypeParametrizedMessageBasedMethod() {
Aggregator aggregator = new AggregatorAdapter(simpleAggregator, "doAggregationOnCollectionOfMessagesParametrizedWithString");
Collection<Message<?>> messages = createCollectionOfMessages();
Message<?> returnedMessge = aggregator.aggregate(messages);
Assert.assertTrue(simpleAggregator.isAggregationPerformed());
Assert.assertEquals("123456789", returnedMessge.getPayload());
}
@Test
public void testAdapterWithPojoBasedMethod() {
Aggregator aggregator = new AggregatorAdapter(simpleAggregator, "doAggregationOnCollectionOfStrings");
Collection<Message<?>> messages = createCollectionOfMessages();
Message<?> returnedMessge = aggregator.aggregate(messages);
Assert.assertTrue(simpleAggregator.isAggregationPerformed());
Assert.assertEquals("123456789", returnedMessge.getPayload());
}
@Test
public void testAdapterWithPojoBasedMethodReturningObject() {
Aggregator aggregator = new AggregatorAdapter(simpleAggregator, "doAggregationOnCollectionOfStringsReturningLong");
Collection<Message<?>> messages = createCollectionOfMessages();
Message<?> returnedMessge = aggregator.aggregate(messages);
Assert.assertTrue(simpleAggregator.isAggregationPerformed());
Assert.assertEquals(123456789l, returnedMessge.getPayload());
}
@Test(expected=IllegalArgumentException.class)
public void testAdapterWithWrongMethodName() {
Aggregator aggregator = new AggregatorAdapter(simpleAggregator, "methodThatDoesNotExist");
}
private static Collection<Message<?>> createCollectionOfMessages() {
Collection<Message<?>> messages = new ArrayList<Message<?>>();
messages.add(new GenericMessage<String>("123"));
messages.add(new GenericMessage<String>("456"));
messages.add(new GenericMessage<String>("789"));
return messages;
}
private class SimpleAggregator {
private volatile boolean aggregationPerformed;
public SimpleAggregator() {
aggregationPerformed = false;
}
public Message<?> doAggregationOnCollectionOfMessages(Collection<Message> messages) {
aggregationPerformed = true;
StringBuffer buffer = new StringBuffer();
for (Message<?> message : messages) {
buffer.append(message.getPayload());
}
return new GenericMessage<String>(buffer.toString());
}
public Message<?> doAggregationOnCollectionOfMessagesParametrizedWithWildcard(Collection<Message<?>> messages) {
aggregationPerformed = true;
StringBuffer buffer = new StringBuffer();
for (Message<?> message : messages) {
buffer.append(message.getPayload());
}
return new GenericMessage<String>(buffer.toString());
}
public Message<?> doAggregationOnCollectionOfMessagesParametrizedWithString(Collection<Message<String>> messages) {
aggregationPerformed = true;
StringBuffer buffer = new StringBuffer();
for (Message<String> message : messages) {
buffer.append(message.getPayload());
}
return new GenericMessage<String>(buffer.toString());
}
public Message<?> doAggregationOnCollectionOfStrings(Collection<String> messages) {
aggregationPerformed = true;
StringBuffer buffer = new StringBuffer();
for (String payload : messages) {
buffer.append(payload);
}
return new GenericMessage<String>(buffer.toString());
}
public Long doAggregationOnCollectionOfStringsReturningLong(Collection<String> messages) {
aggregationPerformed = true;
StringBuffer buffer = new StringBuffer();
for (String payload : messages) {
buffer.append(payload);
}
return Long.parseLong(buffer.toString());
}
public boolean isAggregationPerformed() {
return aggregationPerformed;
}
}
}