From 2788104f0d67a114dff0d0e82e72b5593e811cc7 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Mon, 17 Dec 2012 13:46:20 +0100 Subject: [PATCH 01/12] Initial Classes (not working) for mapping usage. --- pom.xml | 5 ++ .../spring/core/CouchbaseFactory.java | 31 +++++++++ .../convert/AbstractCouchbaseConverter.java | 47 +++++++++++++ .../core/convert/CouchbaseConverter.java | 36 ++++++++++ .../spring/core/convert/CouchbaseWriter.java | 29 ++++++++ .../convert/MappingCouchbaseConverter.java | 68 +++++++++++++++++++ .../BasicCouchbasePersistentEntity.java | 52 ++++++++++++++ .../BasicCouchbasePersistentProperty.java | 47 +++++++++++++ .../mapping/CouchbasePersistentEntity.java | 30 ++++++++ .../mapping/CouchbasePersistentProperty.java | 31 +++++++++ 10 files changed, 376 insertions(+) create mode 100644 src/main/java/com/couchbase/spring/core/CouchbaseFactory.java create mode 100644 src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java create mode 100644 src/main/java/com/couchbase/spring/core/convert/CouchbaseConverter.java create mode 100644 src/main/java/com/couchbase/spring/core/convert/CouchbaseWriter.java create mode 100644 src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java create mode 100644 src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentEntity.java create mode 100644 src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java create mode 100644 src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentEntity.java create mode 100644 src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentProperty.java diff --git a/pom.xml b/pom.xml index ca2642b3..940ad405 100644 --- a/pom.xml +++ b/pom.xml @@ -67,5 +67,10 @@ 3.1.3.RELEASE test + + org.springframework.data + spring-data-commons-core + 1.4.0.RELEASE + diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseFactory.java b/src/main/java/com/couchbase/spring/core/CouchbaseFactory.java new file mode 100644 index 00000000..43acd95f --- /dev/null +++ b/src/main/java/com/couchbase/spring/core/CouchbaseFactory.java @@ -0,0 +1,31 @@ +/** + * 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; + +public interface CouchbaseFactory { + + CouchbaseClient getDb(); + +} diff --git a/src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java b/src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java new file mode 100644 index 00000000..fbe325e1 --- /dev/null +++ b/src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java @@ -0,0 +1,47 @@ +/** + * 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.convert; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.support.GenericConversionService; + +public abstract class AbstractCouchbaseConverter implements CouchbaseConverter, + InitializingBean { + + protected final GenericConversionService conversionService; + + public AbstractCouchbaseConverter( + GenericConversionService conversionService) { + this.conversionService = conversionService; + } + + public ConversionService getConversionService() { + return conversionService; + } + + public void afterPropertiesSet() throws Exception { + + } + +} diff --git a/src/main/java/com/couchbase/spring/core/convert/CouchbaseConverter.java b/src/main/java/com/couchbase/spring/core/convert/CouchbaseConverter.java new file mode 100644 index 00000000..927c40f6 --- /dev/null +++ b/src/main/java/com/couchbase/spring/core/convert/CouchbaseConverter.java @@ -0,0 +1,36 @@ +/** + * 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.convert; + +import com.couchbase.spring.core.mapping.CouchbasePersistentEntity; +import com.couchbase.spring.core.mapping.CouchbasePersistentProperty; +import org.springframework.data.convert.EntityConverter; +import org.springframework.data.convert.EntityReader; + +public interface CouchbaseConverter extends + EntityConverter, + CouchbasePersistentProperty, Object, Object>, + 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 new file mode 100644 index 00000000..7611c807 --- /dev/null +++ b/src/main/java/com/couchbase/spring/core/convert/CouchbaseWriter.java @@ -0,0 +1,29 @@ +/** + * 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.convert; + +import org.springframework.data.convert.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 new file mode 100644 index 00000000..786c069d --- /dev/null +++ b/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java @@ -0,0 +1,68 @@ +/** + * 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.convert; + +import com.couchbase.spring.core.CouchbaseFactory; +import com.couchbase.spring.core.mapping.CouchbasePersistentEntity; +import com.couchbase.spring.core.mapping.CouchbasePersistentProperty; +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; + +public class MappingCouchbaseConverter extends AbstractCouchbaseConverter + implements ApplicationContextAware { + + protected ApplicationContext applicationContext; + protected final MappingContext, + CouchbasePersistentProperty> mappingContext; + + @SuppressWarnings("deprecation") + public MappingCouchbaseConverter(CouchbaseFactory couchbaseFactory, + MappingContext, + CouchbasePersistentProperty> mappingContext) { + super(ConversionServiceFactory.createDefaultConversionService()); + + this.mappingContext = mappingContext; + } + + public MappingContext, + CouchbasePersistentProperty> getMappingContext() { + return mappingContext; + } + + public R read(Class type, Object s) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void write(Object t, Object s) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.applicationContext = applicationContext; + } + +} diff --git a/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentEntity.java b/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentEntity.java new file mode 100644 index 00000000..23d80321 --- /dev/null +++ b/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentEntity.java @@ -0,0 +1,52 @@ +/** + * 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; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.expression.BeanFactoryAccessor; +import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.data.mapping.model.BasicPersistentEntity; +import org.springframework.data.util.TypeInformation; +import org.springframework.expression.spel.support.StandardEvaluationContext; + +public class BasicCouchbasePersistentEntity + extends BasicPersistentEntity + implements CouchbasePersistentEntity, ApplicationContextAware { + + private final StandardEvaluationContext context; + + public BasicCouchbasePersistentEntity(TypeInformation typeInformation) { + super(typeInformation); + + this.context = new StandardEvaluationContext(); + } + + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + context.addPropertyAccessor(new BeanFactoryAccessor()); + context.setBeanResolver(new BeanFactoryResolver(applicationContext)); + context.setRootObject(applicationContext); + } +} diff --git a/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java b/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java new file mode 100644 index 00000000..94e711ea --- /dev/null +++ b/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java @@ -0,0 +1,47 @@ +/** + * 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; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; +import org.springframework.data.mapping.Association; +import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty; +import org.springframework.data.mapping.model.SimpleTypeHolder; + + +public class BasicCouchbasePersistentProperty + extends AnnotationBasedPersistentProperty + implements CouchbasePersistentProperty { + + public BasicCouchbasePersistentProperty(Field field, + PropertyDescriptor propertyDescriptor, CouchbasePersistentEntity owner, + SimpleTypeHolder simpleTypeHolder) { + super(field, propertyDescriptor, owner, simpleTypeHolder); + } + + @Override + protected Association createAssociation() { + return new Association(this, null); + } + +} diff --git a/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentEntity.java b/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentEntity.java new file mode 100644 index 00000000..26d7c56a --- /dev/null +++ b/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentEntity.java @@ -0,0 +1,30 @@ +/** + * 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; + +import org.springframework.data.mapping.PersistentEntity; + +public interface CouchbasePersistentEntity extends + PersistentEntity { + +} diff --git a/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentProperty.java b/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentProperty.java new file mode 100644 index 00000000..df9e7223 --- /dev/null +++ b/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentProperty.java @@ -0,0 +1,31 @@ +/** + * 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; + +import org.springframework.data.mapping.PersistentProperty; + + +public interface CouchbasePersistentProperty extends + PersistentProperty { + +} From f24249955483f54f749f3487a079d0ffebe21ba1 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Mon, 17 Dec 2012 15:52:12 +0100 Subject: [PATCH 02/12] Adding more initial code and first tests on properties. --- .../BasicCouchbasePersistentProperty.java | 35 ++++++- .../mapping/CouchbasePersistentProperty.java | 7 ++ .../spring/core/mapping/Document.java | 41 ++++++++ .../couchbase/spring/core/mapping/Field.java | 23 +++++ .../BasicCouchbasePersistentPropertyTest.java | 96 +++++++++++++++++++ 5 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/couchbase/spring/core/mapping/Document.java create mode 100644 src/main/java/com/couchbase/spring/core/mapping/Field.java create mode 100644 src/test/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentPropertyTest.java diff --git a/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java b/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java index 94e711ea..b2db7d6b 100644 --- a/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java +++ b/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java @@ -27,21 +27,54 @@ import java.lang.reflect.Field; import org.springframework.data.mapping.Association; import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty; import org.springframework.data.mapping.model.SimpleTypeHolder; +import org.springframework.util.StringUtils; - +/** + * Implements annotated property representations of a given Field instance. + * + * This object is used to gather information out of properties on objects + * that need to be persisted. For example, it supports overriding of the + * actual property name by providing custom annotations. + */ public class BasicCouchbasePersistentProperty extends AnnotationBasedPersistentProperty implements CouchbasePersistentProperty { + /** + * Create a new instance of the BasicCouchbasePersistentProperty class. + * + * @param field the field of the original reflection. + * @param propertyDescriptor the PropertyDescriptor. + * @param owner the original owner of the property. + * @param simpleTypeHolder the type holder. + */ public BasicCouchbasePersistentProperty(Field field, PropertyDescriptor propertyDescriptor, CouchbasePersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { super(field, propertyDescriptor, owner, simpleTypeHolder); } + /** + * Creates a new Association. + */ @Override protected Association createAssociation() { return new Association(this, null); } + /** + * Returns the field name of the property. + * + * The field name can be different from the actual property name by using a + * custom annotation. + */ + @Override + public String getFieldName() { + com.couchbase.spring.core.mapping.Field annotation = getField(). + getAnnotation(com.couchbase.spring.core.mapping.Field.class); + + return annotation != null && StringUtils.hasText(annotation.value()) + ? annotation.value() : field.getName(); + } + } diff --git a/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentProperty.java b/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentProperty.java index df9e7223..cc99cd4c 100644 --- a/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentProperty.java +++ b/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentProperty.java @@ -28,4 +28,11 @@ import org.springframework.data.mapping.PersistentProperty; public interface CouchbasePersistentProperty extends PersistentProperty { + /** + * Returns the field name of the property. + * + * The field name can be different from the actual property name by using a + * custom annotation. + */ + String getFieldName(); } diff --git a/src/main/java/com/couchbase/spring/core/mapping/Document.java b/src/main/java/com/couchbase/spring/core/mapping/Document.java new file mode 100644 index 00000000..e0b4dd19 --- /dev/null +++ b/src/main/java/com/couchbase/spring/core/mapping/Document.java @@ -0,0 +1,41 @@ +/** + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.springframework.data.annotation.Persistent; + +/** + * Identifies a domain object to be persisted to Couchbase. + */ +@Persistent +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE }) +public @interface Document { + +} diff --git a/src/main/java/com/couchbase/spring/core/mapping/Field.java b/src/main/java/com/couchbase/spring/core/mapping/Field.java new file mode 100644 index 00000000..8fe39a49 --- /dev/null +++ b/src/main/java/com/couchbase/spring/core/mapping/Field.java @@ -0,0 +1,23 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.couchbase.spring.core.mapping; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation to define custom metadata for document fields. + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +public @interface Field { + + /** + * The key to be used to store the field inside the document. + */ + String value() default ""; + +} diff --git a/src/test/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentPropertyTest.java b/src/test/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentPropertyTest.java new file mode 100644 index 00000000..6766b9f2 --- /dev/null +++ b/src/test/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentPropertyTest.java @@ -0,0 +1,96 @@ +/** + * 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; + +import java.lang.reflect.Field; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; +import org.springframework.data.annotation.Id; +import org.springframework.data.mapping.model.SimpleTypeHolder; +import org.springframework.data.util.ClassTypeInformation; +import org.springframework.util.ReflectionUtils; + +/** + * Verifies the correct behavior of properties on persistable objects. + */ +public class BasicCouchbasePersistentPropertyTest { + + /** + * Holds the entity to test against (contains the properties). + */ + CouchbasePersistentEntity entity; + + /** + * Create an instance of the demo entity. + */ + @Before + public void setUp() { + entity = new BasicCouchbasePersistentEntity( + ClassTypeInformation.from(Beer.class)); + } + + /** + * Verifies the name of the property without annotations. + */ + @Test + public void usesPropertyFieldName() { + Field field = ReflectionUtils.findField(Beer.class, "description"); + assertEquals("description", getPropertyFor(field).getFieldName()); + } + + /** + * Verifies the name of the property with custom name annotation. + */ + @Test + public void usesAnnotatedFieldName() { + Field field = ReflectionUtils.findField(Beer.class, "name"); + assertEquals("foobar", getPropertyFor(field).getFieldName()); + } + + /** + * Helper method to create a property out of the field. + * + * @param field the field to retrieve the properties from. + * @return the actual BasicCouchbasePersistentProperty instance. + */ + private CouchbasePersistentProperty getPropertyFor(Field field) { + return new BasicCouchbasePersistentProperty(field, null, entity, + new SimpleTypeHolder()); + } + + /** + * Simple POJO to test attribute properties and annotations. + */ + public class Beer { + + @Id + private String id; + + @com.couchbase.spring.core.mapping.Field("foobar") + String name; + + String description; + + } +} From 92c56ac8f96338d1dda954923f203739ba04a468 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Mon, 31 Dec 2012 09:21:51 +0100 Subject: [PATCH 03/12] Extending basic support for mapping and adding an abstract configuration. --- pom.xml | 5 + .../spring/cache/CouchbaseCache.java | 4 +- .../spring/cache/CouchbaseCacheManager.java | 7 +- .../AbstractCouchbaseConfiguration.java | 121 ++++++++++++++++++ .../spring/core/CouchbaseMappingContext.java | 64 +++++++++ .../spring/core/CouchbaseOperations.java | 27 ++++ .../spring/core/CouchbaseTemplate.java | 51 ++++++++ .../convert/AbstractCouchbaseConverter.java | 3 +- .../convert/MappingCouchbaseConverter.java | 22 +++- .../BasicCouchbasePersistentEntity.java | 1 + 10 files changed, 296 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/couchbase/spring/config/AbstractCouchbaseConfiguration.java create mode 100644 src/main/java/com/couchbase/spring/core/CouchbaseMappingContext.java create mode 100644 src/main/java/com/couchbase/spring/core/CouchbaseOperations.java create mode 100644 src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java diff --git a/pom.xml b/pom.xml index 940ad405..22fa28c0 100644 --- a/pom.xml +++ b/pom.xml @@ -72,5 +72,10 @@ spring-data-commons-core 1.4.0.RELEASE + + org.codehaus.jackson + jackson-core-asl + 1.9.11 + diff --git a/src/main/java/com/couchbase/spring/cache/CouchbaseCache.java b/src/main/java/com/couchbase/spring/cache/CouchbaseCache.java index adc0b9fb..1be3be41 100644 --- a/src/main/java/com/couchbase/spring/cache/CouchbaseCache.java +++ b/src/main/java/com/couchbase/spring/cache/CouchbaseCache.java @@ -27,8 +27,8 @@ import org.springframework.cache.Cache; import org.springframework.cache.support.SimpleValueWrapper; /** - * The CouchbaseCache class implements the common cache operations on top of - * a CouchbaseClient instance. + * The CouchbaseCache class implements the Spring Cache interface + * on top of Couchbase Server and the Couchbase Java SDK. */ public class CouchbaseCache implements Cache { diff --git a/src/main/java/com/couchbase/spring/cache/CouchbaseCacheManager.java b/src/main/java/com/couchbase/spring/cache/CouchbaseCacheManager.java index 42ff2f2e..213c71ea 100644 --- a/src/main/java/com/couchbase/spring/cache/CouchbaseCacheManager.java +++ b/src/main/java/com/couchbase/spring/cache/CouchbaseCacheManager.java @@ -31,8 +31,11 @@ import org.springframework.cache.Cache; import org.springframework.cache.support.AbstractCacheManager; /** - * The CouchbaseCacheManager handles CouchbaseClient connections and - * orchestrates them as needed. + * The CouchbaseCacheManager orchestrates CouchbaseCache instances. + * + * Since more than one current CouchbaseClient connection can be used + * for caching, the CouchbaseCacheManager orchestrates and handles + * them for the Spring Cache abstraction layer. */ public class CouchbaseCacheManager extends AbstractCacheManager { diff --git a/src/main/java/com/couchbase/spring/config/AbstractCouchbaseConfiguration.java b/src/main/java/com/couchbase/spring/config/AbstractCouchbaseConfiguration.java new file mode 100644 index 00000000..fa1b21f7 --- /dev/null +++ b/src/main/java/com/couchbase/spring/config/AbstractCouchbaseConfiguration.java @@ -0,0 +1,121 @@ +/** + * 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.core.CouchbaseMappingContext; +import com.couchbase.spring.core.CouchbaseTemplate; +import com.couchbase.spring.core.convert.MappingCouchbaseConverter; +import com.couchbase.spring.core.mapping.Document; +import java.util.HashSet; +import java.util.Set; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.data.annotation.Persistent; +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; + +/** + * Base class for Spring Data Couchbase configuration using JavaConfig. + */ +@Configuration +public abstract class AbstractCouchbaseConfiguration { + + /** + * Return the {@link CouchbaseClient} instance to connect to. + */ + @Bean + public abstract CouchbaseClient couchbaseClient() throws Exception; + + /** + * Creates a {@link CouchbaseTemplate}. + */ + @Bean + public CouchbaseTemplate couchbaseTemplate() throws Exception { + return new CouchbaseTemplate(couchbaseClient(), mappingCouchbaseConverter()); + } + + /** + * Creates a {@link MappingCouchbaseConverter} using the configured {@link + * #couchbaseMappingContext}. + */ + @Bean + public MappingCouchbaseConverter mappingCouchbaseConverter() throws Exception { + return new MappingCouchbaseConverter(couchbaseMappingContext()); + } + + /** + * Creates a {@link CouchbaseMappingContext} equipped with entity classes + * scanned from the mapping base package. + */ + @Bean + public CouchbaseMappingContext couchbaseMappingContext() throws Exception { + CouchbaseMappingContext mappingContext = new CouchbaseMappingContext(); + mappingContext.setInitialEntitySet(getInitialEntitySet()); + return mappingContext; + } + + /** + * Scans the mapping base package for classes annotated with {@link Document}. + */ + protected Set> getInitialEntitySet() throws ClassNotFoundException { + String basePackage = getMappingBasePackage(); + Set> initialEntitySet = new HashSet>(); + + if(StringUtils.hasText(basePackage)) { + ClassPathScanningCandidateComponentProvider componentProvider = + new ClassPathScanningCandidateComponentProvider(false); + componentProvider.addIncludeFilter( + new AnnotationTypeFilter(Document.class)); + componentProvider.addIncludeFilter( + new AnnotationTypeFilter(Persistent.class)); + + for (BeanDefinition candidate : + componentProvider.findCandidateComponents(basePackage)) { + initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(), + AbstractCouchbaseConfiguration.class.getClassLoader())); + } + } + + return initialEntitySet; + } + + /** + * Return the base package to scan for mapped {@link Document}s. Will return + * the package name of the configuration class (the concrete class, not this + * one here) by default. So if you have a {@code com.acme.AppConfig} extending + * {@link AbstractCouchbaseConfiguration} the base package will be considered + * {@code com.acme} unless the method is overridden to implement alternate + * behavior. + * + * @return the base package to scan for mapped {@link Document} classes or + * {@literal null} to not enable scanning for entities. + */ + protected String getMappingBasePackage() { + return getClass().getPackage().getName(); + } + +} diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseMappingContext.java b/src/main/java/com/couchbase/spring/core/CouchbaseMappingContext.java new file mode 100644 index 00000000..c1740f7e --- /dev/null +++ b/src/main/java/com/couchbase/spring/core/CouchbaseMappingContext.java @@ -0,0 +1,64 @@ +/** + * 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.spring.core.mapping.BasicCouchbasePersistentEntity; +import com.couchbase.spring.core.mapping.BasicCouchbasePersistentProperty; +import com.couchbase.spring.core.mapping.CouchbasePersistentProperty; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.data.mapping.context.AbstractMappingContext; +import org.springframework.data.mapping.model.SimpleTypeHolder; +import org.springframework.data.util.TypeInformation; + +public class CouchbaseMappingContext + extends AbstractMappingContext, CouchbasePersistentProperty> + implements ApplicationContextAware { + + private ApplicationContext context; + + @Override + protected BasicCouchbasePersistentEntity createPersistentEntity(TypeInformation typeInformation) { + BasicCouchbasePersistentEntity entity = new BasicCouchbasePersistentEntity(typeInformation); + if(context != null) { + entity.setApplicationContext(context); + } + return entity; + } + + @Override + protected CouchbasePersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor, BasicCouchbasePersistentEntity owner, SimpleTypeHolder simpleTypeHolder) { + return new BasicCouchbasePersistentProperty(field, descriptor, owner, simpleTypeHolder); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.context = applicationContext; + super.setApplicationContext(applicationContext); + } + +} diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseOperations.java b/src/main/java/com/couchbase/spring/core/CouchbaseOperations.java new file mode 100644 index 00000000..c6641118 --- /dev/null +++ b/src/main/java/com/couchbase/spring/core/CouchbaseOperations.java @@ -0,0 +1,27 @@ +/** + * 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; + +public interface CouchbaseOperations { + +} diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java b/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java new file mode 100644 index 00000000..59fbd176 --- /dev/null +++ b/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java @@ -0,0 +1,51 @@ +/** + * 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.core.convert.CouchbaseConverter; +import com.couchbase.spring.core.convert.MappingCouchbaseConverter; + +public class CouchbaseTemplate implements CouchbaseOperations { + + private CouchbaseClient client; + private CouchbaseConverter couchbaseConverter; + + public CouchbaseTemplate(CouchbaseClient client) { + this(client, null); + } + + public CouchbaseTemplate(CouchbaseClient client, CouchbaseConverter converter) { + this.client = client; + this.couchbaseConverter = converter == null ? getDefaultConverter(client) : converter; + } + + private CouchbaseConverter getDefaultConverter(CouchbaseClient client) { + MappingCouchbaseConverter converter = new MappingCouchbaseConverter( + new CouchbaseMappingContext()); + converter.afterPropertiesSet(); + return converter; + } + + +} diff --git a/src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java b/src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java index fbe325e1..cf54d9ff 100644 --- a/src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java +++ b/src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java @@ -40,7 +40,8 @@ public abstract class AbstractCouchbaseConverter implements CouchbaseConverter, return conversionService; } - public void afterPropertiesSet() throws Exception { + @Override + public void afterPropertiesSet() { } 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 786c069d..0fed7fb4 100644 --- a/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java +++ b/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java @@ -30,6 +30,8 @@ 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.util.ClassTypeInformation; +import org.springframework.data.util.TypeInformation; public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implements ApplicationContextAware { @@ -39,27 +41,39 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter CouchbasePersistentProperty> mappingContext; @SuppressWarnings("deprecation") - public MappingCouchbaseConverter(CouchbaseFactory couchbaseFactory, - MappingContext, + public MappingCouchbaseConverter(MappingContext, CouchbasePersistentProperty> mappingContext) { super(ConversionServiceFactory.createDefaultConversionService()); this.mappingContext = mappingContext; } + @Override public MappingContext, CouchbasePersistentProperty> getMappingContext() { return mappingContext; } + @Override public R read(Class type, Object s) { throw new UnsupportedOperationException("Not supported yet."); } - public void write(Object t, Object s) { - throw new UnsupportedOperationException("Not supported yet."); + @Override + public void write(Object source, Object target) { + if(source == null) { + return; + } + + TypeInformation type = ClassTypeInformation.from(source.getClass()); + writeInternal(source, target, type); } + protected void writeInternal(Object source, Object target, TypeInformation type) { + System.out.println(type.getActualType()); + } + + @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; diff --git a/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentEntity.java b/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentEntity.java index 23d80321..1a3c4a11 100644 --- a/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentEntity.java +++ b/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentEntity.java @@ -43,6 +43,7 @@ public class BasicCouchbasePersistentEntity this.context = new StandardEvaluationContext(); } + @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { context.addPropertyAccessor(new BeanFactoryAccessor()); From a7f8eb5fbe5dc8026e70137cf481b279c511fd88 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Mon, 31 Dec 2012 13:51:49 +0100 Subject: [PATCH 04/12] 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); + } +} From 66f6df486007a066150edd2e7ac9eff68f245e8f Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Tue, 22 Jan 2013 15:54:34 +0100 Subject: [PATCH 05/12] Cleanup and adding class-level expiry. --- .../spring/core/CouchbaseTemplate.java | 1 - .../convert/MappingCouchbaseConverter.java | 5 +--- .../BasicCouchbasePersistentEntity.java | 10 +++++++ .../mapping/CouchbasePersistentEntity.java | 6 ++++ .../spring/core/mapping/Document.java | 5 ++++ .../spring/core/CouchbaseTemplateTest.java | 30 +++++++++++++++++-- 6 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java b/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java index fdf21a00..048168c1 100644 --- a/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java +++ b/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java @@ -52,7 +52,6 @@ public class CouchbaseTemplate implements CouchbaseOperations { 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/MappingCouchbaseConverter.java b/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java index 545edfdf..1d7a56f6 100644 --- a/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java +++ b/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java @@ -28,11 +28,8 @@ 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; @@ -43,7 +40,6 @@ 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 @@ -110,6 +106,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter String id = wrapper.getProperty(idProperty, String.class, false); target.setId(id); + target.setExpiry(entity.getExpiry()); JsonFactory jsonFactory = new JsonFactory(); OutputStream jsonStream = new ByteArrayOutputStream(); diff --git a/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentEntity.java b/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentEntity.java index 1a3c4a11..ba34de13 100644 --- a/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentEntity.java +++ b/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentEntity.java @@ -50,4 +50,14 @@ public class BasicCouchbasePersistentEntity context.setBeanResolver(new BeanFactoryResolver(applicationContext)); context.setRootObject(applicationContext); } + + public int getExpiry() { + com.couchbase.spring.core.mapping.Document annotation = + getType().getAnnotation(com.couchbase.spring.core.mapping.Document.class); + + if(annotation == null) { + return 0; + } + return annotation.expiry(); + } } diff --git a/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentEntity.java b/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentEntity.java index 26d7c56a..1d42d104 100644 --- a/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentEntity.java +++ b/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentEntity.java @@ -27,4 +27,10 @@ import org.springframework.data.mapping.PersistentEntity; public interface CouchbasePersistentEntity extends PersistentEntity { + /** + * Returns the expiry time for the document. + * + * @return + */ + int getExpiry(); } diff --git a/src/main/java/com/couchbase/spring/core/mapping/Document.java b/src/main/java/com/couchbase/spring/core/mapping/Document.java index e0b4dd19..69792ba9 100644 --- a/src/main/java/com/couchbase/spring/core/mapping/Document.java +++ b/src/main/java/com/couchbase/spring/core/mapping/Document.java @@ -37,5 +37,10 @@ import org.springframework.data.annotation.Persistent; @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE }) public @interface Document { + + /** + * An optional expiry time for the document. + */ + int expiry() default 0; } diff --git a/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java b/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java index 8b58cbec..3ef5304d 100644 --- a/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java +++ b/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java @@ -24,11 +24,13 @@ package com.couchbase.spring.core; import com.couchbase.client.CouchbaseClient; import com.couchbase.spring.TestApplicationConfig; -import net.spy.memcached.internal.GetFuture; +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.data.annotation.Id; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -50,10 +52,34 @@ public class CouchbaseTemplateTest { Beer beer = new Beer(id).setName(name).setActive(active); template.insert(beer); - Object result = (String) client.get(id); + String result = (String) client.get(id); String expected = "{\"active\":" + active + ",\"name\":\"" + name + "\"}"; assertNotNull(result); assertEquals(expected, result); } + + @Test + public void insertDocumentWithExpiry() throws Exception { + String id = "simple-doc-with-expiry"; + DocumentWithExpiry doc = new DocumentWithExpiry(id); + template.insert(doc); + assertNotNull(client.get(id)); + Thread.sleep(3000); + assertNull(client.get(id)); + } + + /** + * A sample document that expires in 2 seconds. + */ + @Document(expiry=2) + class DocumentWithExpiry { + @Id + private final String id; + + public DocumentWithExpiry(String id) { + this.id = id; + } + + } } From d01aaff90df03fe355e2540efa896124e455b466 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Thu, 7 Feb 2013 09:07:10 +0100 Subject: [PATCH 06/12] Upgrading dependencies and more tests for templates. --- .gitignore | 4 + pom.xml | 8 +- .../spring/core/CouchbaseOperations.java | 80 ++++++++++++++++- .../spring/core/CouchbaseTemplate.java | 86 ++++++++++++++++++- .../convert/MappingCouchbaseConverter.java | 11 ++- .../java/com/couchbase/spring/core/Beer.java | 3 + .../spring/core/CouchbaseTemplateTest.java | 52 +++++++++-- 7 files changed, 230 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index e08c7941..bc8404d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ target/ .DS_Store + +.classpath +.project +.settings/* diff --git a/pom.xml b/pom.xml index 22fa28c0..86e1b5c2 100644 --- a/pom.xml +++ b/pom.xml @@ -42,12 +42,12 @@ couchbase couchbase-client - 1.1.0 + 1.1.2 org.springframework spring-context - 3.1.3.RELEASE + 3.2.1.RELEASE jar @@ -64,7 +64,7 @@ org.springframework spring-test - 3.1.3.RELEASE + 3.2.1.RELEASE test @@ -75,7 +75,7 @@ org.codehaus.jackson jackson-core-asl - 1.9.11 + 1.9.12 diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseOperations.java b/src/main/java/com/couchbase/spring/core/CouchbaseOperations.java index 2fc5de58..02efe96e 100644 --- a/src/main/java/com/couchbase/spring/core/CouchbaseOperations.java +++ b/src/main/java/com/couchbase/spring/core/CouchbaseOperations.java @@ -22,10 +22,15 @@ package com.couchbase.spring.core; +import java.util.Collection; + public interface CouchbaseOperations { /** - * Insert the object into the connected bucket. + * Save the given object. + * + * When the document already exists (specified by its unique id), + * then it will be overriden. Otherwise it will be created. * *

* The object is converted to a JSON representation using an instance of @@ -34,5 +39,78 @@ public interface CouchbaseOperations { * * @param objectToSave the object to store in the bucket. */ + void save(Object objectToSave); + + /** + * Save a list of objects. + * + * When one of the documents already exists (specified by its unique id), + * then it will be overriden. Otherwise it will be created. + * + * @param batchToSave the list of objects to store in the bucket. + */ + void save(Collection batchToSave); + + /** + * Insert the given object. + * + * When the document already exists (specified by its unique id), + * then it will not be overriden. Use the {@link CouchbaseOperations#save} + * method for this. + * + *

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

+ * + * @param objectToSave the object to add to the bucket. + */ void insert(Object objectToSave); + + /** + * Insert a list of objects. + * + * When one of the documents already exists (specified by its unique id), + * then it will not be overriden. Use the {@link CouchbaseOperations#save} + * method for this. + * + * @param batchToSave the list of objects to add to the bucket. + */ + void insert(Collection batchToSave); + + /** + * Update the given object. + * + * When the document does not exists (specified by its unique id), + * then it will not be created. Use the {@link CouchbaseOperations#save} + * method for this. + * + *

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

+ * + * @param objectToSave the object to add to the bucket. + */ + void update(Object objectToSave); + + /** + * Insert a list of objects. + * + * When one of the documents does not exists (specified by its unique id), + * then it will not be created. Use the {@link CouchbaseOperations#save} + * method for this. + * + * @param batchToSave the list of objects to add to the bucket. + */ + void update(Collection batchToSave); + + /** + * Find an object by its given Id and map it to the corresponding entity. + * + * @param id the unique ID of the document. + * @param entityClass the entity to map to. + * @return returns the found object or null otherwise. + */ + public T findById(String id, Class entityClass); } diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java b/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java index 048168c1..5be3427f 100644 --- a/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java +++ b/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java @@ -22,15 +22,37 @@ package com.couchbase.spring.core; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.springframework.data.mapping.context.MappingContext; + 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; +import com.couchbase.spring.core.mapping.CouchbasePersistentEntity; +import com.couchbase.spring.core.mapping.CouchbasePersistentProperty; public class CouchbaseTemplate implements CouchbaseOperations { private CouchbaseClient client; private CouchbaseConverter couchbaseConverter; + protected final MappingContext, + CouchbasePersistentProperty> mappingContext; + private static final Collection ITERABLE_CLASSES; + + static { + Set iterableClasses = new HashSet(); + iterableClasses.add(List.class.getName()); + iterableClasses.add(Collection.class.getName()); + iterableClasses.add(Iterator.class.getName()); + ITERABLE_CLASSES = Collections.unmodifiableCollection(iterableClasses); + } public CouchbaseTemplate(CouchbaseClient client) { this(client, null); @@ -39,6 +61,7 @@ public class CouchbaseTemplate implements CouchbaseOperations { public CouchbaseTemplate(CouchbaseClient client, CouchbaseConverter converter) { this.client = client; this.couchbaseConverter = converter == null ? getDefaultConverter(client) : converter; + this.mappingContext = this.couchbaseConverter.getMappingContext(); } private CouchbaseConverter getDefaultConverter(CouchbaseClient client) { @@ -48,12 +71,73 @@ public class CouchbaseTemplate implements CouchbaseOperations { return converter; } - @Override public void insert(Object objectToSave) { + ensureNotIterable(objectToSave); + + ConvertedCouchbaseDocument converted = new ConvertedCouchbaseDocument(); + couchbaseConverter.write(objectToSave, converted); + client.add(converted.getId(), converted.getExpiry(), converted.getValue()); + } + + public void insert(Collection batchToSave) { + Iterator iter = batchToSave.iterator(); + while(iter.hasNext()) { + insert(iter.next()); + } + } + + public void save(Object objectToSave) { + ensureNotIterable(objectToSave); + ConvertedCouchbaseDocument converted = new ConvertedCouchbaseDocument(); couchbaseConverter.write(objectToSave, converted); client.set(converted.getId(), converted.getExpiry(), converted.getValue()); } + + public void save(Collection batchToSave) { + Iterator iter = batchToSave.iterator(); + while(iter.hasNext()) { + save(iter.next()); + } + } + public void update(Object objectToSave) { + ensureNotIterable(objectToSave); + ConvertedCouchbaseDocument converted = new ConvertedCouchbaseDocument(); + couchbaseConverter.write(objectToSave, converted); + client.replace(converted.getId(), converted.getExpiry(), converted.getValue()); + } + + public void update(Collection batchToSave) { + Iterator iter = batchToSave.iterator(); + while(iter.hasNext()) { + save(iter.next()); + } + } + + public T findById(String id, Class entityClass) { + String result = (String) client.get(id); + if(result == null) { + return null; + } + + ConvertedCouchbaseDocument converted = new ConvertedCouchbaseDocument(id, result); + return couchbaseConverter.read(entityClass, converted); + } + + /** + * Make sure the given object is not a iterable. + * + * @param o the object to verify. + */ + protected void ensureNotIterable(Object o) { + if (null != o) { + if (o.getClass().isArray() || ITERABLE_CLASSES.contains(o.getClass().getName())) { + throw new IllegalArgumentException("Cannot use a collection here."); + } + } + } + + } 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 1d7a56f6..253012bc 100644 --- a/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java +++ b/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java @@ -31,6 +31,7 @@ import java.io.OutputStream; import org.codehaus.jackson.JsonEncoding; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.JsonGenerator; +import org.codehaus.jackson.JsonParser; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -64,8 +65,14 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter } @Override - public R read(Class type, ConvertedCouchbaseDocument s) { - throw new UnsupportedOperationException("Not supported yet."); + public R read(Class type, ConvertedCouchbaseDocument doc) { + JsonFactory jsonFactory = new JsonFactory(); + try { + JsonParser parser = jsonFactory.createJsonParser(doc.getValue()); + } catch(Exception e) { + throw new MappingException("Could not read from JSON while converting " + + doc.getId()); + } } @Override diff --git a/src/test/java/com/couchbase/spring/core/Beer.java b/src/test/java/com/couchbase/spring/core/Beer.java index b160b40a..56c18a41 100644 --- a/src/test/java/com/couchbase/spring/core/Beer.java +++ b/src/test/java/com/couchbase/spring/core/Beer.java @@ -24,6 +24,8 @@ package com.couchbase.spring.core; import org.springframework.data.annotation.Id; +import com.couchbase.spring.core.mapping.Field; + /** * Test class for persisting and loading from {@link CouchbaseTemplate}. */ @@ -34,6 +36,7 @@ public class Beer { private String name; + @Field("is_active") private boolean active = true; public Beer(String id) { diff --git a/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java b/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java index 3ef5304d..09223929 100644 --- a/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java +++ b/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java @@ -25,6 +25,7 @@ package com.couchbase.spring.core; import com.couchbase.client.CouchbaseClient; import com.couchbase.spring.TestApplicationConfig; import com.couchbase.spring.core.mapping.Document; +import com.couchbase.spring.core.mapping.Field; import static org.junit.Assert.*; import org.junit.Test; @@ -45,30 +46,70 @@ public class CouchbaseTemplateTest { private CouchbaseTemplate template; @Test - public void insertsSimpleEntityCorrectly() throws Exception { + public void saveSimpleEntityCorrectly() 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); + template.save(beer); String result = (String) client.get(id); - String expected = "{\"active\":" + active + ",\"name\":\"" + name + "\"}"; + String expected = "{\"is_active\":" + active + ",\"name\":\"" + name + "\"}"; assertNotNull(result); assertEquals(expected, result); } @Test - public void insertDocumentWithExpiry() throws Exception { + public void saveDocumentWithExpiry() throws Exception { String id = "simple-doc-with-expiry"; DocumentWithExpiry doc = new DocumentWithExpiry(id); - template.insert(doc); + template.save(doc); assertNotNull(client.get(id)); Thread.sleep(3000); assertNull(client.get(id)); } + @Test + public void insertDoesNotOverride() { + String id ="double-insert-test"; + String expected = "{\"name\":\"Mr. A\"}"; + + SimplePerson doc = new SimplePerson(id, "Mr. A"); + template.insert(doc); + String result = (String) client.get(id); + assertEquals(expected, result); + + doc = new SimplePerson(id, "Mr. B"); + template.insert(doc); + result = (String) client.get(id); + assertEquals(expected, result); + } + + @Test + public void updateDoesNotInsert() { + String id ="update-does-not-insert"; + SimplePerson doc = new SimplePerson(id, "Nice Guy"); + template.update(doc); + assertNull(client.get(id)); + } + + /** + * A sample document with just an id and property. + */ + @Document + class SimplePerson { + @Id + private final String id; + @Field + private final String name; + + public SimplePerson(String id, String name) { + this.id = id; + this.name = name; + } + } + /** * A sample document that expires in 2 seconds. */ @@ -80,6 +121,5 @@ public class CouchbaseTemplateTest { public DocumentWithExpiry(String id) { this.id = id; } - } } From 99852a23c81bc1397d3dd4e9fbd836ceb02e6396 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Thu, 7 Feb 2013 09:08:35 +0100 Subject: [PATCH 07/12] Updating README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 952d036e..fb8d21fd 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,8 @@ It is distributed from the [Couchbase Maven Repository](http://files.couchbase.c ``` Currently, the project depends on the following packages: - * couchbase.couchbase-client: 1.1.0 - * org.springframework.spring-context: 3.1.3.RELEASE + * couchbase.couchbase-client: 1.1.2 + * org.springframework.spring-context: 3.2.1.RELEASE * cglib.cglib: 2.2.2 * (When Testing) junit.junit: 4.11 From f59c182dbe16aeeb593843029bf4ecb63fc88a34 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Thu, 7 Feb 2013 11:19:23 +0100 Subject: [PATCH 08/12] Initial findById implementation. --- .../convert/MappingCouchbaseConverter.java | 36 ++++++++++++++++++- .../BasicCouchbasePersistentEntity.java | 1 + .../BasicCouchbasePersistentProperty.java | 16 +++++++++ .../mapping/CouchbasePersistentProperty.java | 2 ++ .../java/com/couchbase/spring/core/Beer.java | 4 +++ .../spring/core/CouchbaseTemplateTest.java | 16 +++++++++ 6 files changed, 74 insertions(+), 1 deletion(-) 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 253012bc..5a58134b 100644 --- a/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java +++ b/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java @@ -28,10 +28,13 @@ import com.couchbase.spring.core.mapping.CouchbasePersistentProperty; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.lang.reflect.Field; + import org.codehaus.jackson.JsonEncoding; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.JsonParser; +import org.codehaus.jackson.JsonToken; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -66,13 +69,44 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter @Override public R read(Class type, ConvertedCouchbaseDocument doc) { + CouchbasePersistentEntity entity = mappingContext.getPersistentEntity(type); + + R decoded = null; + try { + decoded = type.getConstructor(String.class).newInstance(doc.getId()); + } catch(Exception e) { + throw new MappingException("Could not instantiate object while converting " + + doc.getId()); + } + JsonFactory jsonFactory = new JsonFactory(); try { JsonParser parser = jsonFactory.createJsonParser(doc.getValue()); + parser.nextToken(); + while(parser.nextToken() != JsonToken.END_OBJECT) { + String fieldname = parser.getCurrentName(); + parser.nextToken(); + CouchbasePersistentProperty property = entity.getPersistentProperty(fieldname); + if(property == null) { + continue; + } + Field field = type.getDeclaredField(property.getOriginalName()); + field.setAccessible(true); + + if(property.getType().equals(boolean.class)) { + field.set(decoded, parser.getValueAsBoolean()); + } else if(property.getType().equals(String.class)) { + field.set(decoded, parser.getText()); + } else { + throw new MappingException("Unknown type in JSON found: " + property.getType()); + } + } } catch(Exception e) { throw new MappingException("Could not read from JSON while converting " - + doc.getId()); + + doc.getId(), e); } + + return decoded; } @Override diff --git a/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentEntity.java b/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentEntity.java index ba34de13..941d7d84 100644 --- a/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentEntity.java +++ b/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentEntity.java @@ -60,4 +60,5 @@ public class BasicCouchbasePersistentEntity } return annotation.expiry(); } + } diff --git a/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java b/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java index b2db7d6b..252cd38e 100644 --- a/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java +++ b/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java @@ -76,5 +76,21 @@ public class BasicCouchbasePersistentProperty return annotation != null && StringUtils.hasText(annotation.value()) ? annotation.value() : field.getName(); } + + /** + * Return the name of the property. + * + * This overrides the default implementation to make sure that when + * the property is retrieved from a JSON document it can be found + * even when a alias is used. + */ + @Override + public String getName() { + return getFieldName(); + } + + public String getOriginalName() { + return name; + } } diff --git a/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentProperty.java b/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentProperty.java index cc99cd4c..536f3c33 100644 --- a/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentProperty.java +++ b/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentProperty.java @@ -35,4 +35,6 @@ public interface CouchbasePersistentProperty extends * custom annotation. */ String getFieldName(); + + String getOriginalName(); } diff --git a/src/test/java/com/couchbase/spring/core/Beer.java b/src/test/java/com/couchbase/spring/core/Beer.java index 56c18a41..44a9d5ad 100644 --- a/src/test/java/com/couchbase/spring/core/Beer.java +++ b/src/test/java/com/couchbase/spring/core/Beer.java @@ -65,5 +65,9 @@ public class Beer { public boolean getActive() { return active; } + + public String getId() { + return id; + } } diff --git a/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java b/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java index 09223929..993c88b6 100644 --- a/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java +++ b/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java @@ -94,6 +94,22 @@ public class CouchbaseTemplateTest { assertNull(client.get(id)); } + @Test + public void validfindById() { + String id = "beers:findme-stout"; + String name = "The Findme Stout"; + boolean active = true; + Beer beer = new Beer(id).setName(name).setActive(active); + template.save(beer); + + Beer found = template.findById(id, Beer.class); + assertNotNull(found); + assertEquals(id, found.getId()); + assertEquals(name, found.getName()); + assertEquals(active, found.getActive()); + + } + /** * A sample document with just an id and property. */ From 83aa6e0acf71c6f97020bc60a88cf90080fad3a5 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Thu, 7 Feb 2013 14:51:56 +0100 Subject: [PATCH 09/12] Refactoring read to use instantiators. --- pom.xml | 2 +- .../spring/core/CouchbaseTemplate.java | 6 +- .../convert/AbstractCouchbaseConverter.java | 2 + .../convert/MappingCouchbaseConverter.java | 128 +++++++++++++----- .../BasicCouchbasePersistentProperty.java | 16 --- .../mapping/ConvertedCouchbaseDocument.java | 52 +++++-- .../mapping/CouchbasePersistentProperty.java | 3 +- .../spring/core/CouchbaseTemplateTest.java | 1 - 8 files changed, 141 insertions(+), 69 deletions(-) diff --git a/pom.xml b/pom.xml index 86e1b5c2..dc62902e 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@
org.codehaus.jackson - jackson-core-asl + jackson-mapper-asl 1.9.12 diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java b/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java index 5be3427f..5c342ba0 100644 --- a/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java +++ b/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java @@ -76,7 +76,7 @@ public class CouchbaseTemplate implements CouchbaseOperations { ConvertedCouchbaseDocument converted = new ConvertedCouchbaseDocument(); couchbaseConverter.write(objectToSave, converted); - client.add(converted.getId(), converted.getExpiry(), converted.getValue()); + client.add(converted.getId(), converted.getExpiry(), converted.getRawValue()); } public void insert(Collection batchToSave) { @@ -91,7 +91,7 @@ public class CouchbaseTemplate implements CouchbaseOperations { ConvertedCouchbaseDocument converted = new ConvertedCouchbaseDocument(); couchbaseConverter.write(objectToSave, converted); - client.set(converted.getId(), converted.getExpiry(), converted.getValue()); + client.set(converted.getId(), converted.getExpiry(), converted.getRawValue()); } public void save(Collection batchToSave) { @@ -106,7 +106,7 @@ public class CouchbaseTemplate implements CouchbaseOperations { ConvertedCouchbaseDocument converted = new ConvertedCouchbaseDocument(); couchbaseConverter.write(objectToSave, converted); - client.replace(converted.getId(), converted.getExpiry(), converted.getValue()); + client.replace(converted.getId(), converted.getExpiry(), converted.getRawValue()); } public void update(Collection batchToSave) { diff --git a/src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java b/src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java index cf54d9ff..ecc69ae8 100644 --- a/src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java +++ b/src/main/java/com/couchbase/spring/core/convert/AbstractCouchbaseConverter.java @@ -25,11 +25,13 @@ package com.couchbase.spring.core.convert; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.GenericConversionService; +import org.springframework.data.convert.EntityInstantiators; public abstract class AbstractCouchbaseConverter implements CouchbaseConverter, InitializingBean { protected final GenericConversionService conversionService; + protected EntityInstantiators instantiators = new EntityInstantiators(); public AbstractCouchbaseConverter( GenericConversionService conversionService) { 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 5a58134b..373c5549 100644 --- a/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java +++ b/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java @@ -25,26 +25,38 @@ 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 java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Field; +import java.util.Map; import org.codehaus.jackson.JsonEncoding; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.JsonParser; import org.codehaus.jackson.JsonToken; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.type.TypeReference; 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.convert.EntityInstantiator; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.BeanWrapper; +import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator; import org.springframework.data.mapping.model.MappingException; +import org.springframework.data.mapping.model.ParameterValueProvider; +import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider; +import org.springframework.data.mapping.model.PropertyValueProvider; +import org.springframework.data.mapping.model.SpELContext; +import org.springframework.data.mapping.model.SpELExpressionEvaluator; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; import org.springframework.data.mapping.PropertyHandler; +import org.springframework.util.Assert; public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implements ApplicationContextAware { @@ -52,6 +64,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter protected ApplicationContext applicationContext; protected final MappingContext, CouchbasePersistentProperty> mappingContext; + protected boolean useFieldAccessOnly = true; @SuppressWarnings("deprecation") public MappingCouchbaseConverter(MappingContext, @@ -67,46 +80,58 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter return mappingContext; } + + private ParameterValueProvider getParameterProvider( + CouchbasePersistentEntity entity, ConvertedCouchbaseDocument source, Object parent) { + + CouchbasePropertyValueProvider provider = new CouchbasePropertyValueProvider(source, parent); + PersistentEntityParameterValueProvider parameterProvider = + new PersistentEntityParameterValueProvider( + entity, provider, parent); + + return parameterProvider; + } + @Override public R read(Class type, ConvertedCouchbaseDocument doc) { - CouchbasePersistentEntity entity = mappingContext.getPersistentEntity(type); + return read(type, doc, null); + } + + public R read(Class type, final ConvertedCouchbaseDocument doc, Object parent) { + final CouchbasePersistentEntity entity = (CouchbasePersistentEntity) + mappingContext.getPersistentEntity(type); - R decoded = null; - try { - decoded = type.getConstructor(String.class).newInstance(doc.getId()); - } catch(Exception e) { - throw new MappingException("Could not instantiate object while converting " - + doc.getId()); - } + ParameterValueProvider provider = + getParameterProvider(entity, doc, parent); + EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity); + R instance = instantiator.createInstance(entity, provider); - JsonFactory jsonFactory = new JsonFactory(); - try { - JsonParser parser = jsonFactory.createJsonParser(doc.getValue()); - parser.nextToken(); - while(parser.nextToken() != JsonToken.END_OBJECT) { - String fieldname = parser.getCurrentName(); - parser.nextToken(); - CouchbasePersistentProperty property = entity.getPersistentProperty(fieldname); - if(property == null) { - continue; - } - Field field = type.getDeclaredField(property.getOriginalName()); - field.setAccessible(true); - - if(property.getType().equals(boolean.class)) { - field.set(decoded, parser.getValueAsBoolean()); - } else if(property.getType().equals(String.class)) { - field.set(decoded, parser.getText()); - } else { - throw new MappingException("Unknown type in JSON found: " + property.getType()); - } - } - } catch(Exception e) { - throw new MappingException("Could not read from JSON while converting " - + doc.getId(), e); - } - - return decoded; + final BeanWrapper, R> wrapper = + BeanWrapper.create(instance, conversionService); + final R result = wrapper.getBean(); + + // Set properties not already set in the constructor + entity.doWithProperties(new PropertyHandler() { + public void doWithPersistentProperty(CouchbasePersistentProperty prop) { + + boolean isConstructorProperty = entity.isConstructorArgument(prop); + boolean hasValueForProperty = doc.containsField(prop.getFieldName()); + + if (!hasValueForProperty || isConstructorProperty) { + return; + } + + Object obj = null; + if(prop.getFieldName() == "id") { + obj = doc.getId(); + } else { + obj = doc.get(prop.getFieldName()); + } + wrapper.setProperty(prop, obj, useFieldAccessOnly); + } + }); + + return result; } @Override @@ -178,7 +203,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter jsonGenerator.writeEndObject(); jsonGenerator.close(); - target.setValue(jsonStream.toString()); + target.setRawValue(jsonStream.toString()); } @Override @@ -186,5 +211,34 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter throws BeansException { this.applicationContext = applicationContext; } + + private class CouchbasePropertyValueProvider implements PropertyValueProvider { + + private final ConvertedCouchbaseDocument source; + private final Object parent; + + public CouchbasePropertyValueProvider(ConvertedCouchbaseDocument source, Object parent) { + Assert.notNull(source); + this.source = source; + this.parent = parent; + } + + public T getPropertyValue(CouchbasePersistentProperty property) { + String fieldName = property.getFieldName(); + T value = null; + + if(fieldName == "id") { + value = (T) source.getId(); + } else { + value = (T) source.get(fieldName); + } + + if (value == null) { + return null; + } + + return value; + } + } } diff --git a/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java b/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java index 252cd38e..b2db7d6b 100644 --- a/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java +++ b/src/main/java/com/couchbase/spring/core/mapping/BasicCouchbasePersistentProperty.java @@ -76,21 +76,5 @@ public class BasicCouchbasePersistentProperty return annotation != null && StringUtils.hasText(annotation.value()) ? annotation.value() : field.getName(); } - - /** - * Return the name of the property. - * - * This overrides the default implementation to make sure that when - * the property is retrieved from a JSON document it can be found - * even when a alias is used. - */ - @Override - public String getName() { - return getFieldName(); - } - - public String getOriginalName() { - return name; - } } diff --git a/src/main/java/com/couchbase/spring/core/mapping/ConvertedCouchbaseDocument.java b/src/main/java/com/couchbase/spring/core/mapping/ConvertedCouchbaseDocument.java index cf6bc368..4ff45218 100644 --- a/src/main/java/com/couchbase/spring/core/mapping/ConvertedCouchbaseDocument.java +++ b/src/main/java/com/couchbase/spring/core/mapping/ConvertedCouchbaseDocument.java @@ -22,26 +22,37 @@ package com.couchbase.spring.core.mapping; +import java.util.HashMap; +import java.util.Map; + +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.type.TypeReference; +import org.springframework.data.mapping.model.MappingException; + public class ConvertedCouchbaseDocument { private String id; - private String value; + private String rawValue; private int expiry; + + private Map decoded; public ConvertedCouchbaseDocument() { this("", "", 0); } - public ConvertedCouchbaseDocument(String id, String value) { - this(id, value, 0); + public ConvertedCouchbaseDocument(String id, String rawValue) { + this(id, rawValue, 0); } - public ConvertedCouchbaseDocument(String id, String value, int expiry) { + public ConvertedCouchbaseDocument(String id, String rawValue, int expiry) { this.id = id; - this.value = value; + this.rawValue = rawValue; this.expiry = expiry; + this.decoded = new HashMap(); + parseJson(); } public void setId(String id) { @@ -52,12 +63,14 @@ public class ConvertedCouchbaseDocument { return id; } - public String getValue() { - return value; + public String getRawValue() { + return rawValue; } - public void setValue(String value) { - this.value = value; + public void setRawValue(String value) { + this.rawValue = value; + parseJson(); + } public int getExpiry() { @@ -67,5 +80,26 @@ public class ConvertedCouchbaseDocument { public void setExpiry(int expiry) { this.expiry = expiry; } + + public boolean containsField(String fieldname) { + return decoded.containsKey(fieldname); + } + + public Object get(String fieldname) { + return decoded.get(fieldname); + } + + private void parseJson() { + ObjectMapper mapper = new ObjectMapper(); + try { + if(!getRawValue().isEmpty()) { + Map converted = mapper.readValue(getRawValue(), + new TypeReference>() { }); + this.decoded = converted; + } + } catch(Exception e) { + throw new MappingException("Error while decoding JSON object.", e); + } + } } diff --git a/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentProperty.java b/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentProperty.java index 536f3c33..cc26e5a2 100644 --- a/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentProperty.java +++ b/src/main/java/com/couchbase/spring/core/mapping/CouchbasePersistentProperty.java @@ -35,6 +35,5 @@ public interface CouchbasePersistentProperty extends * custom annotation. */ String getFieldName(); - - String getOriginalName(); + } diff --git a/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java b/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java index 993c88b6..e7c50181 100644 --- a/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java +++ b/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java @@ -107,7 +107,6 @@ public class CouchbaseTemplateTest { assertEquals(id, found.getId()); assertEquals(name, found.getName()); assertEquals(active, found.getActive()); - } /** From 1b7544f1de2ad490633af639031dde9e9857b7aa Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Thu, 7 Feb 2013 15:01:20 +0100 Subject: [PATCH 10/12] dont hardcode the ID property. --- .../core/convert/MappingCouchbaseConverter.java | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) 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 373c5549..ce23e4af 100644 --- a/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java +++ b/src/main/java/com/couchbase/spring/core/convert/MappingCouchbaseConverter.java @@ -29,16 +29,9 @@ import com.couchbase.spring.core.mapping.CouchbasePersistentProperty; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; -import java.lang.reflect.Field; -import java.util.Map; - import org.codehaus.jackson.JsonEncoding; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.JsonGenerator; -import org.codehaus.jackson.JsonParser; -import org.codehaus.jackson.JsonToken; -import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.type.TypeReference; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -46,13 +39,10 @@ import org.springframework.core.convert.support.ConversionServiceFactory; import org.springframework.data.convert.EntityInstantiator; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.BeanWrapper; -import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator; import org.springframework.data.mapping.model.MappingException; import org.springframework.data.mapping.model.ParameterValueProvider; import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider; import org.springframework.data.mapping.model.PropertyValueProvider; -import org.springframework.data.mapping.model.SpELContext; -import org.springframework.data.mapping.model.SpELExpressionEvaluator; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; import org.springframework.data.mapping.PropertyHandler; @@ -122,7 +112,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter } Object obj = null; - if(prop.getFieldName() == "id") { + if(prop.isIdProperty()) { obj = doc.getId(); } else { obj = doc.get(prop.getFieldName()); @@ -224,13 +214,12 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter } public T getPropertyValue(CouchbasePersistentProperty property) { - String fieldName = property.getFieldName(); T value = null; - if(fieldName == "id") { + if(property.isIdProperty()) { value = (T) source.getId(); } else { - value = (T) source.get(fieldName); + value = (T) source.get(property.getFieldName()); } if (value == null) { From 810130858c5171889ff9e3bd6caee3b0dd54eb28 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Thu, 7 Feb 2013 16:29:09 +0100 Subject: [PATCH 11/12] Adding the exception translator --- pom.xml | 5 ++ .../core/CouchbaseExceptionTranslator.java | 60 +++++++++++++++++++ .../spring/core/CouchbaseTemplate.java | 7 +++ 3 files changed, 72 insertions(+) create mode 100644 src/main/java/com/couchbase/spring/core/CouchbaseExceptionTranslator.java diff --git a/pom.xml b/pom.xml index dc62902e..e7f28063 100644 --- a/pom.xml +++ b/pom.xml @@ -77,5 +77,10 @@ jackson-mapper-asl 1.9.12
+ + org.springframework + spring-tx + 3.2.1.RELEASE + diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseExceptionTranslator.java b/src/main/java/com/couchbase/spring/core/CouchbaseExceptionTranslator.java new file mode 100644 index 00000000..271a9a9d --- /dev/null +++ b/src/main/java/com/couchbase/spring/core/CouchbaseExceptionTranslator.java @@ -0,0 +1,60 @@ +/** + * 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 org.springframework.dao.DataAccessException; +import org.springframework.dao.DataAccessResourceFailureException; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.support.PersistenceExceptionTranslator; + +import com.couchbase.client.ObservedException; +import com.couchbase.client.ObservedModifiedException; +import com.couchbase.client.ObservedTimeoutException; +import com.couchbase.client.vbucket.ConnectionException; + +/** + * Simple {@link PersistenceExceptionTranslator} for Couchbase. + * + * Convert the given runtime exception to an appropriate exception from the + * {@code org.springframework.dao} hierarchy. Return {@literal null} if no translation + * is appropriate: any other exception may have resulted from user code, and should not + * be translated. + */ +public class CouchbaseExceptionTranslator implements PersistenceExceptionTranslator { + + @Override + public DataAccessException translateExceptionIfPossible(RuntimeException ex) { + if(ex instanceof ConnectionException) { + return new DataAccessResourceFailureException(ex.getMessage(), ex); + } + + if(ex instanceof ObservedException || + ex instanceof ObservedTimeoutException || + ex instanceof ObservedModifiedException) { + return new DataIntegrityViolationException(ex.getMessage(), ex); + } + + return null; + } + +} diff --git a/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java b/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java index 5c342ba0..f0628fd1 100644 --- a/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java +++ b/src/main/java/com/couchbase/spring/core/CouchbaseTemplate.java @@ -45,6 +45,8 @@ public class CouchbaseTemplate implements CouchbaseOperations { protected final MappingContext, CouchbasePersistentProperty> mappingContext; private static final Collection ITERABLE_CLASSES; + private final CouchbaseExceptionTranslator exceptionTranslator = + new CouchbaseExceptionTranslator(); static { Set iterableClasses = new HashSet(); @@ -138,6 +140,11 @@ public class CouchbaseTemplate implements CouchbaseOperations { } } } + + private RuntimeException potentiallyConvertRuntimeException(RuntimeException ex) { + RuntimeException resolved = this.exceptionTranslator.translateExceptionIfPossible(ex); + return resolved == null ? ex : resolved; + } } From 02bc8b24f1b249a508dd890c374d91b7ff0ab6de Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Mon, 8 Apr 2013 10:35:22 +0200 Subject: [PATCH 12/12] Small test rename. --- .../java/com/couchbase/spring/core/CouchbaseTemplateTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java b/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java index e7c50181..e4e54994 100644 --- a/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java +++ b/src/test/java/com/couchbase/spring/core/CouchbaseTemplateTest.java @@ -95,7 +95,7 @@ public class CouchbaseTemplateTest { } @Test - public void validfindById() { + public void validFindById() { String id = "beers:findme-stout"; String name = "The Findme Stout"; boolean active = true;