You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by co...@apache.org on 2026/06/25 19:47:18 UTC
(tinkerpop) 01/17: Make GraphBinary/GraphSON write path registry-aware for PDT dehydration
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 <co...@colegreer.ca>
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<>();