diff --git a/build.gradle b/build.gradle
index 48bd58667d..2df0dfdc99 100644
--- a/build.gradle
+++ b/build.gradle
@@ -126,7 +126,7 @@ configure(javaprojects) {
springDataMongoVersion = '1.0.0.BUILD-SNAPSHOT'
springDataCommonsVersion = '1.2.0.BUILD-SNAPSHOT'
springDataRedisVersion = '1.0.0.BUILD-SNAPSHOT'
- springGemfireVersion = '1.0.1.RELEASE'
+ springGemfireVersion = '1.1.0.BUILD-SNAPSHOT'
springSecurityVersion = '3.0.5.RELEASE'
springWsVersion = '2.0.2.RELEASE'
@@ -249,6 +249,7 @@ project('spring-integration-gemfire') {
compile "org.springframework:spring-context:$springVersion"
compile "org.springframework.data.gemfire:spring-gemfire:$springGemfireVersion"
testCompile project(":spring-integration-stream")
+ testCompile project(":spring-integration-test")
}
repositories {
mavenRepo urls: 'http://dist.gemstone.com/maven/release' // for gemfire
@@ -444,6 +445,7 @@ project('spring-integration-test') {
compile "junit:junit-dep:$junitVersion"
compile "org.mockito:mockito-all:$mockitoVersion"
compile "org.springframework:spring-context:$springVersion"
+ compile "org.springframework:spring-test:$springVersion"
}
}
diff --git a/spring-integration-gemfire/.springBeans b/spring-integration-gemfire/.springBeans
new file mode 100644
index 0000000000..8a3d5fe7e4
--- /dev/null
+++ b/spring-integration-gemfire/.springBeans
@@ -0,0 +1,15 @@
+
+
+ 1
+
+
+
+
+
+
+ src/test/java/org/springframework/integration/gemfire/inbound/GemfireInboundChannelAdapterTests-context.xml
+ src/test/java/org/springframework/integration/gemfire/outbound/GemfireOutboundChannelAdapterTests-context.xml
+
+
+
+
diff --git a/spring-integration-gemfire/src/main/java/org/springframework/integration/gemfire/config/xml/GemfireInboundChannelAdapterParser.java b/spring-integration-gemfire/src/main/java/org/springframework/integration/gemfire/config/xml/GemfireInboundChannelAdapterParser.java
new file mode 100644
index 0000000000..baa605a095
--- /dev/null
+++ b/spring-integration-gemfire/src/main/java/org/springframework/integration/gemfire/config/xml/GemfireInboundChannelAdapterParser.java
@@ -0,0 +1,68 @@
+/*
+ * 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.config.xml;
+
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.integration.config.xml.AbstractChannelAdapterParser;
+import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
+import org.springframework.util.StringUtils;
+import org.w3c.dom.Element;
+
+/**
+ * @author David Turanski
+ * @since 2.1
+ *
+ */
+public class GemfireInboundChannelAdapterParser extends AbstractChannelAdapterParser {
+
+
+ private static final String ERROR_CHANNEL_ATTRIBUTE = "error-channel";
+
+ private static final String OUTPUT_CHANNEL_PROPERTY = "outputChannel";
+
+ private static final String REGION_ATTRIBUTE = "region";
+
+ private static final String PAYLOAD_EXPRESSION_PROPERTY = "payloadExpression";
+
+ private static final String EXPRESSION_ATTRIBUTE = "expression";
+
+ private static final String GEMFIRE_INBOUND_CACHE_LISTENING_MESSAGE_PRODUCER = "org.springframework.integration.gemfire.inbound.CacheListeningMessageProducer";
+
+ private static final String SUPPORTED_EVENT_TYPES_PROPERTY = "supportedEventTypes";
+
+ private static final String CACHE_EVENTS_ATTRIBUTE = "cache-events";
+
+ /* (non-Javadoc)
+ * @see org.springframework.integration.config.xml.AbstractChannelAdapterParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, java.lang.String)
+ */
+ @Override
+ protected AbstractBeanDefinition doParse(Element element, ParserContext parserContext, String channelName) {
+ BeanDefinitionBuilder listeningMessageProducer = BeanDefinitionBuilder.genericBeanDefinition(GEMFIRE_INBOUND_CACHE_LISTENING_MESSAGE_PRODUCER);
+ IntegrationNamespaceUtils.setValueIfAttributeDefined(listeningMessageProducer, element, EXPRESSION_ATTRIBUTE,PAYLOAD_EXPRESSION_PROPERTY);
+ IntegrationNamespaceUtils.setValueIfAttributeDefined(listeningMessageProducer, element, CACHE_EVENTS_ATTRIBUTE, SUPPORTED_EVENT_TYPES_PROPERTY);
+
+ if (!StringUtils.hasText(REGION_ATTRIBUTE)){
+ parserContext.getReaderContext().error("'region' attribute is required.",element);
+ }
+
+ listeningMessageProducer.addConstructorArgReference(element.getAttribute(REGION_ATTRIBUTE));
+
+ listeningMessageProducer.addPropertyReference(OUTPUT_CHANNEL_PROPERTY, channelName);
+ IntegrationNamespaceUtils.setReferenceIfAttributeDefined(listeningMessageProducer, element, ERROR_CHANNEL_ATTRIBUTE);
+
+ return listeningMessageProducer.getBeanDefinition();
+ }
+
+}
diff --git a/spring-integration-gemfire/src/main/java/org/springframework/integration/gemfire/config/xml/GemfireIntegrationNamespaceHandler.java b/spring-integration-gemfire/src/main/java/org/springframework/integration/gemfire/config/xml/GemfireIntegrationNamespaceHandler.java
new file mode 100644
index 0000000000..a686001814
--- /dev/null
+++ b/spring-integration-gemfire/src/main/java/org/springframework/integration/gemfire/config/xml/GemfireIntegrationNamespaceHandler.java
@@ -0,0 +1,32 @@
+/*
+ * 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.config.xml;
+
+import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler;
+
+/**
+ * @author David Turanski
+ *
+ */
+public class GemfireIntegrationNamespaceHandler extends AbstractIntegrationNamespaceHandler{
+
+ /* (non-Javadoc)
+ * @see org.springframework.beans.factory.xml.NamespaceHandler#init()
+ */
+ public void init() {
+ registerBeanDefinitionParser("inbound-channel-adapter", new GemfireInboundChannelAdapterParser());
+ registerBeanDefinitionParser("outbound-channel-adapter", new GemfireOutboundChannelAdapterParser());
+
+ }
+
+}
diff --git a/spring-integration-gemfire/src/main/java/org/springframework/integration/gemfire/config/xml/GemfireOutboundChannelAdapterParser.java b/spring-integration-gemfire/src/main/java/org/springframework/integration/gemfire/config/xml/GemfireOutboundChannelAdapterParser.java
new file mode 100644
index 0000000000..7c86a5a8f8
--- /dev/null
+++ b/spring-integration-gemfire/src/main/java/org/springframework/integration/gemfire/config/xml/GemfireOutboundChannelAdapterParser.java
@@ -0,0 +1,61 @@
+/*
+ * 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.config.xml;
+
+import java.util.Map;
+
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
+import org.springframework.util.StringUtils;
+import org.springframework.util.xml.DomUtils;
+import org.w3c.dom.Element;
+
+/**
+ * @author David Turanski
+ * @since 2.1
+ *
+ */
+public class GemfireOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser {
+
+ private static final String CACHE_ENTRIES_PROPERTY = "cacheEntries";
+
+ private static final String CACHE_ENTRIES_ELEMENT = "cache-entries";
+
+ private static final String REGION_ATTRIBUTE = "region";
+
+ private static final String GEMFIRE_OUTBOUND_CACHE_WRITING_MESSAGE_HANDLER = "org.springframework.integration.gemfire.outbound.CacheWritingMessageHandler";
+
+ /* (non-Javadoc)
+ * @see org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser#parseConsumer(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)
+ */
+ @Override
+ protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
+ BeanDefinitionBuilder cacheWritingMessageHandler = BeanDefinitionBuilder.genericBeanDefinition(
+ GEMFIRE_OUTBOUND_CACHE_WRITING_MESSAGE_HANDLER);
+ if (!StringUtils.hasText(REGION_ATTRIBUTE)){
+ parserContext.getReaderContext().error("'region' attribute is required.",element);
+ }
+
+ cacheWritingMessageHandler.addConstructorArgReference(element.getAttribute(REGION_ATTRIBUTE));
+
+ Element cacheEntries = DomUtils.getChildElementByTagName(element,CACHE_ENTRIES_ELEMENT);
+ if (cacheEntries != null) {
+ Map,?> map = parserContext.getDelegate().parseMapElement(cacheEntries,cacheWritingMessageHandler.getBeanDefinition());
+ cacheWritingMessageHandler.addPropertyValue(CACHE_ENTRIES_PROPERTY, map);
+ }
+
+ return cacheWritingMessageHandler.getBeanDefinition();
+ }
+}
diff --git a/spring-integration-gemfire/src/main/java/org/springframework/integration/gemfire/inbound/CacheListeningMessageProducer.java b/spring-integration-gemfire/src/main/java/org/springframework/integration/gemfire/inbound/CacheListeningMessageProducer.java
index 8890082244..8d7296e8c0 100644
--- a/spring-integration-gemfire/src/main/java/org/springframework/integration/gemfire/inbound/CacheListeningMessageProducer.java
+++ b/spring-integration-gemfire/src/main/java/org/springframework/integration/gemfire/inbound/CacheListeningMessageProducer.java
@@ -29,6 +29,7 @@ import org.springframework.integration.endpoint.MessageProducerSupport;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.util.Assert;
+import com.gemstone.gemfire.cache.CacheClosedException;
import com.gemstone.gemfire.cache.CacheListener;
import com.gemstone.gemfire.cache.EntryEvent;
import com.gemstone.gemfire.cache.Region;
@@ -42,6 +43,7 @@ import com.gemstone.gemfire.cache.util.CacheListenerAdapter;
* payloadExpression is provided, the {@link EntryEvent} itself will be the payload.
*
* @author Mark Fisher
+ * @author David Turanski
* @since 2.1
*/
@SuppressWarnings({"rawtypes", "unchecked"})
@@ -60,11 +62,10 @@ public class CacheListeningMessageProducer extends MessageProducerSupport {
private final SpelExpressionParser parser = new SpelExpressionParser();
-
public CacheListeningMessageProducer(Region, ?> region) {
Assert.notNull(region, "region must not be null");
this.region = region;
- this.listener = new MessageProducingCacheListener();
+ this.listener = new MessageProducingCacheListener();
}
@@ -95,9 +96,16 @@ public class CacheListeningMessageProducer extends MessageProducerSupport {
if (logger.isInfoEnabled()) {
logger.info("removing MessageProducingCacheListener from GemFire Region '" + this.region.getName() + "'");
}
- this.region.getAttributesMutator().removeCacheListener(this.listener);
+ try {
+ this.region.getAttributesMutator().removeCacheListener(this.listener);
+ } catch (CacheClosedException e) {
+ if (logger.isDebugEnabled()){
+ logger.debug(e.getMessage(),e);
+ }
+ }
+
}
-
+
private class MessageProducingCacheListener extends CacheListenerAdapter {
@@ -143,5 +151,7 @@ public class CacheListeningMessageProducer extends MessageProducerSupport {
sendMessage(MessageBuilder.withPayload(payload).build());
}
}
+
+
}
diff --git a/spring-integration-gemfire/src/main/java/org/springframework/integration/gemfire/outbound/CacheWritingMessageHandler.java b/spring-integration-gemfire/src/main/java/org/springframework/integration/gemfire/outbound/CacheWritingMessageHandler.java
index cde54c777c..bda7c71fdd 100644
--- a/spring-integration-gemfire/src/main/java/org/springframework/integration/gemfire/outbound/CacheWritingMessageHandler.java
+++ b/spring-integration-gemfire/src/main/java/org/springframework/integration/gemfire/outbound/CacheWritingMessageHandler.java
@@ -16,12 +16,18 @@
package org.springframework.integration.gemfire.outbound;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Map.Entry;
import org.springframework.data.gemfire.GemfireCallback;
import org.springframework.data.gemfire.GemfireTemplate;
+import org.springframework.expression.Expression;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.Message;
import org.springframework.integration.core.MessageHandler;
+import org.springframework.integration.handler.AbstractMessageHandler;
import org.springframework.util.Assert;
import com.gemstone.gemfire.GemFireCheckedException;
@@ -29,30 +35,37 @@ import com.gemstone.gemfire.GemFireException;
import com.gemstone.gemfire.cache.Region;
/**
- * A {@link MessageHandler} implementation that writes to a GemFire Region.
- * The Message's payload must be an instance of java.util.Map.
+ * A {@link MessageHandler} implementation that writes to a GemFire Region. The
+ * Message's payload must be an instance of java.util.Map.
*
* @author Mark Fisher
+ * @author David Turanski
* @since 2.1
*/
-public class CacheWritingMessageHandler implements MessageHandler {
+public class CacheWritingMessageHandler extends AbstractMessageHandler {
+ private final Map cacheEntryExpressions = new LinkedHashMap();
private final GemfireTemplate gemfireTemplate = new GemfireTemplate();
-
@SuppressWarnings("rawtypes")
public CacheWritingMessageHandler(Region region) {
Assert.notNull(region, "region must not be null");
this.gemfireTemplate.setRegion(region);
- this.gemfireTemplate.afterPropertiesSet();
+ this.gemfireTemplate.afterPropertiesSet();
}
-
-
- public void handleMessage(Message> message) {
- // TODO: add support for more options to get key/value (SpEL?)
+
+ @Override
+ public void handleMessageInternal(Message> message) {
Object payload = message.getPayload();
- Assert.isTrue(payload instanceof Map, "only Map payloads are supported");
- final Map, ?> map = (Map, ?>) payload;
+ Map, ?> cacheValues = (cacheEntryExpressions.size() > 0)?parseCacheEntries(message):null;
+
+ if (cacheValues == null) {
+ Assert.isTrue(payload instanceof Map, "If cache entry expressions are not configured, then payload must be a Map");
+ cacheValues = (Map, ?>) payload;
+ }
+
+ final Map, ?> map = cacheValues;
+
this.gemfireTemplate.execute(new GemfireCallback