@Override public void destroy() throws Exception { if (this.expireOnDestroy) { if (this.isRunning()) { logger.info("Expiring all messages from message group store: " + this.messageGroupStore); this.messageGroupStore.expireMessageGroups(0); } else { logger.debug("'expireOnDestroy' is set to 'true' but the reaper is not currently running"); } } }
/** * Expire all message groups older than the {@link #setTimeout(long) timeout} provided. Normally this method would * be executed by a scheduled task. */ @Override public void run() { if (this.timeout >= 0 && this.isRunning()) { if (logger.isDebugEnabled()) { logger.debug("Expiring all messages older than timeout=" + this.timeout + " from message group store: " + this.messageGroupStore); } this.messageGroupStore.expireMessageGroups(this.timeout); } }
@Test public void shouldNotPruneWhileCompleting() throws Exception { String correlationKey = "key"; final Message<?> message1 = testMessage(correlationKey, 1, 2); final Message<?> message2 = testMessage(correlationKey, 2, 2); final List<Message<?>> storedMessages = new ArrayList<Message<?>>(); final CountDownLatch bothMessagesHandled = new CountDownLatch(2); when(correlationStrategy.getCorrelationKey(isA(Message.class))).thenReturn(correlationKey); when(processor.processMessageGroup(any(MessageGroup.class))).thenReturn(MessageBuilder.withPayload("grouped").build()); when(outputChannel.send(any(Message.class))).thenReturn(true); handler.handleMessage(message1); bothMessagesHandled.countDown(); storedMessages.add(message1); ExecutorService exec = Executors.newSingleThreadExecutor(); exec.submit(() -> { handler.handleMessage(message2); storedMessages.add(message2); bothMessagesHandled.countDown(); }); assertTrue(bothMessagesHandled.await(10, TimeUnit.SECONDS)); assertEquals(0, store.expireMessageGroups(10000)); exec.shutdownNow(); }
Thread.currentThread().interrupt(); groupStore.expireMessageGroups(50); waitReapCompleteLatch.countDown(); });
@Test // INT-2833 public void testReaperReapsAnEmptyGroup() { final MessageGroupStore groupStore = new SimpleMessageStore(); AggregatingMessageHandler handler = new AggregatingMessageHandler(group -> group, groupStore); final List<Message<?>> outputMessages = new ArrayList<>(); handler.setOutputChannel((message, timeout) -> { /* * Executes when group 'bar' completes normally */ outputMessages.add(message); return true; }); handler.setReleaseStrategy(group -> group.size() == 1); Message<String> message = MessageBuilder.withPayload("foo") .setCorrelationId("bar") .build(); handler.handleMessage(message); assertEquals(1, outputMessages.size()); assertEquals(1, TestUtils.getPropertyValue(handler, "messageStore.groupIdToMessageGroup", Map.class).size()); groupStore.expireMessageGroups(0); assertEquals(0, TestUtils.getPropertyValue(handler, "messageStore.groupIdToMessageGroup", Map.class).size()); }
@Test public void testShouldNotSendPartialResultOnTimeoutByDefault() throws InterruptedException { QueueChannel discardChannel = new QueueChannel(); this.aggregator.setDiscardChannel(discardChannel); QueueChannel replyChannel = new QueueChannel(); Message<?> message = createMessage(3, "ABC", 2, 1, replyChannel, null); CountDownLatch latch = new CountDownLatch(1); AggregatorTestTask task = new AggregatorTestTask(this.aggregator, message, latch); this.taskExecutor.execute(task); assertTrue(latch.await(10, TimeUnit.SECONDS)); assertEquals("Task should have completed within timeout", 0, latch .getCount()); Message<?> reply = replyChannel.receive(10); assertNull("No message should have been sent normally", reply); this.store.expireMessageGroups(-10000); Message<?> discardedMessage = discardChannel.receive(10000); assertNotNull("A message should have been discarded", discardedMessage); assertEquals(message, discardedMessage); }
@Test public void testShouldSendPartialResultOnTimeoutTrue() throws InterruptedException { this.aggregator.setSendPartialResultOnExpiry(true); QueueChannel replyChannel = new QueueChannel(); Message<?> message1 = createMessage(3, "ABC", 3, 1, replyChannel, null); Message<?> message2 = createMessage(5, "ABC", 3, 2, replyChannel, null); CountDownLatch latch = new CountDownLatch(2); AggregatorTestTask task1 = new AggregatorTestTask(this.aggregator, message1, latch); AggregatorTestTask task2 = new AggregatorTestTask(this.aggregator, message2, latch); this.taskExecutor.execute(task1); this.taskExecutor.execute(task2); assertTrue(latch.await(10, TimeUnit.SECONDS)); assertEquals("handlers should have been invoked within time limit", 0, latch.getCount()); this.store.expireMessageGroups(-10000); Message<?> reply = replyChannel.receive(1000); assertNotNull("A reply message should have been received", reply); assertEquals(15, reply.getPayload()); assertNull(task1.getException()); assertNull(task2.getException()); }
@Override public void destroy() throws Exception { if (this.expireOnDestroy) { if (this.isRunning()) { logger.info("Expiring all messages from message group store: " + this.messageGroupStore); this.messageGroupStore.expireMessageGroups(0); } else { logger.debug("'expireOnDestroy' is set to 'true' but the reaper is not currently running"); } } }
/** * Expire all message groups older than the {@link #setTimeout(long) timeout} provided. Normally this method would * be executed by a scheduled task. */ @Override public void run() { if (this.timeout >= 0 && this.isRunning()) { if (logger.isDebugEnabled()) { logger.debug("Expiring all messages older than timeout=" + this.timeout + " from message group store: " + this.messageGroupStore); } this.messageGroupStore.expireMessageGroups(this.timeout); } }
@Test public void testResequencingWithDiscard() throws InterruptedException { QueueChannel discardChannel = new QueueChannel(); Message<?> message1 = createMessage("123", "ABC", 4, 2, null); Message<?> message2 = createMessage("456", "ABC", 4, 1, null); Message<?> message3 = createMessage("789", "ABC", 4, 4, null); this.resequencer.setSendPartialResultOnExpiry(false); this.resequencer.setDiscardChannel(discardChannel); this.resequencer.handleMessage(message1); this.resequencer.handleMessage(message2); assertEquals(1, store.expireMessageGroups(-10000)); Message<?> reply1 = discardChannel.receive(0); Message<?> reply2 = discardChannel.receive(0); Message<?> reply3 = discardChannel.receive(0); // only messages 1 and 2 should have been received by now assertNotNull(reply1); assertNotNull(reply2); assertNull(reply3); ArrayList<Integer> sequence = new ArrayList<>( Arrays.asList(new IntegrationMessageHeaderAccessor(reply1).getSequenceNumber(), new IntegrationMessageHeaderAccessor(reply2).getSequenceNumber())); Collections.sort(sequence); assertEquals("[1, 2]", sequence.toString()); // Once a group is expired, late messages are discarded immediately by default this.resequencer.handleMessage(message3); reply3 = discardChannel.receive(0); assertNotNull(reply3); }
@Test public void waitForAllCustomReleaseStrategyWithLateArrivals() { QueueChannel outputChannel = new QueueChannel(); QueueChannel discardChannel = new QueueChannel(); defaultHandler.setOutputChannel(outputChannel); defaultHandler.setDiscardChannel(discardChannel); defaultHandler.setReleaseStrategy(new SampleSizeReleaseStrategy()); for (int i = 0; i < 5; i++) { defaultHandler.handleMessage(MessageBuilder.withPayload(i).setCorrelationId("A").build()); } assertEquals(5, ((List<?>) outputChannel.receive(0).getPayload()).size()); assertNull(discardChannel.receive(0)); assertEquals(0, store.getMessageGroup("A").getMessages().size()); // send another message with the same correlation id and see it in the discard channel defaultHandler.handleMessage(MessageBuilder.withPayload("foo").setCorrelationId("A").build()); assertNotNull(discardChannel.receive(0)); // expireMessageGroups from aggregator MessageStore and the messages should start accumulating again store.expireMessageGroups(0); defaultHandler.handleMessage(MessageBuilder.withPayload("foo").setCorrelationId("A").build()); assertNull(discardChannel.receive(0)); assertEquals(1, store.getMessageGroup("A").getMessages().size()); }
@Test public void waitForAllDefaultReleaseStrategyWithLateArrivals() { QueueChannel outputChannel = new QueueChannel(); QueueChannel discardChannel = new QueueChannel(); defaultHandler.setOutputChannel(outputChannel); defaultHandler.setDiscardChannel(discardChannel); for (int i = 0; i < 5; i++) { defaultHandler.handleMessage(MessageBuilder.withPayload(i).setSequenceSize(5).setCorrelationId("A").setSequenceNumber(i).build()); } assertEquals(5, ((List<?>) outputChannel.receive(0).getPayload()).size()); assertNull(discardChannel.receive(0)); assertEquals(0, store.getMessageGroup("A").getMessages().size()); // send another message with the same correlation id and see it in the discard channel defaultHandler.handleMessage(MessageBuilder.withPayload("foo").setSequenceSize(5).setCorrelationId("A").setSequenceNumber(3).build()); assertNotNull(discardChannel.receive(0)); // expireMessageGroups from aggregator MessageStore and the messages should start accumulating again store.expireMessageGroups(0); defaultHandler.handleMessage(MessageBuilder.withPayload("foo").setSequenceSize(5).setCorrelationId("A").setSequenceNumber(3).build()); assertNull(discardChannel.receive(0)); assertEquals(1, store.getMessageGroup("A").getMessages().size()); }
@Test @Ignore("Until 5.2 with new 'owner' feature on groups") public void testDontReapMessageOfOtherHandler() { MessageGroupStore groupStore = new SimpleMessageStore(); AggregatingMessageHandler handler1 = new AggregatingMessageHandler(group -> group, groupStore); AggregatingMessageHandler handler2 = new AggregatingMessageHandler(group -> group, groupStore); QueueChannel handler1DiscardChannel = new QueueChannel(); handler1.setDiscardChannel(handler1DiscardChannel); QueueChannel handler2DiscardChannel = new QueueChannel(); handler2.setDiscardChannel(handler2DiscardChannel); handler1.setReleaseStrategy(group -> false); handler2.setReleaseStrategy(group -> false); handler1.handleMessage(MessageBuilder.withPayload("foo").setCorrelationId("foo").build()); handler1.handleMessage(MessageBuilder.withPayload("foo").setCorrelationId("foo").build()); handler2.handleMessage(MessageBuilder.withPayload("foo").setCorrelationId("bar").build()); groupStore.expireMessageGroups(0); assertEquals(2, handler1DiscardChannel.getQueueSize()); assertEquals(1, handler2DiscardChannel.getQueueSize()); }