INT-4550: Disallow multi aggregators on same MGS (#2622)

* INT-4550: Disallow multi aggregators on same MGS

JIRA: https://jira.spring.io/browse/INT-4550

**Cherry-pick to 5.0.x**

* * Introduce `UniqueExpiryCallback`
* Use `UniqueExpiryCallback` in the `AbstractCorrelatingMessageHandler`
* Check for uniqueness in the `AbstractMessageGroupStore`
* Remove duplicate code in the `ConfigurableMongoDbMessageStore`

* * Fix tests according a new logic

* * Address PR review

* Change `Assert.isTrue` to the `logger.error` for backward compatibility
* Revert changes in tests since we don't throw exception anymore
* Fix language on doc

* * Fix Checkstyle violation in the `AbstractMessageGroupStore`

* * Ignore `testDontReapMessageOfOtherHandler()`
This commit is contained in:
Artem Bilan
2018-11-15 14:56:14 -05:00
committed by Gary Russell
parent 3d696ef068
commit 5bf6161112
6 changed files with 71 additions and 120 deletions

View File

@@ -22,7 +22,6 @@ import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
@@ -51,6 +50,7 @@ import org.springframework.integration.store.MessageGroupStore;
import org.springframework.integration.store.MessageStore;
import org.springframework.integration.store.SimpleMessageGroup;
import org.springframework.integration.store.SimpleMessageStore;
import org.springframework.integration.store.UniqueExpiryCallback;
import org.springframework.integration.support.AbstractIntegrationMessageBuilder;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.support.locks.DefaultLockRegistry;
@@ -102,8 +102,6 @@ public abstract class AbstractCorrelatingMessageHandler extends AbstractMessageP
private final Map<UUID, ScheduledFuture<?>> expireGroupScheduledFutures = new ConcurrentHashMap<>();
private final Set<Object> groupIds = ConcurrentHashMap.newKeySet();
private MessageGroupProcessor outputProcessor;
private MessageGroupStore messageStore;
@@ -188,8 +186,9 @@ public abstract class AbstractCorrelatingMessageHandler extends AbstractMessageP
public final void setMessageStore(MessageGroupStore store) {
this.messageStore = store;
store.registerMessageGroupExpiryCallback(
(messageGroupStore, group) -> this.forceReleaseProcessor.processMessageGroup(group));
UniqueExpiryCallback expiryCallback =
(messageGroupStore, group) -> this.forceReleaseProcessor.processMessageGroup(group);
store.registerMessageGroupExpiryCallback(expiryCallback);
}
public void setCorrelationStrategy(CorrelationStrategy correlationStrategy) {
@@ -746,7 +745,6 @@ public abstract class AbstractCorrelatingMessageHandler extends AbstractMessageP
protected void remove(MessageGroup group) {
Object correlationKey = group.getGroupId();
this.messageStore.removeMessageGroup(correlationKey);
this.groupIds.remove(group.getGroupId());
}
protected int findLastReleasedSequenceNumber(Object groupId, Collection<Message<?>> partialSequence) {
@@ -755,7 +753,6 @@ public abstract class AbstractCorrelatingMessageHandler extends AbstractMessageP
}
protected MessageGroup store(Object correlationKey, Message<?> message) {
this.groupIds.add(correlationKey);
return this.messageStore.addMessageToGroup(correlationKey, message);
}
@@ -949,9 +946,7 @@ public abstract class AbstractCorrelatingMessageHandler extends AbstractMessageP
@Override
public Object processMessageGroup(MessageGroup group) {
if (AbstractCorrelatingMessageHandler.this.groupIds.contains(group.getGroupId())) {
forceComplete(group);
}
forceComplete(group);
return null;
}

View File

@@ -42,7 +42,7 @@ public abstract class AbstractMessageGroupStore extends AbstractBatchingMessageG
protected final Log logger = LogFactory.getLog(getClass());
private final Collection<MessageGroupCallback> expiryCallbacks = new LinkedHashSet<MessageGroupCallback>();
private final Collection<MessageGroupCallback> expiryCallbacks = new LinkedHashSet<>();
private final MessageGroupFactory persistentMessageGroupFactory =
new SimpleMessageGroupFactory(SimpleMessageGroupFactory.GroupType.PERSISTENT);
@@ -72,13 +72,10 @@ public abstract class AbstractMessageGroupStore extends AbstractBatchingMessageG
/**
* Convenient injection point for expiry callbacks in the message store. Each of the callbacks provided will simply
* be registered with the store using {@link #registerMessageGroupExpiryCallback(MessageGroupCallback)}.
*
* @param expiryCallbacks the expiry callbacks to add
*/
public void setExpiryCallbacks(Collection<MessageGroupCallback> expiryCallbacks) {
for (MessageGroupCallback callback : expiryCallbacks) {
registerMessageGroupExpiryCallback(callback);
}
expiryCallbacks.forEach(this::registerMessageGroupExpiryCallback);
}
public boolean isTimeoutOnIdle() {
@@ -110,6 +107,17 @@ public abstract class AbstractMessageGroupStore extends AbstractBatchingMessageG
@Override
public void registerMessageGroupExpiryCallback(MessageGroupCallback callback) {
if (callback instanceof UniqueExpiryCallback) {
boolean uniqueExpiryCallbackPresent =
this.expiryCallbacks.stream()
.anyMatch(UniqueExpiryCallback.class::isInstance);
if (!uniqueExpiryCallbackPresent && this.logger.isErrorEnabled()) {
this.logger.error("Only one instance of 'UniqueExpiryCallback' can be registered in the " +
"'MessageGroupStore'. Use a separate 'MessageGroupStore' for each aggregator/resequencer.");
}
}
this.expiryCallbacks.add(callback);
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.store;
/**
* A marker interface extension of the {@link MessageGroupStore.MessageGroupCallback}
* for components which should be registered in the {@link MessageGroupStore} only once.
* The {@link MessageGroupStore} implementation ensures that only once instance of this
* class is present in the expire callbacks.
*
* @author Meherzad Lahewala
* @author Artme Bilan
*
* @since 5.0.10
*/
@FunctionalInterface
public interface UniqueExpiryCallback extends MessageGroupStore.MessageGroupCallback {
}

View File

@@ -425,6 +425,7 @@ public class AbstractCorrelatingMessageHandlerTests {
}
@Test
@Ignore("Until 5.2 with new 'owner' feature on groups")
public void testDontReapMessageOfOtherHandler() {
MessageGroupStore groupStore = new SimpleMessageStore();