public Object processMessageGroup(MessageGroup group) { Integer product = 1; for (Message<?> message : group.getMessages()) { product *= (Integer) message.getPayload(); } return product; }
@Test public void testProcessAndCheckHeaders() throws Exception { when(group.getMessages()).thenReturn(messages); processor = new ExpressionEvaluatingMessageGroupProcessor("#root"); Object result = processor.processMessageGroup(group); assertTrue(result instanceof Message<?>); Message<?> resultMessage = (Message<?>) result; assertEquals("bar", resultMessage.getHeaders().get("foo")); }
@Test public void testProcessAndSendWithSizeExpressionEvaluated() throws Exception { when(group.getMessages()).thenReturn(messages); processor = new ExpressionEvaluatingMessageGroupProcessor("#root.size()"); Object result = processor.processMessageGroup(group); assertTrue(result instanceof Message<?>); Message<?> resultMessage = (Message<?>) result; assertEquals(5, resultMessage.getPayload()); }
@Test public void testMultipleInstancesOfGroupStore() throws Exception { GemfireMessageStore store1 = new GemfireMessageStore(this.cache); store1.afterPropertiesSet(); GemfireMessageStore store2 = new GemfireMessageStore(this.cache); store2.afterPropertiesSet(); Message<?> message = new GenericMessage<String>("1"); store1.addMessageToGroup(1, message); MessageGroup messageGroup = store2.addMessageToGroup(1, new GenericMessage<String>("2")); assertEquals(2, messageGroup.getMessages().size()); GemfireMessageStore store3 = new GemfireMessageStore(this.cache); store3.afterPropertiesSet(); messageGroup = store3.removeMessageFromGroup(1, message); assertEquals(1, messageGroup.getMessages().size()); }
@Test public void testProcessAndSendWithFilterAndProjectionAndMethodInvokingExpressionEvaluated() throws Exception { when(group.getMessages()).thenReturn(messages); processor = new ExpressionEvaluatingMessageGroupProcessor( String.format("T(%s).sum(?[payload>2].![payload])", getClass().getName())); Object result = processor.processMessageGroup(group); assertTrue(result instanceof Message<?>); Message<?> resultMessage = (Message<?>) result; assertEquals(3 + 4 + 5, resultMessage.getPayload()); }
@Test public void testProcessAndSendWithFilterAndProjectionExpressionEvaluated() throws Exception { when(group.getMessages()).thenReturn(messages); processor = new ExpressionEvaluatingMessageGroupProcessor("?[payload>2].![payload]"); Object result = processor.processMessageGroup(group); assertTrue(result instanceof Message<?>); Message<?> resultMessage = (Message<?>) result; assertTrue(resultMessage.getPayload() instanceof Collection<?>); Collection<?> list = (Collection<?>) resultMessage.getPayload(); assertEquals(3, list.size()); assertTrue(list.contains(3)); assertTrue(list.contains(4)); assertTrue(list.contains(5)); }
@Test public void testRemoveMessageGroup() throws Exception { GemfireMessageStore store = new GemfireMessageStore(this.cache); store.afterPropertiesSet(); MessageGroup messageGroup = store.getMessageGroup(1); Message<?> message = new GenericMessage<String>("Hello"); messageGroup = store.addMessageToGroup(messageGroup.getGroupId(), message); assertEquals(1, messageGroup.size()); store.removeMessageGroup(1); MessageGroup messageGroupA = store.getMessageGroup(1); assertNotSame(messageGroup, messageGroupA); assertEquals(0, messageGroupA.getMessages().size()); assertEquals(0, messageGroupA.size()); // make sure the store is properly rebuild from Gemfire store = new GemfireMessageStore(this.cache); store.afterPropertiesSet(); messageGroup = store.getMessageGroup(1); assertEquals(0, messageGroup.getMessages().size()); assertEquals(0, messageGroup.size()); }
protected void expireGroup(Object correlationKey, MessageGroup group) { if (logger.isInfoEnabled()) { logger.info("Expiring MessageGroup with correlationKey[" + correlationKey + "]"); } if (sendPartialResultOnExpiry) { if (logger.isDebugEnabled()) { logger.debug( "Prematurely releasing partially complete group with key [" + correlationKey + "] to: " + getOutputChannel()); } completeGroup(correlationKey, group); } else { if (logger.isDebugEnabled()) { logger.debug( "Discarding messages of partially complete group with key [" + correlationKey + "] to: " + (this.discardChannelName != null ? this.discardChannelName : this.discardChannel)); } for (Message<?> message : group.getMessages()) { discardMessage(message); } } if (this.applicationEventPublisher != null) { this.applicationEventPublisher.publishEvent( new MessageGroupExpiredEvent( this, correlationKey, group.size(), new Date(group.getLastModified()), new Date(), !sendPartialResultOnExpiry)); } }
@Override public boolean canRelease(MessageGroup messageGroup) { boolean canRelease = false; Collection<Message<?>> messages = messageGroup.getMessages(); if (releasePartialSequences && !messages.isEmpty()) { if (logger.isTraceEnabled()) { logger.trace("Considering partial release of group [" + messageGroup + "]"); } Message<?> minMessage = Collections.min(messages, this.comparator); int nextSequenceNumber = new IntegrationMessageHeaderAccessor(minMessage).getSequenceNumber(); int lastReleasedMessageSequence = messageGroup.getLastReleasedMessageSequenceNumber(); if (nextSequenceNumber - lastReleasedMessageSequence == 1) { canRelease = true; ; } } else { int size = messages.size(); if (size == 0) { canRelease = true; } else { int sequenceSize = new IntegrationMessageHeaderAccessor(messageGroup.getOne()).getSequenceSize(); // If there is no sequence then it must be incomplete.... if (sequenceSize == size) { canRelease = true; } } } return canRelease; }
protected void forceComplete(MessageGroup group) { Object correlationKey = group.getGroupId(); // UUIDConverter is no-op if already converted Lock lock = this.lockRegistry.obtain(UUIDConverter.getUUID(correlationKey).toString()); boolean removeGroup = true; try { lock.lockInterruptibly(); try { ScheduledFuture<?> scheduledFuture = this.expireGroupScheduledFutures.remove(UUIDConverter.getUUID(correlationKey)); if (scheduledFuture != null) { boolean canceled = scheduledFuture.cancel(false); if (canceled && logger.isDebugEnabled()) { logger.debug("Cancel 'forceComplete' scheduling for MessageGroup [ " + group + "]."); } } MessageGroup groupNow = group; /* * If the group argument is not already complete, * re-fetch it because it might have changed while we were waiting on * its lock. If the last modified timestamp changed, defer the completion * because the selection condition may have changed such that the group * would no longer be eligible. If the timestamp changed, it's a completely new * group and should not be reaped on this cycle. * * If the group argument is already complete, do not re-fetch. * Note: not all message stores provide a direct reference to its internal * group so the initial 'isComplete()` will only return true for those stores if * the group was already complete at the time of its selection as a candidate. * * If the group is marked complete, only consider it * for reaping if it's empty (and both timestamps are unaltered). */ if (!group.isComplete()) { groupNow = this.messageStore.getMessageGroup(correlationKey); } long lastModifiedNow = groupNow.getLastModified(); int groupSize = groupNow.size(); if ((!groupNow.isComplete() || groupSize == 0) && group.getLastModified() == lastModifiedNow && group.getTimestamp() == groupNow.getTimestamp()) { if (groupSize > 0) { if (releaseStrategy.canRelease(groupNow)) { completeGroup(correlationKey, groupNow); } else { expireGroup(correlationKey, groupNow); } if (!this.expireGroupsUponTimeout) { afterRelease(groupNow, groupNow.getMessages(), true); removeGroup = false; } } else { /* * By default empty groups are removed on the same schedule as non-empty * groups. A longer timeout for empty groups can be enabled by * setting minimumTimeoutForEmptyGroups. */ removeGroup = lastModifiedNow <= (System.currentTimeMillis() - this.minimumTimeoutForEmptyGroups); if (removeGroup && logger.isDebugEnabled()) { logger.debug("Removing empty group: " + correlationKey); } } } else { removeGroup = false; if (logger.isDebugEnabled()) { logger.debug( "Group expiry candidate (" + correlationKey + ") has changed - it may be reconsidered for a future expiration"); } } } catch (MessageDeliveryException e) { removeGroup = false; if (logger.isDebugEnabled()) { logger.debug( "Group expiry candidate (" + correlationKey + ") has been affected by MessageDeliveryException - " + "it may be reconsidered for a future expiration one more time"); } throw e; } finally { try { if (removeGroup) { this.remove(group); } } finally { lock.unlock(); } } } catch (InterruptedException ie) { Thread.currentThread().interrupt(); logger.debug("Thread was interrupted while trying to obtain lock"); } }
@Test @RedisAvailable public void testDelayerHandlerRescheduleWithRedisMessageStore() throws Exception { AbstractApplicationContext context = new ClassPathXmlApplicationContext( "DelayerHandlerRescheduleIntegrationTests-context.xml", this.getClass()); MessageChannel input = context.getBean("input", MessageChannel.class); MessageGroupStore messageStore = context.getBean("messageStore", MessageGroupStore.class); String delayerMessageGroupId = DELAYER_ID + ".messageGroupId"; messageStore.removeMessageGroup(delayerMessageGroupId); Message<String> message1 = MessageBuilder.withPayload("test1").build(); input.send(message1); input.send(MessageBuilder.withPayload("test2").build()); // Emulate restart and check DB state before next start // Interrupt taskScheduler as quickly as possible ThreadPoolTaskScheduler taskScheduler = (ThreadPoolTaskScheduler) IntegrationContextUtils.getTaskScheduler(context); taskScheduler.shutdown(); taskScheduler.getScheduledExecutor().awaitTermination(10, TimeUnit.SECONDS); context.destroy(); try { context.getBean("input", MessageChannel.class); fail("IllegalStateException expected"); } catch (Exception e) { assertTrue(e instanceof IllegalStateException); assertTrue( e.getMessage() .contains("BeanFactory not initialized or already closed - call 'refresh'")); } assertEquals(1, messageStore.getMessageGroupCount()); assertEquals(delayerMessageGroupId, messageStore.iterator().next().getGroupId()); assertEquals(2, messageStore.messageGroupSize(delayerMessageGroupId)); assertEquals(2, messageStore.getMessageCountForAllMessageGroups()); MessageGroup messageGroup = messageStore.getMessageGroup(delayerMessageGroupId); Message<?> messageInStore = messageGroup.getMessages().iterator().next(); Object payload = messageInStore.getPayload(); // INT-3049 assertTrue(payload instanceof DelayHandler.DelayedMessageWrapper); assertEquals(message1, ((DelayHandler.DelayedMessageWrapper) payload).getOriginal()); context.refresh(); PollableChannel output = context.getBean("output", PollableChannel.class); Message<?> message = output.receive(20000); assertNotNull(message); Object payload1 = message.getPayload(); message = output.receive(20000); assertNotNull(message); Object payload2 = message.getPayload(); assertNotSame(payload1, payload2); assertEquals(1, messageStore.getMessageGroupCount()); assertEquals(0, messageStore.messageGroupSize(delayerMessageGroupId)); messageStore.removeMessageGroup(delayerMessageGroupId); }