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"); + } + } + }
