/* * Copyright 2014-2017 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 org.springframework.data.domain.Sort; import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.index.Index; import org.springframework.data.mongodb.core.query.Query; import org.springframework.integration.IntegrationMessageHeaderAccessor; import org.springframework.integration.store.MessageGroup; import org.springframework.integration.store.PriorityCapableChannelMessageStore; import org.springframework.messaging.Message; import org.springframework.util.Assert; /** * MongoDB {@link PriorityCapableChannelMessageStore} implementation. * This message store shall be used for message channels only. * * <p>Provide the {@link #priorityEnabled} option to allow to poll messages via {@code priority} manner. * * <p>As a priority document field the {@link org.springframework.integration.IntegrationMessageHeaderAccessor#PRIORITY} * message header is used. * * <p>The same collection can be used for {@code org.springframework.integration.channel.QueueChannel}s and * {@code org.springframework.integration.channel.PriorityChannel}s, but the different instances of * {@link MongoDbChannelMessageStore} should be used for those cases, and the last one with * {@code priorityEnabled = true} option. * * @author Artem Bilan * @since 4.0 */ public class MongoDbChannelMessageStore extends AbstractConfigurableMongoDbMessageStore implements PriorityCapableChannelMessageStore { public final static String DEFAULT_COLLECTION_NAME = "channelMessages"; private volatile boolean priorityEnabled; public MongoDbChannelMessageStore(MongoTemplate mongoTemplate) { this(mongoTemplate, DEFAULT_COLLECTION_NAME); } public MongoDbChannelMessageStore(MongoTemplate mongoTemplate, String collectionName) { super(mongoTemplate, collectionName); } public MongoDbChannelMessageStore(MongoDbFactory mongoDbFactory) { this(mongoDbFactory, null, DEFAULT_COLLECTION_NAME); } public MongoDbChannelMessageStore(MongoDbFactory mongoDbFactory, MappingMongoConverter mappingMongoConverter) { this(mongoDbFactory, mappingMongoConverter, DEFAULT_COLLECTION_NAME); } public MongoDbChannelMessageStore(MongoDbFactory mongoDbFactory, String collectionName) { this(mongoDbFactory, null, collectionName); } public MongoDbChannelMessageStore(MongoDbFactory mongoDbFactory, MappingMongoConverter mappingMongoConverter, String collectionName) { super(mongoDbFactory, mappingMongoConverter, collectionName); } public void setPriorityEnabled(boolean priorityEnabled) { this.priorityEnabled = priorityEnabled; } @Override public boolean isPriorityEnabled() { return this.priorityEnabled; } @Override public void afterPropertiesSet() throws Exception { super.afterPropertiesSet(); this.mongoTemplate.indexOps(this.collectionName) .ensureIndex(new Index(MessageDocumentFields.GROUP_ID, Sort.Direction.ASC) .on(MessageDocumentFields.PRIORITY, Sort.Direction.DESC) .on(MessageDocumentFields.LAST_MODIFIED_TIME, Sort.Direction.ASC) .on(MessageDocumentFields.SEQUENCE, Sort.Direction.ASC)); } @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 document = new MessageDocument(message); document.setGroupId(groupId); document.setCreatedTime(System.currentTimeMillis()); document.setLastModifiedTime(System.currentTimeMillis()); if (this.priorityEnabled) { document.setPriority(message.getHeaders().get(IntegrationMessageHeaderAccessor.PRIORITY, Integer.class)); } document.setSequence(this.getNextId()); this.addMessageDocument(document); return this.getMessageGroup(groupId); } /** * Not fully used. Only wraps the provided group id. */ @Override public MessageGroup getMessageGroup(Object groupId) { return getMessageGroupFactory().create(groupId); } @Override public Message<?> pollMessageFromGroup(Object groupId) { Assert.notNull(groupId, "'groupId' must not be null"); Sort sort = Sort.by(MessageDocumentFields.LAST_MODIFIED_TIME, MessageDocumentFields.SEQUENCE); if (this.priorityEnabled) { sort = Sort.by(Sort.Direction.DESC, MessageDocumentFields.PRIORITY).and(sort); } Query query = groupIdQuery(groupId).with(sort); MessageDocument document = this.mongoTemplate.findAndRemove(query, MessageDocument.class, this.collectionName); Message<?> message = null; if (document != null) { message = document.getMessage(); } return message; } }