/* * 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.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; import org.springframework.integration.IntegrationMessageHeaderAccessor; import org.springframework.messaging.Message; import org.springframework.util.Assert; /** * Represents a mutable group of correlated messages that is bound to a certain {@link MessageStore} and group id. * The group will grow during its lifetime, when messages are <code>add</code>ed to it. * This MessageGroup is thread safe. * * @author Iwein Fuld * @author Oleg Zhurakousky * @author Dave Syer * @author Gary Russell * @author Artem Bilan * * @since 2.0 */ public class SimpleMessageGroup implements MessageGroup { private final Object groupId; private final Collection<Message<?>> messages; private final Set<Integer> sequences = new HashSet<>(); private final long timestamp; private volatile int lastReleasedMessageSequence; private volatile long lastModified; private volatile boolean complete; public SimpleMessageGroup(Object groupId) { this(Collections.<Message<?>>emptyList(), groupId); } public SimpleMessageGroup(Collection<? extends Message<?>> messages, Object groupId) { this(messages, groupId, System.currentTimeMillis(), false); } public SimpleMessageGroup(MessageGroup messageGroup) { this(messageGroup.getMessages(), messageGroup.getGroupId(), messageGroup.getTimestamp(), messageGroup.isComplete()); } public SimpleMessageGroup(Collection<? extends Message<?>> messages, Object groupId, long timestamp, boolean complete) { this(new LinkedHashSet<Message<?>>(), messages, groupId, timestamp, complete, false); } protected SimpleMessageGroup(Collection<Message<?>> internalStore, Collection<? extends Message<?>> messages, Object groupId, long timestamp, boolean complete, boolean storePreLoaded) { Assert.notNull(internalStore, "'internalStore' must not be null"); this.messages = internalStore; this.groupId = groupId; this.timestamp = timestamp; this.complete = complete; if (!storePreLoaded) { Assert.notNull(messages, "'messages' must not be null"); for (Message<?> message : messages) { if (message != null) { //see INT-2666 addMessage(message); } } } } @Override public long getTimestamp() { return this.timestamp; } @Override public void setLastModified(long lastModified) { this.lastModified = lastModified; } @Override public long getLastModified() { return this.lastModified; } @Override public boolean canAdd(Message<?> message) { return true; } @Override public void add(Message<?> messageToAdd) { addMessage(messageToAdd); } @Override public boolean remove(Message<?> message) { this.sequences.remove(message.getHeaders().get(IntegrationMessageHeaderAccessor.SEQUENCE_NUMBER)); return this.messages.remove(message); } @Override public int getLastReleasedMessageSequenceNumber() { return this.lastReleasedMessageSequence; } private boolean addMessage(Message<?> message) { Integer sequence = message.getHeaders().get(IntegrationMessageHeaderAccessor.SEQUENCE_NUMBER, Integer.class); this.sequences.add(sequence != null ? sequence : 0); return this.messages.add(message); } @Override public Collection<Message<?>> getMessages() { return Collections.unmodifiableCollection(this.messages); } @Override public void setLastReleasedMessageSequenceNumber(int sequenceNumber) { this.lastReleasedMessageSequence = sequenceNumber; } @Override public Object getGroupId() { return this.groupId; } @Override public boolean isComplete() { return this.complete; } @Override public void complete() { this.complete = true; } @Override public int getSequenceSize() { if (size() == 0) { return 0; } return new IntegrationMessageHeaderAccessor(getOne()).getSequenceSize(); } @Override public int size() { return this.messages.size(); } @Override public Message<?> getOne() { synchronized (this.messages) { Iterator<Message<?>> iterator = this.messages.iterator(); return iterator.hasNext() ? iterator.next() : null; } } @Override public void clear() { this.messages.clear(); this.sequences.clear(); } /** * Return true if a message with this sequence number header exists in * the group. * @param sequence the sequence number. * @return true if it exists. * @since 4.3.7 */ public boolean containsSequence(Integer sequence) { return this.sequences.contains(sequence); } @Override public String toString() { return "SimpleMessageGroup{" + "groupId=" + this.groupId + ", messages=" + this.messages + ", timestamp=" + this.timestamp + ", lastModified=" + this.lastModified + '}'; } }