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);
  }