@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(); } }
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"); } }