Allow calls to addCustom on GryoMapper to override.

By allowing override, users get complete control over the gryo serialization 
process and open up more options for Gremlin Server to offer other optional 
serialization views over gryo.


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

Branch: refs/heads/TINKERPOP-1308
Commit: 2e030502804116edc4fb636acf4f5fbe3ae3e2ce
Parents: 6113c92
Author: Stephen Mallette <[email protected]>
Authored: Fri May 20 08:33:59 2016 -0400
Committer: Stephen Mallette <[email protected]>
Committed: Thu May 26 13:05:16 2016 -0400

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |  1 +
 .../upgrade/release-3.2.x-incubating.asciidoc   |  7 +++
 .../gremlin/structure/io/gryo/GryoMapper.java   | 44 +++++++++++---
 .../structure/io/gryo/GryoMapperTest.java       | 63 +++++++++++++++-----
 4 files changed, 92 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/2e030502/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 171b772..d2a8e3e 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -26,6 +26,7 @@ 
image::https://raw.githubusercontent.com/apache/incubator-tinkerpop/master/docs/
 TinkerPop 3.2.1 (NOT OFFICIALLY RELEASED YET)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+* `GryoMapper` allows overrides of existing serializers on calls to 
`addCustom` on the builder.
 * Fixed a `NullPointerException` bug around nested `group()`-steps in OLAP.
 * Fixed a severe bug around halted traversers in a multi-job OLAP traversal 
chain.
 * Ensure a separation of `GraphComputer` and `VertexProgram` configurations in 
`SparkGraphComputer` and `GiraphGraphComputer`.

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/2e030502/docs/src/upgrade/release-3.2.x-incubating.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/upgrade/release-3.2.x-incubating.asciidoc 
b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
index 66e5f8c..dec022f 100644
--- a/docs/src/upgrade/release-3.2.x-incubating.asciidoc
+++ b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
@@ -40,6 +40,13 @@ for Gremlin Console.
 
 See: link:https://issues.apache.org/jira/browse/TINKERPOP-1297[TINKERPOP-1297]
 
+GryoMapper Construction
+^^^^^^^^^^^^^^^^^^^^^^^
+
+It is now possible to override existing serializers with calls to `addCustom` 
on the `GryoMapper` builder. This option
+allows complete control over the serializers used by Gryo. Of course, this 
also makes it possible to produce completely
+non-compliant Gryo files. This feature should be used with caution.
+
 TraversalVertexProgram
 ^^^^^^^^^^^^^^^^^^^^^^
 

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/2e030502/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 d510706..9cae845 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
@@ -68,6 +68,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.gremlin.util.iterator.IteratorUtils;
 import org.apache.tinkerpop.shaded.kryo.ClassResolver;
 import org.apache.tinkerpop.shaded.kryo.Kryo;
 import org.apache.tinkerpop.shaded.kryo.KryoSerializable;
@@ -104,12 +105,14 @@ import java.util.Date;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.TimeZone;
 import java.util.TreeMap;
@@ -396,29 +399,43 @@ public final class GryoMapper implements Mapper<Kryo> {
         }
 
         /**
-         * Register custom classes to serializes with gryo using default 
serialization.
+         * Register custom classes to serializes with gryo using default 
serialization. Note that calling this method
+         * for a class that is already registered will override that 
registration.
          */
         public Builder addCustom(final Class... custom) {
-            if (custom != null && custom.length > 0)
-                serializationList.addAll(Arrays.asList(custom).stream()
-                        .map(c -> Triplet.<Class, Function<Kryo, Serializer>, 
Integer>with(c, null, currentSerializationId.getAndIncrement()))
-                        .collect(Collectors.<Triplet<Class, Function<Kryo, 
Serializer>, Integer>>toList()));
+            if (custom != null && custom.length > 0) {
+                for (Class clazz : custom) {
+                    addCustom(clazz, (Function<Kryo, Serializer>) null);
+                }
+            }
             return this;
         }
 
         /**
-         * Register custom class to serialize with a custom serialization 
class.
+         * Register custom class to serialize with a custom serialization 
class. Note that calling this method for
+         * a class that is already registered will override that registration.
          */
         public Builder addCustom(final Class clazz, final Serializer 
serializer) {
-            serializationList.add(Triplet.with(clazz, kryo -> serializer, 
currentSerializationId.getAndIncrement()));
+            if (null == serializer)
+                addCustom(clazz);
+            else
+                addCustom(clazz, kryo -> serializer);
             return this;
         }
 
         /**
-         * Register a custom class to serialize with a custom serializer as 
returned from a {@link Function}.
+         * Register a custom class to serialize with a custom serializer as 
returned from a {@link Function}. Note
+         * that calling this method for a class that is already registered 
will override that registration.
          */
         public Builder addCustom(final Class clazz, final Function<Kryo, 
Serializer> serializer) {
-            serializationList.add(Triplet.with(clazz, serializer, 
currentSerializationId.getAndIncrement()));
+            final Optional<Triplet<Class, Function<Kryo, Serializer>, 
Integer>> found = findSerializer(clazz);
+            if (found.isPresent()) {
+                final Triplet<Class, Function<Kryo, Serializer>, Integer> t = 
found.get();
+                serializationList.remove(t);
+                serializationList.add(t.setAt1(serializer));
+            } else
+                serializationList.add(Triplet.with(clazz, serializer, 
currentSerializationId.getAndIncrement()));
+
             return this;
         }
 
@@ -473,5 +490,14 @@ public final class GryoMapper implements Mapper<Kryo> {
 
             return new GryoMapper(this);
         }
+
+        private Optional<Triplet<Class, Function<Kryo, Serializer>, Integer>> 
findSerializer(final Class clazz) {
+            final Iterator<Triplet<Class, Function<Kryo, Serializer>, 
Integer>> itty = IteratorUtils.filter(
+                    serializationList, t -> 
t.getValue0().equals(clazz)).iterator();
+            if (itty.hasNext())
+                return Optional.of(itty.next());
+            else
+                return Optional.empty();
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/2e030502/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 861c6f6..29ddc57 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
@@ -29,6 +29,7 @@ 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.Serializer;
 import org.apache.tinkerpop.shaded.kryo.io.Input;
 import org.apache.tinkerpop.shaded.kryo.io.Output;
 import org.junit.Test;
@@ -57,9 +58,12 @@ import java.util.Map;
 import java.util.function.Supplier;
 
 import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.__;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.fail;
 
 /**
  * @author Stephen Mallette (http://stephen.genoprime.com)
@@ -220,85 +224,100 @@ public class GryoMapperTest {
     }
 
     @Test
-    public void shouldHandleDuration()throws Exception  {
+    public void shouldOverrideExistingSerializer() throws Exception {
+        final GryoMapper mapper = GryoMapper.build()
+                .addCustom(Duration.class, new 
OverrideDurationSerializer()).create();
+
+        try (final OutputStream stream = new ByteArrayOutputStream()) {
+            final Output out = new Output(stream);
+            mapper.createMapper().writeObject(out, Duration.ZERO);
+            fail("The OverrideDurationSerializer throws exceptions so this 
should not have worked");
+        } catch (Exception ex) {
+            assertThat(ex, instanceOf(UnsupportedOperationException.class));
+            assertEquals("I don't do anything", ex.getMessage());
+        }
+    }
+
+    @Test
+    public void shouldHandleDuration() throws Exception  {
         final Duration o = Duration.ZERO;
         assertEquals(o, serializeDeserialize(o, Duration.class));
     }
 
     @Test
-    public void shouldHandleInstant()throws Exception  {
+    public void shouldHandleInstant() throws Exception  {
         final Instant o = Instant.ofEpochMilli(System.currentTimeMillis());
         assertEquals(o, serializeDeserialize(o, Instant.class));
     }
 
     @Test
-    public void shouldHandleLocalDate()throws Exception  {
+    public void shouldHandleLocalDate() throws Exception  {
         final LocalDate o = LocalDate.now();
         assertEquals(o, serializeDeserialize(o, LocalDate.class));
     }
 
     @Test
-    public void shouldHandleLocalDateTime()throws Exception  {
+    public void shouldHandleLocalDateTime() throws Exception  {
         final LocalDateTime o = LocalDateTime.now();
         assertEquals(o, serializeDeserialize(o, LocalDateTime.class));
     }
 
     @Test
-    public void shouldHandleLocalTime()throws Exception  {
+    public void shouldHandleLocalTime() throws Exception  {
         final LocalTime o = LocalTime.now();
         assertEquals(o, serializeDeserialize(o, LocalTime.class));
     }
 
     @Test
-    public void shouldHandleMonthDay()throws Exception  {
+    public void shouldHandleMonthDay() throws Exception  {
         final MonthDay o = MonthDay.now();
         assertEquals(o, serializeDeserialize(o, MonthDay.class));
     }
 
     @Test
-    public void shouldHandleOffsetDateTime()throws Exception  {
+    public void shouldHandleOffsetDateTime() throws Exception  {
         final OffsetDateTime o = OffsetDateTime.now();
         assertEquals(o, serializeDeserialize(o, OffsetDateTime.class));
     }
 
     @Test
-    public void shouldHandleOffsetTime()throws Exception  {
+    public void shouldHandleOffsetTime() throws Exception  {
         final OffsetTime o = OffsetTime.now();
         assertEquals(o, serializeDeserialize(o, OffsetTime.class));
     }
 
     @Test
-    public void shouldHandlePeriod()throws Exception  {
+    public void shouldHandlePeriod() throws Exception  {
         final Period o = Period.ofDays(3);
         assertEquals(o, serializeDeserialize(o, Period.class));
     }
 
     @Test
-    public void shouldHandleYear()throws Exception  {
+    public void shouldHandleYear() throws Exception  {
         final Year o = Year.now();
         assertEquals(o, serializeDeserialize(o, Year.class));
     }
 
     @Test
-    public void shouldHandleYearMonth()throws Exception  {
+    public void shouldHandleYearMonth() throws Exception  {
         final YearMonth o = YearMonth.now();
         assertEquals(o, serializeDeserialize(o, YearMonth.class));
     }
 
     @Test
-    public void shouldHandleZonedDateTime()throws Exception  {
+    public void shouldHandleZonedDateTime() throws Exception  {
         final ZonedDateTime o = ZonedDateTime.now();
         assertEquals(o, serializeDeserialize(o, ZonedDateTime.class));
     }
 
     @Test
-    public void shouldHandleZonedOffset()throws Exception  {
+    public void shouldHandleZonedOffset() throws Exception  {
         final ZoneOffset o  = ZonedDateTime.now().getOffset();
         assertEquals(o, serializeDeserialize(o, ZoneOffset.class));
     }
 
     @Test
-    public void shouldHandleTraversalExplanation()throws Exception  {
+    public void shouldHandleTraversalExplanation() throws Exception  {
         final TraversalExplanation te = __().out().outV().outE().explain();
         assertEquals(te.toString(), serializeDeserialize(te, 
TraversalExplanation.class).toString());
     }
@@ -349,4 +368,20 @@ public class GryoMapperTest {
             }
         }
     }
+
+    final static class OverrideDurationSerializer extends Serializer<Duration>
+    {
+        @Override
+        public void write(final Kryo kryo, final Output output, final Duration 
duration)
+        {
+            throw new UnsupportedOperationException("I don't do anything");
+        }
+
+        @Override
+        public Duration read(final Kryo kryo, final Input input, final 
Class<Duration> durationClass)
+        {
+            throw new UnsupportedOperationException("I don't do anything");
+        }
+    }
+
 }

Reply via email to