INT-3387: MessageGroupStore Improvements
JIRA: https://jira.spring.io/browse/INT-3387, https://jira.spring.io/browse/INT-3806 * Introduce ``` MessageGroupStore void addMessagesToGroup(Object groupId, Message<?>... messages); ``` And implement it in all stores. * Use new `addMessagesToGroup` where it is reasonable, e.g. `DelayHandler` * Optimize test-case to use a new store method (where it is possible) * Fix timing delays in the `JdbcMessageStoreTests` * Introduce `PersistentMessageGroup` * Add `AbstractMessageGroupStore#proxyMessageGroupForLazyLoad` to wrap the raw `MessageGroup` to the `PersistentMessageGroup` for lazy-load * Rework `MessageGroupMetadata` do not be `immutable` and allow to store/restore in the `AbstractKeyValueMessageStore` only the `MessageGroupMetadata` * Refactor `ResequencingMessageHandler` and `SequenceSizeReleaseStrategy` a bit for better performance when interact with the `MessageGroup` * Add `AbstractMessageGroupStore#setLazyLoadMessageGroups` to switch off the `lazy-load` behavior and restore the previous full `MessageGroup` logic * Add `What's New` note and `message-store.adoc` paragraph for the lazy-load functionality `GroupType.PERSISTENT` and not lazy by default PR Comments Fix `JdbcMessageStoreTests` timing issues Address PR comments * Add performance test to the `ConfigurableMongoDbMessageGroupStoreTests` * Add JavaDocs for the `MessageGroupFactory` methods * Add `log4j.properties` into the `test` MongoDB module for better traceability * Fix `JdbcMessageStore#getOneMessageFromGroup()` over the `doPollForMessage()` delegation. The `jdbcTemplate.queryForObject()` requires exactly one and only one raw in `resultSet` * Add performance test results into the `message-store.adoc`
This commit is contained in:
committed by
Gary Russell
parent
4e4763d24f
commit
286c421c1a
@@ -95,7 +95,7 @@ public class CorrelatingMessageBarrier extends AbstractMessageHandler implements
|
||||
Object correlationKey = this.correlationStrategy.getCorrelationKey(message);
|
||||
Object lock = getLock(correlationKey);
|
||||
synchronized (lock) {
|
||||
this.store.addMessageToGroup(correlationKey, message);
|
||||
this.store.addMessagesToGroup(correlationKey, message);
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Handled message for key [%s]: %s.", correlationKey, message));
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.integration.aggregator;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.integration.IntegrationMessageHeaderAccessor;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
import org.springframework.integration.store.MessageGroupStore;
|
||||
import org.springframework.integration.store.SimpleMessageStore;
|
||||
@@ -74,12 +73,9 @@ public class ResequencingMessageHandler extends AbstractCorrelatingMessageHandle
|
||||
|
||||
@Override
|
||||
protected void afterRelease(MessageGroup messageGroup, Collection<Message<?>> completedMessages, boolean timeout) {
|
||||
int size = messageGroup.getMessages().size();
|
||||
int sequenceSize = 0;
|
||||
Message<?> message = messageGroup.getOne();
|
||||
if (message != null) {
|
||||
sequenceSize = new IntegrationMessageHeaderAccessor(message).getSequenceSize();
|
||||
}
|
||||
int size = messageGroup.size();
|
||||
int sequenceSize = messageGroup.getSequenceSize();
|
||||
|
||||
// If there is no sequence then it must be incomplete or unbounded
|
||||
if (sequenceSize > 0 && sequenceSize == size) {
|
||||
remove(messageGroup);
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.messaging.Message;
|
||||
* @author Dave Syer
|
||||
* @author Iwein Fuld
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
* @author Enrique Rodríguez
|
||||
*/
|
||||
public class SequenceSizeReleaseStrategy implements ReleaseStrategy {
|
||||
@@ -55,9 +56,9 @@ public class SequenceSizeReleaseStrategy implements ReleaseStrategy {
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag that determines if partial sequences are allowed. If true then as soon as enough messages arrive that can be
|
||||
* ordered they will be released, provided they all have sequence numbers greater than those already released.
|
||||
*
|
||||
* Flag that determines if partial sequences are allowed. If true then as soon as
|
||||
* enough messages arrive that can be ordered they will be released, provided they
|
||||
* all have sequence numbers greater than those already released.
|
||||
* @param releasePartialSequences true when partial sequences should be released.
|
||||
*/
|
||||
public void setReleasePartialSequences(boolean releasePartialSequences) {
|
||||
@@ -69,13 +70,12 @@ public class SequenceSizeReleaseStrategy implements ReleaseStrategy {
|
||||
|
||||
boolean canRelease = false;
|
||||
|
||||
Collection<Message<?>> messages = messageGroup.getMessages();
|
||||
|
||||
if (this.releasePartialSequences && !messages.isEmpty()) {
|
||||
|
||||
int size = messageGroup.size();
|
||||
if (this.releasePartialSequences && size > 0) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Considering partial release of group [" + messageGroup + "]");
|
||||
}
|
||||
Collection<Message<?>> messages = messageGroup.getMessages();
|
||||
Message<?> minMessage = Collections.min(messages, this.comparator);
|
||||
|
||||
int nextSequenceNumber = new IntegrationMessageHeaderAccessor(minMessage).getSequenceNumber();
|
||||
@@ -86,13 +86,11 @@ public class SequenceSizeReleaseStrategy implements ReleaseStrategy {
|
||||
}
|
||||
}
|
||||
else {
|
||||
int size = messages.size();
|
||||
|
||||
if (size == 0) {
|
||||
canRelease = true;
|
||||
}
|
||||
else {
|
||||
int sequenceSize = new IntegrationMessageHeaderAccessor(messageGroup.getOne()).getSequenceSize();
|
||||
int sequenceSize = messageGroup.getSequenceSize();
|
||||
// If there is no sequence then it must be incomplete....
|
||||
if (sequenceSize == size) {
|
||||
canRelease = true;
|
||||
|
||||
@@ -94,38 +94,62 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
|
||||
*/
|
||||
@Override
|
||||
public MessageGroup getMessageGroup(Object groupId) {
|
||||
return buildMessageGroup(groupId, false);
|
||||
MessageGroupMetadata metadata = getGroupMetadata(groupId);
|
||||
if (metadata != null) {
|
||||
|
||||
MessageGroup messageGroup = getMessageGroupFactory()
|
||||
.create(this, groupId, metadata.getTimestamp(), metadata.isComplete());
|
||||
messageGroup.setLastModified(metadata.getLastModified());
|
||||
messageGroup.setLastReleasedMessageSequenceNumber(metadata.getLastReleasedMessageSequenceNumber());
|
||||
return messageGroup;
|
||||
}
|
||||
else {
|
||||
return new SimpleMessageGroup(groupId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a Message to the group with the provided group ID.
|
||||
*/
|
||||
@Override
|
||||
public MessageGroup addMessageToGroup(Object groupId, Message<?> message) {
|
||||
public MessageGroupMetadata getGroupMetadata(Object groupId) {
|
||||
Assert.notNull(groupId, "'groupId' must not be null");
|
||||
Assert.notNull(message, "'message' must not be null");
|
||||
Object mgm = this.doRetrieve(MESSAGE_GROUP_KEY_PREFIX + groupId);
|
||||
if (mgm != null) {
|
||||
Assert.isInstanceOf(MessageGroupMetadata.class, mgm);
|
||||
return (MessageGroupMetadata) mgm;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// add message as is to the MG accessible by the caller
|
||||
MessageGroup messageGroup = getMessageGroup(groupId);
|
||||
@Override
|
||||
public void addMessagesToGroup(Object groupId, Message<?>... messages) {
|
||||
Assert.notNull(groupId, "'groupId' must not be null");
|
||||
Assert.notNull(messages, "'messages' must not be null");
|
||||
|
||||
messageGroup.add(message);
|
||||
MessageGroupMetadata metadata = getGroupMetadata(groupId);
|
||||
SimpleMessageGroup group = null;
|
||||
if (metadata == null) {
|
||||
group = new SimpleMessageGroup(groupId);
|
||||
}
|
||||
|
||||
// enrich Message with additional headers and add it to MS
|
||||
Message<?> enrichedMessage = enrichMessage(message);
|
||||
for (Message<?> message : messages) {
|
||||
// enrich Message with additional headers and add it to MS
|
||||
Message<?> enrichedMessage = enrichMessage(message);
|
||||
addMessage(enrichedMessage);
|
||||
if (metadata != null) {
|
||||
metadata.add(enrichedMessage.getHeaders().getId());
|
||||
}
|
||||
else {
|
||||
group.add(enrichedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
addMessage(enrichedMessage);
|
||||
if (group != null) {
|
||||
metadata = new MessageGroupMetadata(group);
|
||||
}
|
||||
|
||||
// build raw MessageGroup and add enriched Message to it
|
||||
MessageGroup rawGroup = buildMessageGroup(groupId, true);
|
||||
rawGroup.setLastModified(System.currentTimeMillis());
|
||||
rawGroup.add(enrichedMessage);
|
||||
metadata.setLastModified(System.currentTimeMillis());
|
||||
|
||||
// store MessageGroupMetadata built from enriched MG
|
||||
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, new MessageGroupMetadata(rawGroup));
|
||||
|
||||
// return clean MG
|
||||
return getMessageGroup(groupId);
|
||||
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,32 +161,17 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
|
||||
Assert.notNull(groupId, "'groupId' must not be null");
|
||||
Assert.notNull(messageToRemove, "'messageToRemove' must not be null");
|
||||
|
||||
// build raw MG
|
||||
MessageGroup rawGroup = buildMessageGroup(groupId, true);
|
||||
UUID id = messageToRemove.getHeaders().getId();
|
||||
removeMessage(id);
|
||||
|
||||
// create a clean instance of
|
||||
MessageGroup messageGroup = normalizeSimpleMessageGroup(rawGroup);
|
||||
|
||||
|
||||
Message<?> actualMessageToRemove = null;
|
||||
|
||||
for (Message<?> message : rawGroup.getMessages()) {
|
||||
if (message.getHeaders().getId().equals(messageToRemove.getHeaders().getId())) {
|
||||
actualMessageToRemove = message;
|
||||
break;
|
||||
}
|
||||
MessageGroupMetadata metadata = getGroupMetadata(groupId);
|
||||
if (metadata != null) {
|
||||
metadata.remove(id);
|
||||
metadata.setLastModified(System.currentTimeMillis());
|
||||
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, metadata);
|
||||
}
|
||||
|
||||
if (actualMessageToRemove != null) {
|
||||
rawGroup.remove(actualMessageToRemove);
|
||||
removeMessage(messageToRemove.getHeaders().getId());
|
||||
rawGroup.setLastModified(System.currentTimeMillis());
|
||||
|
||||
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, new MessageGroupMetadata(rawGroup));
|
||||
messageGroup = getMessageGroup(groupId);
|
||||
}
|
||||
|
||||
return messageGroup;
|
||||
return getMessageGroup(groupId);
|
||||
}
|
||||
|
||||
|
||||
@@ -188,10 +197,12 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
|
||||
@Override
|
||||
public void completeGroup(Object groupId) {
|
||||
Assert.notNull(groupId, "'groupId' must not be null");
|
||||
MessageGroup messageGroup = buildMessageGroup(groupId, true);
|
||||
messageGroup.complete();
|
||||
messageGroup.setLastModified(System.currentTimeMillis());
|
||||
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, new MessageGroupMetadata(messageGroup));
|
||||
MessageGroupMetadata metadata = getGroupMetadata(groupId);
|
||||
if (metadata != null) {
|
||||
metadata.complete();
|
||||
metadata.setLastModified(System.currentTimeMillis());
|
||||
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, metadata);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,37 +226,62 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
|
||||
@Override
|
||||
public void setLastReleasedSequenceNumberForGroup(Object groupId, int sequenceNumber) {
|
||||
Assert.notNull(groupId, "'groupId' must not be null");
|
||||
MessageGroup messageGroup = buildMessageGroup(groupId, true);
|
||||
messageGroup.setLastReleasedMessageSequenceNumber(sequenceNumber);
|
||||
messageGroup.setLastModified(System.currentTimeMillis());
|
||||
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, new MessageGroupMetadata(messageGroup));
|
||||
MessageGroupMetadata metadata = getGroupMetadata(groupId);
|
||||
if (metadata == null) {
|
||||
SimpleMessageGroup messageGroup = new SimpleMessageGroup(groupId);
|
||||
metadata = new MessageGroupMetadata(messageGroup);
|
||||
}
|
||||
metadata.setLastReleasedMessageSequenceNumber(sequenceNumber);
|
||||
metadata.setLastModified(System.currentTimeMillis());
|
||||
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> pollMessageFromGroup(Object groupId) {
|
||||
Assert.notNull(groupId, "'groupId' must not be null");
|
||||
Object mgm = doRetrieve(MESSAGE_GROUP_KEY_PREFIX + groupId);
|
||||
if (mgm != null) {
|
||||
Assert.isInstanceOf(MessageGroupMetadata.class, mgm);
|
||||
MessageGroupMetadata messageGroupMetadata = (MessageGroupMetadata) mgm;
|
||||
|
||||
UUID firstId = messageGroupMetadata.firstId();
|
||||
MessageGroupMetadata groupMetadata = getGroupMetadata(groupId);
|
||||
if (groupMetadata != null) {
|
||||
UUID firstId = groupMetadata.firstId();
|
||||
if (firstId != null) {
|
||||
messageGroupMetadata.remove(firstId);
|
||||
messageGroupMetadata.setLastModified(System.currentTimeMillis());
|
||||
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, messageGroupMetadata);
|
||||
groupMetadata.remove(firstId);
|
||||
groupMetadata.setLastModified(System.currentTimeMillis());
|
||||
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, groupMetadata);
|
||||
return removeMessage(firstId);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> getOneMessageFromGroup(Object groupId) {
|
||||
MessageGroupMetadata groupMetadata = getGroupMetadata(groupId);
|
||||
if (groupMetadata != null) {
|
||||
UUID messageId = groupMetadata.firstId();
|
||||
if (messageId != null) {
|
||||
return getMessage(messageId);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Message<?>> getMessagesForGroup(Object groupId) {
|
||||
MessageGroupMetadata groupMetadata = getGroupMetadata(groupId);
|
||||
ArrayList<Message<?>> messages = new ArrayList<Message<?>>();
|
||||
if (groupMetadata != null) {
|
||||
Iterator<UUID> messageIds = groupMetadata.messageIdIterator();
|
||||
while (messageIds.hasNext()) {
|
||||
messages.add(getMessage(messageIds.next()));
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Iterator<MessageGroup> iterator() {
|
||||
final Iterator<?> idIterator = normalizeKeys(
|
||||
(Collection<String>) doListKeys(MESSAGE_GROUP_KEY_PREFIX + "*"))
|
||||
.iterator();
|
||||
.iterator();
|
||||
return new MessageGroupIterator(idIterator);
|
||||
}
|
||||
|
||||
@@ -266,13 +302,13 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
|
||||
|
||||
@Override
|
||||
public int messageGroupSize(Object groupId) {
|
||||
Object mgm = doRetrieve(MESSAGE_GROUP_KEY_PREFIX + groupId);
|
||||
MessageGroupMetadata mgm = getGroupMetadata(groupId);
|
||||
if (mgm != null) {
|
||||
Assert.isInstanceOf(MessageGroupMetadata.class, mgm);
|
||||
MessageGroupMetadata messageGroupMetadata = (MessageGroupMetadata) mgm;
|
||||
return messageGroupMetadata.size();
|
||||
return mgm.size();
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected abstract Object doRetrieve(Object id);
|
||||
@@ -308,48 +344,6 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
|
||||
return enrichedMessage;
|
||||
}
|
||||
|
||||
private MessageGroup buildMessageGroup(Object groupId, boolean raw) {
|
||||
Assert.notNull(groupId, "'groupId' must not be null");
|
||||
Object mgm = doRetrieve(MESSAGE_GROUP_KEY_PREFIX + groupId);
|
||||
if (mgm != null) {
|
||||
Assert.isInstanceOf(MessageGroupMetadata.class, mgm);
|
||||
MessageGroupMetadata messageGroupMetadata = (MessageGroupMetadata) mgm;
|
||||
ArrayList<Message<?>> messages = new ArrayList<Message<?>>();
|
||||
|
||||
Iterator<UUID> messageIds = messageGroupMetadata.messageIdIterator();
|
||||
while (messageIds.hasNext()) {
|
||||
UUID next = messageIds.next();
|
||||
if (next != null) {
|
||||
if (raw) {
|
||||
messages.add(getRawMessage(next));
|
||||
}
|
||||
else {
|
||||
messages.add(getMessage(next));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MessageGroup messageGroup = getMessageGroupFactory()
|
||||
.create(messages, groupId, messageGroupMetadata.getTimestamp(), messageGroupMetadata.isComplete());
|
||||
messageGroup.setLastModified(messageGroupMetadata.getLastModified());
|
||||
messageGroup.setLastReleasedMessageSequenceNumber(
|
||||
messageGroupMetadata.getLastReleasedMessageSequenceNumber());
|
||||
return messageGroup;
|
||||
}
|
||||
else {
|
||||
return getMessageGroupFactory().create(groupId);
|
||||
}
|
||||
}
|
||||
|
||||
private MessageGroup normalizeSimpleMessageGroup(MessageGroup messageGroup) {
|
||||
MessageGroup normalizedGroup = getMessageGroupFactory().create(messageGroup.getGroupId());
|
||||
for (Message<?> message : messageGroup.getMessages()) {
|
||||
Message<?> normalizedMessage = normalizeMessage(message);
|
||||
normalizedGroup.add(normalizedMessage);
|
||||
}
|
||||
return normalizedGroup;
|
||||
}
|
||||
|
||||
private Message<?> getRawMessage(UUID id) {
|
||||
Assert.notNull(id, "'id' must not be null");
|
||||
Object message = doRetrieve(MESSAGE_KEY_PREFIX + id);
|
||||
@@ -380,4 +374,5 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -49,6 +49,9 @@ public abstract class AbstractMessageGroupStore extends AbstractBatchingMessageG
|
||||
|
||||
private final Collection<MessageGroupCallback> expiryCallbacks = new LinkedHashSet<MessageGroupCallback>();
|
||||
|
||||
private final MessageGroupFactory persistentMessageGroupFactory =
|
||||
new SimpleMessageGroupFactory(SimpleMessageGroupFactory.GroupType.PERSISTENT);
|
||||
|
||||
private volatile boolean timeoutOnIdle;
|
||||
|
||||
private volatile BeanFactory beanFactory;
|
||||
@@ -57,6 +60,8 @@ public abstract class AbstractMessageGroupStore extends AbstractBatchingMessageG
|
||||
|
||||
private volatile boolean messageBuilderFactorySet;
|
||||
|
||||
private boolean lazyLoadMessageGroups = true;
|
||||
|
||||
public AbstractMessageGroupStore() {
|
||||
super();
|
||||
}
|
||||
@@ -77,6 +82,16 @@ public abstract class AbstractMessageGroupStore extends AbstractBatchingMessageG
|
||||
return this.messageBuilderFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageGroupFactory getMessageGroupFactory() {
|
||||
if (this.lazyLoadMessageGroups) {
|
||||
return this.persistentMessageGroupFactory;
|
||||
}
|
||||
else {
|
||||
return super.getMessageGroupFactory();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)}.
|
||||
@@ -98,13 +113,24 @@ public abstract class AbstractMessageGroupStore extends AbstractBatchingMessageG
|
||||
* the {@link MessageGroup} was created. If you want the timeout to be based on the time
|
||||
* the {@link MessageGroup} was idling (e.g., inactive from the last update) invoke this method with 'true'.
|
||||
* Default is 'false'.
|
||||
*
|
||||
* @param timeoutOnIdle The boolean.
|
||||
*/
|
||||
public void setTimeoutOnIdle(boolean timeoutOnIdle) {
|
||||
this.timeoutOnIdle = timeoutOnIdle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify if the result of the {@link #getMessageGroup(Object)} should be wrapped
|
||||
* to the {@link PersistentMessageGroup} - a lazy-load proxy for messages in group
|
||||
* Defaults to {@code true}.
|
||||
* <p> The target logic is based on the {@link SimpleMessageGroupFactory.GroupType#PERSISTENT}.
|
||||
* @param lazyLoadMessageGroups the {@code boolean} flag to use.
|
||||
* @since 4.3
|
||||
*/
|
||||
public void setLazyLoadMessageGroups(boolean lazyLoadMessageGroups) {
|
||||
this.lazyLoadMessageGroups = lazyLoadMessageGroups;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerMessageGroupExpiryCallback(MessageGroupCallback callback) {
|
||||
this.expiryCallbacks.add(callback);
|
||||
@@ -119,7 +145,7 @@ public abstract class AbstractMessageGroupStore extends AbstractBatchingMessageG
|
||||
|
||||
long timestamp = group.getTimestamp();
|
||||
if (this.isTimeoutOnIdle() && group.getLastModified() > 0) {
|
||||
timestamp = group.getLastModified();
|
||||
timestamp = group.getLastModified();
|
||||
}
|
||||
|
||||
if (timestamp <= threshold) {
|
||||
@@ -175,8 +201,9 @@ public abstract class AbstractMessageGroupStore extends AbstractBatchingMessageG
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> getOneMessageFromGroup(Object groupId) {
|
||||
throw new UnsupportedOperationException("Not yet implemented for this store");
|
||||
public MessageGroup addMessageToGroup(Object groupId, Message<?> message) {
|
||||
addMessagesToGroup(groupId, message);
|
||||
return getMessageGroup(groupId);
|
||||
}
|
||||
|
||||
private void expire(MessageGroup group) {
|
||||
|
||||
@@ -29,11 +29,57 @@ import org.springframework.messaging.Message;
|
||||
*/
|
||||
public interface MessageGroupFactory {
|
||||
|
||||
/**
|
||||
* Create a {@link MessageGroup} instance based on the provided {@code groupId}.
|
||||
* @param groupId the group id to use.
|
||||
* @return the {@link MessageGroup} instance.
|
||||
*/
|
||||
MessageGroup create(Object groupId);
|
||||
|
||||
/**
|
||||
* Create a {@link MessageGroup} instance based on the provided {@code groupId}
|
||||
* and with the {@code messages} for the group.
|
||||
* @param messages the messages for the group.
|
||||
* @param groupId the group id to use.
|
||||
* @return the {@link MessageGroup} instance.
|
||||
*/
|
||||
MessageGroup create(Collection<? extends Message<?>> messages, Object groupId);
|
||||
|
||||
/**
|
||||
* Create a {@link MessageGroup} instance based on the provided {@code groupId}
|
||||
* and with the {@code messages} for the group.
|
||||
* In addition the creating {@code timestamp} and {@code complete} flag may be used to customize
|
||||
* the target {@link MessageGroup} object.
|
||||
* @param messages the messages for the group.
|
||||
* @param groupId the group id to use.
|
||||
* @param timestamp the creation time.
|
||||
* @param complete the {@code boolean} flag to indicate that group is completed.
|
||||
* @return the {@link MessageGroup} instance.
|
||||
*/
|
||||
MessageGroup create(Collection<? extends Message<?>> messages, Object groupId, long timestamp,
|
||||
boolean complete);
|
||||
|
||||
/**
|
||||
* Create a {@link MessageGroup} instance based on the provided {@code groupId}.
|
||||
* The {@link MessageGroupStore} may be consulted for the messages and metadata for the {@link MessageGroup}.
|
||||
* @param messageGroupStore the {@link MessageGroupStore} for additional {@link MessageGroup} information.
|
||||
* @param groupId the group id to use.
|
||||
* @return the {@link MessageGroup} instance.
|
||||
*/
|
||||
MessageGroup create(MessageGroupStore messageGroupStore, Object groupId);
|
||||
|
||||
/**
|
||||
* Create a {@link MessageGroup} instance based on the provided {@code groupId}.
|
||||
* The {@link MessageGroupStore} may be consulted for the messages and metadata for the {@link MessageGroup}.
|
||||
* In addition the creating {@code timestamp} and {@code complete} flag may be used to customize
|
||||
* the target {@link MessageGroup} object.
|
||||
* @param messageGroupStore the {@link MessageGroupStore} for additional {@link MessageGroup} information.
|
||||
* @param groupId the group id to use.
|
||||
* @param timestamp the creation time.
|
||||
* @param complete the {@code boolean} flag to indicate that group is completed.
|
||||
* @return the {@link MessageGroup} instance.
|
||||
*/
|
||||
MessageGroup create(MessageGroupStore messageGroupStore, Object groupId, long timestamp,
|
||||
boolean complete);
|
||||
|
||||
}
|
||||
|
||||
@@ -26,10 +26,11 @@ import org.springframework.messaging.Message;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Immutable Value Object holding metadata about a MessageGroup.
|
||||
* Value Object holding metadata about a MessageGroup in the MessageGroupStore.
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
* @since 2.1
|
||||
*/
|
||||
public class MessageGroupMetadata implements Serializable {
|
||||
@@ -40,47 +41,35 @@ public class MessageGroupMetadata implements Serializable {
|
||||
|
||||
private final List<UUID> messageIds = new LinkedList<UUID>();
|
||||
|
||||
private final boolean complete;
|
||||
|
||||
private final long timestamp;
|
||||
|
||||
private volatile boolean complete;
|
||||
|
||||
private volatile long lastModified;
|
||||
|
||||
private final int lastReleasedMessageSequenceNumber;
|
||||
|
||||
private final boolean hasMessages;
|
||||
|
||||
private final UUID first;
|
||||
private volatile int lastReleasedMessageSequenceNumber;
|
||||
|
||||
public MessageGroupMetadata(MessageGroup messageGroup) {
|
||||
this(messageGroup, true, null);
|
||||
}
|
||||
|
||||
public MessageGroupMetadata(MessageGroup messageGroup, boolean hasMessages, UUID first) {
|
||||
|
||||
Assert.notNull(messageGroup, "'messageGroup' must not be null");
|
||||
this.groupId = messageGroup.getGroupId();
|
||||
if (hasMessages) {
|
||||
for (Message<?> message : messageGroup.getMessages()) {
|
||||
this.messageIds.add(message.getHeaders().getId());
|
||||
}
|
||||
for (Message<?> message : messageGroup.getMessages()) {
|
||||
this.messageIds.add(message.getHeaders().getId());
|
||||
}
|
||||
this.complete = messageGroup.isComplete();
|
||||
this.timestamp = messageGroup.getTimestamp();
|
||||
this.lastReleasedMessageSequenceNumber = messageGroup.getLastReleasedMessageSequenceNumber();
|
||||
this.lastModified = messageGroup.getLastModified();
|
||||
this.hasMessages = hasMessages;
|
||||
this.first = first;
|
||||
}
|
||||
|
||||
public void remove(UUID messageId) {
|
||||
if (!this.hasMessages) {
|
||||
throw new IllegalStateException("Messages are not available, fetch the entire group");
|
||||
}
|
||||
this.messageIds.remove(messageId);
|
||||
}
|
||||
|
||||
public void setLastModified(long lastModified) {
|
||||
boolean add(UUID messageId) {
|
||||
return !this.messageIds.contains(messageId) && this.messageIds.add(messageId);
|
||||
}
|
||||
|
||||
void setLastModified(long lastModified) {
|
||||
this.lastModified = lastModified;
|
||||
}
|
||||
|
||||
@@ -89,9 +78,6 @@ public class MessageGroupMetadata implements Serializable {
|
||||
}
|
||||
|
||||
public Iterator<UUID> messageIdIterator() {
|
||||
if (!this.hasMessages) {
|
||||
throw new IllegalStateException("Messages are not available, fetch the entire group");
|
||||
}
|
||||
return this.messageIds.iterator();
|
||||
}
|
||||
|
||||
@@ -100,18 +86,16 @@ public class MessageGroupMetadata implements Serializable {
|
||||
}
|
||||
|
||||
public UUID firstId() {
|
||||
if (this.first != null) {
|
||||
return this.first;
|
||||
}
|
||||
if (!this.hasMessages) {
|
||||
throw new IllegalStateException("Messages are not available, fetch the entire group");
|
||||
}
|
||||
if (this.messageIds.size() > 0) {
|
||||
return this.messageIds.iterator().next();
|
||||
return this.messageIds.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void complete() {
|
||||
this.complete = true;
|
||||
}
|
||||
|
||||
public boolean isComplete() {
|
||||
return this.complete;
|
||||
}
|
||||
@@ -127,4 +111,9 @@ public class MessageGroupMetadata implements Serializable {
|
||||
public int getLastReleasedMessageSequenceNumber() {
|
||||
return this.lastReleasedMessageSequenceNumber;
|
||||
}
|
||||
|
||||
void setLastReleasedMessageSequenceNumber(int lastReleasedMessageSequenceNumber) {
|
||||
this.lastReleasedMessageSequenceNumber = lastReleasedMessageSequenceNumber;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.messaging.Message;
|
||||
* @author Dave Syer
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 2.0
|
||||
*
|
||||
@@ -36,9 +37,8 @@ import org.springframework.messaging.Message;
|
||||
public interface MessageGroupStore extends BasicMessageGroupStore {
|
||||
|
||||
/**
|
||||
* Optional attribute giving the number of messages in the store over all groups. Implementations may decline to
|
||||
* respond by throwing an exception.
|
||||
*
|
||||
* Optional attribute giving the number of messages in the store over all groups.
|
||||
* Implementations may decline to respond by throwing an exception.
|
||||
* @return the number of messages
|
||||
* @throws UnsupportedOperationException if not implemented
|
||||
*/
|
||||
@@ -46,9 +46,8 @@ public interface MessageGroupStore extends BasicMessageGroupStore {
|
||||
int getMessageCountForAllMessageGroups();
|
||||
|
||||
/**
|
||||
* Optional attribute giving the number of message groups. Implementations may decline
|
||||
* to respond by throwing an exception.
|
||||
*
|
||||
* Optional attribute giving the number of message groups.
|
||||
* Implementations may decline to respond by throwing an exception.
|
||||
* @return the number message groups
|
||||
* @throws UnsupportedOperationException if not implemented
|
||||
*/
|
||||
@@ -56,9 +55,8 @@ public interface MessageGroupStore extends BasicMessageGroupStore {
|
||||
int getMessageGroupCount();
|
||||
|
||||
/**
|
||||
* Persist the deletion of a single message from the group. The group is modified to reflect that 'messageToRemove' is
|
||||
* no longer present in the group.
|
||||
*
|
||||
* Persist the deletion of a single message from the group.
|
||||
* The group is modified to reflect that 'messageToRemove' is no longer present in the group.
|
||||
* @param key The groupId for the group containing the message.
|
||||
* @param messageToRemove The message to be removed.
|
||||
* @return The message Group.
|
||||
@@ -69,27 +67,22 @@ public interface MessageGroupStore extends BasicMessageGroupStore {
|
||||
|
||||
/**
|
||||
* Persist the deletion of messages from the group.
|
||||
*
|
||||
* @param key The groupId for the group containing the message(s).
|
||||
* @param messages The messages to be removed.
|
||||
*
|
||||
* @since 4.2
|
||||
*/
|
||||
void removeMessagesFromGroup(Object key, Collection<Message<?>> messages);
|
||||
|
||||
/**
|
||||
* Persist the deletion of messages from the group.
|
||||
*
|
||||
* @param key The groupId for the group containing the message(s).
|
||||
* @param messages The messages to be removed.
|
||||
*
|
||||
* @since 4.2
|
||||
*/
|
||||
void removeMessagesFromGroup(Object key, Message<?>... messages);
|
||||
|
||||
/**
|
||||
* Register a callback for when a message group is expired through {@link #expireMessageGroups(long)}.
|
||||
*
|
||||
* @param callback A callback to execute when a message group is cleaned up.
|
||||
*/
|
||||
void registerMessageGroupExpiryCallback(MessageGroupCallback callback);
|
||||
@@ -99,10 +92,8 @@ public interface MessageGroupStore extends BasicMessageGroupStore {
|
||||
* each of the registered callbacks on them in turn. For example: call with a timeout of 100 to expire all groups
|
||||
* that were created more than 100 milliseconds ago, and are not yet complete. Use a timeout of 0 (or negative to be
|
||||
* on the safe side) to expire all message groups.
|
||||
*
|
||||
* @param timeout the timeout threshold to use
|
||||
* @return the number of message groups expired
|
||||
*
|
||||
* @see #registerMessageGroupExpiryCallback(MessageGroupCallback)
|
||||
*/
|
||||
@ManagedOperation
|
||||
@@ -110,7 +101,6 @@ public interface MessageGroupStore extends BasicMessageGroupStore {
|
||||
|
||||
/**
|
||||
* Allows you to set the sequence number of the last released Message. Used for Resequencing use cases
|
||||
*
|
||||
* @param groupId The group identifier.
|
||||
* @param sequenceNumber The sequence number.
|
||||
*/
|
||||
@@ -125,7 +115,6 @@ public interface MessageGroupStore extends BasicMessageGroupStore {
|
||||
* Completes this MessageGroup. Completion of the MessageGroup generally means
|
||||
* that this group should not be allowing any more mutating operation to be performed on it.
|
||||
* For example any attempt to add/remove new Message form the group should not be allowed.
|
||||
*
|
||||
* @param groupId The group identifier.
|
||||
*/
|
||||
void completeGroup(Object groupId);
|
||||
@@ -140,13 +129,30 @@ public interface MessageGroupStore extends BasicMessageGroupStore {
|
||||
MessageGroupMetadata getGroupMetadata(Object groupId);
|
||||
|
||||
/**
|
||||
* Return the one {@link org.springframework.messaging.Message} from {@link org.springframework.integration.store.MessageGroup}.
|
||||
* Return the one {@link Message} from {@link MessageGroup}.
|
||||
* @param groupId The group identifier.
|
||||
* @return the {@link org.springframework.messaging.Message}.
|
||||
* @return the {@link Message}.
|
||||
* @since 4.0
|
||||
*/
|
||||
Message<?> getOneMessageFromGroup(Object groupId);
|
||||
|
||||
/**
|
||||
* Store messages with an association to a group id.
|
||||
* This can be used to group messages together.
|
||||
* @param groupId The group id to store messages under.
|
||||
* @param messages The messages to add.
|
||||
* @since 4.3
|
||||
*/
|
||||
void addMessagesToGroup(Object groupId, Message<?>... messages);
|
||||
|
||||
/**
|
||||
* Retrieve messages for the provided group id.
|
||||
* @param groupId The group id to retrieve messages for.
|
||||
* @return the messages for group.
|
||||
* @since 4.3
|
||||
*/
|
||||
Collection<Message<?>> getMessagesForGroup(Object groupId);
|
||||
|
||||
/**
|
||||
* Invoked when a MessageGroupStore expires a group.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright 2016 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;
|
||||
|
||||
import java.util.AbstractCollection;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.integration.IntegrationMessageHeaderAccessor;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
* @since 4.3
|
||||
*/
|
||||
class PersistentMessageGroup implements MessageGroup {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(PersistentMessageGroup.class);
|
||||
|
||||
private MessageGroupStore messageGroupStore;
|
||||
|
||||
private final Collection<Message<?>> messages = new PersistentCollection();
|
||||
|
||||
private final MessageGroup original;
|
||||
|
||||
private volatile Message<?> oneMessage;
|
||||
|
||||
private volatile int size;
|
||||
|
||||
PersistentMessageGroup(MessageGroupStore messageGroupStore, MessageGroup original) {
|
||||
this.messageGroupStore = messageGroupStore;
|
||||
this.original = original;
|
||||
}
|
||||
|
||||
public void setSize(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Message<?>> getMessages() {
|
||||
return Collections.unmodifiableCollection(this.messages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> getOne() {
|
||||
if (this.oneMessage == null) {
|
||||
synchronized (this) {
|
||||
if (this.oneMessage == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Lazy loading of one message for messageGroup: " + this.original.getGroupId());
|
||||
}
|
||||
this.oneMessage = this.messageGroupStore.getOneMessageFromGroup(this.original.getGroupId());
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.oneMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSequenceSize() {
|
||||
if (size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
Message<?> message = getOne();
|
||||
if (message != null) {
|
||||
Integer sequenceSize = message.getHeaders()
|
||||
.get(IntegrationMessageHeaderAccessor.SEQUENCE_SIZE, Integer.class);
|
||||
return (sequenceSize != null ? sequenceSize : 0);
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
if (this.size == 0) {
|
||||
synchronized (this) {
|
||||
if (this.size == 0) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Lazy loading of group size for messageGroup: " + this.original.getGroupId());
|
||||
}
|
||||
this.size = this.messageGroupStore.messageGroupSize(this.original.getGroupId());
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGroupId() {
|
||||
return this.original.getGroupId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAdd(Message<?> message) {
|
||||
return this.original.canAdd(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLastReleasedMessageSequenceNumber() {
|
||||
return this.original.getLastReleasedMessageSequenceNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
return this.original.isComplete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete() {
|
||||
this.original.complete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestamp() {
|
||||
return this.original.getTimestamp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastModified() {
|
||||
return this.original.getLastModified();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastModified(long lastModified) {
|
||||
this.original.setLastModified(lastModified);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(Message<?> messageToAdd) {
|
||||
this.original.add(messageToAdd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Message<?> messageToRemove) {
|
||||
return this.original.remove(messageToRemove);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastReleasedMessageSequenceNumber(int sequenceNumber) {
|
||||
this.original.setLastReleasedMessageSequenceNumber(sequenceNumber);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.original.clear();
|
||||
}
|
||||
|
||||
|
||||
private class PersistentCollection extends AbstractCollection<Message<?>> {
|
||||
|
||||
private volatile Collection<Message<?>> collection;
|
||||
|
||||
private void load() {
|
||||
if (this.collection == null) {
|
||||
synchronized (this) {
|
||||
if (this.collection == null) {
|
||||
Object groupId = PersistentMessageGroup.this.original.getGroupId();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Lazy loading of messages for messageGroup: " + groupId);
|
||||
}
|
||||
this.collection = PersistentMessageGroup.this.messageGroupStore.getMessagesForGroup(groupId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
load();
|
||||
return this.collection.contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
load();
|
||||
return this.collection.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(T[] a) {
|
||||
load();
|
||||
return this.collection.toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
load();
|
||||
return this.collection.containsAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Message<?>> iterator() {
|
||||
load();
|
||||
return this.collection.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return PersistentMessageGroup.this.size();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -59,6 +59,28 @@ public class SimpleMessageGroupFactory implements MessageGroupFactory {
|
||||
return new SimpleMessageGroup(this.type.get(), messages, groupId, timestamp, complete);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageGroup create(MessageGroupStore messageGroupStore, Object groupId) {
|
||||
if (GroupType.PERSISTENT.equals(this.type)) {
|
||||
return new PersistentMessageGroup(messageGroupStore, new SimpleMessageGroup(groupId));
|
||||
}
|
||||
else {
|
||||
return create(messageGroupStore.getMessagesForGroup(groupId), groupId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageGroup create(MessageGroupStore messageGroupStore, Object groupId, long timestamp, boolean complete) {
|
||||
if (GroupType.PERSISTENT.equals(this.type)) {
|
||||
SimpleMessageGroup original = new SimpleMessageGroup(Collections.<Message<?>>emptyList(), groupId,
|
||||
timestamp, complete);
|
||||
return new PersistentMessageGroup(messageGroupStore, original);
|
||||
}
|
||||
else {
|
||||
return create(messageGroupStore.getMessagesForGroup(groupId), groupId, timestamp, complete);
|
||||
}
|
||||
}
|
||||
|
||||
public enum GroupType {
|
||||
|
||||
BLOCKING_QUEUE {
|
||||
@@ -84,6 +106,14 @@ public class SimpleMessageGroupFactory implements MessageGroupFactory {
|
||||
return Collections.<Message<?>>synchronizedSet(new LinkedHashSet<Message<?>>());
|
||||
}
|
||||
|
||||
},
|
||||
PERSISTENT {
|
||||
|
||||
@Override
|
||||
Collection<Message<?>> get() {
|
||||
return HASH_SET.get();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
abstract Collection<Message<?>> get();
|
||||
|
||||
@@ -239,7 +239,10 @@ public class SimpleMessageStore extends AbstractMessageGroupStore
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageGroup addMessageToGroup(Object groupId, Message<?> message) {
|
||||
public void addMessagesToGroup(Object groupId, Message<?>... messages) {
|
||||
Assert.notNull(groupId, "'groupId' must not be null");
|
||||
Assert.notNull(messages, "'messages' must not be null");
|
||||
|
||||
Lock lock = this.lockRegistry.obtain(groupId);
|
||||
try {
|
||||
lock.lockInterruptibly();
|
||||
@@ -247,31 +250,38 @@ public class SimpleMessageStore extends AbstractMessageGroupStore
|
||||
try {
|
||||
UpperBound upperBound;
|
||||
MessageGroup group = this.groupIdToMessageGroup.get(groupId);
|
||||
MessagingException outOfCapacityException =
|
||||
new MessagingException(getClass().getSimpleName() +
|
||||
" was out of capacity (" + this.groupCapacity + ") for group '" + groupId +
|
||||
"', try constructing it with a larger capacity.");
|
||||
if (group == null) {
|
||||
if (this.groupCapacity > 0 && messages.length > this.groupCapacity) {
|
||||
throw outOfCapacityException;
|
||||
}
|
||||
group = getMessageGroupFactory().create(groupId);
|
||||
this.groupIdToMessageGroup.putIfAbsent(groupId, group);
|
||||
upperBound = new UpperBound(this.groupCapacity);
|
||||
upperBound.tryAcquire(-1);
|
||||
for (Message<?> message : messages) {
|
||||
upperBound.tryAcquire(-1);
|
||||
group.add(message);
|
||||
}
|
||||
this.groupToUpperBound.putIfAbsent(groupId, upperBound);
|
||||
}
|
||||
else {
|
||||
upperBound = this.groupToUpperBound.get(groupId);
|
||||
Assert.state(upperBound != null, "'upperBound' must not be null.");
|
||||
lock.unlock();
|
||||
if (!upperBound.tryAcquire(this.upperBoundTimeout)) {
|
||||
unlocked = true;
|
||||
throw new MessagingException(this.getClass().getSimpleName()
|
||||
+ " was out of capacity ("
|
||||
+ this.groupCapacity
|
||||
+ ") for group '"
|
||||
+ groupId
|
||||
+ "', try constructing it with a larger capacity.");
|
||||
for (Message<?> message : messages) {
|
||||
lock.unlock();
|
||||
if (!upperBound.tryAcquire(this.upperBoundTimeout)) {
|
||||
unlocked = true;
|
||||
throw outOfCapacityException;
|
||||
}
|
||||
lock.lockInterruptibly();
|
||||
group.add(message);
|
||||
}
|
||||
lock.lockInterruptibly();
|
||||
}
|
||||
group.add(message);
|
||||
|
||||
group.setLastModified(System.currentTimeMillis());
|
||||
return group;
|
||||
}
|
||||
finally {
|
||||
if (!unlocked) {
|
||||
@@ -445,6 +455,11 @@ public class SimpleMessageStore extends AbstractMessageGroupStore
|
||||
return getMessageGroup(groupId).getOne();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Message<?>> getMessagesForGroup(Object groupId) {
|
||||
return getMessageGroup(groupId).getMessages();
|
||||
}
|
||||
|
||||
public void clearMessageGroup(Object groupId) {
|
||||
Lock lock = this.lockRegistry.obtain(groupId);
|
||||
try {
|
||||
|
||||
@@ -293,9 +293,8 @@ public class AbstractCorrelatingMessageHandlerTests {
|
||||
Method forceComplete =
|
||||
AbstractCorrelatingMessageHandler.class.getDeclaredMethod("forceComplete", MessageGroup.class);
|
||||
forceComplete.setAccessible(true);
|
||||
mgs.addMessageToGroup("foo", new GenericMessage<String>("foo"));
|
||||
GenericMessage<String> secondMessage = new GenericMessage<String>("bar");
|
||||
mgs.addMessageToGroup("foo", secondMessage);
|
||||
mgs.addMessagesToGroup("foo", new GenericMessage<String>("foo"), secondMessage);
|
||||
MessageGroup group = mgs.getMessageGroup("foo");
|
||||
// remove a message
|
||||
mgs.removeMessagesFromGroup("foo", secondMessage);
|
||||
@@ -324,7 +323,7 @@ public class AbstractCorrelatingMessageHandlerTests {
|
||||
QueueChannel outputChannel = new QueueChannel();
|
||||
handler.setOutputChannel(outputChannel);
|
||||
MessageGroupStore mgs = TestUtils.getPropertyValue(handler, "messageStore", MessageGroupStore.class);
|
||||
mgs.addMessageToGroup("foo", new GenericMessage<String>("foo"));
|
||||
mgs.addMessagesToGroup("foo", new GenericMessage<String>("foo"));
|
||||
mgs.completeGroup("foo");
|
||||
mgs = spy(mgs);
|
||||
new DirectFieldAccessor(handler).setPropertyValue("messageStore", mgs);
|
||||
@@ -357,7 +356,7 @@ public class AbstractCorrelatingMessageHandlerTests {
|
||||
QueueChannel outputChannel = new QueueChannel();
|
||||
handler.setOutputChannel(outputChannel);
|
||||
MessageGroupStore mgs = TestUtils.getPropertyValue(handler, "messageStore", MessageGroupStore.class);
|
||||
mgs.addMessageToGroup("foo", new GenericMessage<String>("foo"));
|
||||
mgs.addMessagesToGroup("foo", new GenericMessage<String>("foo"));
|
||||
MessageGroup group = new SimpleMessageGroup(mgs.getMessageGroup("foo"));
|
||||
mgs.completeGroup("foo");
|
||||
mgs = spy(mgs);
|
||||
@@ -393,7 +392,7 @@ public class AbstractCorrelatingMessageHandlerTests {
|
||||
QueueChannel outputChannel = new QueueChannel();
|
||||
handler.setOutputChannel(outputChannel);
|
||||
MessageGroupStore mgs = TestUtils.getPropertyValue(handler, "messageStore", MessageGroupStore.class);
|
||||
mgs.addMessageToGroup("foo", new GenericMessage<String>("foo"));
|
||||
mgs.addMessagesToGroup("foo", new GenericMessage<String>("foo"));
|
||||
MessageGroup group = new SimpleMessageGroup(mgs.getMessageGroup("foo"));
|
||||
mgs = spy(mgs);
|
||||
new DirectFieldAccessor(handler).setPropertyValue("messageStore", mgs);
|
||||
|
||||
@@ -36,7 +36,6 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import reactor.rx.Promise;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
@@ -65,6 +64,8 @@ import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import reactor.rx.Promise;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
@@ -173,7 +174,7 @@ public class GatewayParserTests {
|
||||
this.startResponder(requestChannel, replyChannel);
|
||||
TestService service = context.getBean("promise", TestService.class);
|
||||
Promise<Message<?>> result = service.promise("foo");
|
||||
Message<?> reply = result.await(1, TimeUnit.SECONDS);
|
||||
Message<?> reply = result.await(10, TimeUnit.SECONDS);
|
||||
assertEquals("foo", reply.getPayload());
|
||||
assertNotNull(TestUtils.getPropertyValue(context.getBean("&promise"), "asyncExecutor"));
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.springframework.test.util.ReflectionTestUtils;
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public class MessageStoreTests {
|
||||
|
||||
@@ -80,7 +81,7 @@ public class MessageStoreTests {
|
||||
assertEquals(1, store.getMessageCountForAllMessageGroups());
|
||||
}
|
||||
|
||||
private static class TestMessageStore extends AbstractMessageGroupStore {
|
||||
private static class TestMessageStore extends SimpleMessageStore {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
MessageGroup testMessages =
|
||||
@@ -95,7 +96,7 @@ public class MessageStoreTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageGroup addMessageToGroup(Object correlationKey, Message<?> message) {
|
||||
public void addMessagesToGroup(Object groupId, Message<?>... messages) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user