This is an automated email from the ASF dual-hosted git repository. Cole-Greer pushed a commit to branch simplePDT in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit f6e60c59c2879f8c0892ffda4378d2bfbf928330 Author: Cole Greer <[email protected]> AuthorDate: Wed Jun 24 12:43:24 2026 -0700 Make GraphBinary/GraphSON write path registry-aware for PDT dehydration Fixes the CompositePDT (0xF0) response-path gap where a provider type registered via a ProviderDefinedTypeAdapter but not annotated with @ProviderDefined could not be serialized when returned to the client. GraphBinaryWriter now accepts an optional ProviderDefinedTypeRegistry (parallel to GraphBinaryReader). When a class has no direct serializer and is not @ProviderDefined-annotated, the writer consults the registry by class and dehydrates via the adapter (adapter.toFields) into a ProviderDefinedType. Annotation-based auto-conversion is unchanged; adapter lookup is an additional resolution path. The GraphSON write path gains the same capability via PdtGraphSONSerializerProviderV4, which returns an adapter-based serializer for registry-registered classes. The registry is threaded through GraphBinaryMessageSerializerV4 and AbstractGraphSONMessageSerializerV4. The prior gap-validation test (which asserted the failure) is replaced by GraphBinaryWriterPdtTest#shouldDehydrateRegisteredButUnannotatedTypeViaAdapterOnWritePath, asserting a successful adapter round-trip. tinkerpop-lka Assisted-by: Kiro:claude-opus-4.8 --- .../structure/io/binary/GraphBinaryWriter.java | 59 ++++++++++++++++++-- .../structure/io/graphson/GraphSONMapper.java | 7 +++ .../io/graphson/GraphSONTypeIdResolver.java | 11 ++++ .../graphson/PdtGraphSONSerializerProviderV4.java | 65 ++++++++++++++++++++++ .../io/graphson/PdtGraphSONSerializersV4.java | 54 ++++++++++++++++++ .../io/graphson/PdtGraphSONSerializersV4Test.java | 21 +++++++ .../ser/AbstractGraphSONMessageSerializerV4.java | 4 +- .../util/ser/GraphBinaryMessageSerializerV4.java | 4 +- .../util/ser/binary/GraphBinaryWriterPdtTest.java | 42 ++++++++++++++ 9 files changed, 259 insertions(+), 8 deletions(-) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/GraphBinaryWriter.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/GraphBinaryWriter.java index ef4c07ccda..05fd63c366 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/GraphBinaryWriter.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/GraphBinaryWriter.java @@ -21,9 +21,13 @@ package org.apache.tinkerpop.gremlin.structure.io.binary; import org.apache.tinkerpop.gremlin.structure.io.binary.types.ProviderDefinedTypeSerializer; import org.apache.tinkerpop.gremlin.structure.io.binary.types.TransformSerializer; import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedType; +import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedTypeAdapter; +import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedTypeRegistry; import org.apache.tinkerpop.gremlin.structure.io.Buffer; import java.io.IOException; +import java.util.Map; +import java.util.Optional; /** * Writes a value to a buffer using the {@link TypeSerializer} instances configured in the @@ -44,6 +48,7 @@ import java.io.IOException; */ public class GraphBinaryWriter { private final TypeSerializerRegistry registry; + private final ProviderDefinedTypeRegistry pdtRegistry; private final static byte VALUE_FLAG_NULL = 1; private final static byte VALUE_FLAG_NONE = 0; private final static byte VALUE_FLAG_ORDERED = 2; @@ -57,7 +62,12 @@ public class GraphBinaryWriter { } public GraphBinaryWriter(final TypeSerializerRegistry registry) { + this(registry, null); + } + + public GraphBinaryWriter(final TypeSerializerRegistry registry, final ProviderDefinedTypeRegistry pdtRegistry) { this.registry = registry; + this.pdtRegistry = pdtRegistry; } /** @@ -75,9 +85,9 @@ public class GraphBinaryWriter { final Class<?> objectClass = value.getClass(); - final TypeSerializer<T> serializer = (TypeSerializer<T>) registry.getSerializer(objectClass); + final TypeSerializer<T> serializer = (TypeSerializer<T>) getSerializerOrAdapterFallback(objectClass); if (serializer instanceof ProviderDefinedTypeSerializer && !(value instanceof ProviderDefinedType)) { - serializer.writeValue((T) ProviderDefinedType.from(value), buffer, this, nullable); + serializer.writeValue((T) dehydrateToPdt(value, objectClass), buffer, this, nullable); return; } serializer.writeValue(value, buffer, this, nullable); @@ -94,13 +104,13 @@ public class GraphBinaryWriter { } final Class<?> objectClass = value.getClass(); - final TypeSerializer<T> serializer = (TypeSerializer<T>) registry.getSerializer(objectClass); + final TypeSerializer<T> serializer = (TypeSerializer<T>) getSerializerOrAdapterFallback(objectClass); if (serializer instanceof ProviderDefinedTypeSerializer && !(value instanceof ProviderDefinedType)) { - // Convert @ProviderDefined-annotated object to ProviderDefinedType, then re-enter write(). + // Convert to ProviderDefinedType (via annotation or adapter), then re-enter write(). // On re-entry, ProviderDefinedType.class is directly registered in the registry, // and the instanceof guard prevents double-wrapping. - write((T) ProviderDefinedType.from(value), buffer); + write((T) dehydrateToPdt(value, objectClass), buffer); return; } @@ -155,4 +165,43 @@ public class GraphBinaryWriter { buffer.writeByte(VALUE_FLAG_BULK); } + /** + * Attempts to get a serializer for the given class. If no serializer is found and the pdtRegistry + * has an adapter for the class, returns the CompositePDT serializer. + */ + @SuppressWarnings("unchecked") + private <DT> TypeSerializer<DT> getSerializerOrAdapterFallback(final Class<?> type) throws IOException { + try { + return (TypeSerializer<DT>) registry.getSerializer(type); + } catch (final IOException e) { + if (pdtRegistry != null && pdtRegistry.getAdapterByClass(type).isPresent()) { + return (TypeSerializer<DT>) registry.getSerializer(DataType.COMPOSITE_PDT); + } + throw e; + } + } + + /** + * Dehydrates a value to a {@link ProviderDefinedType} using annotation reflection or an adapter from the + * pdtRegistry. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + private ProviderDefinedType dehydrateToPdt(final Object value, final Class<?> objectClass) { + // Prefer annotation-based conversion + if (objectClass.isAnnotationPresent(org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefined.class)) { + return ProviderDefinedType.from(value); + } + // Fall back to adapter-based conversion + if (pdtRegistry != null) { + final Optional<ProviderDefinedTypeAdapter<?>> opt = pdtRegistry.getAdapterByClass(objectClass); + if (opt.isPresent()) { + final ProviderDefinedTypeAdapter adapter = opt.get(); + final Map<String, Object> fields = adapter.toFields(value); + return new ProviderDefinedType(adapter.typeName(), fields); + } + } + // Should not reach here since getSerializerOrAdapterFallback already validated + return ProviderDefinedType.from(value); + } + } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java index 6877b67c69..3b9b46f884 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapper.java @@ -105,6 +105,9 @@ public class GraphSONMapper implements Mapper<ObjectMapper> { if ((version == GraphSONVersion.V4_0 || version == GraphSONVersion.V3_0 || version == GraphSONVersion.V2_0) && typeInfo != TypeInfo.NO_TYPES) { final GraphSONTypeIdResolver graphSONTypeIdResolver = new GraphSONTypeIdResolver(); + if (pdtRegistry != null && version == GraphSONVersion.V4_0) { + graphSONTypeIdResolver.setPdtRegistry(pdtRegistry); + } final TypeResolverBuilder typer = new GraphSONTypeResolverBuilder(version) .typesEmbedding(this.typeInfo) .valuePropertyName(GraphSONTokens.VALUEPROP) @@ -161,6 +164,10 @@ public class GraphSONMapper implements Mapper<ObjectMapper> { // this provider toStrings all unknown classes and converts keys in Map objects that are Object to String. final DefaultSerializerProvider provider = new GraphSONSerializerProvider(version); om.setSerializerProvider(provider); + } else if (pdtRegistry != null) { + // For V4 with a pdtRegistry, set a provider that converts adapter-registered types to PDT + final DefaultSerializerProvider provider = new PdtGraphSONSerializerProviderV4(pdtRegistry); + om.setSerializerProvider(provider); } if (normalize) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeIdResolver.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeIdResolver.java index 08184d6566..0b535f8141 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeIdResolver.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeIdResolver.java @@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.structure.io.graphson; import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; import org.apache.tinkerpop.gremlin.structure.Element; +import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedTypeRegistry; import org.apache.tinkerpop.shaded.jackson.annotation.JsonTypeInfo; import org.apache.tinkerpop.shaded.jackson.core.type.TypeReference; import org.apache.tinkerpop.shaded.jackson.databind.DatabindContext; @@ -42,6 +43,8 @@ public class GraphSONTypeIdResolver implements TypeIdResolver { private final Map<Class, String> typeToId = new HashMap<>(); + private ProviderDefinedTypeRegistry pdtRegistry; + // Override manually a type definition. public GraphSONTypeIdResolver addCustomType(final String name, final Class clasz) { if (Tree.class.isAssignableFrom(clasz)) { @@ -65,6 +68,10 @@ public class GraphSONTypeIdResolver implements TypeIdResolver { return typeToId; } + public void setPdtRegistry(final ProviderDefinedTypeRegistry pdtRegistry) { + this.pdtRegistry = pdtRegistry; + } + @Override public void init(final JavaType javaType) { } @@ -77,6 +84,10 @@ public class GraphSONTypeIdResolver implements TypeIdResolver { @Override public String idFromValueAndType(final Object o, final Class<?> aClass) { if (!typeToId.containsKey(aClass)) { + // Check if pdtRegistry has an adapter for this class + if (pdtRegistry != null && pdtRegistry.getAdapterByClass(aClass).isPresent()) { + return typeToId.get(org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedType.class); + } // If one wants to serialize an object with a type, but hasn't registered // a typeID for that class, fail. throw new IllegalArgumentException(String.format("Could not find a type identifier for the class : %s. " + diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializerProviderV4.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializerProviderV4.java new file mode 100644 index 0000000000..fb29453da5 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializerProviderV4.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.structure.io.graphson; + +import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedTypeRegistry; +import org.apache.tinkerpop.shaded.jackson.databind.JsonSerializer; +import org.apache.tinkerpop.shaded.jackson.databind.SerializationConfig; +import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider; +import org.apache.tinkerpop.shaded.jackson.databind.ser.DefaultSerializerProvider; +import org.apache.tinkerpop.shaded.jackson.databind.ser.SerializerFactory; + +/** + * A {@link DefaultSerializerProvider} for GraphSON V4 that returns a PDT adapter-based serializer + * for classes registered in the {@link ProviderDefinedTypeRegistry}. + */ +final class PdtGraphSONSerializerProviderV4 extends DefaultSerializerProvider { + private static final long serialVersionUID = 1L; + private final ProviderDefinedTypeRegistry pdtRegistry; + private final JsonSerializer<Object> pdtAdapterSerializer; + + PdtGraphSONSerializerProviderV4(final ProviderDefinedTypeRegistry pdtRegistry) { + super(); + this.pdtRegistry = pdtRegistry; + this.pdtAdapterSerializer = new PdtGraphSONSerializersV4.PdtAdapterJacksonSerializer(pdtRegistry); + } + + private PdtGraphSONSerializerProviderV4(final SerializerProvider src, + final SerializationConfig config, final SerializerFactory f, + final ProviderDefinedTypeRegistry pdtRegistry, + final JsonSerializer<Object> pdtAdapterSerializer) { + super(src, config, f); + this.pdtRegistry = pdtRegistry; + this.pdtAdapterSerializer = pdtAdapterSerializer; + } + + @Override + public JsonSerializer<Object> getUnknownTypeSerializer(final Class<?> aClass) { + if (pdtRegistry != null && pdtRegistry.getAdapterByClass(aClass).isPresent()) { + return pdtAdapterSerializer; + } + return super.getUnknownTypeSerializer(aClass); + } + + @Override + public PdtGraphSONSerializerProviderV4 createInstance(final SerializationConfig config, + final SerializerFactory jsf) { + return new PdtGraphSONSerializerProviderV4(this, config, jsf, pdtRegistry, pdtAdapterSerializer); + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4.java index de116f54aa..03e686917c 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4.java @@ -19,6 +19,7 @@ package org.apache.tinkerpop.gremlin.structure.io.graphson; import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedType; +import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedTypeAdapter; import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedTypeRegistry; import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator; import org.apache.tinkerpop.shaded.jackson.core.JsonParser; @@ -27,10 +28,12 @@ import org.apache.tinkerpop.shaded.jackson.databind.DeserializationContext; import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider; import org.apache.tinkerpop.shaded.jackson.databind.deser.std.StdDeserializer; import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdScalarSerializer; +import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer; import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Optional; /** * GraphSON V4 serializers for {@link ProviderDefinedType}. @@ -113,4 +116,55 @@ final class PdtGraphSONSerializersV4 { return true; } } + + /** + * A serializer that converts raw objects to {@link ProviderDefinedType} using a registered adapter, + * then serializes the resulting PDT in the standard CompositePdt format. + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + static class PdtAdapterJacksonSerializer extends StdSerializer<Object> { + + private final ProviderDefinedTypeRegistry registry; + + PdtAdapterJacksonSerializer(final ProviderDefinedTypeRegistry registry) { + super(Object.class); + this.registry = registry; + } + + @Override + public void serialize(final Object value, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider) throws IOException { + final ProviderDefinedType pdt = toPdt(value); + jsonGenerator.writeStartObject(); + jsonGenerator.writeStringField("type", pdt.getName()); + jsonGenerator.writeFieldName("fields"); + jsonGenerator.writeStartObject(); + for (final Map.Entry<String, Object> entry : pdt.getFields().entrySet()) { + jsonGenerator.writeFieldName(entry.getKey()); + jsonGenerator.writeObject(entry.getValue()); + } + jsonGenerator.writeEndObject(); + jsonGenerator.writeEndObject(); + } + + @Override + public void serializeWithType(final Object value, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, + final org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer typeSerializer) throws IOException { + // Convert to ProviderDefinedType and delegate to its registered typed serializer + final ProviderDefinedType pdt = toPdt(value); + serializerProvider.findTypedValueSerializer(ProviderDefinedType.class, true, null) + .serialize(pdt, jsonGenerator, serializerProvider); + } + + private ProviderDefinedType toPdt(final Object value) throws IOException { + final Optional<ProviderDefinedTypeAdapter<?>> opt = registry.getAdapterByClass(value.getClass()); + if (!opt.isPresent()) { + throw new IOException("No adapter found for " + value.getClass().getName()); + } + final ProviderDefinedTypeAdapter adapter = opt.get(); + final Map<String, Object> fields = adapter.toFields(value); + return new ProviderDefinedType(adapter.typeName(), fields); + } + } } diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4Test.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4Test.java index 20bf1f7238..fa29b1ba68 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4Test.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/PdtGraphSONSerializersV4Test.java @@ -205,6 +205,27 @@ public class PdtGraphSONSerializersV4Test extends AbstractGraphSONTest { assertEquals(1, result.getFields().get("x")); } + @Test + public void shouldDehydrateRegisteredButUnannotatedTypeViaAdapterOnWritePath() throws Exception { + final ProviderDefinedTypeRegistry registry = ProviderDefinedTypeRegistry.empty(); + registry.register(new PointAdapter()); + + final ObjectMapper adapterMapper = GraphSONMapper.build() + .version(GraphSONVersion.V4_0) + .addCustomModule(GraphSONXModuleV4.build()) + .typeInfo(TypeInfo.PARTIAL_TYPES) + .pdtRegistry(registry) + .create().createMapper(); + + final Point original = new Point(5, 9); + final ProviderDefinedType result = serializeDeserialize(adapterMapper, original, ProviderDefinedType.class); + + assertNotNull(result.getHydrated()); + assertTrue(result.getHydrated() instanceof Point); + assertEquals(5, ((Point) result.getHydrated()).x); + assertEquals(9, ((Point) result.getHydrated()).y); + } + @Test public void shouldReturnRawPdtWhenTypeNotRegistered() throws Exception { final ProviderDefinedTypeRegistry registry = ProviderDefinedTypeRegistry.empty(); diff --git a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV4.java b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV4.java index 63c3893fb0..0fdf213b7d 100644 --- a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV4.java +++ b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV4.java @@ -28,6 +28,7 @@ import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper; import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONUtil; import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONVersion; import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONXModuleV4; +import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedTypeRegistry; import org.apache.tinkerpop.gremlin.util.Tokens; import org.apache.tinkerpop.gremlin.util.message.RequestMessage; import org.apache.tinkerpop.gremlin.util.message.ResponseMessage; @@ -77,7 +78,8 @@ public abstract class AbstractGraphSONMessageSerializerV4 extends AbstractMessag private GraphSONMapper.Builder initBuilder() { final GraphSONMapper.Builder b = GraphSONMapper.build(); - return b.addCustomModule(GraphSONXModuleV4.build()).version(GraphSONVersion.V4_0); + return b.addCustomModule(GraphSONXModuleV4.build()).version(GraphSONVersion.V4_0) + .pdtRegistry(ProviderDefinedTypeRegistry.create()); } private GraphSONMapper.Builder applyMaxTokenLimits(final GraphSONMapper.Builder builder, final Map<String, Object> config) { diff --git a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/GraphBinaryMessageSerializerV4.java b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/GraphBinaryMessageSerializerV4.java index 6f2e4c657f..67a4498ab4 100644 --- a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/GraphBinaryMessageSerializerV4.java +++ b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/GraphBinaryMessageSerializerV4.java @@ -68,7 +68,7 @@ public class GraphBinaryMessageSerializerV4 extends AbstractMessageSerializer<Gr public GraphBinaryMessageSerializerV4(final TypeSerializerRegistry registry, final ProviderDefinedTypeRegistry pdtRegistry) { reader = new GraphBinaryReader(registry, pdtRegistry); - writer = new GraphBinaryWriter(registry); + writer = new GraphBinaryWriter(registry, pdtRegistry); mapper = new GraphBinaryMapper(writer, reader); requestSerializer = new RequestMessageSerializer(); @@ -102,7 +102,7 @@ public class GraphBinaryMessageSerializerV4 extends AbstractMessageSerializer<Gr final TypeSerializerRegistry registry = builder.create(); reader = new GraphBinaryReader(registry, ProviderDefinedTypeRegistry.create()); - writer = new GraphBinaryWriter(registry); + writer = new GraphBinaryWriter(registry, ProviderDefinedTypeRegistry.create()); requestSerializer = new RequestMessageSerializer(); } diff --git a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/GraphBinaryWriterPdtTest.java b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/GraphBinaryWriterPdtTest.java index e4483934d5..7576dbe306 100644 --- a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/GraphBinaryWriterPdtTest.java +++ b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/GraphBinaryWriterPdtTest.java @@ -22,8 +22,11 @@ import io.netty.buffer.ByteBufAllocator; import org.apache.tinkerpop.gremlin.structure.io.Buffer; import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader; import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter; +import org.apache.tinkerpop.gremlin.structure.io.binary.TypeSerializerRegistry; import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefined; import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedType; +import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedTypeAdapter; +import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedTypeRegistry; import org.apache.tinkerpop.gremlin.util.ser.NettyBufferFactory; import org.junit.Test; @@ -57,6 +60,21 @@ public class GraphBinaryWriterPdtTest { int value = 1; } + static class UnannotatedTypeAdapter implements ProviderDefinedTypeAdapter<UnannotatedType> { + @Override public String typeName() { return "UnannotatedType"; } + @Override public Class<UnannotatedType> targetClass() { return UnannotatedType.class; } + @Override public Map<String, Object> toFields(final UnannotatedType obj) { + final Map<String, Object> m = new LinkedHashMap<>(); + m.put("value", obj.value); + return m; + } + @Override public UnannotatedType fromFields(final Map<String, Object> fields) { + final UnannotatedType t = new UnannotatedType(); + t.value = (int) fields.get("value"); + return t; + } + } + @Test public void shouldAutoConvertAnnotatedObjectToPdt() throws IOException { final Buffer buffer = bufferFactory.create(allocator.buffer()); @@ -77,6 +95,30 @@ public class GraphBinaryWriterPdtTest { assertTrue(ex.getMessage().contains("UnannotatedType")); } + /** + * Verifies that a type registered via a {@link ProviderDefinedTypeAdapter} (without the {@link ProviderDefined} + * annotation) can be dehydrated on the write path by a registry-aware {@link GraphBinaryWriter} and then + * hydrated back by the reader through the same registry. + */ + @Test + public void shouldDehydrateRegisteredButUnannotatedTypeViaAdapterOnWritePath() throws IOException { + final ProviderDefinedTypeRegistry pdtRegistry = ProviderDefinedTypeRegistry.empty(); + pdtRegistry.register(new UnannotatedTypeAdapter()); + + final GraphBinaryWriter registryWriter = new GraphBinaryWriter(TypeSerializerRegistry.INSTANCE, pdtRegistry); + final GraphBinaryReader registryReader = new GraphBinaryReader(TypeSerializerRegistry.INSTANCE, pdtRegistry); + + final UnannotatedType original = new UnannotatedType(); + original.value = 42; + + final Buffer buffer = bufferFactory.create(allocator.buffer()); + registryWriter.write(original, buffer); + buffer.readerIndex(0); + + final UnannotatedType result = registryReader.read(buffer); + assertEquals(42, result.value); + } + @Test public void shouldNotDoubleWrapProviderDefinedType() throws IOException { final Map<String, Object> fields = new LinkedHashMap<>();
