You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by ok...@apache.org on 2016/01/05 23:54:56 UTC

[24/50] incubator-tinkerpop git commit: Add option on GryoMapper to allow overriding the ClassResolver.

Add option on GryoMapper to allow overriding the ClassResolver.

Included tests to demonstrate capabilities.


Project: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/commit/a94c51f3
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/tree/a94c51f3
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/diff/a94c51f3

Branch: refs/heads/TINKERPOP-1033
Commit: a94c51f368f91a7c1204fb19517d02338ca1b141
Parents: 2f16c77
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Thu Dec 24 06:54:11 2015 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Thu Dec 24 06:54:11 2015 -0500

----------------------------------------------------------------------
 .../structure/io/gryo/GryoClassResolver.java    |   2 +-
 .../gremlin/structure/io/gryo/GryoMapper.java   |  25 +++-
 .../tinkerpop/gremlin/structure/io/IoX.java     |   5 +
 .../gremlin/structure/io/IoXIoRegistry.java     |  63 ++++++++++
 .../tinkerpop/gremlin/structure/io/IoY.java     |   5 +
 .../gremlin/structure/io/IoYIoRegistry.java     |  45 +++++++
 .../structure/io/gryo/GryoMapperTest.java       | 116 +++++++++++++++++++
 7 files changed, 257 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/a94c51f3/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoClassResolver.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoClassResolver.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoClassResolver.java
index 268da9c..5d51d22 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoClassResolver.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoClassResolver.java
@@ -52,7 +52,7 @@ import static org.apache.tinkerpop.shaded.kryo.util.Util.getWrapperClass;
  *
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
-class GryoClassResolver implements ClassResolver {
+public class GryoClassResolver implements ClassResolver {
     static public final byte NAME = -1;
 
     protected Kryo kryo;

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/a94c51f3/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoMapper.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoMapper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoMapper.java
index 1fec5c8..7d945ce 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoMapper.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoMapper.java
@@ -57,6 +57,7 @@ import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertex;
 import org.apache.tinkerpop.gremlin.structure.util.reference.ReferenceVertexProperty;
 import org.apache.tinkerpop.gremlin.structure.util.star.StarGraph;
 import org.apache.tinkerpop.gremlin.structure.util.star.StarGraphGryoSerializer;
+import org.apache.tinkerpop.shaded.kryo.ClassResolver;
 import org.apache.tinkerpop.shaded.kryo.Kryo;
 import org.apache.tinkerpop.shaded.kryo.KryoSerializable;
 import org.apache.tinkerpop.shaded.kryo.Serializer;
@@ -90,6 +91,7 @@ import java.util.UUID;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Function;
+import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
 /**
@@ -125,18 +127,20 @@ public final class GryoMapper implements Mapper<Kryo> {
     public static final byte[] GIO = "gio".getBytes();
     public static final byte[] HEADER = Arrays.copyOf(GIO, 16);
     private final List<Triplet<Class, Function<Kryo, Serializer>, Integer>> serializationList;
-    private boolean registrationRequired;
-    private boolean referenceTracking;
+    private final boolean registrationRequired;
+    private final boolean referenceTracking;
+    private final Supplier<ClassResolver> classResolver;
 
     private GryoMapper(final Builder builder) {
         this.serializationList = builder.serializationList;
         this.registrationRequired = builder.registrationRequired;
         this.referenceTracking = builder.referenceTracking;
+        this.classResolver = builder.classResolver;
     }
 
     @Override
     public Kryo createMapper() {
-        final Kryo kryo = new Kryo(new GryoClassResolver(), new MapReferenceResolver(), new DefaultStreamFactory());
+        final Kryo kryo = new Kryo(classResolver.get(), new MapReferenceResolver(), new DefaultStreamFactory());
         kryo.addDefaultSerializer(Map.Entry.class, new EntrySerializer());
         kryo.setRegistrationRequired(registrationRequired);
         kryo.setReferences(referenceTracking);
@@ -289,6 +293,7 @@ public final class GryoMapper implements Mapper<Kryo> {
 
         private boolean registrationRequired = true;
         private boolean referenceTracking = true;
+        private Supplier<ClassResolver> classResolver = GryoClassResolver::new;
 
         private Builder() {
         }
@@ -304,6 +309,20 @@ public final class GryoMapper implements Mapper<Kryo> {
         }
 
         /**
+         * Provides a custom Kryo {@code ClassResolver} to be supplied to a {@code Kryo} instance.  If this value is
+         * not supplied then it will default to the {@link GryoClassResolver}. To ensure compatibility with Gryo it
+         * is highly recommended that objects passed to this method extend that class.
+         * <p/>
+         * If the {@code ClassResolver} implementation share state, then the {@link Supplier} should typically create
+         * new instances when requested, as the {@link Supplier} will be called for each {@link Kryo} instance created.
+         */
+        public Builder classResolver(final Supplier<ClassResolver> classResolverSupplier) {
+            if (null == classResolverSupplier) throw new IllegalArgumentException("The classResolverSupplier cannot be null");
+            this.classResolver = classResolverSupplier;
+            return this;
+        }
+
+        /**
          * Register custom classes to serializes with gryo using default serialization.
          */
         public Builder addCustom(final Class... custom) {

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/a94c51f3/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoX.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoX.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoX.java
index 2f67382..4c1698f 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoX.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoX.java
@@ -47,4 +47,9 @@ public class IoX {
     public int hashCode() {
         return x.hashCode();
     }
+
+    @Override
+    public String toString() {
+        return x;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/a94c51f3/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoXIoRegistry.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoXIoRegistry.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoXIoRegistry.java
index dabdae8..ddc5477 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoXIoRegistry.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoXIoRegistry.java
@@ -18,7 +18,21 @@
  */
 package org.apache.tinkerpop.gremlin.structure.io;
 
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTokens;
 import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoIo;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
+import org.apache.tinkerpop.shaded.kryo.Kryo;
+import org.apache.tinkerpop.shaded.kryo.Serializer;
+import org.apache.tinkerpop.shaded.kryo.io.Input;
+import org.apache.tinkerpop.shaded.kryo.io.Output;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**
  * @author Stephen Mallette (http://stephen.genoprime.com)
@@ -41,4 +55,53 @@ public class IoXIoRegistry {
             return INSTANCE;
         }
     }
+
+    public static class SerializerToVertexBased extends AbstractIoRegistry {
+        public SerializerToVertexBased() {
+            register(GryoIo.class, IoX.class, new IoXToVertexSerializer());
+        }
+    }
+
+    /**
+     * Converts an {@link IoX} to a {@link DetachedVertex}.
+     */
+    public final static class IoXToVertexSerializer extends Serializer<IoX> {
+        public IoXToVertexSerializer() {
+        }
+
+        @Override
+        public void write(final Kryo kryo, final Output output, final IoX iox) {
+            final Map<String,Object> props = new HashMap<>();
+            addSingleProperty("x", iox.toString(), props);
+            final DetachedVertex vertex = new DetachedVertex(100, Vertex.DEFAULT_LABEL, props);
+            try (final OutputStream stream = new ByteArrayOutputStream()) {
+                final Output detachedOutput = new Output(stream);
+                kryo.writeObject(detachedOutput, vertex);
+
+                // have to remove the first byte because it marks a reference we don't want to have as this
+                // serializer is trying to "act" like a DetachedVertex
+                final byte[] b = detachedOutput.toBytes();
+                output.write(b, 1, b.length - 1);
+            } catch (Exception ex) {
+                ex.printStackTrace();
+            }
+        }
+
+        @Override
+        public IoX read(final Kryo kryo, final Input input, final Class<IoX> ioxClass) {
+            throw new UnsupportedOperationException("IoX writes to DetachedVertex and can't be read back in as IoX");
+        }
+
+        private static void addSingleProperty(final String key, final Object value, final Map<String, Object> props)
+        {
+            final List<Map<String, Object>> propertyNames = new ArrayList<>(1);
+            final Map<String,Object> propertyName = new HashMap<>();
+            propertyName.put(GraphSONTokens.ID, key);
+            propertyName.put(GraphSONTokens.KEY, key);
+            propertyName.put(GraphSONTokens.VALUE, value);
+            propertyNames.add(propertyName);
+
+            props.put(key, propertyNames);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/a94c51f3/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoY.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoY.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoY.java
index ddbb221..5da7cec 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoY.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoY.java
@@ -49,4 +49,9 @@ public class IoY {
     public int hashCode() {
         return y.hashCode();
     }
+
+    @Override
+    public String toString() {
+        return y + "-" + z;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/a94c51f3/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoYIoRegistry.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoYIoRegistry.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoYIoRegistry.java
index 1d03500..53c3404 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoYIoRegistry.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/IoYIoRegistry.java
@@ -19,6 +19,15 @@
 package org.apache.tinkerpop.gremlin.structure.io;
 
 import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoIo;
+import org.apache.tinkerpop.shaded.kryo.Kryo;
+import org.apache.tinkerpop.shaded.kryo.Serializer;
+import org.apache.tinkerpop.shaded.kryo.io.Input;
+import org.apache.tinkerpop.shaded.kryo.io.Output;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * @author Stephen Mallette (http://stephen.genoprime.com)
@@ -41,4 +50,40 @@ public class IoYIoRegistry {
             return INSTANCE;
         }
     }
+
+    public static class SerializerToMapBased extends AbstractIoRegistry {
+        public SerializerToMapBased() {
+            register(GryoIo.class, IoY.class, new IoYToHashMapSerializer());
+        }
+    }
+
+    /**
+     * Converts an {@link IoX} to a {@link HashMap}.
+     */
+    public final static class IoYToHashMapSerializer extends Serializer<IoY> {
+        public IoYToHashMapSerializer() {
+        }
+
+        @Override
+        public void write(final Kryo kryo, final Output output, final IoY ioy) {
+            final Map<String, Object> map = new HashMap<>();
+            map.put("y", ioy.toString());
+            try (final OutputStream stream = new ByteArrayOutputStream()) {
+                final Output detachedOutput = new Output(stream);
+                kryo.writeObject(detachedOutput, map);
+
+                // have to remove the first byte because it marks a reference we don't want to have as this
+                // serializer is trying to "act" like a Map
+                final byte[] b = detachedOutput.toBytes();
+                output.write(b, 1, b.length - 1);
+            } catch (Exception ex) {
+                ex.printStackTrace();
+            }
+        }
+
+        @Override
+        public IoY read(final Kryo kryo, final Input input, final Class<IoY> ioyClass) {
+            throw new UnsupportedOperationException("IoX writes to Map and can't be read back in as IoX");
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/a94c51f3/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoMapperTest.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoMapperTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoMapperTest.java
index 9bdec55..947528c 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoMapperTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoMapperTest.java
@@ -18,11 +18,17 @@
  */
 package org.apache.tinkerpop.gremlin.structure.io.gryo;
 
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.io.IoRegistry;
 import org.apache.tinkerpop.gremlin.structure.io.IoX;
 import org.apache.tinkerpop.gremlin.structure.io.IoXIoRegistry;
 import org.apache.tinkerpop.gremlin.structure.io.IoY;
 import org.apache.tinkerpop.gremlin.structure.io.IoYIoRegistry;
+import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTokens;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
+import org.apache.tinkerpop.shaded.kryo.ClassResolver;
 import org.apache.tinkerpop.shaded.kryo.Kryo;
+import org.apache.tinkerpop.shaded.kryo.Registration;
 import org.apache.tinkerpop.shaded.kryo.io.Input;
 import org.apache.tinkerpop.shaded.kryo.io.Output;
 import org.junit.Test;
@@ -31,6 +37,11 @@ import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
@@ -47,6 +58,111 @@ public class GryoMapperTest {
     }
 
     @Test
+    public void shouldSerializeDeserialize() throws Exception {
+        final GryoMapper mapper = GryoMapper.build().create();
+        final Kryo kryo = mapper.createMapper();
+        try (final OutputStream stream = new ByteArrayOutputStream()) {
+            final Output out = new Output(stream);
+
+            final Map<String,Object> props = new HashMap<>();
+            final List<Map<String, Object>> propertyNames = new ArrayList<>(1);
+            final Map<String,Object> propertyName = new HashMap<>();
+            propertyName.put(GraphSONTokens.ID, "x");
+            propertyName.put(GraphSONTokens.KEY, "x");
+            propertyName.put(GraphSONTokens.VALUE, "no-way-this-will-ever-work");
+            propertyNames.add(propertyName);
+            props.put("x", propertyNames);
+            final DetachedVertex v = new DetachedVertex(100, Vertex.DEFAULT_LABEL, props);
+
+            kryo.writeClassAndObject(out, v);
+
+            try (final InputStream inputStream = new ByteArrayInputStream(out.toBytes())) {
+                final Input input = new Input(inputStream);
+                final DetachedVertex readX = (DetachedVertex) kryo.readClassAndObject(input);
+                assertEquals("no-way-this-will-ever-work", readX.value("x"));
+            }
+        }
+    }
+
+    @Test
+    public void shouldSerializeWithCustomClassResolverToDetachedVertex() throws Exception {
+        final IoRegistry ioRegistry = new IoXIoRegistry.SerializerToVertexBased();
+        final Supplier<ClassResolver> classResolver = new CustomClassResolverSupplier();
+        final GryoMapper mapper = GryoMapper.build().addRegistry(ioRegistry).classResolver(classResolver).create();
+        final Kryo kryo = mapper.createMapper();
+        try (final OutputStream stream = new ByteArrayOutputStream()) {
+            final Output out = new Output(stream);
+            final IoX x = new IoX("no-way-this-will-ever-work");
+
+            kryo.writeClassAndObject(out, x);
+
+            final GryoMapper mapperWithoutKnowledgeOfIox = GryoMapper.build().create();
+            final Kryo kryoWithoutKnowledgeOfIox = mapperWithoutKnowledgeOfIox.createMapper();
+            try (final InputStream inputStream = new ByteArrayInputStream(out.toBytes())) {
+                final Input input = new Input(inputStream);
+                final DetachedVertex readX = (DetachedVertex) kryoWithoutKnowledgeOfIox.readClassAndObject(input);
+                assertEquals("no-way-this-will-ever-work", readX.value("x"));
+            }
+        }
+    }
+
+    @Test
+    public void shouldSerializeWithCustomClassResolverToHashMap() throws Exception {
+        final IoRegistry ioRegistry = new IoXIoRegistry.SerializerToVertexBased();
+        final Supplier<ClassResolver> classResolver = new CustomClassResolverSupplier();
+        final GryoMapper mapper = GryoMapper.build().addRegistry(ioRegistry).classResolver(classResolver).create();
+        final Kryo kryo = mapper.createMapper();
+        try (final OutputStream stream = new ByteArrayOutputStream()) {
+            final Output out = new Output(stream);
+            final IoY y = new IoY(100, 200);
+
+            kryo.writeClassAndObject(out, y);
+
+            final GryoMapper mapperWithoutKnowledgeOfIoy = GryoMapper.build().create();
+            final Kryo kryoWithoutKnowledgeOfIox = mapperWithoutKnowledgeOfIoy.createMapper();
+            try (final InputStream inputStream = new ByteArrayInputStream(out.toBytes())) {
+                final Input input = new Input(inputStream);
+                final Map readY = (HashMap) kryoWithoutKnowledgeOfIox.readClassAndObject(input);
+                assertEquals("100-200", readY.get("y"));
+            }
+        }
+    }
+
+    /**
+     * Creates new {@link CustomClassResolver} when requested.
+     */
+    public static class CustomClassResolverSupplier implements Supplier<ClassResolver> {
+        @Override
+        public ClassResolver get() {
+            return new CustomClassResolver();
+        }
+    }
+
+    /**
+     * A custom {@code ClassResolver} that alters the {@code Registration} returned to Kryo when an {@link IoX} class
+     * is requested, coercing it to a totally different class (a {@link DetachedVertex}).  This coercion demonstrates
+     * how a TinkerPop provider might take a custom internal class and serialize it into something core to
+     * TinkerPop which then removes the requirement for providers to expose serializers on the client side for user
+     * consumption.
+     */
+    public static class CustomClassResolver extends GryoClassResolver {
+        private IoXIoRegistry.IoXToVertexSerializer ioXToVertexSerializer = new IoXIoRegistry.IoXToVertexSerializer();
+        private IoYIoRegistry.IoYToHashMapSerializer ioYToHashMapSerializer = new IoYIoRegistry.IoYToHashMapSerializer();
+
+        public Registration getRegistration(final Class clazz) {
+            if (IoX.class.isAssignableFrom(clazz)) {
+                final Registration registration = super.getRegistration(DetachedVertex.class);
+                return new Registration(registration.getType(), ioXToVertexSerializer, registration.getId());
+            } else if (IoY.class.isAssignableFrom(clazz)) {
+                final Registration registration = super.getRegistration(HashMap.class);
+                return new Registration(registration.getType(), ioYToHashMapSerializer, registration.getId());
+            } else {
+                return super.getRegistration(clazz);
+            }
+        }
+    }
+
+    @Test
     public void shouldSerializeWithoutRegistration() throws Exception {
         final GryoMapper mapper = GryoMapper.build().registrationRequired(false).create();
         final Kryo kryo = mapper.createMapper();