/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.store;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.UUID;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.messaging.Message;
import org.springframework.util.Assert;
/**
* Base class for implementations of Key/Value style {@link MessageGroupStore} and {@link MessageStore}
*
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
* @since 2.1
*/
public abstract class AbstractKeyValueMessageStore extends AbstractMessageGroupStore implements MessageStore {
protected static final String MESSAGE_KEY_PREFIX = "MESSAGE_";
protected static final String MESSAGE_GROUP_KEY_PREFIX = "MESSAGE_GROUP_";
/**
* Represents the time when the message has been added to the store.
* @deprecated since 5.0. This constant isn't used any more.
*/
@Deprecated
protected static final String CREATED_DATE = "CREATED_DATE";
// MessageStore methods
@Override
public Message<?> getMessage(UUID messageId) {
Assert.notNull(messageId, "'messageId' must not be null");
Object object = doRetrieve(MESSAGE_KEY_PREFIX + messageId);
if (object != null) {
return extractMessage(object);
}
else {
return null;
}
}
private Message<?> extractMessage(Object object) {
if (object instanceof MessageHolder) {
return ((MessageHolder) object).getMessage();
}
else if (object instanceof Message) {
return (Message<?>) object;
}
else {
throw new IllegalArgumentException(
"Object of class [" + object.getClass().getName() +
"] must be an instance of [org.springframework.integration.store.MessageHolder].");
}
}
@Override
public MessageMetadata getMessageMetadata(UUID messageId) {
Assert.notNull(messageId, "'messageId' must not be null");
Object object = doRetrieve(MESSAGE_KEY_PREFIX + messageId);
if (object != null) {
extractMessage(object);
if (object instanceof MessageHolder) {
return ((MessageHolder) object).getMessageMetadata();
}
}
return null;
}
@Override
@SuppressWarnings("unchecked")
public <T> Message<T> addMessage(Message<T> message) {
Assert.notNull(message, "'message' must not be null");
UUID messageId = message.getHeaders().getId();
doStoreIfAbsent(MESSAGE_KEY_PREFIX + messageId, new MessageHolder(message));
return (Message<T>) getMessage(messageId);
}
@Override
public Message<?> removeMessage(UUID id) {
Assert.notNull(id, "'id' must not be null");
Object object = doRemove(MESSAGE_KEY_PREFIX + id);
if (object != null) {
return extractMessage(object);
}
else {
return null;
}
}
@Override
@ManagedAttribute
public long getMessageCount() {
Collection<?> messageIds = doListKeys(MESSAGE_KEY_PREFIX + "*");
return (messageIds != null) ? messageIds.size() : 0;
}
// MessageGroupStore methods
/**
* Will create a new instance of SimpleMessageGroup if necessary.
*/
@Override
public MessageGroup getMessageGroup(Object groupId) {
MessageGroupMetadata metadata = getGroupMetadata(groupId);
if (metadata != null) {
MessageGroup messageGroup = getMessageGroupFactory()
.create(this, groupId, metadata.getTimestamp(), metadata.isComplete());
messageGroup.setLastModified(metadata.getLastModified());
messageGroup.setLastReleasedMessageSequenceNumber(metadata.getLastReleasedMessageSequenceNumber());
return messageGroup;
}
else {
return new SimpleMessageGroup(groupId);
}
}
@Override
public MessageGroupMetadata getGroupMetadata(Object groupId) {
Assert.notNull(groupId, "'groupId' must not be null");
Object mgm = this.doRetrieve(MESSAGE_GROUP_KEY_PREFIX + groupId);
if (mgm != null) {
Assert.isInstanceOf(MessageGroupMetadata.class, mgm);
return (MessageGroupMetadata) mgm;
}
return null;
}
@Override
public void addMessagesToGroup(Object groupId, Message<?>... messages) {
Assert.notNull(groupId, "'groupId' must not be null");
Assert.notNull(messages, "'messages' must not be null");
MessageGroupMetadata metadata = getGroupMetadata(groupId);
SimpleMessageGroup group = null;
if (metadata == null) {
group = new SimpleMessageGroup(groupId);
}
for (Message<?> message : messages) {
addMessage(message);
if (metadata != null) {
metadata.add(message.getHeaders().getId());
}
else {
group.add(message);
}
}
if (group != null) {
metadata = new MessageGroupMetadata(group);
// When the group is new reuse "create time" as a "last modified"
metadata.setLastModified(group.getTimestamp());
}
else {
metadata.setLastModified(System.currentTimeMillis());
}
// store MessageGroupMetadata built from enriched MG
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, metadata);
}
@Override
public void removeMessagesFromGroup(Object groupId, Collection<Message<?>> messages) {
Assert.notNull(groupId, "'groupId' must not be null");
Assert.notNull(messages, "'messages' must not be null");
Object mgm = doRetrieve(MESSAGE_GROUP_KEY_PREFIX + groupId);
if (mgm != null) {
Assert.isInstanceOf(MessageGroupMetadata.class, mgm);
MessageGroupMetadata messageGroupMetadata = (MessageGroupMetadata) mgm;
for (Message<?> messageToRemove : messages) {
UUID messageId = messageToRemove.getHeaders().getId();
messageGroupMetadata.remove(messageId);
doRemove(MESSAGE_KEY_PREFIX + messageId);
}
messageGroupMetadata.setLastModified(System.currentTimeMillis());
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, messageGroupMetadata);
}
}
@Override
public void completeGroup(Object groupId) {
Assert.notNull(groupId, "'groupId' must not be null");
MessageGroupMetadata metadata = getGroupMetadata(groupId);
if (metadata != null) {
metadata.complete();
metadata.setLastModified(System.currentTimeMillis());
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, metadata);
}
}
/**
* Remove the MessageGroup with the provided group ID.
*/
@Override
public void removeMessageGroup(Object groupId) {
Assert.notNull(groupId, "'groupId' must not be null");
Object mgm = doRemove(MESSAGE_GROUP_KEY_PREFIX + groupId);
if (mgm != null) {
Assert.isInstanceOf(MessageGroupMetadata.class, mgm);
MessageGroupMetadata messageGroupMetadata = (MessageGroupMetadata) mgm;
Iterator<UUID> messageIds = messageGroupMetadata.messageIdIterator();
while (messageIds.hasNext()) {
removeMessage(messageIds.next());
}
}
}
@Override
public void setLastReleasedSequenceNumberForGroup(Object groupId, int sequenceNumber) {
Assert.notNull(groupId, "'groupId' must not be null");
MessageGroupMetadata metadata = getGroupMetadata(groupId);
if (metadata == null) {
SimpleMessageGroup messageGroup = new SimpleMessageGroup(groupId);
metadata = new MessageGroupMetadata(messageGroup);
}
metadata.setLastReleasedMessageSequenceNumber(sequenceNumber);
metadata.setLastModified(System.currentTimeMillis());
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, metadata);
}
@Override
public Message<?> pollMessageFromGroup(Object groupId) {
MessageGroupMetadata groupMetadata = getGroupMetadata(groupId);
if (groupMetadata != null) {
UUID firstId = groupMetadata.firstId();
if (firstId != null) {
groupMetadata.remove(firstId);
groupMetadata.setLastModified(System.currentTimeMillis());
doStore(MESSAGE_GROUP_KEY_PREFIX + groupId, groupMetadata);
return removeMessage(firstId);
}
}
return null;
}
@Override
public Message<?> getOneMessageFromGroup(Object groupId) {
MessageGroupMetadata groupMetadata = getGroupMetadata(groupId);
if (groupMetadata != null) {
UUID messageId = groupMetadata.firstId();
if (messageId != null) {
return getMessage(messageId);
}
}
return null;
}
@Override
public Collection<Message<?>> getMessagesForGroup(Object groupId) {
MessageGroupMetadata groupMetadata = getGroupMetadata(groupId);
ArrayList<Message<?>> messages = new ArrayList<Message<?>>();
if (groupMetadata != null) {
Iterator<UUID> messageIds = groupMetadata.messageIdIterator();
while (messageIds.hasNext()) {
messages.add(getMessage(messageIds.next()));
}
}
return messages;
}
@Override
@SuppressWarnings("unchecked")
public Iterator<MessageGroup> iterator() {
final Iterator<?> idIterator = normalizeKeys(
(Collection<String>) doListKeys(MESSAGE_GROUP_KEY_PREFIX + "*"))
.iterator();
return new MessageGroupIterator(idIterator);
}
private Collection<String> normalizeKeys(Collection<String> keys) {
Set<String> normalizedKeys = new HashSet<String>();
for (Object key : keys) {
String strKey = (String) key;
if (strKey.startsWith(MESSAGE_GROUP_KEY_PREFIX)) {
strKey = strKey.replace(MESSAGE_GROUP_KEY_PREFIX, "");
}
else if (strKey.startsWith(MESSAGE_KEY_PREFIX)) {
strKey = strKey.replace(MESSAGE_KEY_PREFIX, "");
}
normalizedKeys.add(strKey);
}
return normalizedKeys;
}
@Override
public int messageGroupSize(Object groupId) {
MessageGroupMetadata mgm = getGroupMetadata(groupId);
if (mgm != null) {
return mgm.size();
}
else {
return 0;
}
}
protected abstract Object doRetrieve(Object id);
protected abstract void doStore(Object id, Object objectToStore);
protected abstract void doStoreIfAbsent(Object id, Object objectToStore);
protected abstract Object doRemove(Object id);
protected abstract Collection<?> doListKeys(String keyPattern);
private final class MessageGroupIterator implements Iterator<MessageGroup> {
private final Iterator<?> idIterator;
MessageGroupIterator(Iterator<?> idIterator) {
this.idIterator = idIterator;
}
@Override
public boolean hasNext() {
return this.idIterator.hasNext();
}
@Override
public MessageGroup next() {
Object messageGroupId = this.idIterator.next();
return getMessageGroup(messageGroupId);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}