INT-1059 Both DefaultAggregatingMessageGroupProcessor and MethodInvokingMessageGroupProcessor now extend AbstractAggregatingMessageGroupProcessor. The base class provides default header aggregation. For more detail, see its JavaDoc and the AggregatingMessageGroupProcessorHeaderTests.
This commit is contained in:
@@ -16,11 +16,19 @@
|
||||
|
||||
package org.springframework.integration.aggregator;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.integration.channel.MessageChannelTemplate;
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.core.MessageHeaders;
|
||||
import org.springframework.integration.message.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -35,6 +43,9 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public abstract class AbstractAggregatingMessageGroupProcessor implements MessageGroupProcessor {
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
|
||||
public final void processAndSend(MessageGroup group, MessageChannelTemplate channelTemplate, MessageChannel outputChannel) {
|
||||
Assert.notNull(group, "MessageGroup must not be null");
|
||||
Assert.notNull(outputChannel, "'outputChannel' must not be null");
|
||||
@@ -44,7 +55,44 @@ public abstract class AbstractAggregatingMessageGroupProcessor implements Messag
|
||||
channelTemplate.send(message, outputChannel);
|
||||
}
|
||||
|
||||
protected abstract Map<String, Object> aggregateHeaders(MessageGroup group);
|
||||
/**
|
||||
* This default implementation simply returns all headers that have no conflicts
|
||||
* among the group. An absent header on one or more Messages within the group is
|
||||
* not considered a conflict. Subclasses may override this method with more
|
||||
* advanced conflict-resolution strategies if necessary.
|
||||
*/
|
||||
protected Map<String, Object> aggregateHeaders(MessageGroup group) {
|
||||
Map<String, Object> aggregatedHeaders = new HashMap<String, Object>();
|
||||
Set<String> conflictKeys = new HashSet<String>();
|
||||
List<Message<?>> messages = group.getMessages();
|
||||
if (messages != null) {
|
||||
for (Message<?> message : messages) {
|
||||
MessageHeaders currentHeaders = message.getHeaders();
|
||||
for (String key : currentHeaders.keySet()) {
|
||||
if (MessageHeaders.ID.equals(key) ||
|
||||
MessageHeaders.TIMESTAMP.equals(key) ||
|
||||
MessageHeaders.SEQUENCE_SIZE.equals(key)) {
|
||||
continue;
|
||||
}
|
||||
Object value = currentHeaders.get(key);
|
||||
if (!aggregatedHeaders.containsKey(key)) {
|
||||
aggregatedHeaders.put(key, value);
|
||||
}
|
||||
else if (!value.equals(aggregatedHeaders.get(key))) {
|
||||
conflictKeys.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String keyToRemove : conflictKeys) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Excluding header '" + keyToRemove + "' upon aggregation due to conflict(s) " +
|
||||
"in MessageGroup with correlation key: " + group.getCorrelationKey());
|
||||
}
|
||||
aggregatedHeaders.remove(keyToRemove);
|
||||
}
|
||||
}
|
||||
return aggregatedHeaders;
|
||||
}
|
||||
|
||||
protected abstract Object aggregatePayloads(MessageGroup group);
|
||||
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
package org.springframework.integration.aggregator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -36,13 +34,7 @@ import org.springframework.util.Assert;
|
||||
public class DefaultAggregatingMessageGroupProcessor extends AbstractAggregatingMessageGroupProcessor {
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> aggregateHeaders(MessageGroup group) {
|
||||
// TODO: return all non-conflicting headers
|
||||
return new HashMap<String, Object>();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object aggregatePayloads(MessageGroup group) {
|
||||
protected final Object aggregatePayloads(MessageGroup group) {
|
||||
List<Message<?>> messages = group.getMessages();
|
||||
Assert.notEmpty(messages, this.getClass().getSimpleName() + " cannot process empty message groups");
|
||||
List<Object> payloads = new ArrayList<Object>(messages.size());
|
||||
|
||||
@@ -16,171 +16,171 @@
|
||||
|
||||
package org.springframework.integration.aggregator;
|
||||
|
||||
import org.springframework.integration.annotation.Aggregator;
|
||||
import org.springframework.integration.annotation.Header;
|
||||
import org.springframework.integration.channel.MessageChannelTemplate;
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.core.MessageChannel;
|
||||
import org.springframework.integration.message.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.integration.annotation.Aggregator;
|
||||
import org.springframework.integration.annotation.Header;
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* MessageGroupProcessor that serves as a wrapper around a POJO.
|
||||
*
|
||||
* MessageGroupProcessor that serves as an adapter for the invocation of a POJO method.
|
||||
*
|
||||
* @author Iwein Fuld
|
||||
* @since 2.0.0
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MethodInvokingMessageGroupProcessor implements MessageGroupProcessor {
|
||||
public class MethodInvokingMessageGroupProcessor extends AbstractAggregatingMessageGroupProcessor {
|
||||
|
||||
private final MessageListMethodAdapter adapter;
|
||||
|
||||
|
||||
private final MessageListMethodAdapter adapter;
|
||||
/**
|
||||
* Creates a wrapper around the target passed in. This constructor will
|
||||
* choose the best fitting method and throw an exception when methods are
|
||||
* ambiguous or no fitting methods can be found.
|
||||
*
|
||||
* @param target the object to wrap
|
||||
* @throws IllegalStateException when no single method can be found unambiguously
|
||||
*/
|
||||
public MethodInvokingMessageGroupProcessor(Object target) {
|
||||
this.adapter = new MessageListMethodAdapter(target, this.selectMethodFrom(target));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wrapper around the target passed in. This constructor will choose the best fitting method and throw an
|
||||
* exception when methods are ambiguous or no fitting methods can be found.
|
||||
*
|
||||
* @param target the object to wrap
|
||||
* @throws IllegalStateException when no single method can be found unambiguously
|
||||
*/
|
||||
public MethodInvokingMessageGroupProcessor(Object target) {
|
||||
this.adapter = new MessageListMethodAdapter(target, selectMethodFrom(target));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wrapper around the object passed in. This constructor will look for a named method specifically and
|
||||
* fail when it cannot find a method with the given name.
|
||||
*
|
||||
* @param target the object to wrap
|
||||
* @param method the name of the method to look for
|
||||
*/
|
||||
public MethodInvokingMessageGroupProcessor(Object target, String method) {
|
||||
this.adapter = new MessageListMethodAdapter(target, method);
|
||||
}
|
||||
|
||||
private Method selectMethodFrom(Object target) {
|
||||
Method[] methods = target.getClass().getMethods();
|
||||
Set<Method> candidates = new HashSet<Method>(Arrays.asList(methods));
|
||||
|
||||
removeObjectMethodsFrom(candidates);
|
||||
removeVoidMethodsFrom(candidates);
|
||||
removeListIncompatibleMethodsFrom(candidates);
|
||||
Set<Method> notAnnotatedCandidates = new HashSet<Method>();
|
||||
if (candidates.size() > 1) {
|
||||
notAnnotatedCandidates.addAll(removeNotAnnotatedFrom(candidates));
|
||||
}
|
||||
//if no methods are annotated we need to look in more detail in the unannotated methods
|
||||
if (candidates.size() < 1) {
|
||||
candidates = notAnnotatedCandidates;
|
||||
removeUnfittingFrom(candidates);
|
||||
}
|
||||
|
||||
Assert.state(candidates.size() == 1,
|
||||
"Method selection failed, there should be exactly one candidate, found ["
|
||||
+ candidates + "]");
|
||||
return candidates.iterator().next();
|
||||
}
|
||||
|
||||
private void removeListIncompatibleMethodsFrom(Set<Method> candidates) {
|
||||
removeMethodsMatchingSelector(candidates, new MethodSelector() {
|
||||
public boolean select(Method method) {
|
||||
int found = 0;
|
||||
for (Class<?> parameterClass : method.getParameterTypes()) {
|
||||
if (parameterClass.isAssignableFrom(List.class)) {
|
||||
found++;
|
||||
}
|
||||
}
|
||||
return found != 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void removeVoidMethodsFrom(Set<Method> candidates) {
|
||||
removeMethodsMatchingSelector(candidates, new MethodSelector() {
|
||||
public boolean select(Method method) {
|
||||
return method.getReturnType().getName().equals("void");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Set<Method> removeNotAnnotatedFrom(Set<Method> candidates) {
|
||||
return removeMethodsMatchingSelector(candidates, new MethodSelector() {
|
||||
public boolean select(Method method) {
|
||||
Aggregator annotation = method.getAnnotation(Aggregator.class);
|
||||
return (annotation == null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Set<Method> removeUnfittingFrom(Set<Method> candidates) {
|
||||
return removeMethodsMatchingSelector(candidates, new MethodSelector() {
|
||||
public boolean select(Method method) {
|
||||
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
return (!isFittinglyAnnotated(parameterTypes, parameterAnnotations));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isFittinglyAnnotated(Class<?>[] parameterTypes, Annotation[][] parameterAnnotations) {
|
||||
int candidateParametersFound = 0;
|
||||
for (int i = 0; i < parameterTypes.length; i++) {
|
||||
Class<?> parameterType = parameterTypes[i];
|
||||
if (parameterType.isAssignableFrom(List.class)) {
|
||||
boolean headerAnnotationFound = false;
|
||||
for (Annotation annotation : parameterAnnotations[i]) {
|
||||
if (annotation instanceof Header) {
|
||||
headerAnnotationFound = true;
|
||||
}
|
||||
}
|
||||
if (!headerAnnotationFound) {
|
||||
candidateParametersFound++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return candidateParametersFound == 1;
|
||||
}
|
||||
|
||||
private void removeObjectMethodsFrom(Set<Method> candidates) {
|
||||
removeMethodsMatchingSelector(candidates, new MethodSelector() {
|
||||
public boolean select(Method method) {
|
||||
return method.getDeclaringClass().equals(Object.class);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void processAndSend(MessageGroup group,
|
||||
MessageChannelTemplate channelTemplate, MessageChannel outputChannel) {
|
||||
final Collection<Message<?>> messagesUpForProcessing = group.getMessages();
|
||||
Message reply = MessageBuilder.withPayload(
|
||||
this.adapter.executeMethod(messagesUpForProcessing)).build();
|
||||
|
||||
group.onCompletion();
|
||||
group.onProcessingOf(messagesUpForProcessing
|
||||
.toArray(new Message[messagesUpForProcessing.size()]));
|
||||
|
||||
channelTemplate.send(reply, outputChannel);
|
||||
}
|
||||
|
||||
private Set<Method> removeMethodsMatchingSelector(Set<Method> candidates, MethodSelector selector) {
|
||||
Set<Method> removed = new HashSet<Method>();
|
||||
Iterator<Method> iterator = candidates.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Method method = iterator.next();
|
||||
if (selector.select(method)) {
|
||||
iterator.remove();
|
||||
removed.add(method);
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
/**
|
||||
* Creates a wrapper around the object passed in. This constructor will look
|
||||
* for a named method specifically and fail when it cannot find a method
|
||||
* with the given name.
|
||||
*
|
||||
* @param target the object to wrap
|
||||
* @param method the name of the method to look for
|
||||
*/
|
||||
public MethodInvokingMessageGroupProcessor(Object target, String method) {
|
||||
this.adapter = new MessageListMethodAdapter(target, method);
|
||||
}
|
||||
|
||||
|
||||
private interface MethodSelector {
|
||||
boolean select(Method method);
|
||||
}
|
||||
@Override
|
||||
protected final Object aggregatePayloads(MessageGroup group) {
|
||||
final Collection<Message<?>> messagesUpForProcessing = group.getMessages();
|
||||
Object result = this.adapter.executeMethod(messagesUpForProcessing);
|
||||
group.onCompletion();
|
||||
group.onProcessingOf(messagesUpForProcessing.toArray(new Message[messagesUpForProcessing.size()]));
|
||||
return result;
|
||||
}
|
||||
|
||||
private Method selectMethodFrom(Object target) {
|
||||
Method[] methods = target.getClass().getMethods();
|
||||
Set<Method> candidates = new HashSet<Method>(Arrays.asList(methods));
|
||||
|
||||
removeObjectMethodsFrom(candidates);
|
||||
removeVoidMethodsFrom(candidates);
|
||||
removeListIncompatibleMethodsFrom(candidates);
|
||||
Set<Method> notAnnotatedCandidates = new HashSet<Method>();
|
||||
if (candidates.size() > 1) {
|
||||
notAnnotatedCandidates.addAll(removeNotAnnotatedFrom(candidates));
|
||||
}
|
||||
|
||||
// if no methods are annotated we need to look in more detail in the unannotated methods
|
||||
if (candidates.size() < 1) {
|
||||
candidates = notAnnotatedCandidates;
|
||||
removeUnfittingFrom(candidates);
|
||||
}
|
||||
Assert.state(candidates.size() == 1,
|
||||
"Method selection failed, there should be exactly one candidate, found [" + candidates + "]");
|
||||
return candidates.iterator().next();
|
||||
}
|
||||
|
||||
private void removeListIncompatibleMethodsFrom(Set<Method> candidates) {
|
||||
removeMethodsMatchingSelector(candidates, new MethodSelector() {
|
||||
public boolean select(Method method) {
|
||||
int found = 0;
|
||||
for (Class<?> parameterClass : method.getParameterTypes()) {
|
||||
if (parameterClass.isAssignableFrom(List.class)) {
|
||||
found++;
|
||||
}
|
||||
}
|
||||
return found != 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void removeVoidMethodsFrom(Set<Method> candidates) {
|
||||
removeMethodsMatchingSelector(candidates, new MethodSelector() {
|
||||
public boolean select(Method method) {
|
||||
return method.getReturnType().getName().equals("void");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Set<Method> removeNotAnnotatedFrom(Set<Method> candidates) {
|
||||
return removeMethodsMatchingSelector(candidates, new MethodSelector() {
|
||||
public boolean select(Method method) {
|
||||
Aggregator annotation = method.getAnnotation(Aggregator.class);
|
||||
return (annotation == null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Set<Method> removeUnfittingFrom(Set<Method> candidates) {
|
||||
return removeMethodsMatchingSelector(candidates, new MethodSelector() {
|
||||
public boolean select(Method method) {
|
||||
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
return (!isFittinglyAnnotated(parameterTypes, parameterAnnotations));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isFittinglyAnnotated(Class<?>[] parameterTypes, Annotation[][] parameterAnnotations) {
|
||||
int candidateParametersFound = 0;
|
||||
for (int i = 0; i < parameterTypes.length; i++) {
|
||||
Class<?> parameterType = parameterTypes[i];
|
||||
if (parameterType.isAssignableFrom(List.class)) {
|
||||
boolean headerAnnotationFound = false;
|
||||
for (Annotation annotation : parameterAnnotations[i]) {
|
||||
if (annotation instanceof Header) {
|
||||
headerAnnotationFound = true;
|
||||
}
|
||||
}
|
||||
if (!headerAnnotationFound) {
|
||||
candidateParametersFound++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return candidateParametersFound == 1;
|
||||
}
|
||||
|
||||
private void removeObjectMethodsFrom(Set<Method> candidates) {
|
||||
removeMethodsMatchingSelector(candidates, new MethodSelector() {
|
||||
public boolean select(Method method) {
|
||||
return method.getDeclaringClass().equals(Object.class);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Set<Method> removeMethodsMatchingSelector(Set<Method> candidates, MethodSelector selector) {
|
||||
Set<Method> removed = new HashSet<Method>();
|
||||
Iterator<Method> iterator = candidates.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Method method = iterator.next();
|
||||
if (selector.select(method)) {
|
||||
iterator.remove();
|
||||
removed.add(method);
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
private interface MethodSelector {
|
||||
boolean select(Method method);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.aggregator;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.integration.channel.MessageChannelTemplate;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.core.Message;
|
||||
import org.springframework.integration.message.MessageBuilder;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
public class AggregatingMessageGroupProcessorHeaderTests {
|
||||
|
||||
private final QueueChannel outputChannel = new QueueChannel(1);
|
||||
|
||||
private final MessageChannelTemplate channelTemplate = new MessageChannelTemplate();
|
||||
|
||||
private final MessageGroupProcessor defaultProcessor = new DefaultAggregatingMessageGroupProcessor();
|
||||
|
||||
private final MessageGroupProcessor methodInvokingProcessor =
|
||||
new MethodInvokingMessageGroupProcessor(new TestAggregatorBean(), "aggregate");
|
||||
|
||||
|
||||
@Test
|
||||
public void singleMessageUsingDefaultProcessor() {
|
||||
this.singleMessage(defaultProcessor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleMessageUsingMethodInvokingProcessor() {
|
||||
this.singleMessage(methodInvokingProcessor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void twoMessagesWithoutConflictsUsingDefaultProcessor() {
|
||||
this.twoMessagesWithoutConflicts(defaultProcessor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void twoMessagesWithoutConflictsUsingMethodInvokingProcessor() {
|
||||
this.twoMessagesWithoutConflicts(methodInvokingProcessor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void twoMessagesWithConflictsUsingDefaultProcessor() {
|
||||
this.twoMessagesWithConflicts(defaultProcessor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void twoMessagesWithConflictsUsingMethodInvokingProcessor() {
|
||||
this.twoMessagesWithConflicts(methodInvokingProcessor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingValuesDoNotConflictUsingDefaultProcessor() {
|
||||
this.missingValuesDoNotConflict(defaultProcessor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingValuesDoNotConflictUsingMethodInvokingProcessor() {
|
||||
this.missingValuesDoNotConflict(methodInvokingProcessor);
|
||||
}
|
||||
|
||||
|
||||
private void singleMessage(MessageGroupProcessor processor) {
|
||||
Map<String, Object> headers = new HashMap<String, Object>();
|
||||
headers.put("k1", "value1");
|
||||
headers.put("k2", new Integer(2));
|
||||
Message<?> message = correlatedMessage(1, 1, 1, headers);
|
||||
List<Message<?>> messages = Collections.<Message<?>>singletonList(message);
|
||||
MessageGroup group = new MessageGroup(messages, new SequenceSizeCompletionStrategy(), 1);
|
||||
processor.processAndSend(group, channelTemplate, outputChannel);
|
||||
Message<?> result = outputChannel.receive(0);
|
||||
assertNotNull(result);
|
||||
assertEquals("value1", result.getHeaders().get("k1"));
|
||||
assertEquals(2, result.getHeaders().get("k2"));
|
||||
}
|
||||
|
||||
public void twoMessagesWithoutConflicts(MessageGroupProcessor processor) {
|
||||
Map<String, Object> headers = new HashMap<String, Object>();
|
||||
headers.put("k1", "value1");
|
||||
headers.put("k2", new Integer(2));
|
||||
Message<?> message1 = correlatedMessage(1, 2, 1, headers);
|
||||
Message<?> message2 = correlatedMessage(1, 2, 2, headers);
|
||||
List<Message<?>> messages = Arrays.<Message<?>>asList(message1, message2);
|
||||
MessageGroup group = new MessageGroup(messages, new SequenceSizeCompletionStrategy(), 1);
|
||||
processor.processAndSend(group, channelTemplate, outputChannel);
|
||||
Message<?> result = outputChannel.receive(0);
|
||||
assertNotNull(result);
|
||||
assertEquals("value1", result.getHeaders().get("k1"));
|
||||
assertEquals(2, result.getHeaders().get("k2"));
|
||||
}
|
||||
|
||||
public void twoMessagesWithConflicts(MessageGroupProcessor processor) {
|
||||
Map<String, Object> headers1 = new HashMap<String, Object>();
|
||||
headers1.put("k1", "foo");
|
||||
headers1.put("k2", new Integer(123));
|
||||
Message<?> message1 = correlatedMessage(1, 2, 1, headers1);
|
||||
Map<String, Object> headers2 = new HashMap<String, Object>();
|
||||
headers2.put("k1", "bar");
|
||||
headers2.put("k2", new Integer(123));
|
||||
Message<?> message2 = correlatedMessage(1, 2, 2, headers2);
|
||||
List<Message<?>> messages = Arrays.<Message<?>>asList(message1, message2);
|
||||
MessageGroup group = new MessageGroup(messages, new SequenceSizeCompletionStrategy(), 1);
|
||||
processor.processAndSend(group, channelTemplate, outputChannel);
|
||||
Message<?> result = outputChannel.receive(0);
|
||||
assertNotNull(result);
|
||||
assertNull(result.getHeaders().get("k1"));
|
||||
assertEquals(123, result.getHeaders().get("k2"));
|
||||
}
|
||||
|
||||
public void missingValuesDoNotConflict(MessageGroupProcessor processor) {
|
||||
Map<String, Object> headers1 = new HashMap<String, Object>();
|
||||
headers1.put("only1", "value1");
|
||||
headers1.put("commonTo1And2", "foo");
|
||||
headers1.put("commonToAll", new Integer(123));
|
||||
headers1.put("conflictBetween1And2", "valueFor1");
|
||||
Message<?> message1 = correlatedMessage(1, 3, 1, headers1);
|
||||
Map<String, Object> headers2 = new HashMap<String, Object>();
|
||||
headers2.put("only2", "value2");
|
||||
headers2.put("commonTo1And2", "foo");
|
||||
headers2.put("commonTo2And3", "bar");
|
||||
headers2.put("conflictBetween1And2", "valueFor2");
|
||||
headers2.put("conflictBetween2And3", "valueFor2");
|
||||
headers2.put("commonToAll", new Integer(123));
|
||||
Message<?> message2 = correlatedMessage(1, 3, 2, headers2);
|
||||
Map<String, Object> headers3 = new HashMap<String, Object>();
|
||||
headers3.put("only3", "value3");
|
||||
headers3.put("commonTo2And3", "bar");
|
||||
headers3.put("commonToAll", new Integer(123));
|
||||
headers3.put("conflictBetween2And3", "valueFor3");
|
||||
Message<?> message3 = correlatedMessage(1, 3, 3, headers3);
|
||||
List<Message<?>> messages = Arrays.<Message<?>>asList(message1, message2, message3);
|
||||
MessageGroup group = new MessageGroup(messages, new SequenceSizeCompletionStrategy(), 1);
|
||||
processor.processAndSend(group, channelTemplate, outputChannel);
|
||||
Message<?> result = outputChannel.receive(0);
|
||||
assertNotNull(result);
|
||||
assertEquals("value1", result.getHeaders().get("only1"));
|
||||
assertEquals("value2", result.getHeaders().get("only2"));
|
||||
assertEquals("value3", result.getHeaders().get("only3"));
|
||||
assertEquals("foo", result.getHeaders().get("commonTo1And2"));
|
||||
assertEquals("bar", result.getHeaders().get("commonTo2And3"));
|
||||
assertEquals(123, result.getHeaders().get("commonToAll"));
|
||||
assertNull(result.getHeaders().get("conflictBetween1And2"));
|
||||
assertNull(result.getHeaders().get("conflictBetween2And3"));
|
||||
}
|
||||
|
||||
|
||||
private Message<?> correlatedMessage(Object correlationId, Integer sequenceSize,
|
||||
Integer sequenceNumber, Map<String, Object> headers) {
|
||||
return MessageBuilder.withPayload("test")
|
||||
.setCorrelationId(correlationId)
|
||||
.setSequenceNumber(sequenceNumber)
|
||||
.setSequenceSize(sequenceSize)
|
||||
.copyHeadersIfAbsent(headers)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
private static class TestAggregatorBean {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Object aggregate(List<String> payloads) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String s : payloads) {
|
||||
sb.append(s);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user