INT-3143: Add ConfigurableMongoDbMessageStore

JIRA: https://jira.springsource.org/browse/INT-3143

INT-3143: Polishing, fixed and documentation

JIRA: https://jira.springsource.org/browse/INT-3077
This commit is contained in:
Artem Bilan
2013-11-14 18:57:47 +02:00
committed by Gary Russell
parent 225c0b4c9f
commit d2bb90f7e0
15 changed files with 1739 additions and 707 deletions

View File

@@ -0,0 +1,456 @@
/*
* Copyright 2013 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.mongodb.store;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.springframework.beans.BeansException;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.core.serializer.support.DeserializingConverter;
import org.springframework.core.serializer.support.SerializingConverter;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.IndexOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.CustomConversions;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.index.Index;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Order;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.integration.Message;
import org.springframework.integration.MessageHeaders;
import org.springframework.integration.store.AbstractMessageGroupStore;
import org.springframework.integration.store.MessageGroup;
import org.springframework.integration.store.MessageGroupStore;
import org.springframework.integration.store.MessageStore;
import org.springframework.integration.store.SimpleMessageGroup;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.util.Assert;
/**
* An alternate MongoDB {@link MessageStore} and {@link MessageGroupStore} which allows the user to
* configure the instance of {@link MongoTemplate}. The mechanism of storing the messages/group of messages
* in the store is and is different from {@link MongoDbMessageStore}. Since the store uses serialization of the
* messages by default, all the headers, and the payload of the Message must implement {@link java.io.Serializable}
* interface
*
* @author Amol Nayak
* @author Artem Bilan
* @since 3.0
*/
public class ConfigurableMongoDbMessageStore extends AbstractMessageGroupStore
implements MessageStore, InitializingBean, ApplicationContextAware {
public final static String DEFAULT_COLLECTION_NAME = "configurableStoreMessages";
/**
* The name of the message header that stores a flag to indicate that the message has been saved. This is an
* optimization for the put method.
*/
public static final String SAVED_KEY = ConfigurableMongoDbMessageStore.class.getSimpleName() + ".SAVED";
/**
* The name of the message header that stores a timestamp for the time the message was inserted.
*/
public static final String CREATED_DATE_KEY = ConfigurableMongoDbMessageStore.class.getSimpleName() + ".CREATED_DATE";
private static final String MESSAGE_ID = "messageId";
private static final String GROUP_ID = "groupId";
private static final String LAST_MODIFIED_TIME = "lastModifiedTime";
private static final String LAST_RELEASED_SEQUENCE = "lastReleasedSequence";
private static final String COMPLETE = "complete";
private final String collectionName;
private final MongoDbFactory mongoDbFactory;
private volatile MongoTemplate mongoTemplate;
private volatile MappingMongoConverter mappingMongoConverter;
private ApplicationContext applicationContext;
public ConfigurableMongoDbMessageStore(MongoTemplate mongoTemplate) {
this(mongoTemplate, DEFAULT_COLLECTION_NAME);
}
public ConfigurableMongoDbMessageStore(MongoTemplate mongoTemplate, String collectionName) {
Assert.notNull("'mongoTemplate' must not be null");
Assert.hasText("'collectionName' must not be empty");
this.collectionName = collectionName;
this.mongoTemplate = mongoTemplate;
this.mongoDbFactory = null;
}
public ConfigurableMongoDbMessageStore(MongoDbFactory mongoDbFactory) {
this(mongoDbFactory, null, DEFAULT_COLLECTION_NAME);
}
public ConfigurableMongoDbMessageStore(MongoDbFactory mongoDbFactory, MappingMongoConverter mappingMongoConverter) {
this(mongoDbFactory, mappingMongoConverter, DEFAULT_COLLECTION_NAME);
}
public ConfigurableMongoDbMessageStore(MongoDbFactory mongoDbFactory, String collectionName) {
this(mongoDbFactory, null, collectionName);
}
public ConfigurableMongoDbMessageStore(MongoDbFactory mongoDbFactory, MappingMongoConverter mappingMongoConverter, String collectionName) {
Assert.notNull("'mongoDbFactory' must not be null");
Assert.hasText("'collectionName' must not be empty");
this.collectionName = collectionName;
this.mongoDbFactory = mongoDbFactory;
this.mappingMongoConverter = mappingMongoConverter;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
if (this.mongoTemplate == null) {
if (this.mappingMongoConverter == null) {
this.mappingMongoConverter = new MappingMongoConverter(this.mongoDbFactory, new MongoMappingContext());
this.mappingMongoConverter.setApplicationContext(this.applicationContext);
List<Object> customConverters = new ArrayList<Object>();
customConverters.add(new MongoDbMessageBytesConverter());
this.mappingMongoConverter.setCustomConversions(new CustomConversions(customConverters));
this.mappingMongoConverter.afterPropertiesSet();
}
this.mongoTemplate = new MongoTemplate(this.mongoDbFactory, this.mappingMongoConverter);
if (this.applicationContext != null) {
this.mongoTemplate.setApplicationContext(this.applicationContext);
}
}
IndexOperations indexOperations = this.mongoTemplate.indexOps(this.collectionName);
indexOperations.ensureIndex(new Index(MESSAGE_ID, Order.ASCENDING));
indexOperations.ensureIndex(new Index(GROUP_ID, Order.ASCENDING).on(LAST_MODIFIED_TIME, Order.DESCENDING));
}
@Override
public Message<?> getMessage(UUID id) {
Assert.notNull(id, "'id' must not be null");
MessageDocument document = this.mongoTemplate.findOne(Query.query(Criteria.where(MESSAGE_ID).is(id)),
MessageDocument.class, this.collectionName);
return (document != null) ? document.getMessage() : null;
}
@Override
public <T> Message<T> addMessage(Message<T> message) {
Assert.notNull(message, "'message' must not be null");
this.addMessageDocument(new MessageDocument(message));
return message;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void addMessageDocument(MessageDocument document) {
Message<?> message = document.getMessage();
if (message.getHeaders().containsKey(SAVED_KEY)) {
Message<?> saved = getMessage(message.getHeaders().getId());
if (saved != null) {
if (saved.equals(message)) {
return;
} // We need to save it under its own id
}
}
final long createdDate = document.getCreatedTime() == 0 ? System.currentTimeMillis() : document.getCreatedTime();
Message<?> result = MessageBuilder.fromMessage(message).setHeader(SAVED_KEY, Boolean.TRUE)
.setHeader(CREATED_DATE_KEY, createdDate).build();
Map innerMap = (Map) new DirectFieldAccessor(result.getHeaders()).getPropertyValue("headers");
// using reflection to set ID since it is immutable through MessageHeaders
innerMap.put(MessageHeaders.ID, message.getHeaders().get(MessageHeaders.ID));
innerMap.put(MessageHeaders.TIMESTAMP, message.getHeaders().get(MessageHeaders.TIMESTAMP));
document.setCreatedTime(createdDate);
this.mongoTemplate.insert(document, this.collectionName);
}
@Override
public Message<?> removeMessage(UUID id) {
Assert.notNull(id, "'id' must not be null");
MessageDocument document = this.mongoTemplate.findAndRemove(Query.query(Criteria.where(MESSAGE_ID).is(id)),
MessageDocument.class, this.collectionName);
return (document != null) ? document.getMessage() : null;
}
@Override
public long getMessageCount() {
return this.mongoTemplate.getCollection(this.collectionName).getCount();
}
@Override
public int messageGroupSize(Object groupId) {
long lCount = this.mongoTemplate.count(groupIdQuery(groupId), this.collectionName);
Assert.isTrue(lCount <= Integer.MAX_VALUE, "Message count is out of Integer's range");
return (int) lCount;
}
@Override
public MessageGroup getMessageGroup(Object groupId) {
List<MessageDocument> messageDocuments = this.mongoTemplate.find(groupIdQuery(groupId), MessageDocument.class,
this.collectionName);
long createdTime = 0;
long lastModifiedTime = 0;
int lastReleasedSequence = 0;
boolean complete = false;
if (messageDocuments.size() > 0) {
MessageDocument document = messageDocuments.get(0);
createdTime = document.getCreatedTime();
lastModifiedTime = document.getLastModifiedTime();
complete = document.isComplete();
lastReleasedSequence = document.getLastReleasedSequence();
}
List<Message<?>> messages = new ArrayList<Message<?>>();
for (MessageDocument document : messageDocuments) {
messages.add(document.getMessage());
}
SimpleMessageGroup group = new SimpleMessageGroup(messages, groupId, createdTime, complete);
group.setLastReleasedMessageSequenceNumber(lastReleasedSequence);
group.setLastModified(lastModifiedTime);
return group;
}
@Override
public MessageGroup addMessageToGroup(Object groupId, Message<?> message) {
Assert.notNull(groupId, "'groupId' must not be null");
Assert.notNull(message, "'message' must not be null");
MessageDocument messageDocument = this.mongoTemplate.findOne(groupIdQuery(groupId), MessageDocument.class,
this.collectionName);
long createdTime = 0;
int lastReleasedSequence = 0;
boolean complete = false;
if (messageDocument != null) {
createdTime = messageDocument.getCreatedTime();
lastReleasedSequence = messageDocument.getLastReleasedSequence();
complete = messageDocument.isComplete();
}
MessageDocument document = new MessageDocument(message);
document.setGroupId(groupId);
document.setComplete(complete);
document.setLastReleasedSequence(lastReleasedSequence);
document.setCreatedTime(createdTime == 0 ? System.currentTimeMillis() : createdTime);
document.setLastModifiedTime(System.currentTimeMillis());
this.addMessageDocument(document);
return this.getMessageGroup(groupId);
}
@Override
public MessageGroup removeMessageFromGroup(Object groupId, Message<?> messageToRemove) {
Assert.notNull(groupId, "'groupId' must not be null");
Assert.notNull(messageToRemove, "'messageToRemove' must not be null");
Query query = groupIdQuery(groupId).addCriteria(Criteria.where(MESSAGE_ID).is(messageToRemove.getHeaders().getId()));
this.mongoTemplate.remove(query, this.collectionName);
this.updateGroup(groupId, lastModifiedUpdate());
return this.getMessageGroup(groupId);
}
@Override
public void removeMessageGroup(Object groupId) {
this.mongoTemplate.remove(groupIdQuery(groupId), this.collectionName);
}
@Override
@SuppressWarnings({ "rawtypes" })
public Iterator<MessageGroup> iterator() {
Map<Object, MessageGroup> messageGroupMap = new HashMap<Object, MessageGroup>();
Query query = Query.query(Criteria.where(GROUP_ID).exists(true));
query.fields().include(GROUP_ID);
List<Map> groupIds = this.mongoTemplate.find(query, Map.class, this.collectionName);
for (Map groupId : groupIds) {
Object key = groupId.get(GROUP_ID);
if (!messageGroupMap.containsKey(key)) {
messageGroupMap.put(key, this.getMessageGroup(groupId));
}
}
return messageGroupMap.values().iterator();
}
@Override
public Message<?> pollMessageFromGroup(Object groupId) {
Assert.notNull(groupId, "'groupId' must not be null");
Query query = groupIdQuery(groupId).with(new Sort(Sort.Direction.ASC, LAST_MODIFIED_TIME));
MessageDocument document = this.mongoTemplate.findAndRemove(query, MessageDocument.class, this.collectionName);
Message<?> message = null;
if (document != null) {
message = document.getMessage();
this.updateGroup(groupId, lastModifiedUpdate());
}
return message;
}
@Override
public void setLastReleasedSequenceNumberForGroup(Object groupId, int sequenceNumber) {
this.updateGroup(groupId, lastModifiedUpdate().set(LAST_RELEASED_SEQUENCE, sequenceNumber));
}
@Override
public void completeGroup(Object groupId) {
this.updateGroup(groupId, lastModifiedUpdate().set(COMPLETE, true));
}
private void updateGroup(Object groupId, Update update) {
this.mongoTemplate.updateFirst(groupIdQuery(groupId), update, this.collectionName);
}
private static Update lastModifiedUpdate() {
return Update.update(LAST_MODIFIED_TIME, System.currentTimeMillis());
}
private static Query groupIdQuery(Object groupId) {
return Query.query(Criteria.where(GROUP_ID).is(groupId));
}
/**
* The entity class to wrap {@link Message} to the MongoDB document.
*/
private static class MessageDocument {
private final Message<?> message;
@SuppressWarnings("unused")
private final UUID messageId;
private volatile Long createdTime = 0L;
@SuppressWarnings("unused")
private volatile Object groupId;
private volatile Long lastModifiedTime = 0L;
private volatile Boolean complete = false;
private volatile Integer lastReleasedSequence = 0;
public MessageDocument(Message<?> message) {
Assert.notNull(message, "'message' must not be null");
this.message = message;
this.messageId = message.getHeaders().getId();
}
public Message<?> getMessage() {
return message;
}
public void setGroupId(Object groupId) {
this.groupId = groupId;
}
public Long getLastModifiedTime() {
return lastModifiedTime;
}
public void setLastModifiedTime(long lastModifiedTime) {
this.lastModifiedTime = lastModifiedTime;
}
public Long getCreatedTime() {
return createdTime;
}
public void setCreatedTime(long createdTime) {
this.createdTime = createdTime;
}
public Boolean isComplete() {
return complete;
}
public void setComplete(boolean complete) {
this.complete = complete;
}
public Integer getLastReleasedSequence() {
return lastReleasedSequence;
}
public void setLastReleasedSequence(int lastReleasedSequence) {
this.lastReleasedSequence = lastReleasedSequence;
}
}
/**
* A {@link GenericConverter} implementation to convert {@link Message} to
* serialized {@link byte[]} to store {@link Message} to the MongoDB.
* And vice versa - to convert {@link byte[]} from the MongoDB to the {@link Message}.
*/
private static class MongoDbMessageBytesConverter implements GenericConverter {
private final Converter<Object, byte[]> serializingConverter = new SerializingConverter();
private final Converter<byte[], Object> deserializingConverter = new DeserializingConverter();
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
Set<ConvertiblePair> convertiblePairs = new HashSet<ConvertiblePair>();
convertiblePairs.add(new ConvertiblePair(Message.class, byte[].class));
convertiblePairs.add(new ConvertiblePair(byte[].class, Message.class));
return convertiblePairs;
}
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (Message.class.isAssignableFrom(sourceType.getObjectType())) {
return serializingConverter.convert(source);
}
else {
return deserializingConverter.convert((byte[]) source);
}
}
}
}

View File

@@ -41,18 +41,23 @@ public abstract class MongoDbAvailableTests {
public MongoDbAvailableRule redisAvailableRule = new MongoDbAvailableRule();
protected MongoDbFactory prepareMongoFactory(String... additionalCollectionToDrop) throws Exception{
protected MongoDbFactory prepareMongoFactory(String... additionalCollectionsToDrop) throws Exception {
MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(new Mongo(), "test");
MongoTemplate template = new MongoTemplate(mongoDbFactory);
template.dropCollection("messages");
template.dropCollection("data");
for (String additionalCollection : additionalCollectionToDrop) {
template.dropCollection(additionalCollection);
}
cleanupCollections(mongoDbFactory, additionalCollectionsToDrop);
return mongoDbFactory;
}
public Person createPerson(){
protected void cleanupCollections(MongoDbFactory mongoDbFactory, String... additionalCollectionsToDrop) {
MongoTemplate template = new MongoTemplate(mongoDbFactory);
template.dropCollection("messages");
template.dropCollection("configurableStoreMessages");
template.dropCollection("data");
for (String additionalCollection : additionalCollectionsToDrop) {
template.dropCollection(additionalCollection);
}
}
public Person createPerson() {
Address address = new Address();
address.setCity("Philadelphia");
address.setStreet("2121 Rawn street");
@@ -64,7 +69,7 @@ public abstract class MongoDbAvailableTests {
return person;
}
public Person createPerson(String name){
public Person createPerson(String name) {
Address address = new Address();
address.setCity("Philadelphia");
address.setStreet("2121 Rawn street");
@@ -77,44 +82,61 @@ public abstract class MongoDbAvailableTests {
}
public static class Person {
private Address address;
private String name;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static class Address {
private String street;
private String city;
private String state;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
public static class TestMongoConverter extends MappingMongoConverter {
@@ -134,6 +156,7 @@ public abstract class MongoDbAvailableTests {
public <S> S read(Class<S> clazz, DBObject source) {
return super.read(clazz, source);
}
}
}

View File

@@ -0,0 +1,505 @@
/*
* Copyright 2002-2013 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.mongodb.store;
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.Collection;
import java.util.Iterator;
import java.util.Properties;
import java.util.UUID;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.mongodb.rules.MongoDbAvailable;
import org.springframework.integration.mongodb.rules.MongoDbAvailableTests;
import org.springframework.integration.store.MessageGroup;
import org.springframework.integration.store.MessageGroupStore;
import org.springframework.integration.store.MessageStore;
import org.springframework.integration.store.SimpleMessageGroup;
import org.springframework.integration.support.MessageBuilder;
import com.mongodb.Mongo;
/**
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Amol Nayak
*
*/
public abstract class AbstractMongoDbMessageGroupStoreTests extends MongoDbAvailableTests {
@Test
@MongoDbAvailable
public void testNonExistingEmptyMessageGroup() throws Exception{
this.cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageGroupStore store = getMessageGroupStore();
MessageGroup messageGroup = store.getMessageGroup(1);
assertNotNull(messageGroup);
assertTrue(messageGroup instanceof SimpleMessageGroup);
assertEquals(0, messageGroup.size());
}
@Test
@MongoDbAvailable
public void testMessageGroupWithAddedMessagePrimitiveGroupId() throws Exception{
this.cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageGroupStore store = this.getMessageGroupStore();
MessageStore messageStore = this.getMessageStore();
MessageGroup messageGroup = store.getMessageGroup(1);
Message<?> messageA = new GenericMessage<String>("A");
Message<?> messageB = new GenericMessage<String>("B");
store.addMessageToGroup(1, messageA);
messageGroup = store.addMessageToGroup(1, messageB);
assertNotNull(messageGroup);
assertEquals(2, messageGroup.size());
Message<?> retrievedMessage = messageStore.getMessage(messageA.getHeaders().getId());
assertNotNull(retrievedMessage);
assertEquals(retrievedMessage.getHeaders().getId(), messageA.getHeaders().getId());
// ensure that 'message_group' header that is only used internally is not propagated
assertNull(retrievedMessage.getHeaders().get("message_group"));
}
@Test
@MongoDbAvailable
public void testMessageGroupWithAddedMessageUUIDGroupIdAndUUIDHeader() throws Exception{
this.cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageGroupStore store = this.getMessageGroupStore();
MessageStore messageStore = this.getMessageStore();
Object id = UUID.randomUUID();
MessageGroup messageGroup = store.getMessageGroup(id);
UUID uuidA = UUID.randomUUID();
Message<?> messageA = MessageBuilder.withPayload("A").setHeader("foo", uuidA).build();
UUID uuidB = UUID.randomUUID();
Message<?> messageB = MessageBuilder.withPayload("B").setHeader("foo", uuidB).build();
store.addMessageToGroup(id, messageA);
messageGroup = store.addMessageToGroup(id, messageB);
assertNotNull(messageGroup);
assertEquals(2, messageGroup.size());
Message<?> retrievedMessage = messageStore.getMessage(messageA.getHeaders().getId());
assertNotNull(retrievedMessage);
assertEquals(retrievedMessage.getHeaders().getId(), messageA.getHeaders().getId());
// ensure that 'message_group' header that is only used internally is not propagated
assertNull(retrievedMessage.getHeaders().get("message_group"));
Object fooHeader = retrievedMessage.getHeaders().get("foo");
assertTrue(fooHeader instanceof UUID);
assertEquals(uuidA, fooHeader);
}
@Test
@MongoDbAvailable
public void testCountMessagesInGroup() throws Exception{
this.cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageGroupStore store = this.getMessageGroupStore();
Message<?> messageA = new GenericMessage<String>("A");
Message<?> messageB = new GenericMessage<String>("B");
store.addMessageToGroup(1, messageA);
store.addMessageToGroup(1, messageB);
assertEquals(2, store.messageGroupSize(1));
}
@Test
@MongoDbAvailable
public void testPollMessages() throws Exception{
this.cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageGroupStore store = this.getMessageGroupStore();
Message<?> messageA = new GenericMessage<String>("A");
Message<?> messageB = new GenericMessage<String>("B");
store.addMessageToGroup(1, messageA);
store.addMessageToGroup(1, messageB);
assertEquals(2, store.messageGroupSize(1));
Message<?> out = store.pollMessageFromGroup(1);
assertNotNull(out);
assertEquals("A", out.getPayload());
assertEquals(1, store.messageGroupSize(1));
out = store.pollMessageFromGroup(1);
assertEquals("B", out.getPayload());
assertEquals(0, store.messageGroupSize(1));
}
@Test
@MongoDbAvailable
public void testSameMessageMultipleGroupsPoll() throws Exception{
this.cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageGroupStore store = this.getMessageGroupStore();
Message<?> messageA = new GenericMessage<String>("A");
store.addMessageToGroup(1, messageA);
store.addMessageToGroup(2, messageA);
store.addMessageToGroup(3, messageA);
store.addMessageToGroup(4, messageA);
assertEquals(1, store.messageGroupSize(1));
assertEquals(1, store.messageGroupSize(2));
assertEquals(1, store.messageGroupSize(3));
assertEquals(1, store.messageGroupSize(4));
store.pollMessageFromGroup(3);
assertEquals(1, store.messageGroupSize(1));
assertEquals(1, store.messageGroupSize(2));
assertEquals(0, store.messageGroupSize(3));
assertEquals(1, store.messageGroupSize(4));
store.pollMessageFromGroup(4);
assertEquals(1, store.messageGroupSize(1));
assertEquals(1, store.messageGroupSize(2));
assertEquals(0, store.messageGroupSize(3));
assertEquals(0, store.messageGroupSize(4));
store.pollMessageFromGroup(2);
assertEquals(1, store.messageGroupSize(1));
assertEquals(0, store.messageGroupSize(2));
assertEquals(0, store.messageGroupSize(3));
assertEquals(0, store.messageGroupSize(4));
store.pollMessageFromGroup(1);
assertEquals(0, store.messageGroupSize(1));
assertEquals(0, store.messageGroupSize(2));
assertEquals(0, store.messageGroupSize(3));
assertEquals(0, store.messageGroupSize(4));
}
@Test
@MongoDbAvailable
public void testSameMessageMultipleGroupsRemove() throws Exception{
this.cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageGroupStore store = this.getMessageGroupStore();
Message<?> messageA = new GenericMessage<String>("A");
store.addMessageToGroup(1, messageA);
store.addMessageToGroup(2, messageA);
store.addMessageToGroup(3, messageA);
store.addMessageToGroup(4, messageA);
assertEquals(1, store.messageGroupSize(1));
assertEquals(1, store.messageGroupSize(2));
assertEquals(1, store.messageGroupSize(3));
assertEquals(1, store.messageGroupSize(4));
store.removeMessageFromGroup(3, messageA);
assertEquals(1, store.messageGroupSize(1));
assertEquals(1, store.messageGroupSize(2));
assertEquals(0, store.messageGroupSize(3));
assertEquals(1, store.messageGroupSize(4));
store.removeMessageFromGroup(4, messageA);
assertEquals(1, store.messageGroupSize(1));
assertEquals(1, store.messageGroupSize(2));
assertEquals(0, store.messageGroupSize(3));
assertEquals(0, store.messageGroupSize(4));
store.removeMessageFromGroup(2, messageA);
assertEquals(1, store.messageGroupSize(1));
assertEquals(0, store.messageGroupSize(2));
assertEquals(0, store.messageGroupSize(3));
assertEquals(0, store.messageGroupSize(4));
store.removeMessageFromGroup(1, messageA);
assertEquals(0, store.messageGroupSize(1));
assertEquals(0, store.messageGroupSize(2));
assertEquals(0, store.messageGroupSize(3));
assertEquals(0, store.messageGroupSize(4));
}
@Test
@MongoDbAvailable
public void testMessageGroupUpdatedDateChangesWithEachAddedMessage() throws Exception{
this.cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageGroupStore store = this.getMessageGroupStore();
MessageGroup messageGroup = store.getMessageGroup(1);
Message<?> message = new GenericMessage<String>("Hello");
messageGroup = store.addMessageToGroup(1, message);
assertNotNull(messageGroup);
assertEquals(1, messageGroup.size());
long createdTimestamp = messageGroup.getTimestamp();
long updatedTimestamp = messageGroup.getLastModified();
assertEquals(createdTimestamp, updatedTimestamp);
Thread.sleep(1000);
message = new GenericMessage<String>("Hello again");
messageGroup = store.addMessageToGroup(1, message);
createdTimestamp = messageGroup.getTimestamp();
updatedTimestamp = messageGroup.getLastModified();
assertTrue(updatedTimestamp > createdTimestamp);
assertEquals(2, messageGroup.size());
// make sure the store is properly rebuild from MongoDB
store = this.getMessageGroupStore();
messageGroup = store.getMessageGroup(1);
assertEquals(2, messageGroup.size());
}
@Test
@MongoDbAvailable
public void testMessageGroupMarkingMessage() throws Exception{
this.cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageGroupStore store = this.getMessageGroupStore();
MessageGroup messageGroup = store.getMessageGroup(1);
Message<?> messageA = new GenericMessage<String>("A");
Message<?> messageB = new GenericMessage<String>("B");
store.addMessageToGroup(1, messageA);
messageGroup = store.addMessageToGroup(1, messageB);
assertNotNull(messageGroup);
assertEquals(2, messageGroup.size());
messageGroup = store.removeMessageFromGroup(1, messageA);
assertEquals(1, messageGroup.size());
// validate that the updates were propagated to Mongo as well
store = this.getMessageGroupStore();
messageGroup = store.getMessageGroup(1);
assertEquals(1, messageGroup.size());
}
@Test
@MongoDbAvailable
public void testRemoveMessageGroup() throws Exception{
this.cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageGroupStore store = this.getMessageGroupStore();
MessageStore messageStore = this.getMessageStore();
MessageGroup messageGroup = store.getMessageGroup(1);
Message<?> message = new GenericMessage<String>("Hello");
UUID id = message.getHeaders().getId();
messageGroup = store.addMessageToGroup(1, message);
assertNotNull(messageGroup);
assertEquals(1, messageGroup.size());
message = messageStore.getMessage(id);
assertNotNull(message);
store.removeMessageGroup(1);
MessageGroup messageGroupA = store.getMessageGroup(1);
assertEquals(0, messageGroupA.size());
assertFalse(messageGroupA.equals(messageGroup));
}
@Test
@MongoDbAvailable
public void testCompleteMessageGroup() throws Exception{
this.cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageGroupStore store = this.getMessageGroupStore();
MessageGroup messageGroup = store.getMessageGroup(1);
assertNotNull(messageGroup);
Message<?> message = new GenericMessage<String>("Hello");
store.addMessageToGroup(messageGroup.getGroupId(), message);
store.completeGroup(messageGroup.getGroupId());
messageGroup = store.getMessageGroup(1);
assertTrue(messageGroup.isComplete());
}
@Test
@MongoDbAvailable
public void testLastReleasedSequenceNumber() throws Exception{
this.cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageGroupStore store = this.getMessageGroupStore();
MessageGroup messageGroup = store.getMessageGroup(1);
assertNotNull(messageGroup);
Message<?> message = new GenericMessage<String>("Hello");
store.addMessageToGroup(messageGroup.getGroupId(), message);
store.setLastReleasedSequenceNumberForGroup(messageGroup.getGroupId(), 5);
messageGroup = store.getMessageGroup(1);
assertEquals(5, messageGroup.getLastReleasedMessageSequenceNumber());
}
@Test
@MongoDbAvailable
public void testRemoveMessageFromTheGroup() throws Exception{
this.cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageGroupStore store = this.getMessageGroupStore();
MessageGroup messageGroup = store.getMessageGroup(1);
Message<?> message = new GenericMessage<String>("2");
store.addMessageToGroup(1, new GenericMessage<String>("1"));
store.addMessageToGroup(1, message);
messageGroup = store.addMessageToGroup(1, new GenericMessage<String>("3"));
assertNotNull(messageGroup);
assertEquals(3, messageGroup.size());
messageGroup = store.removeMessageFromGroup(1, message);
assertEquals(2, messageGroup.size());
}
@Test
@MongoDbAvailable
public void testMultipleMessageStores() throws Exception{
this.cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageGroupStore store1 = this.getMessageGroupStore();
MessageGroupStore store2 = this.getMessageGroupStore();
Message<?> message = new GenericMessage<String>("1");
store1.addMessageToGroup(1, message);
store2.addMessageToGroup(1, new GenericMessage<String>("2"));
store1.addMessageToGroup(1, new GenericMessage<String>("3"));
MessageGroupStore store3 = this.getMessageGroupStore();
MessageGroup messageGroup = store3.getMessageGroup(1);
assertNotNull(messageGroup);
assertEquals(3, messageGroup.size());
store3.removeMessageFromGroup(1, message);
messageGroup = store2.getMessageGroup(1);
assertEquals(2, messageGroup.size());
}
@Test
@MongoDbAvailable
public void testMessageGroupIterator() throws Exception{
this.cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageGroupStore store1 = this.getMessageGroupStore();
MessageGroupStore store2 = this.getMessageGroupStore();
Message<?> message = new GenericMessage<String>("1");
store2.addMessageToGroup(1, message);
store1.addMessageToGroup(2, new GenericMessage<String>("2"));
store2.addMessageToGroup(3, new GenericMessage<String>("3"));
MessageGroupStore store3 = this.getMessageGroupStore();
Iterator<MessageGroup> iterator = store3.iterator();
assertNotNull(iterator);
int counter = 0;
while (iterator.hasNext()) {
iterator.next();
counter++;
}
assertEquals(3, counter);
store2.removeMessageFromGroup(1, message);
iterator = store3.iterator();
counter = 0;
while (iterator.hasNext()) {
iterator.next();
counter++;
}
assertEquals(2, counter);
}
// @Test
// @MongoDbAvailable
// public void testConcurrentModifications() throws Exception{
// MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
// final MongoDbMessageStore store1 = new MongoDbMessageStore(mongoDbFactory);
// final MongoDbMessageStore store2 = new MongoDbMessageStore(mongoDbFactory);
//
// final Message<?> message = new GenericMessage<String>("1");
//
// ExecutorService executor = null;
//
// final List<Object> failures = new ArrayList<Object>();
//
// for (int i = 0; i < 100; i++) {
// executor = Executors.newCachedThreadPool();
//
// executor.execute(new Runnable() {
// public void run() {
// MessageGroup group = store1.addMessageToGroup(1, message);
// if (group.getUnmarked().size() != 1){
// failures.add("ADD");
// throw new AssertionFailedError("Failed on ADD");
// }
// }
// });
// executor.execute(new Runnable() {
// public void run() {
// MessageGroup group = store2.removeMessageFromGroup(1, message);
// if (group.getUnmarked().size() != 0){
// failures.add("REMOVE");
// throw new AssertionFailedError("Failed on Remove");
// }
// }
// });
//
// executor.shutdown();
// executor.awaitTermination(10, TimeUnit.SECONDS);
// store2.removeMessageFromGroup(1, message); // ensures that if ADD thread executed after REMOVE, the store is empty for the next cycle
// }
// assertTrue(failures.size() == 0);
// }
protected void testWithAggregatorWithShutdown(String config) throws Exception{
this.cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config, this.getClass());
context.refresh();
MessageChannel input = context.getBean("inputChannel", MessageChannel.class);
QueueChannel output = context.getBean("outputChannel", QueueChannel.class);
Message<?> m1 = MessageBuilder.withPayload("1").setSequenceNumber(1).setSequenceSize(3).setCorrelationId(1).build();
Message<?> m2 = MessageBuilder.withPayload("2").setSequenceNumber(2).setSequenceSize(3).setCorrelationId(1).build();
input.send(m1);
assertNull(output.receive(1000));
input.send(m2);
assertNull(output.receive(1000));
context.close();
context = new ClassPathXmlApplicationContext(config, this.getClass());
input = context.getBean("inputChannel", MessageChannel.class);
output = context.getBean("outputChannel", QueueChannel.class);
Message<?> m3 = MessageBuilder.withPayload("3").setSequenceNumber(3).setSequenceSize(3).setCorrelationId(1).build();
input.send(m3);
assertNotNull(output.receive(2000));
}
@Test
@MongoDbAvailable
public void testWithMessageHistory() throws Exception{
this.cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageGroupStore store = this.getMessageGroupStore();
store.getMessageGroup(1);
Message<?> message = new GenericMessage<String>("Hello");
DirectChannel fooChannel = new DirectChannel();
fooChannel.setBeanName("fooChannel");
DirectChannel barChannel = new DirectChannel();
barChannel.setBeanName("barChannel");
message = MessageHistory.write(message, fooChannel);
message = MessageHistory.write(message, barChannel);
store.addMessageToGroup(1, message);
MessageGroup group = store.getMessageGroup(1);
assertNotNull(group);
Collection<Message<?>> messages = group.getMessages();
assertTrue(!messages.isEmpty());
message = messages.iterator().next();
MessageHistory messageHistory = MessageHistory.read(message);
assertNotNull(messageHistory);
assertEquals(2, messageHistory.size());
Properties fooChannelHistory = messageHistory.get(0);
assertEquals("fooChannel", fooChannelHistory.get("name"));
assertEquals("channel", fooChannelHistory.get("type"));
}
protected abstract MessageGroupStore getMessageGroupStore() throws Exception;
protected abstract MessageStore getMessageStore() throws Exception;
}

View File

@@ -0,0 +1,295 @@
/*
* Copyright 2002-2013 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.mongodb.store;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.Serializable;
import java.util.Properties;
import java.util.UUID;
import org.junit.Test;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.integration.Message;
import org.springframework.integration.channel.DirectChannel;
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.MessageStore;
import org.springframework.integration.support.MessageBuilder;
import com.mongodb.Mongo;
/**
*
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Artem Bilan
* @author Amol Nayak
*
*/
public abstract class AbstractMongoDbMessageStoreTests extends MongoDbAvailableTests {
@Test
@MongoDbAvailable
public void testAddGetWithStringPayload() throws Exception {
cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageStore store = getMessageStore();
Message<?> messageToStore = MessageBuilder.withPayload("Hello").build();
store.addMessage(messageToStore);
Message<?> retrievedMessage = store.getMessage(messageToStore.getHeaders().getId());
assertNotNull(retrievedMessage);
assertEquals(messageToStore.getPayload(), retrievedMessage.getPayload());
assertEquals(messageToStore.getHeaders(), retrievedMessage.getHeaders());
assertEquals(messageToStore, retrievedMessage);
}
@Test
@MongoDbAvailable
public void testAddThenRemoveWithStringPayload() throws Exception {
cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageStore store = getMessageStore();
Message<?> messageToStore = MessageBuilder.withPayload("Hello").build();
store.addMessage(messageToStore);
Message<?> retrievedMessage = store.getMessage(messageToStore.getHeaders().getId());
assertNotNull(retrievedMessage);
store.removeMessage(retrievedMessage.getHeaders().getId());
retrievedMessage = store.getMessage(messageToStore.getHeaders().getId());
assertNull(retrievedMessage);
}
@Test
@MongoDbAvailable
public void testAddGetWithObjectDefaultConstructorPayload() throws Exception {
cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageStore store = getMessageStore();
Person p = new Person();
p.setFname("John");
p.setLname("Doe");
Message<?> messageToStore = MessageBuilder.withPayload(p).build();
store.addMessage(messageToStore);
Message<?> retrievedMessage = store.getMessage(messageToStore.getHeaders().getId());
assertNotNull(retrievedMessage);
assertEquals(messageToStore.getPayload(), retrievedMessage.getPayload());
assertEquals(messageToStore.getHeaders(), retrievedMessage.getHeaders());
assertEquals(messageToStore, retrievedMessage);
}
@Test
@MongoDbAvailable
public void testWithMessageHistory() throws Exception{
cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageStore store = getMessageStore();
Foo foo = new Foo();
foo.setName("foo");
Message<?> message = MessageBuilder.withPayload(foo).
setHeader("foo", foo).
setHeader("bar", new Bar("bar")).
setHeader("baz", new Baz()).
setHeader("abc", new Abc()).
setHeader("xyz", new Xyz()).
build();
DirectChannel fooChannel = new DirectChannel();
fooChannel.setBeanName("fooChannel");
DirectChannel barChannel = new DirectChannel();
barChannel.setBeanName("barChannel");
message = MessageHistory.write(message, fooChannel);
message = MessageHistory.write(message, barChannel);
store.addMessage(message);
message = store.getMessage(message.getHeaders().getId());
assertNotNull(message);
assertTrue(message.getHeaders().get("foo") instanceof Foo);
assertTrue(message.getHeaders().get("bar") instanceof Bar);
assertTrue(message.getHeaders().get("baz") instanceof Baz);
assertTrue(message.getHeaders().get("abc") instanceof Abc);
assertTrue(message.getHeaders().get("xyz") instanceof Xyz);
MessageHistory messageHistory = MessageHistory.read(message);
assertNotNull(messageHistory);
assertEquals(2, messageHistory.size());
Properties fooChannelHistory = messageHistory.get(0);
assertEquals("fooChannel", fooChannelHistory.get("name"));
assertEquals("channel", fooChannelHistory.get("type"));
}
@Test
@MongoDbAvailable
public void testInt3153SequenceDetails() throws Exception{
cleanupCollections(new SimpleMongoDbFactory(new Mongo(), "test"));
MessageStore store = getMessageStore();
Message<?> messageToStore = MessageBuilder.withPayload("test")
.pushSequenceDetails(UUID.randomUUID(), 1, 1)
.pushSequenceDetails(UUID.randomUUID(), 1, 1)
.build();
store.addMessage(messageToStore);
Message<?> retrievedMessage = store.getMessage(messageToStore.getHeaders().getId());
assertNotNull(retrievedMessage);
assertEquals(messageToStore.getPayload(), retrievedMessage.getPayload());
assertEquals(messageToStore.getHeaders(), retrievedMessage.getHeaders());
assertEquals(messageToStore, retrievedMessage);
}
public static class Foo implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static class Bar implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private final String name;
public Bar(String name){
this.name = name;
}
public String getName() {
return name;
}
}
public static class Baz implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private final String name = "baz";
public String getName() {
return name;
}
}
public static class Abc implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private final String name = "abx";
private Abc(){}
public String getName() {
return name;
}
}
public static class Xyz implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
@SuppressWarnings("unused")
private final String name = "xyz";
private Xyz(){}
}
public static class Person implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String fname;
private String lname;
public String getFname() {
return fname;
}
public void setFname(String fname) {
this.fname = fname;
}
public String getLname() {
return lname;
}
public void setLname(String lname) {
this.lname = lname;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((fname == null) ? 0 : fname.hashCode());
result = prime * result + ((lname == null) ? 0 : lname.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Person other = (Person) obj;
if (fname == null) {
if (other.fname != null) {
return false;
}
}
else if (!fname.equals(other.fname)) {
return false;
}
if (lname == null) {
if (other.lname != null) {
return false;
}
}
else if (!lname.equals(other.lname)) {
return false;
}
return true;
}
}
protected abstract MessageStore getMessageStore() throws Exception;
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright 2002-2012 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.mongodb.store;
import static org.junit.Assert.assertEquals;
import java.util.Map;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.integration.Message;
import org.springframework.integration.mongodb.rules.MongoDbAvailable;
import org.springframework.integration.store.MessageStore;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
/**
* @author Amol Nayak
* @author Artem Bilan
*
*/
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 Mongo(), "test");
ConfigurableMongoDbMessageStore mongoDbMessageStore = new ConfigurableMongoDbMessageStore(mongoDbFactory);
GenericApplicationContext testApplicationContext = TestUtils.createTestApplicationContext();
testApplicationContext.refresh();
mongoDbMessageStore.setApplicationContext(testApplicationContext);
mongoDbMessageStore.afterPropertiesSet();
return mongoDbMessageStore;
}
/* (non-Javadoc)
* @see org.springframework.integration.mongodb.store.AbstractMongoDbMessageGroupStoreTests#getMessageStore()
*/
@Override
protected MessageStore getMessageStore() throws Exception {
return this.getMessageGroupStore();
}
@Test
@MongoDbAvailable
public void testWithAggregatorWithShutdown() throws Exception {
super.testWithAggregatorWithShutdown("mongo-aggregator-confugurable-config.xml");
}
@Test
@MongoDbAvailable
public void testWithCustomConverter() throws Exception {
this.prepareMongoFactory("testConfigurableMongoDbMessageStore");
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("ConfigurableMongoDbMessageStore-CustomConverter.xml", this.getClass());
context.refresh();
TestGateway gateway = context.getBean(TestGateway.class);
String result = gateway.service("foo");
assertEquals("FOO", result);
}
public static interface TestGateway {
String service(String payload);
}
public static class MessageReadConverter implements Converter<DBObject, Message<?>> {
@Override
@SuppressWarnings("unchecked")
public Message<?> convert(DBObject source) {
return MessageBuilder.withPayload(source.get("payload")).copyHeaders((Map<String,?>) source.get("headers")).build();
}
}
}

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/data/mongo
http://www.springframework.org/schema/data/mongo/spring-mongo.xsd">
<mongo:db-factory dbname="test"/>
<beans:bean id="messageStore" class="org.springframework.integration.mongodb.store.ConfigurableMongoDbMessageStore">
<beans:constructor-arg ref="mongoDbFactory"/>
<beans:constructor-arg>
<mongo:mapping-converter>
<mongo:custom-converters>
<mongo:converter>
<beans:bean class="org.springframework.integration.mongodb.store.ConfigurableMongoDbMessageGroupStoreTests$MessageReadConverter"/>
</mongo:converter>
</mongo:custom-converters>
</mongo:mapping-converter>
</beans:constructor-arg>
<beans:constructor-arg value="testConfigurableMongoDbMessageStore"/>
</beans:bean>
<gateway id="gateway"
service-interface="org.springframework.integration.mongodb.store.ConfigurableMongoDbMessageGroupStoreTests$TestGateway"
default-request-channel="preInputChannel"/>
<header-enricher input-channel="preInputChannel" output-channel="inputChannel">
<header-channels-to-string/>
</header-enricher>
<channel id="inputChannel">
<queue message-store="messageStore"/>
</channel>
<transformer input-channel="inputChannel" expression="payload.toUpperCase()">
<poller fixed-delay="1000"/>
</transformer>
</beans:beans>

View File

@@ -0,0 +1,123 @@
/*
* Copyright 2002-2012 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.mongodb.store;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.integration.Message;
import org.springframework.integration.MessagingException;
import org.springframework.integration.message.AdviceMessage;
import org.springframework.integration.message.ErrorMessage;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.mongodb.rules.MongoDbAvailable;
import org.springframework.integration.store.MessageStore;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import com.mongodb.Mongo;
/**
* @author Amol Nayak
* @author Artem Bilan
*/
public class ConfigurableMongoDbMessageStoreTests extends AbstractMongoDbMessageStoreTests {
@Override
protected MessageStore getMessageStore() throws Exception {
MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(new Mongo(), "test");
ConfigurableMongoDbMessageStore mongoDbMessageStore = new ConfigurableMongoDbMessageStore(mongoDbFactory);
GenericApplicationContext testApplicationContext = TestUtils.createTestApplicationContext();
testApplicationContext.refresh();
mongoDbMessageStore.setApplicationContext(testApplicationContext);
mongoDbMessageStore.afterPropertiesSet();
return mongoDbMessageStore;
}
@Test
@MongoDbAvailable
public void testInt3076MessageAsPayload() throws Exception{
MessageStore store = this.getMessageStore();
Person p = new Person();
p.setFname("John");
p.setLname("Doe");
Message<?> messageToStore = new GenericMessage<Message<?>>(MessageBuilder.withPayload(p).build());
store.addMessage(messageToStore);
Message<?> retrievedMessage = store.getMessage(messageToStore.getHeaders().getId());
assertNotNull(retrievedMessage);
assertTrue(retrievedMessage.getPayload() instanceof GenericMessage);
assertEquals(messageToStore.getPayload(), retrievedMessage.getPayload());
assertEquals(messageToStore.getHeaders(), retrievedMessage.getHeaders());
assertEquals(((Message<?>) messageToStore.getPayload()).getPayload(), p);
assertEquals(messageToStore, retrievedMessage);
}
@Test
@MongoDbAvailable
public void testInt3076AdviceMessage() throws Exception{
MessageStore store = this.getMessageStore();
Person p = new Person();
p.setFname("John");
p.setLname("Doe");
Message<Person> inputMessage = MessageBuilder.withPayload(p).build();
Message<?> messageToStore = new AdviceMessage("foo", inputMessage);
store.addMessage(messageToStore);
Message<?> retrievedMessage = store.getMessage(messageToStore.getHeaders().getId());
assertNotNull(retrievedMessage);
assertTrue(retrievedMessage instanceof AdviceMessage);
assertEquals(messageToStore.getPayload(), retrievedMessage.getPayload());
assertEquals(messageToStore.getHeaders(), retrievedMessage.getHeaders());
assertEquals(inputMessage, ((AdviceMessage) retrievedMessage).getInputMessage());
assertEquals(messageToStore, retrievedMessage);
}
@Test
@MongoDbAvailable
public void testInt3076ErrorMessage() throws Exception{
MessageStore store = this.getMessageStore();
Person p = new Person();
p.setFname("John");
p.setLname("Doe");
Message<Person> failedMessage = MessageBuilder.withPayload(p).build();
MessagingException messagingException;
try {
throw new RuntimeException("intentional");
}
catch (Exception e) {
messagingException = new MessagingException(failedMessage, "intentional MessagingException", e);
}
Message<?> messageToStore = new ErrorMessage(messagingException);
store.addMessage(messageToStore);
Message<?> retrievedMessage = store.getMessage(messageToStore.getHeaders().getId());
assertNotNull(retrievedMessage);
assertTrue(retrievedMessage instanceof ErrorMessage);
assertThat(retrievedMessage.getPayload(), Matchers.instanceOf(MessagingException.class));
assertEquals("intentional MessagingException", ((MessagingException) retrievedMessage.getPayload()).getMessage());
assertEquals(failedMessage, ((MessagingException) retrievedMessage.getPayload()).getFailedMessage());
assertEquals(messageToStore.getHeaders(), retrievedMessage.getHeaders());
}
}

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<beans:bean id="mongoConnectionFactory" class="org.springframework.data.mongodb.core.SimpleMongoDbFactory">
<beans:constructor-arg>
<beans:bean class="com.mongodb.Mongo"/>
</beans:constructor-arg>
<beans:constructor-arg value="test"/>
</beans:bean>
<beans:bean id="messageStore" class="org.springframework.integration.mongodb.store.ConfigurableMongoDbMessageStore">
<beans:constructor-arg ref="mongoConnectionFactory"/>
</beans:bean>
<channel id="output">
<queue/>
</channel>
<delayer id="#{T (org.springframework.integration.mongodb.store.DelayerHandlerRescheduleIntegrationTests).DELAYER_ID}"
input-channel="input"
output-channel="output"
default-delay="10000"
message-store="messageStore"/>
</beans:beans>

View File

@@ -23,11 +23,10 @@ import static org.junit.Assert.fail;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import org.hamcrest.Matchers;
import org.junit.Rule;
import org.junit.Test;
import org.hamcrest.Matchers;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
@@ -56,10 +55,19 @@ public class DelayerHandlerRescheduleIntegrationTests extends MongoDbAvailableTe
@Test
@MongoDbAvailable
public void testWithMongoDbMessageStore() throws Exception {
this.testDelayerHandlerRescheduleWithMongoDbMessageStore("DelayerHandlerRescheduleIntegrationTests-context.xml");
}
@Test
@MongoDbAvailable
public void testWithConfigurableMongoDbMessageStore() throws Exception {
this.testDelayerHandlerRescheduleWithMongoDbMessageStore("DelayerHandlerRescheduleIntegrationConfigurableTests-context.xml");
}
@SuppressWarnings("unchecked")
public void testDelayerHandlerRescheduleWithMongoDbMessageStore() throws Exception {
AbstractApplicationContext context = new ClassPathXmlApplicationContext(
"DelayerHandlerRescheduleIntegrationTests-context.xml", this.getClass());
private void testDelayerHandlerRescheduleWithMongoDbMessageStore(String config) throws Exception {
AbstractApplicationContext context = new ClassPathXmlApplicationContext(config, this.getClass());
MessageChannel input = context.getBean("input", MessageChannel.class);
MessageGroupStore messageStore = context.getBean("messageStore", MessageGroupStore.class);

View File

@@ -15,470 +15,35 @@
*/
package org.springframework.integration.mongodb.store;
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.Iterator;
import java.util.Properties;
import java.util.UUID;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.message.GenericMessage;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.integration.mongodb.rules.MongoDbAvailable;
import org.springframework.integration.mongodb.rules.MongoDbAvailableTests;
import org.springframework.integration.store.MessageGroup;
import org.springframework.integration.store.SimpleMessageGroup;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.store.MessageStore;
import com.mongodb.Mongo;
/**
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
*
*/
public class MongoDbMessageGroupStoreTests extends MongoDbAvailableTests {
public class MongoDbMessageGroupStoreTests extends AbstractMongoDbMessageGroupStoreTests {
@Test
@MongoDbAvailable
public void testNonExistingEmptyMessageGroup() throws Exception{
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
@Override
protected MongoDbMessageStore getMessageGroupStore() throws Exception {
return new MongoDbMessageStore( new SimpleMongoDbFactory(new Mongo(), "test"));
}
MessageGroup messageGroup = store.getMessageGroup(1);
assertNotNull(messageGroup);
assertTrue(messageGroup instanceof SimpleMessageGroup);
assertEquals(0, messageGroup.size());
@Override
protected MessageStore getMessageStore() throws Exception {
return this.getMessageGroupStore();
}
@Test
@MongoDbAvailable
public void testMessageGroupWithAddedMessagePrimitiveGroupId() throws Exception{
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
MessageGroup messageGroup = store.getMessageGroup(1);
Message<?> messageA = new GenericMessage<String>("A");
Message<?> messageB = new GenericMessage<String>("B");
store.addMessageToGroup(1, messageA);
messageGroup = store.addMessageToGroup(1, messageB);
assertEquals(2, messageGroup.size());
Message<?> retrievedMessage = store.getMessage(messageA.getHeaders().getId());
assertNotNull(retrievedMessage);
assertEquals(retrievedMessage.getHeaders().getId(), messageA.getHeaders().getId());
// ensure that 'message_group' header that is only used internally is not propagated
assertNull(retrievedMessage.getHeaders().get("message_group"));
public void testWithAggregatorWithShutdown() throws Exception {
super.testWithAggregatorWithShutdown("mongo-aggregator-config.xml");
}
@Test
@MongoDbAvailable
public void testMessageGroupWithAddedMessageUUIDGroupIdAndUUIDHeader() throws Exception{
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
Object id = UUID.randomUUID();
MessageGroup messageGroup = store.getMessageGroup(id);
UUID uuidA = UUID.randomUUID();
Message<?> messageA = MessageBuilder.withPayload("A").setHeader("foo", uuidA).build();
UUID uuidB = UUID.randomUUID();
Message<?> messageB = MessageBuilder.withPayload("B").setHeader("foo", uuidB).build();
store.addMessageToGroup(id, messageA);
messageGroup = store.addMessageToGroup(id, messageB);
assertEquals(2, messageGroup.size());
Message<?> retrievedMessage = store.getMessage(messageA.getHeaders().getId());
assertNotNull(retrievedMessage);
assertEquals(retrievedMessage.getHeaders().getId(), messageA.getHeaders().getId());
// ensure that 'message_group' header that is only used internally is not propagated
assertNull(retrievedMessage.getHeaders().get("message_group"));
Object fooHeader = retrievedMessage.getHeaders().get("foo");
assertTrue(fooHeader instanceof UUID);
assertEquals(uuidA, fooHeader);
}
@Test
@MongoDbAvailable
public void testCountMessagesInGroup() throws Exception{
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
Message<?> messageA = new GenericMessage<String>("A");
Message<?> messageB = new GenericMessage<String>("B");
store.addMessageToGroup(1, messageA);
store.addMessageToGroup(1, messageB);
assertEquals(2, store.messageGroupSize(1));
}
@Test
@MongoDbAvailable
public void testPollMessages() throws Exception{
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
Message<?> messageA = new GenericMessage<String>("A");
Message<?> messageB = new GenericMessage<String>("B");
store.addMessageToGroup(1, messageA);
store.addMessageToGroup(1, messageB);
assertEquals(2, store.messageGroupSize(1));
Message<?> out = store.pollMessageFromGroup(1);
assertEquals("A", out.getPayload());
assertEquals(1, store.messageGroupSize(1));
out = store.pollMessageFromGroup(1);
assertEquals("B", out.getPayload());
assertEquals(0, store.messageGroupSize(1));
}
@Test
@MongoDbAvailable
public void testSameMessageMultipleGroupsPoll() throws Exception{
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
Message<?> messageA = new GenericMessage<String>("A");
store.addMessageToGroup(1, messageA);
store.addMessageToGroup(2, messageA);
store.addMessageToGroup(3, messageA);
store.addMessageToGroup(4, messageA);
assertEquals(1, store.messageGroupSize(1));
assertEquals(1, store.messageGroupSize(2));
assertEquals(1, store.messageGroupSize(3));
assertEquals(1, store.messageGroupSize(4));
store.pollMessageFromGroup(3);
assertEquals(1, store.messageGroupSize(1));
assertEquals(1, store.messageGroupSize(2));
assertEquals(0, store.messageGroupSize(3));
assertEquals(1, store.messageGroupSize(4));
store.pollMessageFromGroup(4);
assertEquals(1, store.messageGroupSize(1));
assertEquals(1, store.messageGroupSize(2));
assertEquals(0, store.messageGroupSize(3));
assertEquals(0, store.messageGroupSize(4));
store.pollMessageFromGroup(2);
assertEquals(1, store.messageGroupSize(1));
assertEquals(0, store.messageGroupSize(2));
assertEquals(0, store.messageGroupSize(3));
assertEquals(0, store.messageGroupSize(4));
store.pollMessageFromGroup(1);
assertEquals(0, store.messageGroupSize(1));
assertEquals(0, store.messageGroupSize(2));
assertEquals(0, store.messageGroupSize(3));
assertEquals(0, store.messageGroupSize(4));
}
@Test
@MongoDbAvailable
public void testSameMessageMultipleGroupsRemove() throws Exception{
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
Message<?> messageA = new GenericMessage<String>("A");
store.addMessageToGroup(1, messageA);
store.addMessageToGroup(2, messageA);
store.addMessageToGroup(3, messageA);
store.addMessageToGroup(4, messageA);
assertEquals(1, store.messageGroupSize(1));
assertEquals(1, store.messageGroupSize(2));
assertEquals(1, store.messageGroupSize(3));
assertEquals(1, store.messageGroupSize(4));
store.removeMessageFromGroup(3, messageA);
assertEquals(1, store.messageGroupSize(1));
assertEquals(1, store.messageGroupSize(2));
assertEquals(0, store.messageGroupSize(3));
assertEquals(1, store.messageGroupSize(4));
store.removeMessageFromGroup(4, messageA);
assertEquals(1, store.messageGroupSize(1));
assertEquals(1, store.messageGroupSize(2));
assertEquals(0, store.messageGroupSize(3));
assertEquals(0, store.messageGroupSize(4));
store.removeMessageFromGroup(2, messageA);
assertEquals(1, store.messageGroupSize(1));
assertEquals(0, store.messageGroupSize(2));
assertEquals(0, store.messageGroupSize(3));
assertEquals(0, store.messageGroupSize(4));
store.removeMessageFromGroup(1, messageA);
assertEquals(0, store.messageGroupSize(1));
assertEquals(0, store.messageGroupSize(2));
assertEquals(0, store.messageGroupSize(3));
assertEquals(0, store.messageGroupSize(4));
}
@Test
@MongoDbAvailable
public void testMessageGroupUpdatedDateChangesWithEachAddedMessage() throws Exception{
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
MessageGroup messageGroup = store.getMessageGroup(1);
Message<?> message = new GenericMessage<String>("Hello");
messageGroup = store.addMessageToGroup(1, message);
assertEquals(1, messageGroup.size());
long createdTimestamp = messageGroup.getTimestamp();
long updatedTimestamp = messageGroup.getLastModified();
assertEquals(createdTimestamp, updatedTimestamp);
Thread.sleep(1000);
message = new GenericMessage<String>("Hello again");
messageGroup = store.addMessageToGroup(1, message);
createdTimestamp = messageGroup.getTimestamp();
updatedTimestamp = messageGroup.getLastModified();
assertTrue(updatedTimestamp > createdTimestamp);
assertEquals(2, messageGroup.size());
// make sure the store is properly rebuild from MongoDB
store = new MongoDbMessageStore(mongoDbFactory);
messageGroup = store.getMessageGroup(1);
assertEquals(2, messageGroup.size());
}
@Test
@MongoDbAvailable
public void testMessageGroupMarkingMessage() throws Exception{
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
MessageGroup messageGroup = store.getMessageGroup(1);
Message<?> messageA = new GenericMessage<String>("A");
Message<?> messageB = new GenericMessage<String>("B");
store.addMessageToGroup(1, messageA);
messageGroup = store.addMessageToGroup(1, messageB);
assertEquals(2, messageGroup.size());
messageGroup = store.removeMessageFromGroup(1, messageA);
assertEquals(1, messageGroup.size());
// validate that the updates were propagated to Mongo as well
store = new MongoDbMessageStore(mongoDbFactory);
messageGroup = store.getMessageGroup(1);
assertEquals(1, messageGroup.size());
}
@Test
@MongoDbAvailable
public void testRemoveMessageGroup() throws Exception{
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
MessageGroup messageGroup = store.getMessageGroup(1);
Message<?> message = new GenericMessage<String>("Hello");
UUID id = message.getHeaders().getId();
messageGroup = store.addMessageToGroup(1, message);
assertEquals(1, messageGroup.size());
message = store.getMessage(id);
assertNotNull(message);
store.removeMessageGroup(1);
MessageGroup messageGroupA = store.getMessageGroup(1);
assertEquals(0, messageGroupA.size());
assertFalse(messageGroupA.equals(messageGroup));
}
@Test
@MongoDbAvailable
public void testCompleteMessageGroup() throws Exception{
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
MessageGroup messageGroup = store.getMessageGroup(1);
Message<?> message = new GenericMessage<String>("Hello");
store.addMessageToGroup(messageGroup.getGroupId(), message);
store.completeGroup(messageGroup.getGroupId());
messageGroup = store.getMessageGroup(1);
assertTrue(messageGroup.isComplete());
}
@Test
@MongoDbAvailable
public void testLastReleasedSequenceNumber() throws Exception{
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
MessageGroup messageGroup = store.getMessageGroup(1);
Message<?> message = new GenericMessage<String>("Hello");
store.addMessageToGroup(messageGroup.getGroupId(), message);
store.setLastReleasedSequenceNumberForGroup(messageGroup.getGroupId(), 5);
messageGroup = store.getMessageGroup(1);
assertEquals(5, messageGroup.getLastReleasedMessageSequenceNumber());
}
@Test
@MongoDbAvailable
public void testRemoveMessageFromTheGroup() throws Exception{
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
MessageGroup messageGroup = store.getMessageGroup(1);
Message<?> message = new GenericMessage<String>("2");
store.addMessageToGroup(1, new GenericMessage<String>("1"));
store.addMessageToGroup(1, message);
messageGroup = store.addMessageToGroup(1, new GenericMessage<String>("3"));
assertEquals(3, messageGroup.size());
messageGroup = store.removeMessageFromGroup(1, message);
assertEquals(2, messageGroup.size());
}
@Test
@MongoDbAvailable
public void testMultipleMessageStores() throws Exception{
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbMessageStore store1 = new MongoDbMessageStore(mongoDbFactory);
MongoDbMessageStore store2 = new MongoDbMessageStore(mongoDbFactory);
Message<?> message = new GenericMessage<String>("1");
store1.addMessageToGroup(1, message);
store2.addMessageToGroup(1, new GenericMessage<String>("2"));
store1.addMessageToGroup(1, new GenericMessage<String>("3"));
MongoDbMessageStore store3 = new MongoDbMessageStore(mongoDbFactory);
MessageGroup messageGroup = store3.getMessageGroup(1);
assertEquals(3, messageGroup.size());
store3.removeMessageFromGroup(1, message);
messageGroup = store2.getMessageGroup(1);
assertEquals(2, messageGroup.size());
}
@Test
@MongoDbAvailable
public void testMessageGroupIterator() throws Exception{
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbMessageStore store1 = new MongoDbMessageStore(mongoDbFactory);
MongoDbMessageStore store2 = new MongoDbMessageStore(mongoDbFactory);
Message<?> message = new GenericMessage<String>("1");
store2.addMessageToGroup(1, message);
store1.addMessageToGroup(2, new GenericMessage<String>("2"));
store2.addMessageToGroup(3, new GenericMessage<String>("3"));
MongoDbMessageStore store3 = new MongoDbMessageStore(mongoDbFactory);
Iterator<MessageGroup> iterator = store3.iterator();
int counter = 0;
while (iterator.hasNext()) {
iterator.next();
counter++;
}
assertEquals(3, counter);
store2.removeMessageFromGroup(1, message);
iterator = store3.iterator();
counter = 0;
while (iterator.hasNext()) {
iterator.next();
counter++;
}
assertEquals(2, counter);
}
// @Test
// @MongoDbAvailable
// public void testConcurrentModifications() throws Exception{
// MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
// final MongoDbMessageStore store1 = new MongoDbMessageStore(mongoDbFactory);
// final MongoDbMessageStore store2 = new MongoDbMessageStore(mongoDbFactory);
//
// final Message<?> message = new GenericMessage<String>("1");
//
// ExecutorService executor = null;
//
// final List<Object> failures = new ArrayList<Object>();
//
// for (int i = 0; i < 100; i++) {
// executor = Executors.newCachedThreadPool();
//
// executor.execute(new Runnable() {
// public void run() {
// MessageGroup group = store1.addMessageToGroup(1, message);
// if (group.getUnmarked().size() != 1){
// failures.add("ADD");
// throw new AssertionFailedError("Failed on ADD");
// }
// }
// });
// executor.execute(new Runnable() {
// public void run() {
// MessageGroup group = store2.removeMessageFromGroup(1, message);
// if (group.getUnmarked().size() != 0){
// failures.add("REMOVE");
// throw new AssertionFailedError("Failed on Remove");
// }
// }
// });
//
// executor.shutdown();
// executor.awaitTermination(10, TimeUnit.SECONDS);
// store2.removeMessageFromGroup(1, message); // ensures that if ADD thread executed after REMOVE, the store is empty for the next cycle
// }
// assertTrue(failures.size() == 0);
// }
@Test
@MongoDbAvailable
public void testWithAggregatorWithShutdown() throws Exception{
this.prepareMongoFactory(); // for this test it only ensures that DB was flushed before test
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("mongo-aggregator-config.xml", this.getClass());
MessageChannel input = context.getBean("inputChannel", MessageChannel.class);
QueueChannel output = context.getBean("outputChannel", QueueChannel.class);
Message<?> m1 = MessageBuilder.withPayload("1").setSequenceNumber(1).setSequenceSize(3).setCorrelationId(1).build();
Message<?> m2 = MessageBuilder.withPayload("2").setSequenceNumber(2).setSequenceSize(3).setCorrelationId(1).build();
input.send(m1);
assertNull(output.receive(1000));
input.send(m2);
assertNull(output.receive(1000));
context.close();
context = new ClassPathXmlApplicationContext("mongo-aggregator-config.xml", this.getClass());
input = context.getBean("inputChannel", MessageChannel.class);
output = context.getBean("outputChannel", QueueChannel.class);
Message<?> m3 = MessageBuilder.withPayload("3").setSequenceNumber(3).setSequenceSize(3).setCorrelationId(1).build();
input.send(m3);
assertNotNull(output.receive(2000));
}
@Test
@MongoDbAvailable
public void testWithMessageHistory() throws Exception{
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
store.getMessageGroup(1);
Message<?> message = new GenericMessage<String>("Hello");
DirectChannel fooChannel = new DirectChannel();
fooChannel.setBeanName("fooChannel");
DirectChannel barChannel = new DirectChannel();
barChannel.setBeanName("barChannel");
message = MessageHistory.write(message, fooChannel);
message = MessageHistory.write(message, barChannel);
store.addMessageToGroup(1, message);
message = store.getMessageGroup(1).getMessages().iterator().next();
MessageHistory messageHistory = MessageHistory.read(message);
assertNotNull(messageHistory);
assertEquals(2, messageHistory.size());
Properties fooChannelHistory = messageHistory.get(0);
assertEquals("fooChannel", fooChannelHistory.get("name"));
assertEquals("channel", fooChannelHistory.get("type"));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2013 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.
@@ -16,7 +16,12 @@
package org.springframework.integration.mongodb.store;
import static org.junit.Assert.assertEquals;
import java.io.Serializable;
import org.junit.Test;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.integration.Message;
@@ -28,18 +33,17 @@ import org.springframework.integration.transformer.ClaimCheckOutTransformer;
import com.mongodb.Mongo;
import static org.junit.Assert.assertEquals;
/**
* @author Mark Fisher
* @author Artem Bilan
*/
public class MongoDbMessageStoreClaimCheckIntegrationTests extends MongoDbAvailableTests{
public class MongoDbMessageStoreClaimCheckIntegrationTests extends MongoDbAvailableTests {
@Test
@Test
@MongoDbAvailable
public void stringPayload() throws Exception {
MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(new Mongo(), "test");
MongoDbMessageStore messageStore = new MongoDbMessageStore(mongoDbFactory);
MongoDbMessageStore messageStore = new MongoDbMessageStore(mongoDbFactory);
ClaimCheckInTransformer checkin = new ClaimCheckInTransformer(messageStore);
ClaimCheckOutTransformer checkout = new ClaimCheckOutTransformer(messageStore);
Message<?> originalMessage = MessageBuilder.withPayload("test1").build();
@@ -49,12 +53,9 @@ public class MongoDbMessageStoreClaimCheckIntegrationTests extends MongoDbAvaila
assertEquals(claimCheckMessage.getPayload(), checkedOutMessage.getHeaders().getId());
assertEquals(originalMessage.getPayload(), checkedOutMessage.getPayload());
assertEquals(originalMessage, checkedOutMessage);
//System.out.println("original: " + originalMessage);
//System.out.println("claimcheck: " + claimCheckMessage);
//System.out.println("checkedout: " + checkedOutMessage);
}
@Test
@Test
@MongoDbAvailable
public void objectPayload() throws Exception {
MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(new Mongo(), "test");
@@ -74,14 +75,52 @@ public class MongoDbMessageStoreClaimCheckIntegrationTests extends MongoDbAvaila
assertEquals(originalMessage, checkedOutMessage);
}
@Test
@MongoDbAvailable
public void stringPayloadConfigurable() throws Exception {
MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(new Mongo(), "test");
ConfigurableMongoDbMessageStore messageStore = new ConfigurableMongoDbMessageStore(mongoDbFactory);
messageStore.afterPropertiesSet();
ClaimCheckInTransformer checkin = new ClaimCheckInTransformer(messageStore);
ClaimCheckOutTransformer checkout = new ClaimCheckOutTransformer(messageStore);
Message<?> originalMessage = MessageBuilder.withPayload("test1").build();
Message<?> claimCheckMessage = checkin.transform(originalMessage);
assertEquals(originalMessage.getHeaders().getId(), claimCheckMessage.getPayload());
Message<?> checkedOutMessage = checkout.transform(claimCheckMessage);
assertEquals(claimCheckMessage.getPayload(), checkedOutMessage.getHeaders().getId());
assertEquals(originalMessage.getPayload(), checkedOutMessage.getPayload());
assertEquals(originalMessage, checkedOutMessage);
}
@SuppressWarnings("unused")
private static class Beverage {
@Test
@MongoDbAvailable
public void objectPayloadConfigurable() throws Exception {
MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(new Mongo(), "test");
ConfigurableMongoDbMessageStore messageStore = new ConfigurableMongoDbMessageStore(mongoDbFactory);
messageStore.afterPropertiesSet();
ClaimCheckInTransformer checkin = new ClaimCheckInTransformer(messageStore);
ClaimCheckOutTransformer checkout = new ClaimCheckOutTransformer(messageStore);
Beverage payload = new Beverage();
payload.setName("latte");
payload.setShots(3);
payload.setIced(false);
Message<?> originalMessage = MessageBuilder.withPayload(payload).build();
Message<?> claimCheckMessage = checkin.transform(originalMessage);
assertEquals(originalMessage.getHeaders().getId(), claimCheckMessage.getPayload());
Message<?> checkedOutMessage = checkout.transform(claimCheckMessage);
assertEquals(originalMessage.getPayload(), checkedOutMessage.getPayload());
assertEquals(claimCheckMessage.getPayload(), checkedOutMessage.getHeaders().getId());
assertEquals(originalMessage, checkedOutMessage);
}
@SuppressWarnings("serial")
private static class Beverage implements Serializable {
private String name;
private int shots;
private int shots;
private boolean iced;
@SuppressWarnings("unused")
public String getName() {
return name;
}
@@ -90,6 +129,7 @@ public class MongoDbMessageStoreClaimCheckIntegrationTests extends MongoDbAvaila
this.name = name;
}
@SuppressWarnings("unused")
public int getShots() {
return shots;
}
@@ -98,6 +138,7 @@ public class MongoDbMessageStoreClaimCheckIntegrationTests extends MongoDbAvaila
this.shots = shots;
}
@SuppressWarnings("unused")
public boolean isIced() {
return iced;
}
@@ -144,6 +185,7 @@ public class MongoDbMessageStoreClaimCheckIntegrationTests extends MongoDbAvaila
}
return true;
}
}
}

View File

@@ -13,246 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.mongodb.store;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.Properties;
import java.util.UUID;
import org.junit.Test;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.integration.Message;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.mongodb.rules.MongoDbAvailable;
import org.springframework.integration.mongodb.rules.MongoDbAvailableTests;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.store.MessageStore;
import com.mongodb.Mongo;
/**
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Artem Bilan
*
*/
public class MongoDbMessageStoreTests extends MongoDbAvailableTests{
public class MongoDbMessageStoreTests extends AbstractMongoDbMessageStoreTests {
@Test
@MongoDbAvailable
public void addGetWithStringPayload() throws Exception {
MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(new Mongo(), "test");
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
Message<?> messageToStore = MessageBuilder.withPayload("Hello").build();
store.addMessage(messageToStore);
Message<?> retrievedMessage = store.getMessage(messageToStore.getHeaders().getId());
assertNotNull(retrievedMessage);
assertEquals(messageToStore.getPayload(), retrievedMessage.getPayload());
assertEquals(messageToStore.getHeaders(), retrievedMessage.getHeaders());
assertEquals(messageToStore, retrievedMessage);
}
@Test
@MongoDbAvailable
public void addThenRemoveWithStringPayload() throws Exception {
MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(new Mongo(), "test");
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
Message<?> messageToStore = MessageBuilder.withPayload("Hello").build();
store.addMessage(messageToStore);
Message<?> retrievedMessage = store.getMessage(messageToStore.getHeaders().getId());
assertNotNull(retrievedMessage);
store.removeMessage(retrievedMessage.getHeaders().getId());
retrievedMessage = store.getMessage(messageToStore.getHeaders().getId());
assertNull(retrievedMessage);
}
@Test
@MongoDbAvailable
public void addGetWithObjectDefaultConstructorPayload() throws Exception {
MongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(new Mongo(), "test");
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
Person p = new Person();
p.setFname("John");
p.setLname("Doe");
Message<?> messageToStore = MessageBuilder.withPayload(p).build();
store.addMessage(messageToStore);
Message<?> retrievedMessage = store.getMessage(messageToStore.getHeaders().getId());
assertNotNull(retrievedMessage);
assertEquals(messageToStore.getPayload(), retrievedMessage.getPayload());
assertEquals(messageToStore.getHeaders(), retrievedMessage.getHeaders());
assertEquals(messageToStore, retrievedMessage);
}
@Test
@MongoDbAvailable
public void testWithMessageHistory() throws Exception{
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
Foo foo = new Foo();
foo.setName("foo");
Message<?> message = MessageBuilder.withPayload(foo).
setHeader("foo", foo).
setHeader("bar", new Bar("bar")).
setHeader("baz", new Baz()).
setHeader("abc", new Abc()).
setHeader("xyz", new Xyz()).
build();
DirectChannel fooChannel = new DirectChannel();
fooChannel.setBeanName("fooChannel");
DirectChannel barChannel = new DirectChannel();
barChannel.setBeanName("barChannel");
message = MessageHistory.write(message, fooChannel);
message = MessageHistory.write(message, barChannel);
store.addMessage(message);
message = store.getMessage(message.getHeaders().getId());
assertTrue(message.getHeaders().get("foo") instanceof Foo);
assertTrue(message.getHeaders().get("bar") instanceof Bar);
assertTrue(message.getHeaders().get("baz") instanceof Baz);
assertTrue(message.getHeaders().get("abc") instanceof Abc);
assertTrue(message.getHeaders().get("xyz") instanceof Xyz);
MessageHistory messageHistory = MessageHistory.read(message);
assertNotNull(messageHistory);
assertEquals(2, messageHistory.size());
Properties fooChannelHistory = messageHistory.get(0);
assertEquals("fooChannel", fooChannelHistory.get("name"));
assertEquals("channel", fooChannelHistory.get("type"));
}
@Test
@MongoDbAvailable
public void testInt3153SequenceDetails() throws Exception{
MongoDbFactory mongoDbFactory = this.prepareMongoFactory();
MongoDbMessageStore store = new MongoDbMessageStore(mongoDbFactory);
Message<?> messageToStore = MessageBuilder.withPayload("test")
.pushSequenceDetails(UUID.randomUUID(), 1, 1)
.pushSequenceDetails(UUID.randomUUID(), 1, 1)
.build();
store.addMessage(messageToStore);
Message<?> retrievedMessage = store.getMessage(messageToStore.getHeaders().getId());
assertNotNull(retrievedMessage);
assertEquals(messageToStore.getPayload(), retrievedMessage.getPayload());
assertEquals(messageToStore.getHeaders(), retrievedMessage.getHeaders());
assertEquals(messageToStore, retrievedMessage);
}
public static class Foo{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static class Bar{
private String name;
public Bar(String name){
this.name = name;
}
public String getName() {
return name;
}
}
public static class Baz{
private String name = "baz";
public String getName() {
return name;
}
}
public static class Abc{
private String name = "abx";
private Abc(){}
public String getName() {
return name;
}
}
public static class Xyz{
@SuppressWarnings("unused")
private String name = "xyz";
private Xyz(){}
}
public static class Person {
private String fname;
private String lname;
public String getFname() {
return fname;
}
public void setFname(String fname) {
this.fname = fname;
}
public String getLname() {
return lname;
}
public void setLname(String lname) {
this.lname = lname;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((fname == null) ? 0 : fname.hashCode());
result = prime * result + ((lname == null) ? 0 : lname.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Person other = (Person) obj;
if (fname == null) {
if (other.fname != null) {
return false;
}
}
else if (!fname.equals(other.fname)) {
return false;
}
if (lname == null) {
if (other.lname != null) {
return false;
}
}
else if (!lname.equals(other.lname)) {
return false;
}
return true;
}
@Override
protected MessageStore getMessageStore() throws Exception {
return new MongoDbMessageStore(new SimpleMongoDbFactory(new Mongo(), "test"));
}
}

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<int:aggregator input-channel="inputChannel" output-channel="outputChannel" message-store="mongoStore"/>
<int:channel id="outputChannel">
<int:queue/>
</int:channel>
<bean id="mongoStore" class="org.springframework.integration.mongodb.store.ConfigurableMongoDbMessageStore">
<constructor-arg ref="mongoConnectionFactory"/>
</bean>
<bean id="mongoConnectionFactory" class="org.springframework.data.mongodb.core.SimpleMongoDbFactory">
<constructor-arg>
<bean class="com.mongodb.Mongo"/>
</constructor-arg>
<constructor-arg value="test"/>
</bean>
</beans>

View File

@@ -111,6 +111,33 @@
and an <emphasis>Aggregator</emphasis>. As you can see it is a simple bean configuration, and it expects a
<classname>MongoDbFactory</classname> as a constructor argument.
</para>
<important>
<para>
<classname>MongoDbMessageStore</classname> uses custom <classname>MappingMongoConverter</classname> implementation
on background to store <interfacename>Message</interfacename>s to MongoDB documents and there is some limitations
for properties (<code>payload</code> and <code>headers</code> values) of provided <interfacename>Message</interfacename>.
For example <classname>ErrorMessage</classname> can't be converted to the MongoDB document, because it has an
<classname>Exception</classname> property, which is recursive by nature. And there is no ability to configure
some custom converters for complex domain <code>payload</code>s or <code>headers</code> values.
To achieve these capabilities, the separate MongoDB <interfacename>MessageStore</interfacename> implementation has been
introduced; see next paragraph.
</para>
</important>
<para>
<emphasis>Spring Integration 3.0</emphasis> introduced <classname>ConfigurableMongoDbMessageStore</classname> -
<interfacename>MessageStore</interfacename> and <interfacename>MessageGroupStore</interfacename> implementation.
This class can apply as one of constructor argument <classname>MongoTemplate</classname>, with which you can provide
some custom <classname>WriteConcern</classname>, for example. Another constructor requires
<classname>MappingMongoConverter</classname>, alongside with <interfacename>MongoDbFactory</interfacename>,
which allows to provide some custom conversions for <interfacename>Message</interfacename>s and their properties.
Note, by default <classname>ConfigurableMongoDbMessageStore</classname> uses standard Java serialization
to write/read <interfacename>Message</interfacename>s to/from MongoDB and relies on default values of other
properties from <classname>MongoTemplate</classname>, which is built from provided
<interfacename>MongoDbFactory</interfacename> and <classname>MappingMongoConverter</classname>.
The default name for collection of <classname>ConfigurableMongoDbMessageStore</classname> is
<code>configurableStoreMessages</code>. It is recommended to use this implementation for robust and flexible solutions.
The <classname>MongoDbMessageStore</classname> remains for backward compatibility and may be removed in future releases.
</para>
</section>
<section id="mongodb-inbound-channel-adapter">
@@ -275,4 +302,4 @@
</para>
</section>
</chapter>
</chapter>

View File

@@ -177,6 +177,15 @@
a message. See <xref linkend="header-enricher"/> for more information.
</para>
</section>
<section id="3.0-configurable-mongo-MS">
<title>MongoDB support: New ConfigurableMongoDbMessageStore</title>
<para>
To provide more robust and flexible implementation of <interfacename>MessageStore</interfacename>
for MongoDB support has been introduced new <classname>ConfigurableMongoDbMessageStore</classname>
component. It doesn't have backward compatibility, but it is recommended to use it for new applications.
See <xref linkend="mongodb"/> for more information.
</para>
</section>
</section>
<section id="3.0-general">