From a7f8eb5fbe5dc8026e70137cf481b279c511fd88 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Mon, 31 Dec 2012 13:51:49 +0100 Subject: [PATCH] Making basic inserts with mapping work. --- .../spring/core/CouchbaseOperations.java | 11 +++ .../spring/core/CouchbaseTemplate.java | 9 ++ .../core/convert/CouchbaseConverter.java | 8 +- .../spring/core/convert/CouchbaseWriter.java | 3 +- .../convert/MappingCouchbaseConverter.java | 82 +++++++++++++++++-- .../mapping/ConvertedCouchbaseDocument.java | 71 ++++++++++++++++ .../{config => }/TestApplicationConfig.java | 7 +- .../cache/CouchbaseCacheManagerTest.java | 2 +- .../spring/cache/CouchbaseCacheTest.java | 2 +- .../AbstractCouchbaseConfigurationTest.java | 67 +++++++++++++++ .../java/com/couchbase/spring/core/Beer.java} | 41 +++++++++- .../spring/core/CouchbaseTemplateTest.java | 59 +++++++++++++ 12 files changed, 344 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/couchbase/spring/core/mapping/ConvertedCouchbaseDocument.java rename src/test/java/com/couchbase/spring/{config => }/TestApplicationConfig.java (91%) create mode 100644 src/test/java/com/couchbase/spring/config/AbstractCouchbaseConfigurationTest.java rename src/{main/java/com/couchbase/spring/core/CouchbaseFactory.java => test/java/com/couchbase/spring/core/Beer.java} (62%) create mode 100644 src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseOperations.java b/src/main/java/com/couchbase/spring/core/CouchbaseOperations.java index c6641118..2fc5de58 100644 --- a/src/main/java/com/couchbase/spring/core/CouchbaseOperations.java +++ b/src/main/java/com/couchbase/spring/core/CouchbaseOperations.java @@ -24,4 +24,15 @@ package com.couchbase.spring.core; public interface CouchbaseOperations { + /** + * Insert the object into the connected bucket. + * + *

+ * The object is converted to a JSON representation using an instance of + * {@link CouchbaseConverter}. + *

+ * + * @param objectToSave the object to store in the bucket. + */ + void insert(Object objectToSave); } diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java b/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java index 59fbd176..fdf21a00 100644 --- a/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java +++ b/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java @@ -25,6 +25,7 @@ package com.couchbase.spring.core; import com.couchbase.client.CouchbaseClient; import com.couchbase.spring.core.convert.CouchbaseConverter; import com.couchbase.spring.core.convert.MappingCouchbaseConverter; +import com.couchbase.spring.core.mapping.ConvertedCouchbaseDocument; public class CouchbaseTemplate implements CouchbaseOperations { @@ -47,5 +48,13 @@ public class CouchbaseTemplate implements CouchbaseOperations { return converter; } + @Override + public void insert(Object objectToSave) { + ConvertedCouchbaseDocument converted = new ConvertedCouchbaseDocument(); + couchbaseConverter.write(objectToSave, converted); + + client.set(converted.getId(), converted.getExpiry(), converted.getValue()); + } + } diff --git a/src/main/java/com/couchbase/spring/core/convert/CouchbaseConverter.java b/src/main/java/com/couchbase/spring/core/convert/CouchbaseConverter.java index 927c40f6..f0c06148 100644 --- a/src/main/java/com/couchbase/spring/core/convert/CouchbaseConverter.java +++ b/src/main/java/com/couchbase/spring/core/convert/CouchbaseConverter.java @@ -22,6 +22,7 @@ package com.couchbase.spring.core.convert; +import com.couchbase.spring.core.mapping.ConvertedCouchbaseDocument; import com.couchbase.spring.core.mapping.CouchbasePersistentEntity; import com.couchbase.spring.core.mapping.CouchbasePersistentProperty; import org.springframework.data.convert.EntityConverter; @@ -29,8 +30,7 @@ import org.springframework.data.convert.EntityReader; public interface CouchbaseConverter extends EntityConverter, - CouchbasePersistentProperty, Object, Object>, - CouchbaseWriter, - EntityReader { - + CouchbasePersistentProperty, Object, ConvertedCouchbaseDocument>, + CouchbaseWriter, + EntityReader { } diff --git a/src/main/java/com/couchbase/spring/core/convert/CouchbaseWriter.java b/src/main/java/com/couchbase/spring/core/convert/CouchbaseWriter.java index 7611c807..99b4fd8e 100644 --- a/src/main/java/com/couchbase/spring/core/convert/CouchbaseWriter.java +++ b/src/main/java/com/couchbase/spring/core/convert/CouchbaseWriter.java @@ -24,6 +24,7 @@ package com.couchbase.spring.core.convert; import org.springframework.data.convert.EntityWriter; -public interface CouchbaseWriter extends EntityWriter { +public interface CouchbaseWriter extends + EntityWriter { } \ No newline at end of file diff --git a/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java b/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java index 0fed7fb4..545edfdf 100644 --- a/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java +++ b/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java @@ -22,16 +22,29 @@ package com.couchbase.spring.core.convert; -import com.couchbase.spring.core.CouchbaseFactory; +import com.couchbase.spring.core.mapping.ConvertedCouchbaseDocument; import com.couchbase.spring.core.mapping.CouchbasePersistentEntity; import com.couchbase.spring.core.mapping.CouchbasePersistentProperty; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.codehaus.jackson.JsonEncoding; +import org.codehaus.jackson.JsonFactory; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.JsonGenerator; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.convert.support.ConversionServiceFactory; import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.mapping.model.BeanWrapper; +import org.springframework.data.mapping.model.MappingException; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; +import org.springframework.core.convert.ConversionService; +import org.springframework.data.mapping.PropertyHandler; public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implements ApplicationContextAware { @@ -55,22 +68,79 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter } @Override - public R read(Class type, Object s) { + public R read(Class type, ConvertedCouchbaseDocument s) { throw new UnsupportedOperationException("Not supported yet."); } @Override - public void write(Object source, Object target) { + public void write(Object source, ConvertedCouchbaseDocument target) { if(source == null) { return; } TypeInformation type = ClassTypeInformation.from(source.getClass()); - writeInternal(source, target, type); + try { + writeInternal(source, target, type); + } catch (IOException ex) { + throw new MappingException("Could not translate to JSON while converting " + + source.getClass().getName()); + } + } - protected void writeInternal(Object source, Object target, TypeInformation type) { - System.out.println(type.getActualType()); + protected void writeInternal(final Object source, + ConvertedCouchbaseDocument target, TypeInformation type) + throws IOException { + CouchbasePersistentEntity entity = mappingContext.getPersistentEntity( + source.getClass()); + + if(entity == null) { + throw new MappingException("No mapping metadata found for entity of type " + + source.getClass().getName()); + } + + final CouchbasePersistentProperty idProperty = entity.getIdProperty(); + if(idProperty == null) { + throw new MappingException("ID property required for entity of type " + + source.getClass().getName()); + } + + final BeanWrapper, Object> wrapper = + BeanWrapper.create(source, conversionService); + + String id = wrapper.getProperty(idProperty, String.class, false); + target.setId(id); + + JsonFactory jsonFactory = new JsonFactory(); + OutputStream jsonStream = new ByteArrayOutputStream(); + final JsonGenerator jsonGenerator = jsonFactory.createJsonGenerator( + jsonStream, JsonEncoding.UTF8); + + jsonGenerator.writeStartObject(); + entity.doWithProperties(new PropertyHandler() { + @Override + public void doWithPersistentProperty(CouchbasePersistentProperty prop) { + if(prop.equals(idProperty)) { + return; + } + + Object propertyValue = wrapper.getProperty(prop, prop.getType(), false); + if(propertyValue != null) { + try { + jsonGenerator.writeFieldName(prop.getFieldName()); + jsonGenerator.writeObject(propertyValue); + } catch (IOException ex) { + throw new MappingException("Could not translate to JSON while converting " + + source.getClass().getName()); + } + } + + } + }); + jsonGenerator.writeEndObject(); + jsonGenerator.close(); + + target.setValue(jsonStream.toString()); } @Override diff --git a/src/main/java/com/couchbase/spring/core/mapping/ConvertedCouchbaseDocument.java b/src/main/java/com/couchbase/spring/core/mapping/ConvertedCouchbaseDocument.java new file mode 100644 index 00000000..cf6bc368 --- /dev/null +++ b/src/main/java/com/couchbase/spring/core/mapping/ConvertedCouchbaseDocument.java @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2009-2012 Couchbase, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING + * IN THE SOFTWARE. + */ + +package com.couchbase.spring.core.mapping; + +public class ConvertedCouchbaseDocument { + + private String id; + + private String value; + + private int expiry; + + public ConvertedCouchbaseDocument() { + this("", "", 0); + } + + public ConvertedCouchbaseDocument(String id, String value) { + this(id, value, 0); + } + + public ConvertedCouchbaseDocument(String id, String value, int expiry) { + this.id = id; + this.value = value; + this.expiry = expiry; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public int getExpiry() { + return expiry; + } + + public void setExpiry(int expiry) { + this.expiry = expiry; + } + +} diff --git a/src/test/java/com/couchbase/spring/config/TestApplicationConfig.java b/src/test/java/com/couchbase/spring/TestApplicationConfig.java similarity index 91% rename from src/test/java/com/couchbase/spring/config/TestApplicationConfig.java rename to src/test/java/com/couchbase/spring/TestApplicationConfig.java index e9165232..4aba2b8c 100644 --- a/src/test/java/com/couchbase/spring/config/TestApplicationConfig.java +++ b/src/test/java/com/couchbase/spring/TestApplicationConfig.java @@ -20,9 +20,10 @@ * IN THE SOFTWARE. */ -package com.couchbase.spring.config; +package com.couchbase.spring; import com.couchbase.client.CouchbaseClient; +import com.couchbase.spring.config.AbstractCouchbaseConfiguration; import java.io.IOException; import java.net.URI; import java.util.Arrays; @@ -32,12 +33,13 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @Configuration -public class TestApplicationConfig { +public class TestApplicationConfig extends AbstractCouchbaseConfiguration { @Autowired private Environment env; @Bean + @Override public CouchbaseClient couchbaseClient() throws IOException { String defaultHost = "http://127.0.0.1:8091/pools"; String host = env.getProperty("couchbase.host", defaultHost); @@ -46,4 +48,5 @@ public class TestApplicationConfig { String pass = env.getProperty("couchbase.password", ""); return new CouchbaseClient(Arrays.asList(URI.create(host)), bucket, pass); } + } diff --git a/src/test/java/com/couchbase/spring/cache/CouchbaseCacheManagerTest.java b/src/test/java/com/couchbase/spring/cache/CouchbaseCacheManagerTest.java index e71ecea9..da538f87 100644 --- a/src/test/java/com/couchbase/spring/cache/CouchbaseCacheManagerTest.java +++ b/src/test/java/com/couchbase/spring/cache/CouchbaseCacheManagerTest.java @@ -23,7 +23,7 @@ package com.couchbase.spring.cache; import com.couchbase.client.CouchbaseClient; -import com.couchbase.spring.config.TestApplicationConfig; +import com.couchbase.spring.TestApplicationConfig; import java.util.HashMap; import static org.junit.Assert.*; import org.junit.Test; diff --git a/src/test/java/com/couchbase/spring/cache/CouchbaseCacheTest.java b/src/test/java/com/couchbase/spring/cache/CouchbaseCacheTest.java index 6e09ea1a..1e149c8f 100644 --- a/src/test/java/com/couchbase/spring/cache/CouchbaseCacheTest.java +++ b/src/test/java/com/couchbase/spring/cache/CouchbaseCacheTest.java @@ -23,7 +23,7 @@ package com.couchbase.spring.cache; import com.couchbase.client.CouchbaseClient; -import com.couchbase.spring.config.TestApplicationConfig; +import com.couchbase.spring.TestApplicationConfig; import static org.junit.Assert.*; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/src/test/java/com/couchbase/spring/config/AbstractCouchbaseConfigurationTest.java b/src/test/java/com/couchbase/spring/config/AbstractCouchbaseConfigurationTest.java new file mode 100644 index 00000000..2604755b --- /dev/null +++ b/src/test/java/com/couchbase/spring/config/AbstractCouchbaseConfigurationTest.java @@ -0,0 +1,67 @@ +/** + * Copyright (C) 2009-2012 Couchbase, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING + * IN THE SOFTWARE. + */ + +package com.couchbase.spring.config; + +import com.couchbase.client.CouchbaseClient; +import com.couchbase.spring.TestApplicationConfig; +import com.couchbase.spring.core.mapping.Document; +import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * Unit test for {@link AbstractCouchbaseConfiguration} + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestApplicationConfig.class) +public class AbstractCouchbaseConfigurationTest { + + @Autowired + private CouchbaseClient client; + + @Test + public void usesConfigClassPackageAsBaseMappingPackage() throws Exception { + AbstractCouchbaseConfiguration config = new SampleCouchbaseConfiguration(); + + assertEquals(config.getMappingBasePackage(), + SampleCouchbaseConfiguration.class.getPackage().getName()); + assertEquals(config.getInitialEntitySet().size(), 1); + assertTrue(config.getInitialEntitySet().contains(Entity.class)); + } + + class SampleCouchbaseConfiguration extends AbstractCouchbaseConfiguration { + @Bean + @Override + public CouchbaseClient couchbaseClient() throws Exception { + return client; + } + } + + @Document + static class Entity { + } +} diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseFactory.java b/src/test/java/com/couchbase/spring/core/Beer.java similarity index 62% rename from src/main/java/com/couchbase/spring/core/CouchbaseFactory.java rename to src/test/java/com/couchbase/spring/core/Beer.java index 43acd95f..b160b40a 100644 --- a/src/main/java/com/couchbase/spring/core/CouchbaseFactory.java +++ b/src/test/java/com/couchbase/spring/core/Beer.java @@ -22,10 +22,45 @@ package com.couchbase.spring.core; -import com.couchbase.client.CouchbaseClient; +import org.springframework.data.annotation.Id; -public interface CouchbaseFactory { +/** + * Test class for persisting and loading from {@link CouchbaseTemplate}. + */ +public class Beer { - CouchbaseClient getDb(); + @Id + private final String id; + + private String name; + + private boolean active = true; + + public Beer(String id) { + this.id = id; + } + + @Override + public String toString() { + return "Beer [id=" + id + ", name=" + name + ", active=" + active + "]"; + } + + public Beer setName(String name) { + this.name = name; + return this; + } + + public String getName() { + return name; + } + + public Beer setActive(boolean active) { + this.active = active; + return this; + } + + public boolean getActive() { + return active; + } } diff --git a/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java b/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java new file mode 100644 index 00000000..8b58cbec --- /dev/null +++ b/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java @@ -0,0 +1,59 @@ +/** + * Copyright (C) 2009-2012 Couchbase, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING + * IN THE SOFTWARE. + */ + +package com.couchbase.spring.core; + +import com.couchbase.client.CouchbaseClient; +import com.couchbase.spring.TestApplicationConfig; +import net.spy.memcached.internal.GetFuture; +import static 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; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestApplicationConfig.class) +public class CouchbaseTemplateTest { + + @Autowired + private CouchbaseClient client; + + @Autowired + private CouchbaseTemplate template; + + @Test + public void insertsSimpleEntityCorrectly() throws Exception { + String id = "beers:awesome-stout"; + String name = "The Awesome Stout"; + boolean active = false; + Beer beer = new Beer(id).setName(name).setActive(active); + + template.insert(beer); + Object result = (String) client.get(id); + + String expected = "{\"active\":" + active + ",\"name\":\"" + name + "\"}"; + assertNotNull(result); + assertEquals(expected, result); + } +}