From 3c4efa9af3127e54f30b18273c5546ee47310be5 Mon Sep 17 00:00:00 2001 From: Adrian Cieplak Date: Fri, 28 Sep 2018 16:13:47 +0200 Subject: [PATCH 1/2] Introduce new property 'subzero.referenceresolver.class' where we could specify Kryo's ReferenceResolver implementation. --- README.md | 1 + .../internal/strategy/KryoStrategy.java | 36 +++++++++++---- ...yoStrategyCustomReferenceResolverTest.java | 41 +++++++++++++++++ .../strategy/NullReferenceResolver.java | 46 +++++++++++++++++++ 4 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 subzero-core/src/test/java/info/jerrinot/subzero/internal/strategy/KryoStrategyCustomReferenceResolverTest.java create mode 100644 subzero-core/src/test/java/info/jerrinot/subzero/internal/strategy/NullReferenceResolver.java diff --git a/README.md b/README.md index 8f4315c..35d3a4a 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,7 @@ version with regular dependencies: Default value: 16KB - System property `subzero.base.type.id` sets base for auto-generated type id +- System property `subzero.referenceresolver.class` sets the ReferenceResolver implementation. Default value: `com.esotericsoftware.kryo.util.MapReferenceResolver` ## Custom Kryo Serializers SubZero can use custom Kryo serializers. diff --git a/subzero-core/src/main/java/info/jerrinot/subzero/internal/strategy/KryoStrategy.java b/subzero-core/src/main/java/info/jerrinot/subzero/internal/strategy/KryoStrategy.java index 6281a41..36628cc 100644 --- a/subzero-core/src/main/java/info/jerrinot/subzero/internal/strategy/KryoStrategy.java +++ b/subzero-core/src/main/java/info/jerrinot/subzero/internal/strategy/KryoStrategy.java @@ -1,29 +1,35 @@ package info.jerrinot.subzero.internal.strategy; +import com.esotericsoftware.kryo.ClassResolver; import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.ReferenceResolver; +import com.esotericsoftware.kryo.StreamFactory; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.InputChunked; import com.esotericsoftware.kryo.io.Output; import com.esotericsoftware.kryo.io.OutputChunked; import com.esotericsoftware.kryo.util.DefaultStreamFactory; -import com.esotericsoftware.kryo.util.MapReferenceResolver; import com.hazelcast.core.HazelcastInstance; import info.jerrinot.subzero.internal.ClassLoaderUtils; import info.jerrinot.subzero.internal.IdGeneratorUtils; import org.objenesis.strategy.StdInstantiatorStrategy; -import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import static java.lang.Boolean.getBoolean; import static java.lang.Integer.getInteger; +import static java.lang.String.format; +import static java.lang.System.getProperty; public abstract class KryoStrategy { private static final int BUFFER_SIZE = getInteger("subzero.buffer.size.kb", 16) * 1024; private static final boolean IGNORE_HAZELCAST_CLASSLOADER = getBoolean("subzero.classloading.ignore"); + private static final String DEFAULT_REFERENCE_RESOLVER_CLASS = "com.esotericsoftware.kryo.util.MapReferenceResolver"; + private static final String REFERENCE_RESOLVER_CLASS_SYSTEM_PROPERTY = "subzero.referenceresolver.class"; + private HazelcastInstance hazelcastInstance; private final ThreadLocal KRYOS = new ThreadLocal() { @@ -41,10 +47,10 @@ private Kryo newKryoInstance() { kryo = new Kryo(); } else { ClassLoader classLoader = ClassLoaderUtils.getConfiguredClassLoader(hazelcastInstance); - DelegatingClassResolver classResolver = new DelegatingClassResolver(classLoader); - MapReferenceResolver mapReferenceResolver = new MapReferenceResolver(); - DefaultStreamFactory defaultStreamFactory = new DefaultStreamFactory(); - kryo = new Kryo(classResolver, mapReferenceResolver, defaultStreamFactory); + ClassResolver classResolver = new DelegatingClassResolver(classLoader); + ReferenceResolver referenceResolver = createReferenceResolver(); + StreamFactory defaultStreamFactory = new DefaultStreamFactory(); + kryo = new Kryo(classResolver, referenceResolver, defaultStreamFactory); } registerCustomSerializers(kryo); kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy())); @@ -61,7 +67,7 @@ public void setHazelcastInstance(HazelcastInstance hazelcastInstance) { this.hazelcastInstance = hazelcastInstance; } - public void write(OutputStream out, T object) throws IOException { + public void write(OutputStream out, T object) { KryoContext kryoContext = KRYOS.get(); OutputChunked output = kryoContext.getOutputChunked(); output.setOutputStream(out); @@ -72,12 +78,11 @@ public void write(OutputStream out, T object) throws IOException { abstract void writeObject(Kryo kryo, Output output, T object); - public T read(InputStream in) throws IOException { + public T read(InputStream in) { KryoContext kryoContext = KRYOS.get(); InputChunked input = kryoContext.getInputChunked(); input.setInputStream(in); - T object = readObject(kryoContext.getKryo(), input); - return object; + return readObject(kryoContext.getKryo(), input); } abstract T readObject(Kryo kryo, Input input); @@ -87,4 +92,15 @@ public void destroy(HazelcastInstance hazelcastInstance) { } public abstract int newId(); + + private static ReferenceResolver createReferenceResolver() { + String referenceResolverClass = getProperty(REFERENCE_RESOLVER_CLASS_SYSTEM_PROPERTY, DEFAULT_REFERENCE_RESOLVER_CLASS); + try { + Class resolverClass = Class.forName(referenceResolverClass); + return (ReferenceResolver) resolverClass.getConstructor().newInstance(); + + } catch (Exception e) { + throw new IllegalArgumentException(format("could not create ReferenceResolver with class %s", referenceResolverClass), e); + } + } } diff --git a/subzero-core/src/test/java/info/jerrinot/subzero/internal/strategy/KryoStrategyCustomReferenceResolverTest.java b/subzero-core/src/test/java/info/jerrinot/subzero/internal/strategy/KryoStrategyCustomReferenceResolverTest.java new file mode 100644 index 0000000..37ee0e1 --- /dev/null +++ b/subzero-core/src/test/java/info/jerrinot/subzero/internal/strategy/KryoStrategyCustomReferenceResolverTest.java @@ -0,0 +1,41 @@ +package info.jerrinot.subzero.internal.strategy; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.ReferenceResolver; +import info.jerrinot.subzero.UserSerializer; +import org.junit.Test; + +import java.lang.reflect.Field; + +import static org.junit.Assert.assertEquals; + +public class KryoStrategyCustomReferenceResolverTest { + + @SuppressWarnings("unchecked") + @Test + public void should_use_strategy_with_specified_reference_resolver() throws Exception { + // given + System.setProperty("subzero.referenceresolver.class", "info.jerrinot.subzero.internal.strategy.NullReferenceResolver"); + GlobalKryoStrategy kryoStrategy = new GlobalKryoStrategy(NULL_USER_SERIALIZER); + + // when + Field field = KryoStrategy.class.getDeclaredField("KRYOS"); + field.setAccessible(true); + Class actualClassResolver = ((ThreadLocal) field.get(kryoStrategy)) + .get().getKryo().getReferenceResolver().getClass(); + + // then + assertEquals(NullReferenceResolver.class, actualClassResolver); + } + + private static final UserSerializer NULL_USER_SERIALIZER = new UserSerializer() { + @Override + public void registerSingleSerializer(Kryo kryo, Class clazz) { + // do nothing + } + @Override + public void registerAllSerializers(Kryo kryo) { + // do nothing + } + }; +} diff --git a/subzero-core/src/test/java/info/jerrinot/subzero/internal/strategy/NullReferenceResolver.java b/subzero-core/src/test/java/info/jerrinot/subzero/internal/strategy/NullReferenceResolver.java new file mode 100644 index 0000000..e4e72c2 --- /dev/null +++ b/subzero-core/src/test/java/info/jerrinot/subzero/internal/strategy/NullReferenceResolver.java @@ -0,0 +1,46 @@ +package info.jerrinot.subzero.internal.strategy; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.ReferenceResolver; + +public class NullReferenceResolver implements ReferenceResolver { + @Override + public void setKryo(Kryo kryo) { + // do nothing + } + + @Override + public int getWrittenId(Object object) { + return 0; + } + + @Override + public int addWrittenObject(Object object) { + return 0; + } + + @Override + public int nextReadId(Class type) { + return 0; + } + + @Override + public void setReadObject(int id, Object object) { + // do nothing + } + + @Override + public Object getReadObject(Class type, int id) { + return null; + } + + @Override + public void reset() { + // do nothing + } + + @Override + public boolean useReferences(Class type) { + return false; + } +} From bf7a0857db40f8e879fcdf69489046a18a89c49c Mon Sep 17 00:00:00 2001 From: Adrian Cieplak Date: Fri, 5 Oct 2018 09:46:51 +0200 Subject: [PATCH 2/2] Applay 'subzero.referenceresolver.class' even if we ignore hazelcast classloader --- .../info/jerrinot/subzero/internal/strategy/KryoStrategy.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subzero-core/src/main/java/info/jerrinot/subzero/internal/strategy/KryoStrategy.java b/subzero-core/src/main/java/info/jerrinot/subzero/internal/strategy/KryoStrategy.java index 36628cc..abb4989 100644 --- a/subzero-core/src/main/java/info/jerrinot/subzero/internal/strategy/KryoStrategy.java +++ b/subzero-core/src/main/java/info/jerrinot/subzero/internal/strategy/KryoStrategy.java @@ -44,7 +44,7 @@ protected KryoContext initialValue() { private Kryo newKryoInstance() { Kryo kryo; if (IGNORE_HAZELCAST_CLASSLOADER) { - kryo = new Kryo(); + kryo = new Kryo(createReferenceResolver()); } else { ClassLoader classLoader = ClassLoaderUtils.getConfiguredClassLoader(hazelcastInstance); ClassResolver classResolver = new DelegatingClassResolver(classLoader);