@Override
  protected void handleMessageInternal(Message<?> message) throws Exception {
    Object correlationKey = correlationStrategy.getCorrelationKey(message);
    Assert.state(
        correlationKey != null,
        "Null correlation not allowed.  Maybe the CorrelationStrategy is failing?");

    if (logger.isDebugEnabled()) {
      logger.debug("Handling message with correlationKey [" + correlationKey + "]: " + message);
    }

    UUID groupIdUuid = UUIDConverter.getUUID(correlationKey);
    Lock lock = this.lockRegistry.obtain(groupIdUuid.toString());

    lock.lockInterruptibly();
    try {
      ScheduledFuture<?> scheduledFuture = this.expireGroupScheduledFutures.remove(groupIdUuid);
      if (scheduledFuture != null) {
        boolean canceled = scheduledFuture.cancel(true);
        if (canceled && logger.isDebugEnabled()) {
          logger.debug(
              "Cancel 'forceComplete' scheduling for MessageGroup with Correlation Key [ "
                  + correlationKey
                  + "].");
        }
      }
      MessageGroup messageGroup = messageStore.getMessageGroup(correlationKey);
      if (this.sequenceAware) {
        messageGroup = new SequenceAwareMessageGroup(messageGroup);
      }

      if (!messageGroup.isComplete() && messageGroup.canAdd(message)) {
        if (logger.isTraceEnabled()) {
          logger.trace("Adding message to group [ " + messageGroup + "]");
        }
        messageGroup = this.store(correlationKey, message);

        if (releaseStrategy.canRelease(messageGroup)) {
          Collection<Message<?>> completedMessages = null;
          try {
            completedMessages = this.completeGroup(message, correlationKey, messageGroup);
          } finally {
            // Always clean up even if there was an exception
            // processing messages
            this.afterRelease(messageGroup, completedMessages);
          }
        } else {
          scheduleGroupToForceComplete(messageGroup);
        }
      } else {
        discardMessage(message);
      }
    } finally {
      lock.unlock();
    }
  }
 public final void setMessageStore(MessageGroupStore store) {
   this.messageStore = store;
   store.registerMessageGroupExpiryCallback(
       new MessageGroupCallback() {
         @Override
         public void execute(MessageGroupStore messageGroupStore, MessageGroup group) {
           forceReleaseProcessor.processMessageGroup(group);
         }
       });
 }
 protected MessageGroup store(Object correlationKey, Message<?> message) {
   return messageStore.addMessageToGroup(correlationKey, message);
 }
 void remove(MessageGroup group) {
   Object correlationKey = group.getGroupId();
   messageStore.removeMessageGroup(correlationKey);
 }
  @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);
  }