adds support for an <aggregator> element in the integration namespace (INT-95). Currently supports only references to beans implementing the Aggregator interface. The 'method' attribute can be defined, but it is not currently in use.

This commit is contained in:
Marius Bogoevici
2008-02-27 19:15:06 +00:00
parent 559d858e3e
commit 44cb3e17a1
8 changed files with 412 additions and 1 deletions

View File

@@ -0,0 +1,123 @@
/*
* 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.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.channel.MessageChannel;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.StringMessage;
import org.springframework.integration.router.AggregatingMessageHandler;
import org.springframework.integration.router.Aggregator;
import org.springframework.integration.router.CompletionStrategy;
import org.springframework.util.ReflectionUtils;
/**
* @author Marius Bogoevici
*/
public class AggregatorParserTests {
private ApplicationContext context;
@Before
public void setUp() {
context = new ClassPathXmlApplicationContext("aggregatorParserTests.xml", this.getClass());
}
@Test
public void testAggregation() {
AggregatingMessageHandler aggregatingHandler = (AggregatingMessageHandler) context
.getBean("aggregatorWithReference");
TestAggregator aggregatorBean = (TestAggregator) context.getBean("aggregatorBean");
List<Message<?>> outboundMessages = new ArrayList<Message<?>>();
outboundMessages.add(createMessage("123", "id1", 3, 1, null));
outboundMessages.add(createMessage("789", "id1", 3, 3, null));
outboundMessages.add(createMessage("456", "id1", 3, 2, null));
for (Message<?> message : outboundMessages) {
aggregatingHandler.handle(message);
}
Assert.assertEquals("One and only one message must have been aggregated", 1, aggregatorBean
.getAggregatedMessages().size());
Message<?> aggregatedMessage = aggregatorBean.getAggregatedMessages().get("id1");
Assert.assertEquals("The aggreggated message payload is not correct", "123456789", aggregatedMessage
.getPayload());
}
@Test
public void testPropertyAssignment() throws Exception {
AggregatingMessageHandler completeAggregatingMessageHandler = (AggregatingMessageHandler) context
.getBean("completelyDefinedAggregator");
TestAggregator testAggregator = (TestAggregator) context.getBean("aggregatorBean");
CompletionStrategy completionStrategy = (CompletionStrategy) context.getBean("completionStrategy");
MessageChannel defaultReplyChannel = (MessageChannel) context.getBean("replyChannel");
MessageChannel discardChannel = (MessageChannel) context.getBean("discardChannel");
Assert.assertEquals("The AggregatingMessageHandler is not injected with the appropriate Aggregator instance",
testAggregator, getPropertyValue(completeAggregatingMessageHandler, "aggregator", Aggregator.class));
Assert.assertEquals(
"The AggregatingMessageHandler is not injected with the appropriate CompletionStrategy instance",
completionStrategy, getPropertyValue(completeAggregatingMessageHandler, "completionStrategy",
CompletionStrategy.class));
Assert.assertEquals("The AggregatingMessageHandler is not injected with the appropriate default reply channel",
defaultReplyChannel, getPropertyValue(completeAggregatingMessageHandler, "defaultReplyChannel",
MessageChannel.class));
Assert.assertEquals("The AggregatingMessageHandler is not injected with the appropriate discard channel",
discardChannel, getPropertyValue(completeAggregatingMessageHandler, "discardChannel",
MessageChannel.class));
Assert.assertEquals("The AggregatingMessageHandler is not set with the appropriate timeout value", 86420000l,
getPropertyValue(completeAggregatingMessageHandler, "sendTimeout", long.class));
Assert.assertEquals(
"The AggregatingMessageHandler is not configured with the appropriate 'send partial results on timeout' flag",
true, getPropertyValue(completeAggregatingMessageHandler, "sendPartialResultOnTimeout",
boolean.class));
Assert.assertEquals("The AggregatingMessageHandler is not configured with the appropriate reaper interval",
135l, getPropertyValue(completeAggregatingMessageHandler, "reaperInterval", long.class));
Assert.assertEquals(
"The AggregatingMessageHandler is not configured with the appropriate tracked correlationId capacity",
99, getPropertyValue(completeAggregatingMessageHandler, "trackedCorrelationIdCapacity", int.class));
}
private static Message<?> createMessage(String payload, Object correlationId, int sequenceSize, int sequenceNumber,
MessageChannel replyChannel) {
StringMessage message = new StringMessage(payload);
message.getHeader().setCorrelationId(correlationId);
message.getHeader().setSequenceSize(sequenceSize);
message.getHeader().setSequenceNumber(sequenceNumber);
message.getHeader().setReturnAddress(replyChannel);
return message;
}
/**
* Reading private fields through reflection, since they don't have setters
* @param beanUnderTest
* @param fieldName
* @return the value of the field
* @throws Exception
*/
private static Object getPropertyValue(Object beanUnderTest, String fieldName, Class<?> type) throws Exception {
Field field = ReflectionUtils.findField(beanUnderTest.getClass(), fieldName, type);
ReflectionUtils.makeAccessible(field);
return field.get(beanUnderTest);
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.integration.message.Message;
import org.springframework.integration.message.StringMessage;
import org.springframework.integration.router.Aggregator;
import org.springframework.integration.router.MessageSequenceComparator;
/**
* @author Marius Bogoevici
*/
public class TestAggregator implements Aggregator {
ConcurrentHashMap<Object, Message<?>> aggregatedMessages = new ConcurrentHashMap<Object, Message<?>>();
public Message<?> aggregate(List<Message<?>> messages) {
List<Message<?>> sortableList = new ArrayList<Message<?>>(messages);
Collections.sort(sortableList, new MessageSequenceComparator());
StringBuffer buffer = new StringBuffer();
for (Message<?> message : sortableList) {
buffer.append(message.getPayload().toString());
}
Message<?> returnedMessage = new StringMessage(buffer.toString());
aggregatedMessages.put(messages.get(0).getHeader().getCorrelationId(), returnedMessage);
return returnedMessage;
}
public ConcurrentHashMap<Object, Message<?>> getAggregatedMessages() {
return aggregatedMessages;
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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.List;
import org.springframework.integration.message.Message;
import org.springframework.integration.router.CompletionStrategy;
/**
* @author Marius Bogoevici
*/
public class TestCompletionStrategy implements CompletionStrategy {
public boolean isComplete(List<Message<?>> messages) {
throw new UnsupportedOperationException("This is not intended to be implemented, but to verify injection into an <aggregator>");
}
}

View File

@@ -0,0 +1,26 @@
<?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="aggregatorWithReference" ref="aggregatorBean"/>
<aggregator id="completelyDefinedAggregator"
ref="aggregatorBean"
completion-strategy="completionStrategy"
default-reply-channel="replyChannel"
discard-channel="discardChannel"
send-timeout="86420000"
send-partial-result-on-timeout="true"
reaper-interval="135"
tracked-correlation-id-capacity="99"/>
<channel id="replyChannel"/>
<channel id="discardChannel"/>
<beans:bean id="aggregatorBean" class="org.springframework.integration.config.TestAggregator"/>
<beans:bean id="completionStrategy" class="org.springframework.integration.config.TestCompletionStrategy"/>
</beans:beans>