diff --git a/spring-batch-infrastructure/pom.xml b/spring-batch-infrastructure/pom.xml index 6f1c075b8..fad12c11f 100644 --- a/spring-batch-infrastructure/pom.xml +++ b/spring-batch-infrastructure/pom.xml @@ -190,7 +190,7 @@ org.springframework.data - spring-data-commons-core + spring-data-commons true @@ -203,6 +203,16 @@ spring-data-neo4j true + + org.springframework.data + spring-data-gemfire + true + + + org.springframework.data + spring-data-redis + true + org.codehaus.woodstox woodstox-core-asl diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/KeyValueItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/KeyValueItemWriter.java new file mode 100644 index 000000000..08a5bd3df --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/KeyValueItemWriter.java @@ -0,0 +1,84 @@ +/* + * Copyright 2002-2013 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.batch.item; + +import java.util.List; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.core.convert.converter.Converter; +import org.springframework.util.Assert; + +/** + * A base class to implement any {@link ItemWriter} that writes to a key value store + * using a {@link Converter} to derive a key from an item + * + * @author David Turanski + * @since 2.2 + * + */ +public abstract class KeyValueItemWriter implements ItemWriter, InitializingBean { + + protected Converter itemKeyMapper; + protected boolean delete; + + /* (non-Javadoc) + * @see org.springframework.batch.item.ItemWriter#write(java.util.List) + */ + @Override + public void write(List items) throws Exception { + if (items == null) { + return; + } + for (V item : items) { + K key = itemKeyMapper.convert(item); + writeKeyValue(key, item); + } + } + + /** + * Subclasses implement this method to write each item to key value store + * @param key the key + * @param value the item + */ + protected abstract void writeKeyValue(K key, V value); + + /** + * afterPropertiesSet() hook + */ + protected abstract void init(); + + /** + * Set the {@link Converter} to use to derive the key from the item + * @param itemKeyMapper + */ + public void setItemKeyMapper(Converter itemKeyMapper) { + this.itemKeyMapper = itemKeyMapper; + } + + /** + * Sets the delete flag to have the item writer perform deletes + * @param delete + */ + public void setDelete(boolean delete) { + this.delete = delete; + } + + /* (non-Javadoc) + * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() + */ + @Override + public void afterPropertiesSet() throws Exception { + Assert.notNull(itemKeyMapper, "itemKeyMapper requires a Converter type."); + init(); + } +} diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/SpELItemKeyMapper.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/SpELItemKeyMapper.java new file mode 100644 index 000000000..f4c1b8d97 --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/SpELItemKeyMapper.java @@ -0,0 +1,40 @@ +/* + * Copyright 2002-2013 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.batch.item; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; + +/** + * An implementation of {@link Converter} that uses SpEL to map a Value to a key + * @author David Turanski + * @since 2.2 + */ +public class SpELItemKeyMapper implements Converter { + private final ExpressionParser parser = new SpelExpressionParser(); + private final Expression parsedExpression; + + public SpELItemKeyMapper(String keyExpression) { + parsedExpression = parser.parseExpression(keyExpression); + } + /* (non-Javadoc) + * @see org.springframework.batch.item.ItemKeyMapper#mapKey(java.lang.Object) + */ + @SuppressWarnings("unchecked") + @Override + public K convert(V item) { + return (K) parsedExpression.getValue(item); + } +} diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/GemfireItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/GemfireItemWriter.java new file mode 100644 index 000000000..0c8b64a91 --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/GemfireItemWriter.java @@ -0,0 +1,55 @@ +/* + * Copyright 2002-2013 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.batch.item.data; + +import org.springframework.batch.item.KeyValueItemWriter; +import org.springframework.data.gemfire.GemfireTemplate; +import org.springframework.util.Assert; + +/** + * An {@ link ItemWriter} that stores items in GemFire + * + * @author David Turanski + * @since 2.2 + * + */ +public class GemfireItemWriter extends KeyValueItemWriter { + private GemfireTemplate gemfireTemplate; + /** + * @param gemfireTemplate the {@link GemfireTemplate} to set + */ + public void setTemplate(GemfireTemplate gemfireTemplate) { + this.gemfireTemplate = gemfireTemplate; + } + + /* (non-Javadoc) + * @see org.springframework.batch.item.KeyValueItemWriter#writeKeyValue(java.lang.Object, java.lang.Object) + */ + @Override + protected void writeKeyValue(K key, V value) { + if (delete) { + gemfireTemplate.remove(key); + } else { + gemfireTemplate.put(key, value); + } + } + + /* (non-Javadoc) + * @see org.springframework.batch.item.KeyValueItemWriter#init() + */ + @Override + protected void init() { + Assert.notNull(gemfireTemplate, "A GemfireTemplate is required."); + } + +} diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/SpELMappingGemfireItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/SpELMappingGemfireItemWriter.java new file mode 100644 index 000000000..9c0a42b13 --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/data/SpELMappingGemfireItemWriter.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002-2013 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.batch.item.data; + +import org.springframework.batch.item.SpELItemKeyMapper; +import org.springframework.util.Assert; + +/** + * A convenient {@link GemfireItemWriter} implementation that uses a {@ link SpELItemKeyMapper} + * + * @author David Turanski + * @since 2.2 + */ +public class SpELMappingGemfireItemWriter extends GemfireItemWriter{ + /** + * A constructor that accepts a SpEL expression used to derive the key + * @param keyExpression + */ + SpELMappingGemfireItemWriter(String keyExpression) { + super(); + Assert.hasText(keyExpression,"a valid keyExpression is required."); + setItemKeyMapper(new SpELItemKeyMapper(keyExpression)); + } +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/data/GemfireItemWriterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/data/GemfireItemWriterTests.java new file mode 100644 index 000000000..657b64833 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/data/GemfireItemWriterTests.java @@ -0,0 +1,133 @@ +package org.springframework.batch.item.data; + +import static org.junit.Assert.fail; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.batch.item.SpELItemKeyMapper; +import org.springframework.data.gemfire.GemfireTemplate; +import org.springframework.core.convert.converter.Converter; + +@SuppressWarnings({ "rawtypes", "serial", "unchecked" }) +public class GemfireItemWriterTests { + + private GemfireItemWriter writer; + @Mock + private GemfireTemplate template; + + //private PlatformTransactionManager transactionManager = new ResourcelessTransactionManager(); + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + writer = new GemfireItemWriter(); + writer.setTemplate(template); + writer.setItemKeyMapper(new SpELItemKeyMapper("bar.val")); + writer.afterPropertiesSet(); + } + + @Test + public void testAfterPropertiesSet() throws Exception { + writer = new GemfireItemWriter(); + + try { + writer.afterPropertiesSet(); + fail("Expected exception was not thrown"); + } catch (IllegalArgumentException iae) { + } + + writer.setTemplate(template); + try { + writer.afterPropertiesSet(); + fail("Expected exception was not thrown"); + } catch (IllegalArgumentException iae) { + } + + writer.setItemKeyMapper(new SpELItemKeyMapper("foo")); + writer.afterPropertiesSet(); + } + + @Test + public void testBasicWrite() throws Exception { + List items = new ArrayList() { + { + add(new Foo(new Bar("val1"))); + add(new Foo(new Bar("val2"))); + } + }; + + writer.write(items); + + verify(template).put("val1", items.get(0)); + verify(template).put("val2", items.get(1)); + } + + @Test + public void testBasicDelete() throws Exception { + List items = new ArrayList() { + { + add(new Foo(new Bar("val1"))); + add(new Foo(new Bar("val2"))); + } + }; + writer.setDelete(true); + writer.write(items); + + verify(template).remove("val1"); + verify(template).remove("val2"); + } + + @Test + public void testWriteWithCustomItemKeyMapper() throws Exception { + List items = new ArrayList() { + { + add(new Foo(new Bar("val1"))); + add(new Foo(new Bar("val2"))); + } + }; + writer = new GemfireItemWriter(); + writer.setTemplate(template); + writer.setItemKeyMapper(new Converter() { + + @Override + public String convert(Foo item) { + String index = item.bar.val.replaceAll("val", ""); + return "item" + index; + } + }); + writer.afterPropertiesSet(); + writer.write(items); + + verify(template).put("item1", items.get(0)); + verify(template).put("item2", items.get(1)); + } + + @Test + public void testWriteNoTransactionNoItems() throws Exception { + writer.write(null); + verifyZeroInteractions(template); + } + + static class Foo { + public Bar bar; + + public Foo(Bar bar) { + this.bar = bar; + } + } + + static class Bar { + public String val; + + public Bar(String b1) { + this.val = b1; + } + } +} diff --git a/spring-batch-parent/pom.xml b/spring-batch-parent/pom.xml index 53eec5b43..d68929fe5 100644 --- a/spring-batch-parent/pom.xml +++ b/spring-batch-parent/pom.xml @@ -152,6 +152,11 @@ Apache Foundation Snapshot Repository http://people.apache.org/maven-snapshot-repository/ + + springsource-milestone + SpringSource Milestone Repository + https://repo.springsource.org/libs-milestone/ + @@ -691,8 +696,8 @@ org.springframework.data - spring-data-commons-core - 1.4.0.RC1 + spring-data-commons + 1.5.0.RELEASE true @@ -710,7 +715,19 @@ org.springframework.data spring-data-neo4j - 2.1.0.RELEASE + 2.2.0.RELEASE + true + + + org.springframework.data + spring-data-gemfire + 1.3.0.M1 + true + + + org.springframework.data + spring-data-redis + 1.0.3.RELEASE true diff --git a/spring-batch-samples/pom.xml b/spring-batch-samples/pom.xml index 41e050dd2..20f53e65e 100644 --- a/spring-batch-samples/pom.xml +++ b/spring-batch-samples/pom.xml @@ -232,7 +232,7 @@ org.springframework.data - spring-data-commons-core + spring-data-commons true