use of org.springframework.integration.store.MessageGroup in project spring-integration by spring-projects.
the class JdbcMessageStoreTests method testCompletedNotExpiredGroupINT3037.
@Test
public void testCompletedNotExpiredGroupINT3037() throws Exception {
/*
* based on the aggregator scenario as follows;
*
* send three messages in
* 1 of 2
* 2 of 2
* 2 of 2 (last again)
*
* expected behavior is that the LAST message (2 of 2 repeat) should be on the discard channel
* (discard behavior performed by the AbstractCorrelatingMessageHandler.handleMessageInternal)
*/
final JdbcMessageStore messageStore = new JdbcMessageStore(dataSource);
// init
String groupId = "group";
// build the messages
Message<?> oneOfTwo = MessageBuilder.withPayload("hello").setSequenceNumber(1).setSequenceSize(2).setCorrelationId(groupId).build();
Message<?> twoOfTwo = MessageBuilder.withPayload("world").setSequenceNumber(2).setSequenceSize(2).setCorrelationId(groupId).build();
// add to the messageStore
messageStore.addMessagesToGroup(groupId, oneOfTwo, twoOfTwo);
// check that 2 messages are there
assertTrue(messageStore.getMessageGroupCount() == 1);
assertTrue(messageStore.getMessageCount() == 2);
// retrieve the group (like in the aggregator)
MessageGroup messageGroup = messageStore.getMessageGroup(groupId);
// 'complete' the group
messageStore.completeGroup(messageGroup.getGroupId());
// now clear the messages
for (Message<?> message : messageGroup.getMessages()) {
messageStore.removeMessagesFromGroup(groupId, message);
}
// end for
// 'add' the other message --> emulated by getting the messageGroup
messageGroup = messageStore.getMessageGroup(groupId);
// should be marked 'complete' --> old behavior it would not
assertTrue(messageGroup.isComplete());
}
use of org.springframework.integration.store.MessageGroup in project spring-integration by spring-projects.
the class AbstractCorrelatingMessageHandler method processForceRelease.
private void processForceRelease(Object groupId) {
MessageGroup messageGroup = this.messageStore.getMessageGroup(groupId);
this.forceReleaseProcessor.processMessageGroup(messageGroup);
}
use of org.springframework.integration.store.MessageGroup in project spring-integration by spring-projects.
the class AbstractCorrelatingMessageHandler method removeEmptyGroupAfterTimeout.
private void removeEmptyGroupAfterTimeout(MessageGroup messageGroup, long timeout) {
Object groupId = messageGroup.getGroupId();
UUID groupUuid = UUIDConverter.getUUID(groupId);
ScheduledFuture<?> scheduledFuture = getTaskScheduler().schedule(() -> {
Lock lock = this.lockRegistry.obtain(groupUuid.toString());
try {
lock.lockInterruptibly();
try {
this.expireGroupScheduledFutures.remove(groupUuid);
/*
* Obtain a fresh state for group from the MessageStore,
* since it could be changed while we have waited for lock.
*/
MessageGroup groupNow = this.messageStore.getMessageGroup(groupUuid);
boolean removeGroup = groupNow.size() == 0 && groupNow.getLastModified() <= (System.currentTimeMillis() - this.minimumTimeoutForEmptyGroups);
if (removeGroup) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Removing empty group: " + groupUuid);
}
remove(messageGroup);
}
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Thread was interrupted while trying to obtain lock." + "Rescheduling empty MessageGroup [ " + groupId + "] for removal.");
}
removeEmptyGroupAfterTimeout(messageGroup, timeout);
}
}, new Date(System.currentTimeMillis() + timeout));
if (this.logger.isDebugEnabled()) {
this.logger.debug("Schedule empty MessageGroup [ " + groupId + "] for removal.");
}
this.expireGroupScheduledFutures.put(groupUuid, scheduledFuture);
}
use of org.springframework.integration.store.MessageGroup in project spring-integration by spring-projects.
the class AbstractCorrelatingMessageHandlerTests method testReaperDoesntReapAProcessingGroup.
// INT-2751
@Test
public void testReaperDoesntReapAProcessingGroup() throws Exception {
final MessageGroupStore groupStore = new SimpleMessageStore();
final CountDownLatch waitForSendLatch = new CountDownLatch(1);
final CountDownLatch waitReapStartLatch = new CountDownLatch(1);
final CountDownLatch waitReapCompleteLatch = new CountDownLatch(1);
AbstractCorrelatingMessageHandler handler = new AbstractCorrelatingMessageHandler(group -> group, groupStore) {
@Override
protected void afterRelease(MessageGroup group, Collection<Message<?>> completedMessages) {
}
};
handler.setReleasePartialSequences(true);
/*
* Runs "reap" when group 'bar' is in completion
*/
ExecutorService exec = Executors.newSingleThreadExecutor();
exec.execute(() -> {
try {
waitReapStartLatch.await(10, TimeUnit.SECONDS);
} catch (InterruptedException e1) {
Thread.currentThread().interrupt();
}
waitForSendLatch.countDown();
try {
Thread.sleep(100);
} catch (InterruptedException e2) {
Thread.currentThread().interrupt();
}
groupStore.expireMessageGroups(50);
waitReapCompleteLatch.countDown();
});
final List<Message<?>> outputMessages = new ArrayList<Message<?>>();
handler.setOutputChannel((message, timeout) -> {
/*
* Executes when group 'bar' completes normally
*/
outputMessages.add(message);
// wake reaper
waitReapStartLatch.countDown();
try {
waitForSendLatch.await(10, TimeUnit.SECONDS);
// wait a little longer for reaper to grab groups
Thread.sleep(2000);
// simulate tx commit
groupStore.removeMessageGroup("bar");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return true;
});
handler.setReleaseStrategy(group -> group.size() == 2);
QueueChannel discards = new QueueChannel();
handler.setDiscardChannel(discards);
handler.setSendPartialResultOnExpiry(true);
Message<String> message = MessageBuilder.withPayload("foo").setCorrelationId("qux").build();
// partial group that will be reaped
handler.handleMessage(message);
message = MessageBuilder.withPayload("foo").setCorrelationId("bar").build();
// full group that should not be reaped
handler.handleMessage(message);
message = MessageBuilder.withPayload("baz").setCorrelationId("bar").build();
handler.handleMessage(message);
assertTrue(waitReapCompleteLatch.await(10, TimeUnit.SECONDS));
// Before INT-2751 we got bar + bar + qux
// bar + qux
assertEquals(2, outputMessages.size());
// normal release
// 'bar'
assertEquals(2, ((MessageGroup) outputMessages.get(0).getPayload()).size());
// reaper release
// 'qux'
assertEquals(1, ((MessageGroup) outputMessages.get(1).getPayload()).size());
assertNull(discards.receive(0));
exec.shutdownNow();
}
use of org.springframework.integration.store.MessageGroup in project spring-integration by spring-projects.
the class AbstractCorrelatingMessageHandlerTests method testDontReapIfAlreadyCompleteAfterRefetch.
/*
* INT-3216 - Verifies the complete early exit is taken after a refresh.
*/
@Test
public void testDontReapIfAlreadyCompleteAfterRefetch() throws Exception {
MessageGroupProcessor mgp = new DefaultAggregatingMessageGroupProcessor();
AggregatingMessageHandler handler = new AggregatingMessageHandler(mgp);
handler.setReleaseStrategy(group -> true);
QueueChannel outputChannel = new QueueChannel();
handler.setOutputChannel(outputChannel);
MessageGroupStore mgs = TestUtils.getPropertyValue(handler, "messageStore", MessageGroupStore.class);
mgs.addMessagesToGroup("foo", new GenericMessage<String>("foo"));
MessageGroup group = new SimpleMessageGroup(mgs.getMessageGroup("foo"));
mgs.completeGroup("foo");
mgs = spy(mgs);
new DirectFieldAccessor(handler).setPropertyValue("messageStore", mgs);
Method forceComplete = AbstractCorrelatingMessageHandler.class.getDeclaredMethod("forceComplete", MessageGroup.class);
forceComplete.setAccessible(true);
MessageGroup groupInStore = (MessageGroup) TestUtils.getPropertyValue(mgs, "groupIdToMessageGroup", Map.class).get("foo");
assertTrue(groupInStore.isComplete());
assertFalse(group.isComplete());
new DirectFieldAccessor(group).setPropertyValue("lastModified", groupInStore.getLastModified());
forceComplete.invoke(handler, group);
verify(mgs).getMessageGroup("foo");
assertNull(outputChannel.receive(0));
}
Aggregations