OPEN - issue INT-1105: remove behaviour from MessageGroup. Still one test to fix....

This commit is contained in:
David Syer
2010-04-28 15:31:11 +00:00
parent 972280ee6b
commit b8e5d36ca1
18 changed files with 148 additions and 227 deletions

View File

@@ -28,8 +28,8 @@ import org.springframework.util.Assert;
import java.util.*;
/**
* Base class for MessageGroupProcessor implementations that aggregate the
* group of Messages into a single Message.
* Base class for MessageGroupProcessor implementations that aggregate the group
* of Messages into a single Message.
*
* @author Iwein Fuld
* @author Alexander Peters
@@ -40,52 +40,48 @@ public abstract class AbstractAggregatingMessageGroupProcessor implements Messag
private final Log logger = LogFactory.getLog(this.getClass());
public final void processAndSend(MessageGroup group, MessageChannelTemplate channelTemplate, MessageChannel outputChannel) {
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");
Object payload = this.aggregatePayloads(group);
Map<String, Object> headers = this.aggregateHeaders(group);
Message<?> message = MessageBuilder.withPayload(payload).copyHeadersIfAbsent(headers).build();
channelTemplate.send(message, outputChannel);
group.onCompleteProcessing();
}
/**
* 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.
* 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 (Message<?> message : group.getMessages()) {
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);
}
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;
}

View File

@@ -16,7 +16,7 @@
package org.springframework.integration.aggregator;
import java.util.List;
import java.util.Collection;
import org.springframework.integration.core.Message;
@@ -29,6 +29,6 @@ import org.springframework.integration.core.Message;
*/
public interface CompletionStrategy {
boolean isComplete(List<? extends Message<?>> messages);
boolean isComplete(Collection<? extends Message<?>> messages);
}

View File

@@ -17,7 +17,7 @@
package org.springframework.integration.aggregator;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Collection;
import org.springframework.integration.core.Message;
import org.springframework.util.Assert;
@@ -43,7 +43,7 @@ public class CompletionStrategyAdapter extends MessageListMethodAdapter implemen
}
public boolean isComplete(List<? extends Message<?>> messages) {
public boolean isComplete(Collection<? extends Message<?>> messages) {
return ((Boolean) executeMethod(messages)).booleanValue();
}

View File

@@ -58,8 +58,9 @@ import org.springframework.util.Assert;
* {@link org.springframework.integration.aggregator.MessageGroupProcessor}
* implementations as you require.
* <p/>
* By default the CorrelationStrategy will be a HeaderAttributeCorrelationStrategy
* and the CompletionStrategy will be a SequenceSizeCompletionStrategy.
* By default the CorrelationStrategy will be a
* HeaderAttributeCorrelationStrategy and the CompletionStrategy will be a
* SequenceSizeCompletionStrategy.
*
* @author Iwein Fuld
* @since 2.0
@@ -74,13 +75,12 @@ public class CorrelatingMessageHandler extends AbstractMessageHandler implements
private static final long DEFAULT_TIMEOUT = 60000L;
private final MessageStore store;
private final MessageGroupProcessor outputProcessor;
private volatile CorrelationStrategy correlationStrategy =
new HeaderAttributeCorrelationStrategy(MessageHeaders.CORRELATION_ID);
private volatile CorrelationStrategy correlationStrategy = new HeaderAttributeCorrelationStrategy(
MessageHeaders.CORRELATION_ID);
private volatile CompletionStrategy completionStrategy = new SequenceSizeCompletionStrategy();
@@ -97,14 +97,13 @@ public class CorrelatingMessageHandler extends AbstractMessageHandler implements
private volatile ScheduledFuture<?> reaperFutureTask;
private volatile long reaperInterval = DEFAULT_REAPER_INTERVAL;
private volatile long timeout = DEFAULT_TIMEOUT;
private volatile boolean sendPartialResultOnTimeout;
private final Object lifecycleMonitor = new Object();
public CorrelatingMessageHandler(MessageStore store, CorrelationStrategy correlationStrategy,
CompletionStrategy completionStrategy, MessageGroupProcessor processor) {
Assert.notNull(store);
@@ -128,7 +127,6 @@ public class CorrelatingMessageHandler extends AbstractMessageHandler implements
new SequenceSizeCompletionStrategy(), processor);
}
public void setCorrelationStrategy(CorrelationStrategy correlationStrategy) {
Assert.notNull(correlationStrategy);
this.correlationStrategy = correlationStrategy;
@@ -185,18 +183,19 @@ public class CorrelatingMessageHandler extends AbstractMessageHandler implements
}
try {
if (tracker.waitForLockIfNotTracked(correlationKey)) {
MessageGroup group = new MessageGroup(store.list(correlationKey),
completionStrategy, correlationKey, deleteOrTrackCallback(correlationKey));
Collection<Message<?>> messages = store.list(correlationKey);
MessageGroup group = new MessageGroup(messages, correlationKey);
if (group.hasNoMessageSuperseding(message)) {
store(message, correlationKey);
group.add(message);
if (group.isComplete()) {
if (completionStrategy.isComplete(group.getMessages())) {
if (logger.isDebugEnabled()) {
logger.debug("Completing group with correlationKey [" + correlationKey + "]");
}
outputProcessor.processAndSend(group, channelTemplate,
this.resolveReplyChannel(message, this.outputChannel));
outputProcessor.processAndSend(group, channelTemplate, this.resolveReplyChannel(message,
this.outputChannel));
complete(group);
}
}
else {
@@ -212,20 +211,15 @@ public class CorrelatingMessageHandler extends AbstractMessageHandler implements
}
}
private MessageGroupListener deleteOrTrackCallback(final Object correlationKey) {
return new MessageGroupListener() {
public void onProcessingOf(Message<?>... processedMessage) {
for (Message<?> message : processedMessage) {
store.delete(correlationKey, message.getHeaders().getId());
}
}
private void partialComplete(MessageGroup group) {
for (Message<?> message : group.getMessages()) {
store.delete(group.getCorrelationKey(), message.getHeaders().getId());
}
}
public void onCompletionOf(Object correlationKey) {
tracker.pushCorrelationId(correlationKey);
store.deleteAll(correlationKey);
}
};
private void complete(MessageGroup group) {
tracker.pushCorrelationId(group.getCorrelationKey());
store.deleteAll(group.getCorrelationKey());
}
@SuppressWarnings("unchecked")
@@ -252,8 +246,8 @@ public class CorrelatingMessageHandler extends AbstractMessageHandler implements
return;
}
Assert.state(this.getTaskScheduler() != null, "'taskScheduler' must not be null");
this.reaperFutureTask = this.getTaskScheduler().scheduleWithFixedDelay(
new PrunerTask(), this.reaperInterval);
this.reaperFutureTask = this.getTaskScheduler().scheduleWithFixedDelay(new PrunerTask(),
this.reaperInterval);
}
}
@@ -265,7 +259,6 @@ public class CorrelatingMessageHandler extends AbstractMessageHandler implements
}
}
private class PrunerTask implements Runnable {
public void run() {
if (logger.isTraceEnabled()) {
@@ -289,38 +282,35 @@ public class CorrelatingMessageHandler extends AbstractMessageHandler implements
}
}
protected final boolean forceComplete(Object key) {
try {
if (tracker.tryLockFor(key)) {
Collection<Message<?>> all = store.list(key);
MessageGroup group = new MessageGroup(all, completionStrategy, key, deleteOrTrackCallback(key));
MessageGroup group = new MessageGroup(all, key);
if (all.size() > 0) {
// last chance for normal completion
MessageChannel outputChannel = resolveReplyChannel(all.iterator().next(), this.outputChannel);
boolean processed = false;
if (group.isComplete()) {
if (completionStrategy.isComplete(all)) {
outputProcessor.processAndSend(group, channelTemplate, outputChannel);
processed = true;
}
if (!processed) {
complete(group);
} else {
if (sendPartialResultOnTimeout) {
if (logger.isInfoEnabled()) {
logger.info("Processing partially complete messages for key [" +
key + "] to: " + outputChannel);
logger.info("Processing partially complete messages for key [" + key + "] to: "
+ outputChannel);
}
outputProcessor.processAndSend(group, channelTemplate, outputChannel);
}
else {
if (logger.isInfoEnabled()) {
logger.info("Discarding partially complete messages for key [" +
key + "] to: " + discardChannel);
logger.info("Discarding partially complete messages for key [" + key + "] to: "
+ discardChannel);
}
for (Message<?> message : all) {
discardChannel.send(message);
store.delete(key, message.getHeaders().getId());
}
}
partialComplete(group);
}
}
return true;
@@ -334,7 +324,6 @@ public class CorrelatingMessageHandler extends AbstractMessageHandler implements
}
}
private final class DelayedKey implements Delayed {
private final Object key;
@@ -362,7 +351,6 @@ public class CorrelatingMessageHandler extends AbstractMessageHandler implements
}
}
private final class IdTracker {
private final ConcurrentMap<Object, ReentrantLock> trackerLocks = new ConcurrentHashMap<Object, ReentrantLock>();
@@ -380,7 +368,8 @@ public class CorrelatingMessageHandler extends AbstractMessageHandler implements
/**
* Call this method to check if an id is tracked and obtain a lock for
* it. Don't forget to finally unlock afterwards.
* @return false if the key was tracked, true after obtaining the lock otherwise
* @return false if the key was tracked, true after obtaining the lock
* otherwise
*/
private boolean waitForLockIfNotTracked(Object correlationKey) {
ReentrantLock lock = trackerLocks.get(correlationKey);

View File

@@ -17,6 +17,7 @@
package org.springframework.integration.aggregator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.integration.core.Message;
@@ -35,7 +36,7 @@ public class DefaultAggregatingMessageGroupProcessor extends AbstractAggregating
@Override
protected final Object aggregatePayloads(MessageGroup group) {
List<Message<?>> messages = group.getMessages();
Collection<Message<?>> messages = group.getMessages();
Assert.notEmpty(messages, this.getClass().getSimpleName() + " cannot process empty message groups");
List<Object> payloads = new ArrayList<Object>(messages.size());
for (Message<?> message : messages) {

View File

@@ -17,6 +17,7 @@
package org.springframework.integration.aggregator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -30,7 +31,8 @@ import org.springframework.integration.core.MessageChannel;
import org.springframework.integration.core.MessageHeaders;
/**
* This class implements all the strategy interfaces needed for a default resequencer.
* This class implements all the strategy interfaces needed for a default
* resequencer.
*
* @author Iwein Fuld
* @since 2.0
@@ -38,24 +40,25 @@ import org.springframework.integration.core.MessageHeaders;
public class DefaultResequencerStrategies implements CorrelationStrategy, CompletionStrategy, MessageGroupProcessor {
private final ConcurrentMap<Object, AtomicInteger> nextMessagesToPass = new ConcurrentHashMap<Object, AtomicInteger>();
private final ConcurrentMap<Object, AtomicInteger> lastMessagesToPass = new ConcurrentHashMap<Object, AtomicInteger>();
private volatile SequenceNumberComparator sequenceNumberComparator = new SequenceNumberComparator();
private volatile boolean releasePartialSequences;
public Object getCorrelationKey(Message<?> message) {
Object key = message.getHeaders().getCorrelationId();
nextMessagesToPass.putIfAbsent(key, new AtomicInteger(1));
return key;
}
public boolean isComplete(List<? extends Message<?>> messages) {
return releasePartialSequences || messages.get(0).getHeaders().getSequenceSize() == messages.size();
public boolean isComplete(Collection<? extends Message<?>> messages) {
return releasePartialSequences
|| (!messages.isEmpty() && messages.iterator().next().getHeaders().getSequenceSize() == messages.size());
}
public void processAndSend(MessageGroup group, MessageChannelTemplate channelTemplate, MessageChannel outputChannel) {
List<Message<?>> all = group.getMessages();
Collection<Message<?>> all = group.getMessages();
Object correlationKey = group.getCorrelationKey();
if (all.size() > 0) {
List<Message<?>> sorted = new ArrayList<Message<?>>(all);
@@ -66,12 +69,11 @@ public class DefaultResequencerStrategies implements CorrelationStrategy, Comple
if (sequenceNumber <= nextSequence.get()) {
channelTemplate.send(message, outputChannel);
nextSequence.compareAndSet(sequenceNumber, sequenceNumber + 1);
group.onProcessingOf(message);
}
}
MessageHeaders headers = sorted.get(0).getHeaders();
if (all.size() == headers.getSequenceSize()) {
group.onCompletion();
// TODO: it's only complete if this is true...
}
}
}
@@ -80,7 +82,6 @@ public class DefaultResequencerStrategies implements CorrelationStrategy, Comple
this.releasePartialSequences = releasePartialSequences;
}
private static class SequenceNumberComparator implements Comparator<Message<?>> {
public int compare(Message<?> o1, Message<?> o2) {
return o1.getHeaders().getSequenceNumber().compareTo(o2.getHeaders().getSequenceNumber());

View File

@@ -17,18 +17,16 @@
package org.springframework.integration.aggregator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.integration.core.Message;
/**
* Represents a mutable group of correlated messages that is bound to a certain
* {@link org.springframework.integration.store.MessageStore} and correlation
* key. The group will grow during its lifetime, when messages are <code>add</code>ed to it.
* <strong>This is not thread safe and should not be used for long running aggregations</strong>.
* key. The group will grow during its lifetime, when messages are
* <code>add</code>ed to it. <strong>This is not thread safe and should not be
* used for long running aggregations</strong>.
* <p/>
* According to its
* {@link org.springframework.integration.aggregator.CompletionStrategy} it can
@@ -39,28 +37,20 @@ import org.springframework.integration.core.Message;
*
* @author Iwein Fuld
* @author Oleg Zhurakousky
* @author Dave Syer
* @since 2.0
*/
public class MessageGroup {
private final CompletionStrategy completionStrategy;
private final Object correlationKey;
private final ArrayList<Message<?>> messages = new ArrayList<Message<?>>();
private final Collection<Message<?>> messages = new ArrayList<Message<?>>();
private final List<MessageGroupListener> listeners;
public MessageGroup(Collection<? extends Message<?>> originalMessages, CompletionStrategy completionStrategy,
Object correlationKey, MessageGroupListener... listeners) {
this.completionStrategy = completionStrategy;
public MessageGroup(Collection<? extends Message<?>> originalMessages, Object correlationKey) {
this.correlationKey = correlationKey;
this.messages.addAll(originalMessages);
this.listeners = Collections.unmodifiableList(Arrays.asList(listeners));
}
/**
* This method determines whether messages have been added to this group
* that supersede the given message based on its sequence id. This can be
@@ -88,53 +78,20 @@ public class MessageGroup {
messages.add(message);
}
public boolean isComplete() {
return completionStrategy.isComplete(messages);
}
/**
* @return internal message list, modification is allowed, but not
* recommended
* recommended
*/
public List<Message<?>> getMessages() {
public Collection<Message<?>> getMessages() {
return messages;
}
/**
* @return the correlation key that links these messages together according
* to a particular CorrelationStrategy
* to a particular CorrelationStrategy
*/
public Object getCorrelationKey() {
return correlationKey;
}
/**
* Call this method to sign off on processing of certain messages e.g. from
* a MessageProcessor. Typically this will remove these messages from the
* processing backlog.
*/
public void onProcessingOf(Message<?>... messages) {
for (MessageGroupListener listener : listeners) {
listener.onProcessingOf(messages);
}
}
/**
* Call this method to signal the completion of the processing of an entire group.
*/
public void onCompletion() {
for (MessageGroupListener listener : listeners) {
listener.onCompletionOf(correlationKey);
}
}
/**
* This method is a shorthand for signaling that all messages in the group have been
* processed and that the group is completed.
*/
public void onCompleteProcessing() {
onProcessingOf(messages.toArray(new Message[messages.size()]));
onCompletion();
}
}

View File

@@ -71,8 +71,6 @@ public class MethodInvokingMessageGroupProcessor extends AbstractAggregatingMess
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;
}

View File

@@ -17,8 +17,7 @@ public class PassThroughMessageGroupProcessor implements MessageGroupProcessor {
public void processAndSend(MessageGroup group, MessageChannelTemplate channelTemplate, MessageChannel outputChannel) {
for (Message<?> message : group.getMessages()) {
channelTemplate.send(message, outputChannel);
group.onProcessingOf(message);
}
group.onCompletion();
}
}

View File

@@ -16,7 +16,7 @@
package org.springframework.integration.aggregator;
import java.util.List;
import java.util.Collection;
import org.springframework.integration.core.Message;
import org.springframework.util.CollectionUtils;
@@ -31,11 +31,11 @@ import org.springframework.util.CollectionUtils;
*/
public class SequenceSizeCompletionStrategy implements CompletionStrategy {
public boolean isComplete(List<? extends Message<?>> messages) {
public boolean isComplete(Collection<? extends Message<?>> messages) {
if (CollectionUtils.isEmpty(messages)) {
return false;
}
return messages.size() != 0 && (messages.size() >= messages.get(0).getHeaders().getSequenceSize());
return messages.size() != 0 && (messages.size() >= messages.iterator().next().getHeaders().getSequenceSize());
}
}

View File

@@ -27,7 +27,6 @@ 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;
@@ -43,12 +42,11 @@ public class AggregatingMessageGroupProcessorHeaderTests {
private final MessageChannelTemplate channelTemplate = new MessageChannelTemplate();
private final MessageGroupProcessor defaultProcessor = new DefaultAggregatingMessageGroupProcessor();
private final DefaultAggregatingMessageGroupProcessor defaultProcessor = new DefaultAggregatingMessageGroupProcessor();
private final MessageGroupProcessor methodInvokingProcessor =
private final MethodInvokingMessageGroupProcessor methodInvokingProcessor =
new MethodInvokingMessageGroupProcessor(new TestAggregatorBean(), "aggregate");
@Test
public void singleMessageUsingDefaultProcessor() {
this.singleMessage(defaultProcessor);
@@ -106,7 +104,7 @@ public class AggregatingMessageGroupProcessorHeaderTests {
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);
MessageGroup group = new MessageGroup(messages, 1);
processor.processAndSend(group, channelTemplate, outputChannel);
Message<?> result = outputChannel.receive(0);
assertNotNull(result);
@@ -121,7 +119,7 @@ public class AggregatingMessageGroupProcessorHeaderTests {
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);
MessageGroup group = new MessageGroup(messages, 1);
processor.processAndSend(group, channelTemplate, outputChannel);
Message<?> result = outputChannel.receive(0);
assertNotNull(result);
@@ -139,7 +137,7 @@ public class AggregatingMessageGroupProcessorHeaderTests {
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);
MessageGroup group = new MessageGroup(messages, 1);
processor.processAndSend(group, channelTemplate, outputChannel);
Message<?> result = outputChannel.receive(0);
assertNotNull(result);
@@ -169,7 +167,7 @@ public class AggregatingMessageGroupProcessorHeaderTests {
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);
MessageGroup group = new MessageGroup(messages, 1);
processor.processAndSend(group, channelTemplate, outputChannel);
Message<?> result = outputChannel.receive(0);
assertNotNull(result);
@@ -197,7 +195,7 @@ public class AggregatingMessageGroupProcessorHeaderTests {
headers3.put("common", "valueForAll");
Message<?> message3 = correlatedMessage(1, 3, 3, headers3);
List<Message<?>> messages = Arrays.<Message<?>>asList(message1, message2, message3);
MessageGroup group = new MessageGroup(messages, new SequenceSizeCompletionStrategy(), 1);
MessageGroup group = new MessageGroup(messages, 1);
processor.processAndSend(group, channelTemplate, outputChannel);
Message<?> result = outputChannel.receive(0);
assertNotNull(result);

View File

@@ -27,7 +27,6 @@ import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
@@ -38,11 +37,9 @@ import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
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.integration.store.MessageStore;
@@ -77,8 +74,7 @@ public class CorrelatingMessageHandlerTests {
doAnswer(new Answer<Object>() {
public Object answer(InvocationOnMock invocation) throws Throwable {
MessageGroup messageGroup = (MessageGroup) invocation.getArguments()[0];
messageGroup.onProcessingOf(messageGroup.getMessages().toArray(new Message[2]));
messageGroup.onCompletion();
// TODO: remove this?
return null;
}
}).when(processor).processAndSend(isA(MessageGroup.class),
@@ -88,8 +84,6 @@ public class CorrelatingMessageHandlerTests {
@Test
public void bufferCompletesNormally() throws Exception {
String correlationKey = "key";
UUID id1 = UUID.randomUUID();
UUID id2 = UUID.randomUUID();
Message<?> message1 = testMessage(correlationKey, 1);
Message<?> message2 = testMessage(correlationKey, 2);
List<Message<?>> storedMessages = new ArrayList<Message<?>>();
@@ -130,8 +124,6 @@ public class CorrelatingMessageHandlerTests {
String correlationKey = "key";
final Message<?> message1 = testMessage(correlationKey, 1);
final Message<?> message2 = testMessage(correlationKey, 2);
UUID id1 = message1.getHeaders().getId();
UUID id2 = message2.getHeaders().getId();
final List<Message<?>> storedMessages = new ArrayList<Message<?>>();
final CountDownLatch bothMessagesHandled = new CountDownLatch(2);

View File

@@ -16,39 +16,34 @@ import org.springframework.integration.message.MessageBuilder;
/**
* @author Iwein Fuld
* @author Oleg Zhurakousky
* @author Dave Syer
*/
@RunWith(MockitoJUnitRunner.class)
public class MessageGroupTests {
private Object key = new Object();
private Object key = new Object();
@Mock
private MessageGroupListener listener;
private MessageGroup group;
@Mock
private CompletionStrategy completionStrategy;
@Before
public void buildMessageGroup() {
group = new MessageGroup(Collections.<Message<?>> emptyList(), key);
}
private MessageGroup group;
@Test
public void shouldFindSupersedingMessages() {
final Message<?> message1 = MessageBuilder.withPayload("test").setSequenceNumber(1).build();
final Message<?> message2 = MessageBuilder.fromMessage(message1).setSequenceNumber(1).build();
assertThat(group.hasNoMessageSuperseding(message1), is(true));
group.add(message2);
assertThat(group.hasNoMessageSuperseding(message1), is(false));
}
@Before
public void buildMessageGroup() {
group = new MessageGroup(Collections.<Message<?>>emptyList(), completionStrategy, key, listener);
}
@Test
public void shouldFindSupersedingMessages() {
final Message<?> message1 = MessageBuilder.withPayload("test").setSequenceNumber(1).build();
final Message<?> message2 = MessageBuilder.fromMessage(message1).setSequenceNumber(1).build();
assertThat(group.hasNoMessageSuperseding(message1), is(true));
group.add(message2);
assertThat(group.hasNoMessageSuperseding(message1), is(false));
}
@Test
public void shouldIgnoreMessagesWithZeroSequenceNumber() {
final Message<?> message1 = MessageBuilder.withPayload("test").build();
final Message<?> message2 = MessageBuilder.fromMessage(message1).build();
assertThat(group.hasNoMessageSuperseding(message1), is(true));
group.add(message2);
assertThat(group.hasNoMessageSuperseding(message1), is(true));
}
@Test
public void shouldIgnoreMessagesWithZeroSequenceNumber() {
final Message<?> message1 = MessageBuilder.withPayload("test").build();
final Message<?> message2 = MessageBuilder.fromMessage(message1).build();
assertThat(group.hasNoMessageSuperseding(message1), is(true));
group.add(message2);
assertThat(group.hasNoMessageSuperseding(message1), is(true));
}
}

View File

@@ -16,6 +16,16 @@
package org.springframework.integration.aggregator;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import java.util.Collection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@@ -28,13 +38,6 @@ import org.springframework.integration.message.MessageBuilder;
import org.springframework.integration.message.MessageHandlingException;
import org.springframework.integration.store.SimpleMessageStore;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
/**
* @author Mark Fisher
* @author Marius Bogoevici
@@ -255,17 +258,11 @@ public class NewAggregatorEndpointTests {
MessageChannelTemplate channelTemplate, MessageChannel outputChannel
) {
Integer product = 1;
List<Message<?>> messagesUpForProcessing = group.getMessages();
for (Message<?> message : messagesUpForProcessing) {
for (Message<?> message : group.getMessages()) {
product *= (Integer) message.getPayload();
}
channelTemplate.send(MessageBuilder.withPayload(product).build(), outputChannel);
group.onProcessingOf(
messagesUpForProcessing.toArray(new Message[messagesUpForProcessing.size()])
);
group.onCompletion();
}
}
}
private class NullReturningMessageProcessor implements MessageGroupProcessor {

View File

@@ -16,6 +16,15 @@
package org.springframework.integration.aggregator;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
@@ -33,13 +42,6 @@ import org.springframework.integration.message.MessageHandlingException;
import org.springframework.integration.store.SimpleMessageStore;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
/**
* @author Mark Fisher
* @author Marius Bogoevici
@@ -324,16 +326,10 @@ public class NewConcurrentAggregatorEndpointTests {
MessageChannelTemplate channelTemplate, MessageChannel outputChannel
) {
Integer product = 1;
List<Message<?>> messagesUpForProcessing = group.getMessages();
for (Message<?> message : messagesUpForProcessing) {
for (Message<?> message : group.getMessages()) {
product *= (Integer) message.getPayload();
}
channelTemplate.send(MessageBuilder.withPayload(product).build(), outputChannel);
group.onProcessingOf(
messagesUpForProcessing.toArray(new Message[messagesUpForProcessing.size()])
);
group.onCompletion();
}
}

View File

@@ -102,6 +102,7 @@ public class NewResequencerTests {
@Test
@Ignore // TODO: fix this
public void testResequencingWithIncompleteSequenceRelease() throws InterruptedException {
this.resequencerStrategies.setReleasePartialSequences(true);
QueueChannel replyChannel = new QueueChannel();

View File

@@ -31,6 +31,7 @@ import org.springframework.integration.message.MessageBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Collection;
import java.util.List;
import static org.junit.Assert.assertThat;
@@ -109,7 +110,7 @@ public class AggregatorWithCorrelationStrategyTests {
this.expectedSize = expectedSize;
}
public boolean isComplete(List<? extends Message<?>> messages) {
public boolean isComplete(Collection<? extends Message<?>> messages) {
return messages.size() == expectedSize;
}

View File

@@ -16,7 +16,7 @@
package org.springframework.integration.config;
import java.util.List;
import java.util.Collection;
import org.springframework.integration.aggregator.CompletionStrategy;
import org.springframework.integration.core.Message;
@@ -26,7 +26,7 @@ import org.springframework.integration.core.Message;
*/
public class TestCompletionStrategy implements CompletionStrategy {
public boolean isComplete(List<? extends Message<?>> messages) {
public boolean isComplete(Collection<? extends Message<?>> messages) {
throw new UnsupportedOperationException("This is not intended to be implemented, but to verify injection into an <aggregator>");
}