From 8fb390ee8892894afa4540f344e579f540a3fe9a Mon Sep 17 00:00:00 2001 From: Thomas Darimont Date: Mon, 4 Nov 2013 22:23:26 +0100 Subject: [PATCH] DATAMONGO-348 - Added serialization support for lazy-loading for DBRefs. --- .../core/convert/DefaultDbRefResolver.java | 30 ++++++++++- .../DbRefMappingMongoConverterUnitTests.java | 53 ++++++++++++++++++- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java index 899417aae..a607b71fb 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java @@ -15,6 +15,10 @@ */ package org.springframework.data.mongodb.core.convert; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; import java.lang.reflect.Method; import org.aopalliance.intercept.MethodInterceptor; @@ -150,7 +154,8 @@ public class DefaultDbRefResolver implements DbRefResolver { * * @author Thomas Darimont */ - static class LazyLoadingInterceptor implements MethodInterceptor, org.springframework.cglib.proxy.MethodInterceptor { + static class LazyLoadingInterceptor implements MethodInterceptor, org.springframework.cglib.proxy.MethodInterceptor, + Serializable { private final DbRefResolverCallback callback; private final MongoPersistentProperty property; @@ -194,12 +199,33 @@ public class DefaultDbRefResolver implements DbRefResolver { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { + ensureResolved(); + + return method.invoke(result, args); + } + + private void ensureResolved() { + if (!resolved) { this.result = resolve(); this.resolved = true; } + } - return method.invoke(result, args); + private void writeObject(ObjectOutputStream out) throws IOException { + + ensureResolved(); + out.writeObject(this.result); + } + + private void readObject(ObjectInputStream in) throws IOException { + + try { + this.resolved = true; // Object is guaranteed to be resolved after serializations + this.result = in.readObject(); + } catch (ClassNotFoundException e) { + throw new LazyLoadingException("Could not deserialize result", e); + } } /** diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java index 6d71aff10..7d626ab37 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java @@ -21,6 +21,7 @@ import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import static org.springframework.data.mongodb.core.convert.LazyLoadingTestUtils.*; +import java.io.Serializable; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; @@ -42,6 +43,7 @@ import org.springframework.data.mongodb.core.MongoExceptionTranslator; import org.springframework.data.mongodb.core.convert.MappingMongoConverterUnitTests.Person; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; +import org.springframework.util.SerializationUtils; import com.mongodb.BasicDBObject; import com.mongodb.DBObject; @@ -256,6 +258,35 @@ public class DbRefMappingMongoConverterUnitTests { assertThat(result.dbRefToConcreteTypeWithPersistenceConstructorWithoutDefaultConstructor.getValue(), is(value)); } + /** + * @see DATAMONGO-348 + */ + @Test + public void lazyLoadingProxyForSerializableLazyDbRefOnConcreteType() { + + String id = "42"; + String value = "bubu"; + MappingMongoConverter converterSpy = spy(converter); + doReturn(new BasicDBObject("_id", id).append("value", value)).when(converterSpy).readRef((DBRef) any()); + + BasicDBObject dbo = new BasicDBObject(); + SerializableClassWithLazyDbRefs lazyDbRefs = new SerializableClassWithLazyDbRefs(); + lazyDbRefs.dbRefToSerializableTarget = new SerializableLazyDbRefTarget(id, value); + converterSpy.write(lazyDbRefs, dbo); + + SerializableClassWithLazyDbRefs result = converterSpy.read(SerializableClassWithLazyDbRefs.class, dbo); + + SerializableClassWithLazyDbRefs deserializedResult = (SerializableClassWithLazyDbRefs) transport(result); + + assertThat(deserializedResult.dbRefToSerializableTarget.getId(), is(id)); + assertProxyIsResolved(deserializedResult.dbRefToSerializableTarget, true); + assertThat(deserializedResult.dbRefToSerializableTarget.getValue(), is(value)); + } + + private Object transport(Object result) { + return SerializationUtils.deserialize(SerializationUtils.serialize(result)); + } + class MapDBRef { @org.springframework.data.mongodb.core.mapping.DBRef Map map; } @@ -278,7 +309,16 @@ public class DbRefMappingMongoConverterUnitTests { @org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) LazyDbRefTargetWithPeristenceConstructorWithoutDefaultConstructor dbRefToConcreteTypeWithPersistenceConstructorWithoutDefaultConstructor; } - static class LazyDbRefTarget { + static class SerializableClassWithLazyDbRefs implements Serializable { + + private static final long serialVersionUID = 1L; + + @org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) SerializableLazyDbRefTarget dbRefToSerializableTarget; + } + + static class LazyDbRefTarget implements Serializable { + + private static final long serialVersionUID = 1L; @Id String id; String value; @@ -336,4 +376,15 @@ public class DbRefMappingMongoConverterUnitTests { super(id.toString(), value.toString()); } } + + static class SerializableLazyDbRefTarget extends LazyDbRefTarget implements Serializable { + + public SerializableLazyDbRefTarget() {} + + public SerializableLazyDbRefTarget(String id, String value) { + super(id, value); + } + + private static final long serialVersionUID = 1L; + } }