INT-2152
parent issue for INT-2134, INT-2135, INT-2142, INT-2155, INT-2158
This commit is contained in:
committed by
Mark Fisher
parent
86e59c8d3f
commit
946b9e2b82
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.gemfire.store;
|
||||
|
||||
import org.springframework.integration.Message;
|
||||
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
|
||||
/**
|
||||
* Provides GemFire specific support as a backing key-value based {@link org.springframework.integration.store.MessageGroupStore}.
|
||||
* Currently, this support is limited to explicitly depending on GemFire {@link com.gemstone.gemfire.cache.Region}s, but
|
||||
* might conceptually also support optimized key traversal (using a {@link com.gemstone.gemfire.cache.query.Query}, for example).
|
||||
*
|
||||
* @author Josh Long
|
||||
* @since 2.1
|
||||
* @see {@link org.springframework.integration.gemfire.store.KeyValueMessageGroupStore}
|
||||
*/
|
||||
public class GemfireMessageGroupStore extends KeyValueMessageGroupStore {
|
||||
|
||||
public GemfireMessageGroupStore(
|
||||
Region<Object, KeyValueMessageGroup> groupIdToMessageGroup,
|
||||
Region<String, Message<?>> marked,
|
||||
Region<String, Message<?>> unmarked ) {
|
||||
super(groupIdToMessageGroup, marked, unmarked);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,44 +16,73 @@
|
||||
|
||||
package org.springframework.integration.gemfire.store;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.store.MessageStore;
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.data.gemfire.RegionFactoryBean;
|
||||
import org.springframework.integration.store.AbstractKeyValueMessageStore;
|
||||
import org.springframework.integration.store.MessageGroupStore;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
|
||||
import com.gemstone.gemfire.cache.Cache;
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
|
||||
/**
|
||||
* Gemfire implementation of the key/value style {@link MessageStore} and {@link MessageGroupStore}
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @since 2.1
|
||||
*/
|
||||
public class GemfireMessageStore implements MessageStore {
|
||||
public class GemfireMessageStore extends AbstractKeyValueMessageStore{
|
||||
|
||||
private final Region<UUID, Message<?>> region;
|
||||
|
||||
public GemfireMessageStore(Region<UUID, Message<?>> region) {
|
||||
Assert.notNull(region, "region must not be null");
|
||||
this.region = region;
|
||||
private final Region<Object, Object> messageStoreRegion;
|
||||
|
||||
public GemfireMessageStore(Cache cache) {
|
||||
Assert.notNull(cache, "'cache' must not be null");
|
||||
try {
|
||||
RegionFactoryBean<Object, Object> messageRegionFactoryBean = new RegionFactoryBean<Object, Object>();
|
||||
messageRegionFactoryBean.setBeanName("messageStoreRegion");
|
||||
messageRegionFactoryBean.setCache(cache);
|
||||
messageRegionFactoryBean.afterPropertiesSet();
|
||||
this.messageStoreRegion = messageRegionFactoryBean.getObject();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Failed to initialize Gemfire Region");
|
||||
}
|
||||
}
|
||||
|
||||
public Message<?> getMessage(UUID id) {
|
||||
return this.region.get(id);
|
||||
@Override
|
||||
protected Object doRetrieve(Object id) {
|
||||
Assert.notNull(id, "'id' must not be null");
|
||||
return this.messageStoreRegion.get(id);
|
||||
}
|
||||
|
||||
public <T> Message<T> addMessage(Message<T> message) {
|
||||
this.region.put(message.getHeaders().getId(), message);
|
||||
return message;
|
||||
@Override
|
||||
protected void doStore(Object id, Object objectToStore) {
|
||||
Assert.notNull(id, "'id' must not be null");
|
||||
Assert.notNull(objectToStore, "'objectToStore' must not be null");
|
||||
this.messageStoreRegion.put(id, objectToStore);
|
||||
}
|
||||
|
||||
public Message<?> removeMessage(UUID id) {
|
||||
return this.region.remove(id);
|
||||
@Override
|
||||
protected Object doRemove(Object id) {
|
||||
Assert.notNull(id, "'id' must not be null");
|
||||
return this.messageStoreRegion.remove(id);
|
||||
}
|
||||
|
||||
@ManagedAttribute
|
||||
public long getMessageCount() {
|
||||
return this.region.size();
|
||||
@Override
|
||||
protected Collection<?> doListKeys(String keyPattern) {
|
||||
Assert.hasText(keyPattern, "'keyPattern' must not be empty");
|
||||
Collection<Object> keys = this.messageStoreRegion.keySet();
|
||||
List<Object> keyList = new ArrayList<Object>();
|
||||
for (Object key : keys) {
|
||||
String keyValue = key.toString();
|
||||
if (PatternMatchUtils.simpleMatch(keyPattern, keyValue)){
|
||||
keyList.add(keyValue);
|
||||
}
|
||||
}
|
||||
return keyList;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,340 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.gemfire.store;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
|
||||
/**
|
||||
* A {@link org.springframework.integration.store.MessageGroup} that manipulates keys and values to provide persistence.
|
||||
* Responsible for managing one group's messages as a {@link org.springframework.integration.store.MessageGroup}.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @since 2.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class KeyValueMessageGroup implements MessageGroup, Serializable {
|
||||
|
||||
/**
|
||||
* this should not be persisted. it's passed in through {@link KeyValueMessageGroupStore}, which has the reference to the {@link java.util.concurrent.ConcurrentMap} instance that should be set here
|
||||
*/
|
||||
private transient Map<String, Message<?>> marked;
|
||||
|
||||
/**
|
||||
* this should not be persisted. it's passed in through {@link KeyValueMessageGroupStore}, which has the reference to the {@link java.util.concurrent.ConcurrentMap} instance that should be set here
|
||||
*/
|
||||
private transient Map<String, Message<?>> unmarked;
|
||||
|
||||
/**
|
||||
* the #groupId is the unique ID to associate this aggregation of {@link org.springframework.integration.Message}s
|
||||
*/
|
||||
private Object groupId;
|
||||
|
||||
/**
|
||||
* passed in through the {@link org.springframework.integration.store.MessageGroupStore}
|
||||
*/
|
||||
private long timestamp;
|
||||
|
||||
|
||||
/**
|
||||
* default javabean ctor (so that this object plays well as a {@link java.io.Serializable} object)
|
||||
*/
|
||||
public KeyValueMessageGroup() {
|
||||
}
|
||||
|
||||
public KeyValueMessageGroup(Object groupId) {
|
||||
this(groupId, System.currentTimeMillis(), null, null);
|
||||
}
|
||||
|
||||
public KeyValueMessageGroup(Object groupId, long timestamp,
|
||||
ConcurrentMap<String, Message<?>> marked,
|
||||
ConcurrentMap<String, Message<?>> unmarked) {
|
||||
this.groupId = groupId;
|
||||
this.timestamp = timestamp;
|
||||
this.marked = marked;
|
||||
this.unmarked = unmarked;
|
||||
}
|
||||
|
||||
public KeyValueMessageGroup(Object groupId,
|
||||
ConcurrentMap<String, Message<?>> marked,
|
||||
ConcurrentMap<String, Message<?>> unmarked) {
|
||||
this(groupId, System.currentTimeMillis(), marked, unmarked);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return groupId.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof KeyValueMessageGroup) {
|
||||
Object otherGroupId = ((KeyValueMessageGroup) obj).getGroupId();
|
||||
return getGroupId().equals(otherGroupId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setUnmarked(Map<String, Message<?>> unmarked) {
|
||||
this.unmarked = unmarked;
|
||||
}
|
||||
|
||||
public void setMarked( Map<String, Message<?>> marked) {
|
||||
this.marked = marked;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the timestamp (milliseconds since epoch) associated with the creation of this group
|
||||
*/
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query if the message can be added.
|
||||
*/
|
||||
public boolean canAdd(Message<?> message) {
|
||||
return !isMember(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this {@link org.springframework.integration.Message} to the
|
||||
* {@link org.springframework.integration.store.MessageGroup}, delegating in this case to the {@link #unmarked} field
|
||||
*
|
||||
* @param message the {@link org.springframework.integration.Message} you are adding to the {@link java.util.Map}
|
||||
*/
|
||||
public void add(Message<?> message) {
|
||||
if (isMember(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String unmarkedKey = this.unmarkedKey(message);
|
||||
this.unmarked.put(unmarkedKey, (Message<?>) message);
|
||||
}
|
||||
|
||||
/**
|
||||
* the only reason we differentiate the keys is so that conceptually you could use the <em>same</em> {@link java.util.Map} instance for both <em>marked</em> and <em>unmarked</em> messages.
|
||||
*
|
||||
* This method simply differentiates the key, building on {@link #baseKey(org.springframework.integration.Message)}'s return value
|
||||
*
|
||||
* @param msg the {@link org.springframework.integration.Message} from which the key should be generated.
|
||||
* @return a String to be used as a key
|
||||
*/
|
||||
protected String markedKey(Message<?> msg) {
|
||||
return baseKey(msg) + "-m";
|
||||
}
|
||||
|
||||
/**
|
||||
* the only reason we differentiate the keys is so that conceptually you could use the <em>same</em> {@link java.util.Map} instance for both <em>marked</em> and <em>unmarked</em> messages.
|
||||
*
|
||||
* This method simply differentiates the key, building on {@link #baseKey(org.springframework.integration.Message)}'s return value
|
||||
*
|
||||
* @param msg the {@link org.springframework.integration.Message} from which the key should be generated.
|
||||
* @return a String to be used as a key
|
||||
*/
|
||||
protected String unmarkedKey(Message<?> msg) {
|
||||
return baseKey(msg) + "-u";
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this {@link org.springframework.integration.Message} from this {@link org.springframework.integration.store.MessageGroup}'s memory
|
||||
*
|
||||
* @param message the message to remove
|
||||
*/
|
||||
public void remove(Message<?> message) {
|
||||
if (unmarked.containsValue(message)) {
|
||||
unmarked.remove(unmarkedKey(message));
|
||||
}
|
||||
|
||||
if (marked.containsValue(message)) {
|
||||
marked.remove(markedKey(message));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* the groupKey is based on the groupID and it sits at the beginning of all the keys for this {@link org.springframework.integration.store.MessageGroup}s keys
|
||||
*
|
||||
* @return a string based on {@link #getGroupId()}
|
||||
*/
|
||||
protected String groupKey() {
|
||||
return (getGroupId()).toString();
|
||||
}
|
||||
|
||||
protected String baseKey(Message<?> msg) {
|
||||
String groupKey = groupKey();
|
||||
UUID id = msg.getHeaders().getId();
|
||||
Integer sn = msg.getHeaders().getSequenceNumber();
|
||||
Integer ss = msg.getHeaders().getSequenceSize();
|
||||
|
||||
return String.format("%s-%s-%s-%s", groupKey, id.toString(),
|
||||
sn.toString(), ss.toString());
|
||||
}
|
||||
|
||||
public Collection<Message<?>> getUnmarked() {
|
||||
return getMessagesForMessageGroup(this.unmarked);
|
||||
}
|
||||
|
||||
/**
|
||||
* this method will be used to discover all the messages for a given group in a {@link com.gemstone.gemfire.cache.Region}
|
||||
*
|
||||
* @param region the region from which we're hoping to discover these {@link org.springframework.integration.Message}s
|
||||
* @return a collection of messages
|
||||
*/
|
||||
protected Collection<Message<?>> getMessagesForMessageGroup(
|
||||
Map<String, Message<?>> region) {
|
||||
try {
|
||||
String groupMsgKey = groupKey();
|
||||
Collection<Message<?>> msgs = new ArrayList<Message<?>>();
|
||||
|
||||
for (String k : region.keySet()) {
|
||||
if (k.startsWith(groupMsgKey)) {
|
||||
msgs.add(region.get(k));
|
||||
}
|
||||
}
|
||||
|
||||
return msgs;
|
||||
} catch (Throwable th) {
|
||||
throw new RuntimeException(th);
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<Message<?>> getMarked() {
|
||||
return getMessagesForMessageGroup(this.marked);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the key that links these messages together
|
||||
*/
|
||||
public Object getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the group is complete (i.e. no more messages are expected to be added)
|
||||
*/
|
||||
public boolean isComplete() {
|
||||
if (size() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int sequenceSize = getSequenceSize();
|
||||
|
||||
return (sequenceSize > 0) && (sequenceSize == size());
|
||||
}
|
||||
|
||||
public int getSequenceSize() {
|
||||
if (size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return getOne().getHeaders().getSequenceSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the given message in this group. If the message is not part of this group then this call has no effect.
|
||||
*
|
||||
* @param messageToMark the message that should be marked
|
||||
*/
|
||||
public void mark(Message<?> messageToMark) {
|
||||
if (this.unmarked.containsValue(messageToMark)) {
|
||||
this.unmarked.remove(baseKey(messageToMark));
|
||||
}
|
||||
|
||||
this.marked.put(baseKey(messageToMark), messageToMark);
|
||||
}
|
||||
|
||||
public void markAll() {
|
||||
for (Message<?> msg : getUnmarked())
|
||||
mark(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the total number of messages (marked and unmarked) in this group
|
||||
*/
|
||||
public int size() {
|
||||
return getMarked().size() + getUnmarked().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a single message from the group
|
||||
*/
|
||||
public Message<?> getOne() {
|
||||
if (!this.unmarked.isEmpty()) {
|
||||
String aKey = this.unmarked.keySet().iterator().next();
|
||||
|
||||
return this.unmarked.get(aKey);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method determines whether messages have been added to this group that supersede the given message based on
|
||||
* its sequence id. This can be helpful to avoid ending up with sequences larger than their required sequence size
|
||||
* or sequences that are missing certain sequence numbers.
|
||||
*
|
||||
* @param message the message to test for candidacy
|
||||
*
|
||||
* @return whether or not the message is a member of the group
|
||||
*
|
||||
*/
|
||||
protected boolean isMember(Message<?> message) {
|
||||
if (size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Integer messageSequenceNumber = message.getHeaders().getSequenceNumber();
|
||||
|
||||
if ((messageSequenceNumber != null) && (messageSequenceNumber > 0)) {
|
||||
Integer messageSequenceSize = message.getHeaders().getSequenceSize();
|
||||
|
||||
if (!messageSequenceSize.equals(getSequenceSize())) {
|
||||
return true;
|
||||
} else {
|
||||
if (containsSequenceNumber(getUnmarked(), messageSequenceNumber) ||
|
||||
containsSequenceNumber(getUnmarked(),
|
||||
messageSequenceNumber)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean containsSequenceNumber(Collection<Message<?>> messages,
|
||||
Integer messageSequenceNumber) {
|
||||
for (Message<?> member : messages) {
|
||||
Integer memberSequenceNumber = member.getHeaders()
|
||||
.getSequenceNumber();
|
||||
|
||||
if (messageSequenceNumber.equals(memberSequenceNumber)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.gemfire.store;
|
||||
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.store.AbstractMessageGroupStore;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Provides an implementation of {@link org.springframework.integration.store.MessageGroupStore} that delegates to a backend Gemfire instance.
|
||||
* Gemfire holds keys and values. This class provides a strategy to hold objects.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @since 2.1
|
||||
*/
|
||||
public class KeyValueMessageGroupStore extends AbstractMessageGroupStore {
|
||||
|
||||
/**
|
||||
* Required {@link com.gemstone.gemfire.cache.Region} to managed the association of groups => {@link KeyValueMessageGroup}
|
||||
*/
|
||||
protected Map<Object, KeyValueMessageGroup> groupIdToMessageGroup;
|
||||
|
||||
/**
|
||||
* Required {@link com.gemstone.gemfire.cache.Region} to manage the #unmarked data
|
||||
*/
|
||||
protected Map<String, Message<?>> unmarked;
|
||||
|
||||
/**
|
||||
* Required {@link com.gemstone.gemfire.cache.Region} to manage the #marked data
|
||||
*/
|
||||
protected Map<String, Message<?>> marked;
|
||||
|
||||
|
||||
/**
|
||||
* Create a KeyValueMessageGroupStore with two backing regions to handle the state management.
|
||||
*
|
||||
* @param groupIdToMessageGroup the region to associate
|
||||
* @param marked the collection that will hold which messages are marked (delivered)
|
||||
* @param unmarked the collection that holds which messages are unmarked (not yet delivered)
|
||||
*/
|
||||
public KeyValueMessageGroupStore(Map<Object, KeyValueMessageGroup> groupIdToMessageGroup, Map<String, Message<?>> marked, Map<String, Message<?>> unmarked) {
|
||||
this.marked = marked;
|
||||
this.unmarked = unmarked;
|
||||
this.groupIdToMessageGroup = groupIdToMessageGroup;
|
||||
}
|
||||
|
||||
|
||||
public MessageGroup getMessageGroup(Object groupId) {
|
||||
Assert.notNull(groupId, "'groupId' must not be null");
|
||||
return this.getMessageGroupInternal(groupId);
|
||||
}
|
||||
|
||||
public MessageGroup addMessageToGroup(Object groupId, Message<?> message) {
|
||||
KeyValueMessageGroup group = getMessageGroupInternal(groupId);
|
||||
group.add(message);
|
||||
return group;
|
||||
}
|
||||
|
||||
public MessageGroup markMessageGroup(MessageGroup group) {
|
||||
Object groupId = group.getGroupId();
|
||||
KeyValueMessageGroup internal = getMessageGroupInternal(groupId);
|
||||
internal.markAll();
|
||||
return internal;
|
||||
}
|
||||
|
||||
public void removeMessageGroup(Object groupId) {
|
||||
groupIdToMessageGroup.remove(groupId);
|
||||
}
|
||||
|
||||
public MessageGroup removeMessageFromGroup(Object key, Message<?> messageToRemove) {
|
||||
KeyValueMessageGroup group = getMessageGroupInternal(key);
|
||||
group.remove(messageToRemove);
|
||||
return group;
|
||||
}
|
||||
|
||||
public MessageGroup markMessageFromGroup(Object key, Message<?> messageToMark) {
|
||||
KeyValueMessageGroup group = getMessageGroupInternal(key);
|
||||
group.mark(messageToMark);
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<MessageGroup> iterator() {
|
||||
return new HashSet<MessageGroup>(groupIdToMessageGroup.values()).iterator();
|
||||
}
|
||||
|
||||
protected KeyValueMessageGroup ensureMessageGroupHasReferencesToRegions(KeyValueMessageGroup keyValueMessageGroup) {
|
||||
if (keyValueMessageGroup == null) {
|
||||
return null;
|
||||
}
|
||||
keyValueMessageGroup.setMarked(this.marked);
|
||||
keyValueMessageGroup.setUnmarked(this.unmarked);
|
||||
return keyValueMessageGroup;
|
||||
}
|
||||
|
||||
protected KeyValueMessageGroup getMessageGroupInternal(Object groupId) {
|
||||
if (!groupIdToMessageGroup.containsKey(groupId)) {
|
||||
groupIdToMessageGroup.put(groupId, new KeyValueMessageGroup(groupId));
|
||||
}
|
||||
return ensureMessageGroupHasReferencesToRegions(groupIdToMessageGroup.get( groupId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
* Copyright 2007-2011 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.gemfire.store;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.data.gemfire.CacheFactoryBean;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
import org.springframework.integration.store.SimpleMessageGroup;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
|
||||
import com.gemstone.gemfire.cache.Cache;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
*
|
||||
*/
|
||||
public class GemfireGroupStoreTests {
|
||||
|
||||
private Cache cache;
|
||||
|
||||
@Test
|
||||
public void testNonExistingEmptyMessageGroup() throws Exception{
|
||||
GemfireMessageStore store = new GemfireMessageStore(this.cache);
|
||||
|
||||
MessageGroup messageGroup = store.getMessageGroup(1);
|
||||
assertNotNull(messageGroup);
|
||||
assertTrue(messageGroup instanceof SimpleMessageGroup);
|
||||
assertEquals(0, messageGroup.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageGroupWithAddedMessage() throws Exception{
|
||||
GemfireMessageStore store = new GemfireMessageStore(this.cache);
|
||||
|
||||
MessageGroup messageGroup = store.getMessageGroup(1);
|
||||
Message<?> message = new GenericMessage<String>("Hello");
|
||||
messageGroup = store.addMessageToGroup(1, message);
|
||||
assertEquals(1, messageGroup.size());
|
||||
|
||||
// make sure the store is properly rebuild from Gemfire
|
||||
store = new GemfireMessageStore(this.cache);
|
||||
|
||||
messageGroup = store.getMessageGroup(1);
|
||||
assertEquals(1, messageGroup.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveMessageGroup() throws Exception{
|
||||
GemfireMessageStore store = new GemfireMessageStore(this.cache);
|
||||
|
||||
MessageGroup messageGroup = store.getMessageGroup(1);
|
||||
Message<?> message = new GenericMessage<String>("Hello");
|
||||
messageGroup = store.addMessageToGroup(messageGroup.getGroupId(), message);
|
||||
assertEquals(1, messageGroup.size());
|
||||
|
||||
store.removeMessageGroup(1);
|
||||
MessageGroup messageGroupA = store.getMessageGroup(1);
|
||||
assertNotSame(messageGroup, messageGroupA);
|
||||
assertEquals(0, messageGroupA.getMarked().size());
|
||||
assertEquals(0, messageGroupA.getUnmarked().size());
|
||||
assertEquals(0, messageGroupA.size());
|
||||
|
||||
// make sure the store is properly rebuild from Gemfire
|
||||
store = new GemfireMessageStore(this.cache);
|
||||
|
||||
messageGroup = store.getMessageGroup(1);
|
||||
|
||||
assertEquals(0, messageGroup.getMarked().size());
|
||||
assertEquals(0, messageGroup.getUnmarked().size());
|
||||
assertEquals(0, messageGroup.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveMessageFromTheGroup() throws Exception{
|
||||
GemfireMessageStore store = new GemfireMessageStore(this.cache);
|
||||
|
||||
MessageGroup messageGroup = store.getMessageGroup(1);
|
||||
Message<?> message = new GenericMessage<String>("2");
|
||||
store.addMessageToGroup(messageGroup.getGroupId(), new GenericMessage<String>("1"));
|
||||
store.addMessageToGroup(messageGroup.getGroupId(), message);
|
||||
messageGroup = store.addMessageToGroup(messageGroup.getGroupId(), new GenericMessage<String>("3"));
|
||||
assertEquals(3, messageGroup.size());
|
||||
|
||||
messageGroup = store.removeMessageFromGroup(1, message);
|
||||
assertEquals(2, messageGroup.size());
|
||||
|
||||
// make sure the store is properly rebuild from Gemfire
|
||||
store = new GemfireMessageStore(this.cache);
|
||||
|
||||
messageGroup = store.getMessageGroup(1);
|
||||
assertEquals(2, messageGroup.size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMarkAllMessagesInMessageGroup() throws Exception{
|
||||
GemfireMessageStore store = new GemfireMessageStore(this.cache);
|
||||
|
||||
MessageGroup messageGroup = store.getMessageGroup(1);
|
||||
store.addMessageToGroup(messageGroup.getGroupId(), new GenericMessage<String>("1"));
|
||||
store.addMessageToGroup(messageGroup.getGroupId(), new GenericMessage<String>("2"));
|
||||
messageGroup = store.addMessageToGroup(messageGroup.getGroupId(), new GenericMessage<String>("3"));
|
||||
|
||||
assertEquals(3, messageGroup.getUnmarked().size());
|
||||
assertEquals(0, messageGroup.getMarked().size());
|
||||
messageGroup = store.markMessageGroup(messageGroup);
|
||||
|
||||
assertEquals(0, messageGroup.getUnmarked().size());
|
||||
assertEquals(3, messageGroup.getMarked().size());
|
||||
|
||||
// make sure the store is properly rebuild from Gemfire
|
||||
store = new GemfireMessageStore(this.cache);
|
||||
|
||||
messageGroup = store.getMessageGroup(1);
|
||||
assertEquals(0, messageGroup.getUnmarked().size());
|
||||
assertEquals(3, messageGroup.getMarked().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveNonExistingMessageFromTheGroup() throws Exception{
|
||||
GemfireMessageStore store = new GemfireMessageStore(this.cache);
|
||||
|
||||
MessageGroup messageGroup = store.getMessageGroup(1);
|
||||
store.addMessageToGroup(messageGroup.getGroupId(), new GenericMessage<String>("1"));
|
||||
store.removeMessageFromGroup(1, new GenericMessage<String>("2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveNonExistingMessageFromNonExistingTheGroup() throws Exception{
|
||||
GemfireMessageStore store = new GemfireMessageStore(this.cache);
|
||||
store.removeMessageFromGroup(1, new GenericMessage<String>("2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMarkMessageInMessageGroup() throws Exception{
|
||||
GemfireMessageStore store = new GemfireMessageStore(this.cache);
|
||||
MessageGroup messageGroup = store.getMessageGroup(1);
|
||||
Message<?> messageToMark = new GenericMessage<String>("1");
|
||||
store.addMessageToGroup(messageGroup.getGroupId(), messageToMark);
|
||||
store.addMessageToGroup(messageGroup.getGroupId(), new GenericMessage<String>("2"));
|
||||
messageGroup = store.addMessageToGroup(messageGroup.getGroupId(), new GenericMessage<String>("3"));
|
||||
|
||||
assertEquals(3, messageGroup.getUnmarked().size());
|
||||
assertEquals(0, messageGroup.getMarked().size());
|
||||
messageGroup = store.markMessageFromGroup(1, messageToMark);
|
||||
assertEquals(2, messageGroup.getUnmarked().size());
|
||||
assertEquals(1, messageGroup.getMarked().size());
|
||||
|
||||
// make sure the store is properly rebuild from Gemfire
|
||||
store = new GemfireMessageStore(this.cache);
|
||||
|
||||
messageGroup = store.getMessageGroup(1);
|
||||
assertEquals(2, messageGroup.getUnmarked().size());
|
||||
assertEquals(1, messageGroup.getMarked().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompleteMessageGroup() throws Exception{
|
||||
GemfireMessageStore store = new GemfireMessageStore(this.cache);
|
||||
MessageGroup messageGroup = store.getMessageGroup(1);
|
||||
Message<?> messageToMark = new GenericMessage<String>("1");
|
||||
store.addMessageToGroup(messageGroup.getGroupId(), messageToMark);
|
||||
store.completeGroup(messageGroup.getGroupId());
|
||||
messageGroup = store.getMessageGroup(1);
|
||||
assertTrue(messageGroup.isComplete());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLastReleasedSequenceNumber() throws Exception{
|
||||
GemfireMessageStore store = new GemfireMessageStore(this.cache);
|
||||
MessageGroup messageGroup = store.getMessageGroup(1);
|
||||
Message<?> messageToMark = new GenericMessage<String>("1");
|
||||
store.addMessageToGroup(messageGroup.getGroupId(), messageToMark);
|
||||
store.setLastReleasedSequenceNumberForGroup(messageGroup.getGroupId(), 5);
|
||||
messageGroup = store.getMessageGroup(1);
|
||||
assertEquals(5, messageGroup.getLastReleasedMessageSequenceNumber());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleInstancesOfGroupStore() throws Exception{
|
||||
GemfireMessageStore store1 = new GemfireMessageStore(this.cache);
|
||||
|
||||
GemfireMessageStore store2 = new GemfireMessageStore(this.cache);
|
||||
|
||||
Message<?> message = new GenericMessage<String>("1");
|
||||
store1.addMessageToGroup(1, message);
|
||||
MessageGroup messageGroup = store2.addMessageToGroup(1, new GenericMessage<String>("2"));
|
||||
|
||||
assertEquals(2, messageGroup.getUnmarked().size());
|
||||
assertEquals(0, messageGroup.getMarked().size());
|
||||
|
||||
GemfireMessageStore store3 = new GemfireMessageStore(this.cache);
|
||||
|
||||
messageGroup = store3.markMessageFromGroup(1, message);
|
||||
|
||||
assertEquals(1, messageGroup.getUnmarked().size());
|
||||
assertEquals(1, messageGroup.getMarked().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIteratorOfMessageGroups() throws Exception{
|
||||
GemfireMessageStore store1 = new GemfireMessageStore(this.cache);
|
||||
GemfireMessageStore store2 = new GemfireMessageStore(this.cache);
|
||||
|
||||
store1.addMessageToGroup(1, new GenericMessage<String>("1"));
|
||||
store2.addMessageToGroup(2, new GenericMessage<String>("2"));
|
||||
store1.addMessageToGroup(3, new GenericMessage<String>("3"));
|
||||
store2.addMessageToGroup(3, new GenericMessage<String>("3A"));
|
||||
|
||||
Iterator<MessageGroup> messageGroups = store1.iterator();
|
||||
int counter = 0;
|
||||
while (messageGroups.hasNext()) {
|
||||
messageGroups.next();
|
||||
counter++;
|
||||
}
|
||||
assertEquals(3, counter);
|
||||
|
||||
store2.removeMessageGroup(3);
|
||||
|
||||
messageGroups = store1.iterator();
|
||||
counter = 0;
|
||||
while (messageGroups.hasNext()) {
|
||||
messageGroups.next();
|
||||
counter++;
|
||||
}
|
||||
assertEquals(2, counter);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testConcurrentModifications() throws Exception{
|
||||
|
||||
final GemfireMessageStore store1 = new GemfireMessageStore(this.cache);
|
||||
final GemfireMessageStore store2 = new GemfireMessageStore(this.cache);
|
||||
|
||||
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
|
||||
public void testWithAggregatorWithShutdown(){
|
||||
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("gemfire-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 = new ClassPathXmlApplicationContext("gemfire-aggregator-config-a.xml", this.getClass());
|
||||
MessageChannel inputA = context.getBean("inputChannel", MessageChannel.class);
|
||||
QueueChannel outputA = context.getBean("outputChannel", QueueChannel.class);
|
||||
|
||||
Message<?> m3 = MessageBuilder.withPayload("3").setSequenceNumber(3).setSequenceSize(3).setCorrelationId(1).build();
|
||||
inputA.send(m3);
|
||||
assertNotNull(outputA.receive(1000));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void init() throws Exception{
|
||||
CacheFactoryBean cacheFactoryBean = new CacheFactoryBean();
|
||||
cacheFactoryBean.afterPropertiesSet();
|
||||
this.cache = (Cache)cacheFactoryBean.getObject();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup(){
|
||||
this.cache.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.gemfire.store;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.gemfire.CacheFactoryBean;
|
||||
import org.springframework.data.gemfire.RegionFactoryBean;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.aggregator.CorrelationStrategy;
|
||||
import org.springframework.integration.aggregator.HeaderAttributeCorrelationStrategy;
|
||||
import org.springframework.integration.aggregator.ReleaseStrategy;
|
||||
import org.springframework.integration.aggregator.SequenceSizeReleaseStrategy;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.core.MessagingTemplate;
|
||||
import org.springframework.integration.gemfire.store.KeyValueMessageGroup;
|
||||
import org.springframework.integration.gemfire.store.KeyValueMessageGroupStore;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.gemstone.gemfire.cache.Cache;
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
|
||||
/**
|
||||
* Our aggregator needs a
|
||||
* {@link org.springframework.integration.gemfire.store.KeyValueMessageGroupStore}
|
||||
* . This handles configuration of the ancillary objects.
|
||||
*
|
||||
* @author Josh Long
|
||||
* @since 2.1
|
||||
*/
|
||||
@Configuration
|
||||
public class GemfireMessageGroupStoreTestConfiguration {
|
||||
|
||||
public static List<String> LIST_OF_STRINGS = Arrays.asList("1,2,3,4,5".split(","));
|
||||
|
||||
static private Log log = LogFactory.getLog(GemfireMessageGroupStoreTestConfiguration.class);
|
||||
|
||||
@Value("${correlation-header}")
|
||||
private String correlationHeader;
|
||||
|
||||
@Bean
|
||||
public Cache cache() throws Throwable {
|
||||
CacheFactoryBean cacheFactoryBean = new CacheFactoryBean();
|
||||
cacheFactoryBean.afterPropertiesSet();
|
||||
return (Cache)cacheFactoryBean.getObject();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Region<Object, KeyValueMessageGroup> messageGroupRegion() throws Throwable {
|
||||
RegionFactoryBean<Object, KeyValueMessageGroup> regionFactoryBean = new RegionFactoryBean<Object, KeyValueMessageGroup>();
|
||||
regionFactoryBean.setName("messageGroupRegion");
|
||||
regionFactoryBean.setCache(cache());
|
||||
regionFactoryBean.afterPropertiesSet();
|
||||
return regionFactoryBean.getObject();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Region<String, Message<?>> unmarkedRegion() throws Throwable {
|
||||
RegionFactoryBean<String, Message<?>> regionFactoryBean = new RegionFactoryBean<String, Message<?>>();
|
||||
regionFactoryBean.setName("unmarkedRegion");
|
||||
regionFactoryBean.setCache(cache());
|
||||
regionFactoryBean.afterPropertiesSet();
|
||||
return regionFactoryBean.getObject();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Region<String, Message<?>> markedRegion() throws Throwable {
|
||||
RegionFactoryBean<String, Message<?>> regionFactoryBean = new RegionFactoryBean<String, Message<?>>();
|
||||
regionFactoryBean.setName("markedRegion");
|
||||
regionFactoryBean.setCache(cache());
|
||||
regionFactoryBean.afterPropertiesSet();
|
||||
return regionFactoryBean.getObject();
|
||||
}
|
||||
|
||||
@Bean(name = "messageGroupStoreActivator")
|
||||
public FakeMessageConsumer serviceActivator() {
|
||||
return new FakeMessageConsumer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ReleaseStrategy releaseStrategy() {
|
||||
return new SequenceSizeReleaseStrategy(false);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CorrelationStrategy correlationStrategy() {
|
||||
return new HeaderAttributeCorrelationStrategy(this.correlationHeader);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public KeyValueMessageGroupStore gemfireMessageGroupStore() throws Throwable {
|
||||
return new KeyValueMessageGroupStore(messageGroupRegion(), markedRegion(), unmarkedRegion());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FakeMessageProducer producer() {
|
||||
return new FakeMessageProducer();
|
||||
}
|
||||
|
||||
static public class FakeMessageConsumer {
|
||||
|
||||
private List<Collection<Object>> batches = new ArrayList<Collection<Object>>();
|
||||
|
||||
public List<Collection<Object>> getBatches() {
|
||||
return this.batches;
|
||||
}
|
||||
|
||||
@ServiceActivator
|
||||
public void activateAsMessagesArriveInBatches(Message<Collection<Object>> msg) throws Throwable {
|
||||
Collection<Object> payloads = msg.getPayload();
|
||||
batches.add(payloads);
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(payloads);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static public class FakeMessageProducer implements InitializingBean, SmartLifecycle {
|
||||
public boolean isAutoStartup() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void stop(Runnable callback) {
|
||||
stop();
|
||||
callback.run();
|
||||
}
|
||||
|
||||
public int getPhase() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
@Qualifier("i")
|
||||
private MessageChannel messageChannel;
|
||||
|
||||
private MessagingTemplate messagingTemplate = new MessagingTemplate();
|
||||
|
||||
private volatile boolean running = false;
|
||||
|
||||
@Value("${correlation-header}")
|
||||
private String correlationHeader;
|
||||
|
||||
public void sendManyMessages(int correlationValue, Collection<String> lines) throws Throwable {
|
||||
Assert.notNull(lines, "the collection must be non-null");
|
||||
Assert.notEmpty(lines, "the collection must not be empty");
|
||||
int ctr = 0;
|
||||
int size = lines.size();
|
||||
for (String l : lines) {
|
||||
Message<?> msg = MessageBuilder.withPayload(l).setCorrelationId(this.correlationHeader)
|
||||
.setHeader(this.correlationHeader, correlationValue).setSequenceNumber(++ctr)
|
||||
.setSequenceSize(size).build();
|
||||
this.messagingTemplate.send(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.messagingTemplate.setDefaultChannel(this.messageChannel);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
running = true;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
try {
|
||||
sendManyMessages(i, LIST_OF_STRINGS);
|
||||
}
|
||||
catch (Throwable throwable) {
|
||||
throw new RuntimeException(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
running = false;
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?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:util="http://www.springframework.org/schema/util"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
|
||||
|
||||
<bean class="org.springframework.integration.gemfire.store.GemfireMessageGroupStoreTestConfiguration"/>
|
||||
|
||||
<context:property-placeholder location="org/springframework/integration/gemfire/store/common.properties"/>
|
||||
|
||||
<int:channel id="i"/>
|
||||
|
||||
<int:aggregator release-strategy="releaseStrategy" correlation-strategy="correlationStrategy" message-store="gemfireMessageGroupStore" input-channel="i" output-channel="o" />
|
||||
|
||||
<int:channel id="o"/>
|
||||
|
||||
<int:service-activator input-channel="o" ref="messageGroupStoreActivator" />
|
||||
|
||||
<util:properties id="props" location="org/springframework/integration/gemfire/store/gfe-cache.properties"/>
|
||||
|
||||
</beans>
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.gemfire.store;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* Tests the Gemfire
|
||||
* {@link org.springframework.integration.store.MessageGroupStore}
|
||||
* implementation,
|
||||
* {@link org.springframework.integration.gemfire.store.GemfireMessageGroupStore}
|
||||
* .
|
||||
* <p/>
|
||||
* It tests the {@link org.springframework.integration.store.MessageGroupStore}
|
||||
* by sending 10 batches of letters (all of the same width), and then counting
|
||||
* on the other end that indeed all 10 batches arrived and that all letters
|
||||
* expected are there. *
|
||||
*
|
||||
* @author Josh Long
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class GemfireMessageGroupStoreTests {
|
||||
|
||||
@Autowired
|
||||
private GemfireMessageGroupStoreTestConfiguration.FakeMessageConsumer consumer;
|
||||
@Autowired
|
||||
private GemfireMessageGroupStoreTestConfiguration.FakeMessageProducer producer;
|
||||
|
||||
private List<String> letters = GemfireMessageGroupStoreTestConfiguration.LIST_OF_STRINGS;
|
||||
|
||||
private int maxSize = 10;
|
||||
|
||||
@Test
|
||||
public void testGemfireMessageGroupStore() throws Exception {
|
||||
producer.afterPropertiesSet();
|
||||
producer.start();
|
||||
List<Collection<Object>> batches = consumer.getBatches();
|
||||
assertEquals(maxSize, batches.size());
|
||||
for (Collection<Object> collection : batches) {
|
||||
Assert.assertTrue(letters.size() == collection.size());
|
||||
for (String c : this.letters) {
|
||||
Assert.assertTrue(collection.contains(c));
|
||||
}
|
||||
for (Object o : collection) {
|
||||
Assert.assertTrue(o instanceof String);
|
||||
}
|
||||
}
|
||||
producer.stop();
|
||||
}
|
||||
}
|
||||
@@ -16,20 +16,15 @@
|
||||
|
||||
package org.springframework.integration.gemfire.store;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.data.gemfire.CacheFactoryBean;
|
||||
import org.springframework.data.gemfire.RegionFactoryBean;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.store.MessageStore;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
|
||||
import com.gemstone.gemfire.cache.Cache;
|
||||
import com.gemstone.gemfire.cache.Region;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
@@ -42,12 +37,8 @@ public class GemfireMessageStoreTests {
|
||||
CacheFactoryBean cacheFactoryBean = new CacheFactoryBean();
|
||||
cacheFactoryBean.afterPropertiesSet();
|
||||
Cache cache = (Cache)cacheFactoryBean.getObject();
|
||||
RegionFactoryBean<UUID, Message<?>> regionFactoryBean = new RegionFactoryBean<UUID, Message<?>>();
|
||||
regionFactoryBean.setName("test.addAndGetMessage");
|
||||
regionFactoryBean.setCache(cache);
|
||||
regionFactoryBean.afterPropertiesSet();
|
||||
Region<UUID, Message<?>> region = regionFactoryBean.getObject();
|
||||
MessageStore store = new GemfireMessageStore(region);
|
||||
MessageStore store = new GemfireMessageStore(cache);
|
||||
|
||||
Message<?> message = MessageBuilder.withPayload("test").build();
|
||||
store.addMessage(message);
|
||||
Message<?> retrieved = store.getMessage(message.getHeaders().getId());
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?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-2.0.xsd">
|
||||
|
||||
|
||||
<int:aggregator input-channel="inputChannel" output-channel="outputChannel" message-store="gemfireStore"/>
|
||||
|
||||
<int:channel id="outputChannel">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
<bean id="gemfireStore" class="org.springframework.integration.gemfire.store.GemfireMessageStore">
|
||||
<constructor-arg ref="cache"/>
|
||||
</bean>
|
||||
|
||||
<bean id="cache" class="org.springframework.data.gemfire.CacheFactoryBean"/>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?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-2.0.xsd">
|
||||
|
||||
|
||||
<int:aggregator input-channel="inputChannel" output-channel="outputChannel" message-store="gemfireStore"/>
|
||||
|
||||
<int:channel id="outputChannel">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
<bean id="gemfireStore" class="org.springframework.integration.gemfire.store.GemfireMessageStore">
|
||||
<constructor-arg ref="myCache"/>
|
||||
</bean>
|
||||
|
||||
<bean id="myCache" class="org.springframework.data.gemfire.CacheFactoryBean"/>
|
||||
|
||||
</beans>
|
||||
Reference in New Issue
Block a user