INT-3642: Improve MessageGroupStore Removal

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

Currently, `removeMessageFromGroup` rebuilds the group on every removal.

In every case where this method is used in the framework, the result is not used.

Add `removeMessagesFromGroup` that removes a collection of messages and returns no result.

INT-3642: Polishing - PR Comments

INT-3642: Polishing and Fix Group Metadata Size
This commit is contained in:
Gary Russell
2015-06-16 09:01:29 -04:00
committed by Artem Bilan
parent 0065ed8c49
commit f3d525a5e8
23 changed files with 472 additions and 107 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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
@@ -26,6 +26,7 @@ import org.springframework.messaging.Message;
*
* @author Oleg Zhurakousky
* @author Artem Bilan
* @author Gary Russell
* @since 2.1
*/
public class AggregatingMessageHandler extends AbstractCorrelatingMessageHandler {
@@ -64,9 +65,7 @@ public class AggregatingMessageHandler extends AbstractCorrelatingMessageHandler
remove(messageGroup);
}
else {
for (Message<?> message : messageGroup.getMessages()) {
this.messageStore.removeMessageFromGroup(messageGroup.getGroupId(), message);
}
this.messageStore.removeMessagesFromGroup(messageGroup.getGroupId(), messageGroup.getMessages());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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
@@ -43,6 +43,7 @@ import org.springframework.messaging.Message;
*
* @author Iwein Fuld
* @author Oleg Zhurakousky
* @author Gary Russell
*
* @see AbstractCorrelatingMessageHandler
*/
@@ -118,7 +119,7 @@ public class CorrelatingMessageBarrier extends AbstractMessageHandler implements
Iterator<Message<?>> messages = group.getMessages().iterator();
if (messages.hasNext()) {
nextMessage = messages.next();
store.removeMessageFromGroup(key, nextMessage);
this.store.removeMessagesFromGroup(key, nextMessage);
if (log.isDebugEnabled()) {
log.debug(String.format("Released message for key [%s]: %s.", key, nextMessage));
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2015 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
@@ -25,6 +25,7 @@ import org.springframework.messaging.Message;
* Will remove {@link MessageGroup}s only if 'sequenceSize' is provided and reached.
*
* @author Oleg Zhurakousky
* @author Gary Russell
* @since 2.1
*/
public class ResequencingMessageHandler extends AbstractCorrelatingMessageHandler {
@@ -83,9 +84,7 @@ public class ResequencingMessageHandler extends AbstractCorrelatingMessageHandle
if (completedMessages != null){
int lastReleasedSequenceNumber = this.findLastReleasedSequenceNumber(messageGroup.getGroupId(), completedMessages);
messageStore.setLastReleasedSequenceNumberForGroup(messageGroup.getGroupId(), lastReleasedSequenceNumber);
for (Message<?> msg : completedMessages) {
this.messageStore.removeMessageFromGroup(messageGroup.getGroupId(), msg);
}
this.messageStore.removeMessagesFromGroup(messageGroup.getGroupId(), completedMessages);
}
if (timeout) {
this.messageStore.completeGroup(messageGroup.getGroupId());

View File

@@ -352,7 +352,7 @@ public class DelayHandler extends AbstractReplyProducingMessageHandler implement
private void doReleaseMessage(Message<?> message) {
if (removeDelayedMessageFromMessageStore(message)) {
this.messageStore.removeMessageFromGroup(this.messageGroupId, message);
this.messageStore.removeMessagesFromGroup(this.messageGroupId, message);
this.handleMessageInternal(message);
}
else {

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2015 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;
/**
* @author Gary Russell
* @since 4.2
*
*/
public abstract class AbstractBatchingMessageGroupStore implements BasicMessageGroupStore {
private static final int DEFAULT_REMOVE_BATCH_SIZE = 100;
private volatile int removeBatchSize = DEFAULT_REMOVE_BATCH_SIZE;
/**
* Set the batch size when bulk removing messages from groups for message stores
* that support batch removal.
* Default 100.
* @param removeBatchSize the batch size.
* @since 4.2
*/
public void setRemoveBatchSize(int removeBatchSize) {
this.removeBatchSize = removeBatchSize;
}
public int getRemoveBatchSize() {
return removeBatchSize;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors
* Copyright 2002-2015 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.
@@ -34,6 +34,7 @@ import org.springframework.util.Assert;
* Base class for implementations of Key/Value style {@link MessageGroupStore} and {@link MessageStore}
*
* @author Oleg Zhurakousky
* @author Gary Russell
* @since 2.1
*/
public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupStore implements MessageStore{
@@ -48,9 +49,9 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
@Override
public Message<?> getMessage(UUID id) {
Message<?> message = this.getRawMessage(id);
Message<?> message = getRawMessage(id);
if (message != null){
return this.normalizeMessage(message);
return normalizeMessage(message);
}
return null;
}
@@ -60,19 +61,19 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
public <T> Message<T> addMessage(Message<T> message) {
Assert.notNull(message, "'message' must not be null");
UUID messageId = message.getHeaders().getId();
this.doStore(MESSAGE_KEY_PREFIX + messageId, message);
return (Message<T>) this.getRawMessage(messageId);
doStore(MESSAGE_KEY_PREFIX + messageId, message);
return (Message<T>) getRawMessage(messageId);
}
@Override
public Message<?> removeMessage(UUID id) {
Assert.notNull(id, "'id' must not be null");
Object message = this.doRemove(MESSAGE_KEY_PREFIX + id);
Object message = doRemove(MESSAGE_KEY_PREFIX + id);
if (message != null) {
Assert.isInstanceOf(Message.class, message);
}
if (message != null){
return this.normalizeMessage((Message<?>) message);
return normalizeMessage((Message<?>) message);
}
return null;
}
@@ -80,7 +81,7 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
@Override
@ManagedAttribute
public long getMessageCount() {
Collection<?> messageIds = this.doListKeys(MESSAGE_KEY_PREFIX + "*");
Collection<?> messageIds = doListKeys(MESSAGE_KEY_PREFIX + "*");
return (messageIds != null) ? messageIds.size() : 0;
}
@@ -92,7 +93,7 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
*/
@Override
public MessageGroup getMessageGroup(Object groupId) {
return this.buildMessageGroup(groupId, false);
return buildMessageGroup(groupId, false);
}
@@ -105,25 +106,25 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
Assert.notNull(message, "'message' must not be null");
// add message as is to the MG accessible by the caller
SimpleMessageGroup messageGroup = this.getSimpleMessageGroup(this.getMessageGroup(groupId));
SimpleMessageGroup messageGroup = getSimpleMessageGroup(getMessageGroup(groupId));
messageGroup.add(message);
// enrich Message with additional headers and add it to MS
Message<?> enrichedMessage = this.enrichMessage(message);
Message<?> enrichedMessage = enrichMessage(message);
this.addMessage(enrichedMessage);
addMessage(enrichedMessage);
// build raw MessageGroup and add enriched Message to it
SimpleMessageGroup rawGroup = this.buildMessageGroup(groupId, true);
SimpleMessageGroup rawGroup = buildMessageGroup(groupId, true);
rawGroup.setLastModified(System.currentTimeMillis());
rawGroup.add(enrichedMessage);
// store MessageGroupMetadata built from enriched MG
this.doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, new MessageGroupMetadata(rawGroup));
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, new MessageGroupMetadata(rawGroup));
// return clean MG
return this.getMessageGroup(groupId);
return getMessageGroup(groupId);
}
/**
@@ -135,33 +136,52 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
Assert.notNull(messageToRemove, "'messageToRemove' must not be null");
// build raw MG
SimpleMessageGroup rawGroup = this.buildMessageGroup(groupId, true);
SimpleMessageGroup rawGroup = buildMessageGroup(groupId, true);
// create a clean instance of
SimpleMessageGroup messageGroup = this.normalizeSimpleMessageGroup(rawGroup);
SimpleMessageGroup messageGroup = normalizeSimpleMessageGroup(rawGroup);
for (Message<?> message : rawGroup.getMessages()) {
if (message.getHeaders().getId().equals(messageToRemove.getHeaders().getId())){
rawGroup.remove(message);
}
}
this.removeMessage(messageToRemove.getHeaders().getId());
removeMessage(messageToRemove.getHeaders().getId());
rawGroup.setLastModified(System.currentTimeMillis());
this.doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, new MessageGroupMetadata(rawGroup));
messageGroup = this.getSimpleMessageGroup(this.getMessageGroup(groupId));
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, new MessageGroupMetadata(rawGroup));
messageGroup = getSimpleMessageGroup(getMessageGroup(groupId));
return messageGroup;
}
@Override
public void removeMessagesFromGroup(Object groupId, Collection<Message<?>> messages) {
Assert.notNull(groupId, "'groupId' must not be null");
Assert.notNull(messages, "'messages' must not be null");
Object mgm = doRetrieve(MESSAGE_GROUP_KEY_PREFIX + groupId);
if (mgm != null) {
Assert.isInstanceOf(MessageGroupMetadata.class, mgm);
MessageGroupMetadata messageGroupMetadata = (MessageGroupMetadata) mgm;
for (Message<?> messageToRemove : messages) {
UUID messageId = messageToRemove.getHeaders().getId();
messageGroupMetadata.remove(messageId);
doRemove(MESSAGE_KEY_PREFIX + messageId);
}
messageGroupMetadata.setLastModified(System.currentTimeMillis());
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, messageGroupMetadata);
}
}
@Override
public void completeGroup(Object groupId) {
Assert.notNull(groupId, "'groupId' must not be null");
SimpleMessageGroup messageGroup = this.buildMessageGroup(groupId, true);
SimpleMessageGroup messageGroup = buildMessageGroup(groupId, true);
messageGroup.complete();
messageGroup.setLastModified(System.currentTimeMillis());
this.doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, new MessageGroupMetadata(messageGroup));
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, new MessageGroupMetadata(messageGroup));
}
/**
@@ -170,14 +190,14 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
@Override
public void removeMessageGroup(Object groupId) {
Assert.notNull(groupId, "'groupId' must not be null");
Object mgm = this.doRemove(MESSAGE_GROUP_KEY_PREFIX + groupId);
Object mgm = doRemove(MESSAGE_GROUP_KEY_PREFIX + groupId);
if (mgm != null) {
Assert.isInstanceOf(MessageGroupMetadata.class, mgm);
MessageGroupMetadata messageGroupMetadata = (MessageGroupMetadata) mgm;
Iterator<UUID> messageIds = messageGroupMetadata.messageIdIterator();
while (messageIds.hasNext()){
this.removeMessage(messageIds.next());
removeMessage(messageIds.next());
}
}
}
@@ -185,16 +205,16 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
@Override
public void setLastReleasedSequenceNumberForGroup(Object groupId, int sequenceNumber) {
Assert.notNull(groupId, "'groupId' must not be null");
SimpleMessageGroup messageGroup = this.buildMessageGroup(groupId, true);
SimpleMessageGroup messageGroup = buildMessageGroup(groupId, true);
messageGroup.setLastReleasedMessageSequenceNumber(sequenceNumber);
messageGroup.setLastModified(System.currentTimeMillis());
this.doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, new MessageGroupMetadata(messageGroup));
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, new MessageGroupMetadata(messageGroup));
}
@Override
public Message<?> pollMessageFromGroup(Object groupId) {
Assert.notNull(groupId, "'groupId' must not be null");
Object mgm = this.doRetrieve(MESSAGE_GROUP_KEY_PREFIX + groupId);
Object mgm = doRetrieve(MESSAGE_GROUP_KEY_PREFIX + groupId);
if (mgm != null) {
Assert.isInstanceOf(MessageGroupMetadata.class, mgm);
MessageGroupMetadata messageGroupMetadata = (MessageGroupMetadata) mgm;
@@ -203,8 +223,8 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
if (firstId != null){
messageGroupMetadata.remove(firstId);
messageGroupMetadata.setLastModified(System.currentTimeMillis());
this.doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, messageGroupMetadata);
return this.removeMessage(firstId);
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, messageGroupMetadata);
return removeMessage(firstId);
}
}
return null;
@@ -213,8 +233,8 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
@Override
@SuppressWarnings("unchecked")
public Iterator<MessageGroup> iterator() {
final Iterator<?> idIterator = this.normalizeKeys(
(Collection<String>) this.doListKeys(MESSAGE_GROUP_KEY_PREFIX + "*"))
final Iterator<?> idIterator = normalizeKeys(
(Collection<String>) doListKeys(MESSAGE_GROUP_KEY_PREFIX + "*"))
.iterator();
return new MessageGroupIterator(idIterator);
}
@@ -236,7 +256,7 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
@Override
public int messageGroupSize(Object groupId) {
Object mgm = this.doRetrieve(MESSAGE_GROUP_KEY_PREFIX + groupId);
Object mgm = doRetrieve(MESSAGE_GROUP_KEY_PREFIX + groupId);
if (mgm != null) {
Assert.isInstanceOf(MessageGroupMetadata.class, mgm);
MessageGroupMetadata messageGroupMetadata = (MessageGroupMetadata) mgm;
@@ -255,7 +275,7 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
@SuppressWarnings({ "rawtypes", "unchecked" })
private Message<?> normalizeMessage(Message<?> message){
Message<?> normalizedMessage = this.getMessageBuilderFactory().fromMessage(message)
Message<?> normalizedMessage = getMessageBuilderFactory().fromMessage(message)
.removeHeader("CREATED_DATE")
.build();
Map innerMap = (Map) new DirectFieldAccessor(normalizedMessage.getHeaders()).getPropertyValue("headers");
@@ -269,7 +289,7 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private Message<?> enrichMessage(Message<?> message){
Message<?> enrichedMessage = this.getMessageBuilderFactory().fromMessage(message)
Message<?> enrichedMessage = getMessageBuilderFactory().fromMessage(message)
.setHeader(CREATED_DATE, System.currentTimeMillis())
.build();
Map innerMap = (Map) new DirectFieldAccessor(enrichedMessage.getHeaders()).getPropertyValue("headers");
@@ -280,7 +300,7 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
private SimpleMessageGroup buildMessageGroup(Object groupId, boolean raw){
Assert.notNull(groupId, "'groupId' must not be null");
Object mgm = this.doRetrieve(MESSAGE_GROUP_KEY_PREFIX + groupId);
Object mgm = doRetrieve(MESSAGE_GROUP_KEY_PREFIX + groupId);
if (mgm != null) {
Assert.isInstanceOf(MessageGroupMetadata.class, mgm);
MessageGroupMetadata messageGroupMetadata = (MessageGroupMetadata) mgm;
@@ -289,10 +309,10 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
Iterator<UUID> messageIds = messageGroupMetadata.messageIdIterator();
while (messageIds.hasNext()){
if (raw){
messages.add(this.getRawMessage(messageIds.next()));
messages.add(getRawMessage(messageIds.next()));
}
else {
messages.add(this.getMessage(messageIds.next()));
messages.add(getMessage(messageIds.next()));
}
}
@@ -319,7 +339,7 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
private SimpleMessageGroup normalizeSimpleMessageGroup(SimpleMessageGroup messageGroup){
SimpleMessageGroup normalizedGroup = new SimpleMessageGroup(messageGroup.getGroupId());
for (Message<?> message : messageGroup.getMessages()) {
Message<?> normailizedMessage = this.normalizeMessage(message);
Message<?> normailizedMessage = normalizeMessage(message);
normalizedGroup.add(normailizedMessage);
}
return normalizedGroup;
@@ -327,7 +347,7 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
private Message<?> getRawMessage(UUID id) {
Assert.notNull(id, "'id' must not be null");
Object message = this.doRetrieve(MESSAGE_KEY_PREFIX + id);
Object message = doRetrieve(MESSAGE_KEY_PREFIX + id);
return (Message<?>) message;
}
@@ -341,7 +361,7 @@ public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupS
@Override
public boolean hasNext() {
return idIterator.hasNext();
return this.idIterator.hasNext();
}
@Override

View File

@@ -13,6 +13,7 @@
package org.springframework.integration.store;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
@@ -39,8 +40,8 @@ import org.springframework.messaging.Message;
*/
@ManagedResource
@IntegrationManagedResource
public abstract class AbstractMessageGroupStore implements MessageGroupStore, Iterable<MessageGroup>,
BeanFactoryAware {
public abstract class AbstractMessageGroupStore extends AbstractBatchingMessageGroupStore
implements MessageGroupStore, Iterable<MessageGroup>, BeanFactoryAware {
protected final Log logger = LogFactory.getLog(getClass());
@@ -165,6 +166,11 @@ public abstract class AbstractMessageGroupStore implements MessageGroupStore, It
throw new UnsupportedOperationException("Not yet implemented for this store");
}
@Override
public void removeMessagesFromGroup(Object key, Message<?>... messages) {
removeMessagesFromGroup(key, Arrays.asList(messages));
}
@Override
public Message<?> getOneMessageFromGroup(Object groupId) {
throw new UnsupportedOperationException("Not yet implemented for this store");

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@@ -50,8 +50,6 @@ public class MessageGroupMetadata implements Serializable {
private final boolean hasMessages;
private final int size;
private final UUID first;
public MessageGroupMetadata(MessageGroup messageGroup) {
@@ -66,10 +64,6 @@ public class MessageGroupMetadata implements Serializable {
for (Message<?> message : messageGroup.getMessages()) {
this.messageIds.add(message.getHeaders().getId());
}
this.size = this.messageIds.size();
}
else {
this.size = messageGroup.size();
}
this.complete = messageGroup.isComplete();
this.timestamp = messageGroup.getTimestamp();
@@ -102,7 +96,7 @@ public class MessageGroupMetadata implements Serializable {
}
public int size(){
return this.size;
return this.messageIds.size();
}
public UUID firstId(){

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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
@@ -12,6 +12,7 @@
*/
package org.springframework.integration.store;
import java.util.Collection;
import java.util.Iterator;
import org.springframework.jmx.export.annotation.ManagedAttribute;
@@ -50,7 +51,7 @@ public interface MessageGroupStore extends BasicMessageGroupStore {
int getMessageGroupCount();
/**
* Persist a deletion on a single message from the group. The group is modified to reflect that 'messageToRemove' is
* 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.
@@ -59,6 +60,26 @@ public interface MessageGroupStore extends BasicMessageGroupStore {
*/
MessageGroup removeMessageFromGroup(Object key, Message<?> messageToRemove);
/**
* 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)}.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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
@@ -269,6 +269,30 @@ public class SimpleMessageStore extends AbstractMessageGroupStore
}
}
@Override
public void removeMessagesFromGroup(Object groupId, Collection<Message<?>> messages) {
Lock lock = this.lockRegistry.obtain(groupId);
try {
lock.lockInterruptibly();
try {
SimpleMessageGroup group = this.groupIdToMessageGroup.get(groupId);
Assert.notNull(group, "MessageGroup for groupId '" + groupId + "' " +
"can not be located while attempting to remove Message(s) from the MessageGroup");
for (Message<?> messageToRemove : messages) {
group.remove(messageToRemove);
}
group.setLastModified(System.currentTimeMillis());
}
finally {
lock.unlock();
}
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new MessagingException("Interrupted while obtaining lock", e);
}
}
@Override
public Iterator<MessageGroup> iterator() {
return new HashSet<MessageGroup>(groupIdToMessageGroup.values()).iterator();
@@ -325,7 +349,7 @@ public class SimpleMessageStore extends AbstractMessageGroupStore
if (!CollectionUtils.isEmpty(messageList)){
message = messageList.iterator().next();
if (message != null){
this.removeMessageFromGroup(groupId, message);
this.removeMessagesFromGroup(groupId, message);
}
}
return message;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2015 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.
@@ -25,9 +25,10 @@ import java.util.Iterator;
import java.util.List;
import org.junit.Test;
import org.springframework.integration.store.MessageGroupStore.MessageGroupCallback;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.integration.store.MessageGroupStore.MessageGroupCallback;
import org.springframework.test.util.ReflectionTestUtils;
/**
@@ -40,6 +41,7 @@ public class MessageStoreTests {
public void shouldRegisterCallbacks() throws Exception {
TestMessageStore store = new TestMessageStore();
store.setExpiryCallbacks(Arrays.<MessageGroupCallback> asList(new MessageGroupStore.MessageGroupCallback() {
@Override
public void execute(MessageGroupStore messageGroupStore, MessageGroup group) {
}
}));
@@ -52,6 +54,7 @@ public class MessageStoreTests {
TestMessageStore store = new TestMessageStore();
final List<String> list = new ArrayList<String>();
store.registerMessageGroupExpiryCallback(new MessageGroupCallback() {
@Override
public void execute(MessageGroupStore messageGroupStore, MessageGroup group) {
list.add(group.getOne().getPayload().toString());
messageGroupStore.removeMessageGroup(group.getGroupId());
@@ -84,41 +87,55 @@ public class MessageStoreTests {
private boolean removed = false;
@Override
public Iterator<MessageGroup> iterator() {
return Arrays.asList(testMessages).iterator();
}
@Override
public MessageGroup addMessageToGroup(Object correlationKey, Message<?> message) {
throw new UnsupportedOperationException();
}
@Override
public MessageGroup getMessageGroup(Object correlationKey) {
return removed ? new SimpleMessageGroup(correlationKey) : testMessages;
}
@Override
public MessageGroup removeMessageFromGroup(Object key, Message<?> messageToRemove) {
throw new UnsupportedOperationException();
}
@Override
public void removeMessagesFromGroup(Object key, Collection<Message<?>> messages) {
throw new UnsupportedOperationException();
}
@Override
public void removeMessageGroup(Object correlationKey) {
if (correlationKey.equals(testMessages.getGroupId())) {
removed = true;
}
}
@Override
public void setLastReleasedSequenceNumberForGroup(Object groupId, int sequenceNumber) {
throw new UnsupportedOperationException();
}
@Override
public void completeGroup(Object groupId) {
throw new UnsupportedOperationException();
}
@Override
public Message<?> pollMessageFromGroup(Object groupId) {
return null;
}
@Override
public int messageGroupSize(Object groupId) {
return 0;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2015 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.
@@ -27,10 +27,11 @@ import java.util.Collection;
import java.util.List;
import org.junit.Test;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessagingException;
import org.springframework.integration.store.MessageGroupStore.MessageGroupCallback;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessagingException;
import org.springframework.test.util.ReflectionTestUtils;
/**
@@ -120,6 +121,7 @@ public class SimpleMessageStoreTests {
public void shouldRegisterCallbacks() throws Exception {
SimpleMessageStore store = new SimpleMessageStore();
store.setExpiryCallbacks(Arrays.<MessageGroupCallback> asList(new MessageGroupStore.MessageGroupCallback() {
@Override
public void execute(MessageGroupStore messageGroupStore, MessageGroup group) {
}
}));
@@ -132,6 +134,7 @@ public class SimpleMessageStoreTests {
SimpleMessageStore store = new SimpleMessageStore();
final List<String> list = new ArrayList<String>();
store.registerMessageGroupExpiryCallback(new MessageGroupCallback() {
@Override
public void execute(MessageGroupStore messageGroupStore, MessageGroup group) {
list.add(group.getOne().getPayload().toString());
messageGroupStore.removeMessageGroup(group.getGroupId());
@@ -148,4 +151,21 @@ public class SimpleMessageStoreTests {
}
@Test
public void testAddAndRemoveMessagesFromMessageGroup() throws Exception {
SimpleMessageStore messageStore = new SimpleMessageStore();
String groupId = "X";
List<Message<?>> messages = new ArrayList<Message<?>>();
for (int i = 0; i < 25; i++) {
Message<String> message = MessageBuilder.withPayload("foo").setCorrelationId(groupId).build();
messageStore.addMessageToGroup(groupId, message);
messages.add(message);
}
MessageGroup group = messageStore.getMessageGroup(groupId);
assertEquals(25, group.size());
messageStore.removeMessagesFromGroup(groupId, messages);
group = messageStore.getMessageGroup(groupId);
assertEquals(0, group.size());
}
}

View File

@@ -20,6 +20,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.junit.After;
@@ -30,6 +32,7 @@ import org.springframework.data.gemfire.CacheFactoryBean;
import org.springframework.data.gemfire.RegionFactoryBean;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.store.MessageGroup;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
@@ -98,6 +101,25 @@ public class GemfireMessageStoreTests {
assertEquals("channel", fooChannelHistory.get("type"));
}
@Test
public void testAddAndRemoveMessagesFromMessageGroup() throws Exception {
GemfireMessageStore messageStore = new GemfireMessageStore(this.region);
messageStore.afterPropertiesSet();
String groupId = "X";
List<Message<?>> messages = new ArrayList<Message<?>>();
for (int i = 0; i < 25; i++) {
Message<String> message = MessageBuilder.withPayload("foo").setCorrelationId(groupId).build();
messageStore.addMessageToGroup(groupId, message);
messages.add(message);
}
MessageGroup group = messageStore.getMessageGroup(groupId);
assertEquals(25, group.size());
messageStore.removeMessagesFromGroup(groupId, messages);
group = messageStore.getMessageGroup(groupId);
assertEquals(0, group.size());
}
@Before
public void init() throws Exception {
CacheFactoryBean cacheFactoryBean = new CacheFactoryBean();

View File

@@ -19,6 +19,7 @@ import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
@@ -47,6 +48,7 @@ import org.springframework.integration.store.SimpleMessageGroup;
import org.springframework.integration.util.UUIDConverter;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ParameterizedPreparedStatementSetter;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.RowMapper;
@@ -483,6 +485,40 @@ public class JdbcMessageStore extends AbstractMessageGroupStore implements Messa
return getMessageGroup(groupId);
}
@Override
public void removeMessagesFromGroup(Object groupId, Collection<Message<?>> messages) {
Assert.notNull(groupId, "'groupId' must not be null");
Assert.notNull(messages, "'messages' must not be null");
final String groupKey = getKey(groupId);
if (logger.isDebugEnabled()){
logger.debug("Removing messages from group with group key=" + groupKey);
}
jdbcTemplate.batchUpdate(getQuery(Query.REMOVE_MESSAGE_FROM_GROUP),
messages,
getRemoveBatchSize(),
new ParameterizedPreparedStatementSetter<Message<?>>() {
@Override
public void setValues(PreparedStatement ps, Message<?> messageToRemove) throws SQLException {
ps.setString(1, groupKey);
ps.setString(2, getKey(messageToRemove.getHeaders().getId()));
ps.setString(3, region);
}
});
jdbcTemplate.batchUpdate(getQuery(Query.DELETE_MESSAGE),
messages,
getRemoveBatchSize(),
new ParameterizedPreparedStatementSetter<Message<?>>() {
@Override
public void setValues(PreparedStatement ps, Message<?> messageToRemove) throws SQLException {
ps.setString(1, getKey(messageToRemove.getHeaders().getId()));
ps.setString(2, region);
}
});
this.updateMessageGroup(groupKey);
}
@Override
public void removeMessageGroup(Object groupId) {
@@ -560,7 +596,7 @@ public class JdbcMessageStore extends AbstractMessageGroupStore implements Messa
Message<?> polledMessage = this.doPollForMessage(key);
if (polledMessage != null){
this.removeMessageFromGroup(groupId, polledMessage);
this.removeMessagesFromGroup(groupId, polledMessage);
}
return polledMessage;
}

View File

@@ -30,6 +30,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
@@ -246,6 +248,25 @@ public class JdbcMessageStoreTests {
assertEquals(0, group.size());
}
@Test
@Transactional
@DirtiesContext
public void testAddAndRemoveMessagesFromMessageGroup() throws Exception {
String groupId = "X";
messageStore.setRemoveBatchSize(10);
List<Message<?>> messages = new ArrayList<Message<?>>();
for (int i = 0; i < 25; i++) {
Message<String> message = MessageBuilder.withPayload("foo").setCorrelationId(groupId).build();
messageStore.addMessageToGroup(groupId, message);
messages.add(message);
}
MessageGroup group = messageStore.getMessageGroup(groupId);
assertEquals(25, group.size());
messageStore.removeMessagesFromGroup(groupId, messages);
group = messageStore.getMessageGroup(groupId);
assertEquals(0, group.size());
}
@Test
@Transactional
public void testRemoveMessageGroup() throws Exception {

View File

@@ -49,6 +49,7 @@ import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.integration.store.AbstractBatchingMessageGroupStore;
import org.springframework.integration.store.BasicMessageGroupStore;
import org.springframework.integration.support.DefaultMessageBuilderFactory;
import org.springframework.integration.support.MessageBuilderFactory;
@@ -65,8 +66,8 @@ import org.springframework.util.Assert;
* @since 4.0
*/
public abstract class AbstractConfigurableMongoDbMessageStore implements BasicMessageGroupStore, InitializingBean,
ApplicationContextAware {
public abstract class AbstractConfigurableMongoDbMessageStore extends AbstractBatchingMessageGroupStore
implements BasicMessageGroupStore, InitializingBean, ApplicationContextAware {
public final static String SEQUENCE_NAME = "messagesSequence";

View File

@@ -17,6 +17,7 @@
package org.springframework.integration.mongodb.store;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
@@ -48,6 +49,7 @@ import org.springframework.util.Assert;
*
* @author Amol Nayak
* @author Artem Bilan
* @author Gary Russell
* @since 3.0
*/
public class ConfigurableMongoDbMessageStore extends AbstractConfigurableMongoDbMessageStore
@@ -205,10 +207,39 @@ public class ConfigurableMongoDbMessageStore extends AbstractConfigurableMongoDb
Query query = groupIdQuery(groupId)
.addCriteria(Criteria.where(MessageDocumentFields.MESSAGE_ID).is(messageToRemove.getHeaders().getId()));
mongoTemplate.remove(query, collectionName);
this.mongoTemplate.remove(query, this.collectionName);
updateGroup(groupId, lastModifiedUpdate());
return getMessageGroup(groupId);
}
@Override
public void removeMessagesFromGroup(Object groupId, Collection<Message<?>> messages) {
Assert.notNull(groupId, "'groupId' must not be null");
Assert.notNull(messages, "'messageToRemove' must not be null");
Collection<UUID> ids = new ArrayList<UUID>();
for (Message<?> messageToRemove : messages) {
ids.add(messageToRemove.getHeaders().getId());
if (ids.size() >= getRemoveBatchSize()) {
removeMessages(groupId, ids);
ids.clear();
}
}
if (ids.size() > 0) {
removeMessages(groupId, ids);
}
updateGroup(groupId, lastModifiedUpdate());
}
private void removeMessages(Object groupId, Collection<UUID> ids) {
Query query = groupIdQuery(groupId)
.addCriteria(Criteria.where(MessageDocumentFields.MESSAGE_ID).in(ids.toArray()));
this.mongoTemplate.remove(query, this.collectionName);
}
@Override
public void removeMessagesFromGroup(Object groupId, Message<?>... messages) {
removeMessagesFromGroup(groupId, Arrays.asList(messages));
}
@Override

View File

@@ -17,6 +17,7 @@
package org.springframework.integration.mongodb.store;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -76,6 +77,7 @@ import org.springframework.util.StringUtils;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.BulkWriteOperation;
import com.mongodb.DBObject;
@@ -313,6 +315,35 @@ public class MongoDbMessageStore extends AbstractMessageGroupStore
return getMessageGroup(groupId);
}
@Override
public void removeMessagesFromGroup(Object groupId, Collection<Message<?>> messages) {
Assert.notNull(groupId, "'groupId' must not be null");
Assert.notNull(messages, "'messageToRemove' must not be null");
Collection<UUID> ids = new ArrayList<UUID>();
for (Message<?> messageToRemove : messages) {
ids.add(messageToRemove.getHeaders().getId());
if (ids.size() >= getRemoveBatchSize()) {
bulkRemove(groupId, ids);
ids.clear();
}
}
if (ids.size() > 0) {
bulkRemove(groupId, ids);
}
updateGroup(groupId, lastModifiedUpdate());
}
private void bulkRemove(Object groupId, Collection<UUID> ids) {
BulkWriteOperation bulkOp = this.template.getCollection(this.collectionName)
.initializeOrderedBulkOperation();
for (UUID id : ids) {
bulkOp.find(whereMessageIdIsAndGroupIdIs(id, groupId).getQueryObject())
.remove();
}
bulkOp.execute();
}
@Override
public void removeMessageGroup(Object groupId) {
this.template.remove(whereGroupIdIs(groupId), this.collectionName);
@@ -400,7 +431,6 @@ public class MongoDbMessageStore extends AbstractMessageGroupStore
return new Query(Criteria.where("headers.id._value").is(id.toString()).and(GROUP_ID_KEY).is(groupId));
}
private static Query whereGroupIdOrder(Object groupId) {
return whereGroupIdIs(groupId).with(new Sort(Sort.Direction.DESC, GROUP_UPDATE_TIMESTAMP_KEY, SEQUENCE));
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2015 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.
@@ -15,14 +15,19 @@
*/
package org.springframework.integration.mongodb.store;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import com.mongodb.MongoClient;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@@ -32,6 +37,7 @@ import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.mongodb.rules.MongoDbAvailable;
import org.springframework.integration.mongodb.rules.MongoDbAvailableTests;
import org.springframework.integration.store.AbstractBatchingMessageGroupStore;
import org.springframework.integration.store.MessageGroup;
import org.springframework.integration.store.MessageGroupStore;
import org.springframework.integration.store.MessageStore;
@@ -41,6 +47,8 @@ import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.GenericMessage;
import com.mongodb.MongoClient;
/**
* @author Oleg Zhurakousky
* @author Gary Russell
@@ -394,6 +402,26 @@ public abstract class AbstractMongoDbMessageGroupStoreTests extends MongoDbAvail
assertEquals(2, counter);
}
@Test
@MongoDbAvailable
public void testAddAndRemoveMessagesFromMessageGroup() throws Exception {
MessageGroupStore messageStore = (MessageGroupStore) this.getMessageStore();
String groupId = "X";
messageStore.removeMessageGroup("X");
((AbstractBatchingMessageGroupStore) messageStore).setRemoveBatchSize(10);
List<Message<?>> messages = new ArrayList<Message<?>>();
for (int i = 0; i < 25; i++) {
Message<String> message = MessageBuilder.withPayload("foo").setCorrelationId(groupId).build();
messageStore.addMessageToGroup(groupId, message);
messages.add(message);
}
MessageGroup group = messageStore.getMessageGroup(groupId);
assertEquals(25, group.size());
messageStore.removeMessagesFromGroup(groupId, messages);
group = messageStore.getMessageGroup(groupId);
assertEquals(0, group.size());
}
// @Test
// @MongoDbAvailable
// public void testConcurrentModifications() throws Exception{
@@ -461,6 +489,7 @@ public abstract class AbstractMongoDbMessageGroupStoreTests extends MongoDbAvail
Message<?> m3 = MessageBuilder.withPayload("3").setSequenceNumber(3).setSequenceSize(3).setCorrelationId(1).build();
input.send(m3);
assertNotNull(output.receive(2000));
context.close();
}
@Test

View File

@@ -15,12 +15,12 @@
*/
package org.springframework.integration.mongodb.store;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import java.util.Map;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import org.hamcrest.Matchers;
import org.junit.Test;
@@ -38,6 +38,9 @@ import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
/**
* @author Amol Nayak
* @author Artem Bilan
@@ -45,9 +48,6 @@ import org.springframework.messaging.Message;
*/
public class ConfigurableMongoDbMessageGroupStoreTests extends AbstractMongoDbMessageGroupStoreTests {
/* (non-Javadoc)
* @see org.springframework.integration.mongodb.store.AbstractMongoDbMessageGroupStoreTests#getMessageGroupStore()
*/
@Override
protected ConfigurableMongoDbMessageStore getMessageGroupStore() throws Exception {
MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(new MongoClient(), "test");
@@ -59,9 +59,6 @@ public class ConfigurableMongoDbMessageGroupStoreTests extends AbstractMongoDbMe
return mongoDbMessageStore;
}
/* (non-Javadoc)
* @see org.springframework.integration.mongodb.store.AbstractMongoDbMessageGroupStoreTests#getMessageStore()
*/
@Override
protected MessageStore getMessageStore() throws Exception {
return this.getMessageGroupStore();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2007-2014 the original author or authors
* Copyright 2007-2015 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.
@@ -29,8 +29,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import junit.framework.AssertionFailedError;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
@@ -51,6 +49,8 @@ import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.GenericMessage;
import junit.framework.AssertionFailedError;
/**
* @author Oleg Zhurakousky
* @author Artem Bilan
@@ -62,7 +62,7 @@ public class RedisMessageGroupStoreTests extends RedisAvailableTests {
@Before
@After
public void setUpTearDown() {
StringRedisTemplate template = this.createStringRedisTemplate(this.getConnectionFactoryForTest());
StringRedisTemplate template = createStringRedisTemplate(getConnectionFactoryForTest());
template.delete("MESSAGE_GROUP_1");
template.delete("MESSAGE_GROUP_2");
template.delete("MESSAGE_GROUP_3");
@@ -71,7 +71,7 @@ public class RedisMessageGroupStoreTests extends RedisAvailableTests {
@Test
@RedisAvailable
public void testNonExistingEmptyMessageGroup() throws Exception{
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
RedisConnectionFactory jcf = getConnectionFactoryForTest();
RedisMessageStore store = new RedisMessageStore(jcf);
MessageGroup messageGroup = store.getMessageGroup(1);
@@ -83,7 +83,7 @@ public class RedisMessageGroupStoreTests extends RedisAvailableTests {
@Test
@RedisAvailable
public void testMessageGroupUpdatedDateChangesWithEachAddedMessage() throws Exception{
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
RedisConnectionFactory jcf = getConnectionFactoryForTest();
RedisMessageStore store = new RedisMessageStore(jcf);
MessageGroup messageGroup = store.getMessageGroup(1);
@@ -110,7 +110,7 @@ public class RedisMessageGroupStoreTests extends RedisAvailableTests {
@Test
@RedisAvailable
public void testMessageGroupWithAddedMessage() throws Exception{
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
RedisConnectionFactory jcf = getConnectionFactoryForTest();
RedisMessageStore store = new RedisMessageStore(jcf);
MessageGroup messageGroup = store.getMessageGroup(1);
@@ -128,7 +128,7 @@ public class RedisMessageGroupStoreTests extends RedisAvailableTests {
@Test
@RedisAvailable
public void testRemoveMessageGroup() throws Exception{
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
RedisConnectionFactory jcf = getConnectionFactoryForTest();
RedisMessageStore store = new RedisMessageStore(jcf);
MessageGroup messageGroup = store.getMessageGroup(1);
@@ -155,7 +155,7 @@ public class RedisMessageGroupStoreTests extends RedisAvailableTests {
@Test
@RedisAvailable
public void testCompleteMessageGroup() throws Exception{
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
RedisConnectionFactory jcf = getConnectionFactoryForTest();
RedisMessageStore store = new RedisMessageStore(jcf);
MessageGroup messageGroup = store.getMessageGroup(1);
@@ -169,7 +169,7 @@ public class RedisMessageGroupStoreTests extends RedisAvailableTests {
@Test
@RedisAvailable
public void testLastReleasedSequenceNumber() throws Exception{
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
RedisConnectionFactory jcf = getConnectionFactoryForTest();
RedisMessageStore store = new RedisMessageStore(jcf);
MessageGroup messageGroup = store.getMessageGroup(1);
@@ -183,7 +183,7 @@ public class RedisMessageGroupStoreTests extends RedisAvailableTests {
@Test
@RedisAvailable
public void testRemoveMessageFromTheGroup() throws Exception{
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
RedisConnectionFactory jcf = getConnectionFactoryForTest();
RedisMessageStore store = new RedisMessageStore(jcf);
MessageGroup messageGroup = store.getMessageGroup(1);
@@ -206,7 +206,7 @@ public class RedisMessageGroupStoreTests extends RedisAvailableTests {
@Test
@RedisAvailable
public void testWithMessageHistory() throws Exception{
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
RedisConnectionFactory jcf = getConnectionFactoryForTest();
RedisMessageStore store = new RedisMessageStore(jcf);
store.getMessageGroup(1);
@@ -233,7 +233,7 @@ public class RedisMessageGroupStoreTests extends RedisAvailableTests {
@Test
@RedisAvailable
public void testRemoveNonExistingMessageFromTheGroup() throws Exception{
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
RedisConnectionFactory jcf = getConnectionFactoryForTest();
RedisMessageStore store = new RedisMessageStore(jcf);
MessageGroup messageGroup = store.getMessageGroup(1);
@@ -244,7 +244,7 @@ public class RedisMessageGroupStoreTests extends RedisAvailableTests {
@Test
@RedisAvailable
public void testRemoveNonExistingMessageFromNonExistingTheGroup() throws Exception{
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
RedisConnectionFactory jcf = getConnectionFactoryForTest();
RedisMessageStore store = new RedisMessageStore(jcf);
store.removeMessageFromGroup(1, new GenericMessage<String>("2"));
}
@@ -254,7 +254,7 @@ public class RedisMessageGroupStoreTests extends RedisAvailableTests {
@Test
@RedisAvailable
public void testMultipleInstancesOfGroupStore() throws Exception{
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
RedisConnectionFactory jcf = getConnectionFactoryForTest();
RedisMessageStore store1 = new RedisMessageStore(jcf);
RedisMessageStore store2 = new RedisMessageStore(jcf);
@@ -275,7 +275,7 @@ public class RedisMessageGroupStoreTests extends RedisAvailableTests {
@Test
@RedisAvailable
public void testIteratorOfMessageGroups() throws Exception{
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
RedisConnectionFactory jcf = getConnectionFactoryForTest();
RedisMessageStore store1 = new RedisMessageStore(jcf);
RedisMessageStore store2 = new RedisMessageStore(jcf);
@@ -317,7 +317,7 @@ public class RedisMessageGroupStoreTests extends RedisAvailableTests {
@Test
@RedisAvailable @Ignore
public void testConcurrentModifications() throws Exception{
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
RedisConnectionFactory jcf = getConnectionFactoryForTest();
final RedisMessageStore store1 = new RedisMessageStore(jcf);
final RedisMessageStore store2 = new RedisMessageStore(jcf);
@@ -361,7 +361,7 @@ public class RedisMessageGroupStoreTests extends RedisAvailableTests {
@Test
@RedisAvailable
public void testWithAggregatorWithShutdown(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("redis-aggregator-config.xml", this.getClass());
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("redis-aggregator-config.xml", getClass());
MessageChannel input = context.getBean("inputChannel", MessageChannel.class);
QueueChannel output = context.getBean("outputChannel", QueueChannel.class);
@@ -373,7 +373,7 @@ public class RedisMessageGroupStoreTests extends RedisAvailableTests {
assertNull(output.receive(1000));
context.close();
context = new ClassPathXmlApplicationContext("redis-aggregator-config.xml", this.getClass());
context = new ClassPathXmlApplicationContext("redis-aggregator-config.xml", getClass());
input = context.getBean("inputChannel", MessageChannel.class);
output = context.getBean("outputChannel", QueueChannel.class);
@@ -383,4 +383,25 @@ public class RedisMessageGroupStoreTests extends RedisAvailableTests {
context.close();
}
@Test
@RedisAvailable
public void testAddAndRemoveMessagesFromMessageGroup() throws Exception {
RedisConnectionFactory jcf = getConnectionFactoryForTest();
RedisMessageStore messageStore = new RedisMessageStore(jcf);
String groupId = "X";
messageStore.removeMessageGroup("X");
List<Message<?>> messages = new ArrayList<Message<?>>();
for (int i = 0; i < 25; i++) {
Message<String> message = MessageBuilder.withPayload("foo").setCorrelationId(groupId).build();
messageStore.addMessageToGroup(groupId, message);
messages.add(message);
}
MessageGroup group = messageStore.getMessageGroup(groupId);
assertEquals(25, group.size());
messageStore.removeMessagesFromGroup(groupId, messages);
group = messageStore.getMessageGroup(groupId);
assertEquals(0, group.size());
messageStore.removeMessageGroup("X");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2007-2013 the original author or authors
* Copyright 2007-2015 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.
@@ -21,6 +21,8 @@ import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
@@ -34,11 +36,14 @@ import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.redis.rules.RedisAvailable;
import org.springframework.integration.redis.rules.RedisAvailableTests;
import org.springframework.integration.store.MessageGroup;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.GenericMessage;
/**
* @author Oleg Zhurakousky
* @author Gary Russell
*
*/
public class RedisMessageStoreTests extends RedisAvailableTests {
@@ -153,6 +158,23 @@ public class RedisMessageStoreTests extends RedisAvailableTests {
assertEquals("channel", fooChannelHistory.get("type"));
}
@Test
@RedisAvailable
public void testAddAndRemoveMessagesFromMessageGroup() throws Exception {
RedisConnectionFactory jcf = this.getConnectionFactoryForTest();
RedisMessageStore messageStore = new RedisMessageStore(jcf);
String groupId = "X";
List<Message<?>> messages = new ArrayList<Message<?>>();
for (int i = 0; i < 25; i++) {
Message<String> message = MessageBuilder.withPayload("foo").setCorrelationId(groupId).build();
messageStore.addMessageToGroup(groupId, message);
messages.add(message);
}
messageStore.removeMessagesFromGroup(groupId, messages);
MessageGroup group = messageStore.getMessageGroup(groupId);
assertEquals(0, group.size());
}
@SuppressWarnings("serial")
public static class Person implements Serializable{
private Address address;

View File

@@ -168,3 +168,12 @@ metadata store if it implements `Flushable` (e.g. the `PropertiesPersistenMetada
When using Java 8, gateway methods can now return `CompletableFuture<?>`.
See <<gw-completable-future>> for more information.
[[x4.2-aggregator-perf]]
==== Aggregator Performance
This release includes some performance improvements for aggregating components (aggregator, resequencer, etc),
by more efficiently removing messages from groups when they are released.
New methods (`removeMessagesFromGroup`) have been added to the message store.
Set the `removeBatchSize` property (default `100`) to adjust the number of messages deleted in each operation.
Currently, JDBC, Redis and MongoDB message stores support this property.