@Test
  public void testMessageGroupWithAddedMessage() throws Exception {
    GemfireMessageStore store = new GemfireMessageStore(this.cache);
    store.afterPropertiesSet();
    MessageGroup messageGroup = store.getMessageGroup(1);
    Message<?> message = new GenericMessage<String>("Hello");
    messageGroup = store.addMessageToGroup(1, message);
    assertEquals(1, messageGroup.size());

    // make sure the store is properly rebuild from Gemfire
    store = new GemfireMessageStore(this.cache);
    store.afterPropertiesSet();

    messageGroup = store.getMessageGroup(1);
    assertEquals(1, messageGroup.size());
  }
  @Test
  @MongoDbAvailable
  public void testRemoveMessageFromTheGroup() throws Exception {
    MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
    MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);

    MessageGroup messageGroup = store.getMessageGroup(1);
    Message<?> message = new GenericMessage<String>("2");
    store.addMessageToGroup(1, new GenericMessage<String>("1"));
    store.addMessageToGroup(1, message);
    messageGroup = store.addMessageToGroup(1, new GenericMessage<String>("3"));

    assertEquals(3, messageGroup.size());

    messageGroup = store.removeMessageFromGroup(1, message);
    assertEquals(2, messageGroup.size());
  }
 @Test
 public void testNonExistingEmptyMessageGroup() throws Exception {
   GemfireMessageStore store = new GemfireMessageStore(this.cache);
   store.afterPropertiesSet();
   MessageGroup messageGroup = store.getMessageGroup(1);
   assertNotNull(messageGroup);
   assertTrue(messageGroup instanceof SimpleMessageGroup);
   assertEquals(0, messageGroup.size());
 }
  @Test
  @MongoDbAvailable
  public void testRemoveMessageGroup() throws Exception {
    MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
    MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);

    MessageGroup messageGroup = store.getMessageGroup(1);
    Message<?> message = new GenericMessage<String>("Hello");
    UUID id = message.getHeaders().getId();
    messageGroup = store.addMessageToGroup(1, message);
    assertEquals(1, messageGroup.size());
    message = store.getMessage(id);
    assertNotNull(message);

    store.removeMessageGroup(1);
    MessageGroup messageGroupA = store.getMessageGroup(1);
    assertEquals(0, messageGroupA.size());
    assertFalse(messageGroupA.equals(messageGroup));
  }
  @Test
  @MongoDbAvailable
  public void testNonExistingEmptyMessageGroup() throws Exception {
    MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
    MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);

    MessageGroup messageGroup = store.getMessageGroup(1);
    assertNotNull(messageGroup);
    assertTrue(messageGroup instanceof SimpleMessageGroup);
    assertEquals(0, messageGroup.size());
  }
  @Test
  public void testRemoveMessageFromTheGroup() throws Exception {
    GemfireMessageStore store = new GemfireMessageStore(this.cache);
    store.afterPropertiesSet();
    MessageGroup messageGroup = store.getMessageGroup(1);
    Message<?> message = new GenericMessage<String>("2");

    messageGroup =
        store.addMessageToGroup(messageGroup.getGroupId(), new GenericMessage<String>("1"));
    messageGroup = store.getMessageGroup(1);
    assertEquals(1, messageGroup.size());
    Thread.sleep(
        1); // since it adds to a local region some times CREATED_DATE ends up to be the same
    // Unrealistic in a real scenario

    messageGroup = store.addMessageToGroup(messageGroup.getGroupId(), message);
    messageGroup = store.getMessageGroup(1);
    assertEquals(2, messageGroup.size());
    Thread.sleep(1);

    messageGroup =
        store.addMessageToGroup(messageGroup.getGroupId(), new GenericMessage<String>("3"));
    messageGroup = store.getMessageGroup(1);
    assertEquals(3, messageGroup.size());

    messageGroup = store.removeMessageFromGroup(messageGroup.getGroupId(), message);
    messageGroup = store.getMessageGroup(1);
    assertEquals(2, messageGroup.size());

    // make sure the store is properly rebuild from Gemfire
    store = new GemfireMessageStore(this.cache);
    store.afterPropertiesSet();

    messageGroup = store.getMessageGroup(1);
    assertEquals(2, messageGroup.size());
  }
  @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());
  }
  @Test
  @MongoDbAvailable
  public void testMessageGroupWithAddedMessage() throws Exception {
    MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
    MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);

    MessageGroup messageGroup = store.getMessageGroup(1);
    Message<?> messageA = new GenericMessage<String>("A");
    Message<?> messageB = new GenericMessage<String>("B");
    store.addMessageToGroup(1, messageA);
    messageGroup = store.addMessageToGroup(1, messageB);
    assertEquals(2, messageGroup.size());
    Message<?> retrievedMessage = store.getMessage(messageA.getHeaders().getId());
    assertNotNull(retrievedMessage);
    assertEquals(retrievedMessage.getHeaders().getId(), messageA.getHeaders().getId());
    // ensure that 'message_group' header that is only used internally is not propagated
    assertNull(retrievedMessage.getHeaders().get("message_group"));
  }
 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));
   }
 }
  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");
    }
  }