This is an automated email from the ASF dual-hosted git repository.

kenhuuu pushed a commit to branch 3.7-dev
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git


The following commit(s) were added to refs/heads/3.7-dev by this push:
     new 739d5c4a6f Restrict GraphSON 1.0 embedded-type deserialization (#3586)
739d5c4a6f is described below

commit 739d5c4a6fdeb4bfef62ebffaf933d5e8ca98d21
Author: Guian Gumpac <[email protected]>
AuthorDate: Mon Aug 31 09:50:53 2026 -0700

    Restrict GraphSON 1.0 embedded-type deserialization (#3586)
    
    Assisted-by: Kiro: Claude Opus 4.8
---
 CHANGELOG.asciidoc                                 |   1 +
 .../gremlin-server/gremlin-server-integration.yaml |   2 +-
 docs/src/dev/io/graphson.asciidoc                  |  10 +-
 docs/src/reference/gremlin-applications.asciidoc   |  24 +-
 docs/src/upgrade/release-3.7.x.asciidoc            |  39 ++
 .../structure/io/graphson/GraphSONMapper.java      | 237 ++++++++-
 .../com/example/gadget/GraphSONTestGadgets.java    | 127 +++++
 .../io/graphson/GraphSONMapperBuilderTest.java     |  40 ++
 .../GraphSONMapperPartialEmbeddedTypeTest.java     | 592 +++++++++++++++++++++
 .../gremlin/driver/ClusterConfigTest.java          |  54 ++
 .../gremlin/server/GremlinDriverIntegrateTest.java |  16 +-
 .../tinkerpop/gremlin/server/SettingsTest.java     |  44 ++
 .../gremlin/server/gremlin-server-integration.yaml |   1 +
 .../gremlin/structure/io/IoCustomTest.java         |   5 +-
 .../tinkerpop/gremlin/structure/io/IoTest.java     |   6 +-
 .../ser/AbstractGraphSONMessageSerializerV1.java   |  11 +
 .../util/ser/AbstractMessageSerializer.java        |   8 +-
 .../util/ser/GraphSONMessageSerializerV1Test.java  |  67 +++
 18 files changed, 1263 insertions(+), 21 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index cfb2b6bf03..70b64a4595 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -30,6 +30,7 @@ 
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 * Added verification to `io()` ensuring a class named by `IO.reader`, 
`IO.writer` or `IO.registry` implements the expected interface before loading 
and initializing it.
 * Fixed `io().write()` to resolve its `GraphWriter` before opening the output 
file, so that a writer which cannot be constructed no longer truncates the 
target.
 * Fixed `TinkerGraph` GraphSON deserialization to fail fast with 
`JsonParseException` on malformed `tinker:graph` input.
+* Changed GraphSON 1.0 typed deserialization to resolve a `@class` type id 
only when the name is in a set of exact class names.
 * Restricted Hadoop/Spark OLAP `io()` from a remotely submitted traversal to 
operator-approved reader/writer classes and `with()` keys, adding the 
`gremlin.io.trusted`, `gremlin.io.approvedClasses` and 
`gremlin.io.approvedGraphConfigKeys` options.
 * Restricted OLAP `GraphComputer.configure()` from a remotely submitted 
traversal to built-in and operator-approved keys, adding the 
`gremlin.io.approvedComputerConfigKeys` option.
 * Fixed `subgraph()` to throw a descriptive error identifying the required 
`Edge` input instead of an internal `ClassCastException` when the traversal 
produces a non-edge value.
diff --git a/docker/gremlin-server/gremlin-server-integration.yaml 
b/docker/gremlin-server/gremlin-server-integration.yaml
index d039479508..e5b94459ad 100644
--- a/docker/gremlin-server/gremlin-server-integration.yaml
+++ b/docker/gremlin-server/gremlin-server-integration.yaml
@@ -40,7 +40,7 @@ scriptEngines: {
 serializers:
   - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3, config: { 
ioRegistries: 
[org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3] }}
   - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV2, config: { 
ioRegistries: 
[org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV2] }}
-  - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV1, config: { 
ioRegistries: 
[org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV1] }}
+  - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV1, config: { 
ioRegistries: 
[org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV1], 
allowedTypeIdNames: [java.awt.Point] }}
   - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1 }
   - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1, config: { 
serializeResultToString: true }}
 processors:
diff --git a/docs/src/dev/io/graphson.asciidoc 
b/docs/src/dev/io/graphson.asciidoc
index e1ed00d798..29e57cba73 100644
--- a/docs/src/dev/io/graphson.asciidoc
+++ b/docs/src/dev/io/graphson.asciidoc
@@ -140,7 +140,15 @@ Version 1.0 of GraphSON was released with TinkerPop 3.0.0. 
It is referred to by
 When types are embedded, GraphSON uses the standard
 link:https://github.com/FasterXML/jackson-databind[Jackson] type embedding 
approach that writes the full Java class
 name into a "@class" field in the JSON. While this approach isn't especially 
language agnostic it does at least give
-some hint as to what the expected type is.
+some hint as to what the expected type is. On read, that class name resolves 
only when it is in a set of exact class
+names, derived from the types that GraphSON 2.0 and 3.0 register plus the 
concrete runtime classes that GraphSON 1.0
+writes and the registry does not declare. Names are matched by equality, and 
an array is decided by its component
+name. A parameterized type id is always refused. The `java.lang.Class` type is 
refused by default but can be
+explicitly allowed. A provider or application can add its own class names with
+`GraphSONMapper.Builder.addAllowedTypeIdName(String...)`, for example 
`addAllowedTypeIdName("com.example.MyType")`.
+For a very narrow set of types, typed GraphSON 1.0 writing and reading are 
asymmetric. The writer may emit a concrete
+or parameterized Java type id that the reader refuses. Examples include 
`ByteBuffer` and `EnumMap`, which can be
+written but cannot be read back.
 
 This section focuses on non-embedded types and their formats as there was 
little usage of embedded types in generalized
 object serialization use cases. The format was simply too cumbersome to parse 
of non-Jackson enabled libraries and the
diff --git a/docs/src/reference/gremlin-applications.asciidoc 
b/docs/src/reference/gremlin-applications.asciidoc
index bdf8263b4d..81c157247f 100644
--- a/docs/src/reference/gremlin-applications.asciidoc
+++ b/docs/src/reference/gremlin-applications.asciidoc
@@ -1176,6 +1176,7 @@ The drivers are designed to use that type information to 
properly produce result
 system and may not function correctly without it. Generally speaking, 
`GraphBinary` is always the best choice for the
 drivers.
 
+[[server-graphson]]
 ===== GraphSON
 
 The GraphSON serializer produces human-readable output in JSON format and is a 
good configuration choice for those
@@ -1211,14 +1212,30 @@ Configuring GraphSON in the Gremlin Server 
configuration looks like this:
   - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3 }
 ----
 
-Gremlin Server is configured by default with GraphSON 3.0 as shown above. It 
has the following configuration option:
+Gremlin Server is configured by default with GraphSON 3.0 as shown above. It 
has the following configuration options:
 
 [width="100%",cols="3,10,^2",options="header"]
 |=========================================================
 |Key |Description |Default
 |ioRegistries |A list of `IoRegistry` implementations to be applied to the 
serializer. |_none_
+|allowedTypeIdNames |A list of fully qualified class names that 
`GraphSONMessageSerializerV1` is allowed to resolve from `@class` type ids, in 
addition to its defaults. |_none_
 |=========================================================
 
+The `allowedTypeIdNames` option only affects typed GraphSON 1.0 reads. The 
equivalent mapper configuration uses
+`GraphSONMapper.Builder.addAllowedTypeIdName(String...)`:
+
+[source,java]
+----
+GraphSONMapper.build().version(GraphSONVersion.V1_0).typeInfo(TypeInfo.PARTIAL_TYPES)
+        .addAllowedTypeIdName("com.example.MyType", 
"com.example.MyOtherType").create();
+----
+
+Each configured name is matched exactly, so it does not allow subclasses or 
other classes in the same package. An
+array is checked by its component class name, so allowing `com.example.MyType` 
also allows arrays of that class.
+Parameterized type ids are rejected before class-name matching. Therefore, a 
type id such as
+`java.util.EnumMap<...>` cannot be enabled by adding either 
`java.util.EnumMap` or the parameterized type id to the
+configured names.
+
 It is worth noting that GraphSON 1.0 still has some appeal for some users as 
it can be configured to produce an untyped
 JSON format which is a bit easier to consume than its successors which embed 
data types into the output. This version
 of GraphSON tends to be the one that users like to utilize when 
<<connecting-via-http,connecting via HTTP>> and is still
@@ -1228,7 +1245,10 @@ To configure Gremlin Server this way, the 
`GraphSONMessageSerializerV1` must be
 
 [source,yaml]
 ----
-  - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV1 }
+  - className: 
org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV1
+    config:
+      allowedTypeIdNames:
+        - com.example.MyType
   - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3 }
 ----
 
diff --git a/docs/src/upgrade/release-3.7.x.asciidoc 
b/docs/src/upgrade/release-3.7.x.asciidoc
index 05524f9299..a97738f3db 100644
--- a/docs/src/upgrade/release-3.7.x.asciidoc
+++ b/docs/src/upgrade/release-3.7.x.asciidoc
@@ -88,6 +88,45 @@ GryoIo.build().graph(graph).onMapper(m -> 
((GryoMapper.Builder) m)
 
 See: link:https://issues.apache.org/jira/browse/TINKERPOP-3278[TINKERPOP-3278]
 
+==== GraphSON 1.0 Type Allow-List
+
+Typed GraphSON 1.0 now resolves a Java class named by an `@class` property 
only when that exact name is allowed.
+This change is breaking on read for provider and application types outside the 
default set. Some values that GraphSON
+1.0 can write, including `ByteBuffer` and `EnumMap`, also no longer 
round-trip. The written format is unchanged.
+
+The failure looks like this:
+
+[source,text]
+----
+Could not resolve type id ...: Configured `PolymorphicTypeValidator` ... 
denied resolution
+----
+
+Further simple class names can be added on the mapper:
+
+[source,java]
+----
+GraphSONMapper.build().version(GraphSONVersion.V1_0).typeInfo(TypeInfo.PARTIAL_TYPES)
+        .addAllowedTypeIdName("com.example.MyType", 
"com.example.MyOtherType").create();
+----
+
+Expected application types can be added to the message serializer 
configuration:
+
+[source,yaml]
+----
+serializers:
+  - className: 
org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV1
+    config:
+      allowedTypeIdNames:
+        - com.example.MyType
+----
+
+Allowlisting cannot restore a parameterized type id such as 
`java.util.EnumMap<...>` because GraphSON 1.0 rejects
+all parameterized type ids unconditionally. Affected applications can convert 
values to non-parameterized
+representations such as `HashMap`, or move to GraphSON 2.0, GraphSON 3.0 or 
GraphBinary.
+
+See: 
link:https://tinkerpop.apache.org/docs/3.7.7/reference/#server-graphson[GraphSON
 Server Configuration],
+link:https://tinkerpop.apache.org/docs/3.7.7/dev/io/#graphson-1d0[GraphSON 1.0 
IO Documentation]
+
 ==== OLAP Restrictions
 
 The OLAP `io()` step on `HadoopGraph` (`g.withComputer().io(path)...`) 
historically transferred every
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 cda357caae..bab483d1d2 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
@@ -27,6 +27,13 @@ import 
org.apache.tinkerpop.shaded.jackson.core.JsonGenerator;
 import org.apache.tinkerpop.shaded.jackson.core.StreamReadConstraints;
 import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper;
 import org.apache.tinkerpop.shaded.jackson.databind.SerializationFeature;
+import org.apache.tinkerpop.shaded.jackson.databind.JavaType;
+import org.apache.tinkerpop.shaded.jackson.databind.DatabindContext;
+import org.apache.tinkerpop.shaded.jackson.databind.DeserializationContext;
+import org.apache.tinkerpop.shaded.jackson.databind.cfg.MapperConfig;
+import 
org.apache.tinkerpop.shaded.jackson.databind.jsontype.PolymorphicTypeValidator;
+import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeIdResolver;
+import org.apache.tinkerpop.shaded.jackson.databind.jsontype.NamedType;
 import 
org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeResolverBuilder;
 import 
org.apache.tinkerpop.shaded.jackson.databind.jsontype.impl.StdTypeResolverBuilder;
 import org.apache.tinkerpop.shaded.jackson.databind.module.SimpleModule;
@@ -35,12 +42,17 @@ import org.javatuples.Pair;
 
 import java.sql.Timestamp;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.TimeZone;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
 import java.util.UUID;
 
 /**
@@ -63,6 +75,49 @@ import java.util.UUID;
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
 public class GraphSONMapper implements Mapper<ObjectMapper> {
+
+    // Java base value types registered for GraphSON 2.0/3.0, shared by 
registerJavaBaseTypes and the V1 name set.
+    private static final List<Class> GRAPHSON_JAVA_BASE_TYPES = Arrays.asList(
+            UUID.class, Class.class, Calendar.class, Date.class, 
TimeZone.class, Timestamp.class);
+
+    // concrete runtime class names GraphSON 1.0 writes that 
registeredV2V3Types() does not supply, as it declares
+    // only the java.util.List, Map and Set interfaces
+    private static final List<String> GRAPHSON_1_0_ALLOWED_EXTRA_TYPE_NAMES = 
Arrays.asList(
+            "java.lang.Boolean",
+            "java.lang.Object",
+            "java.lang.String",
+            "java.net.URI",
+            "java.sql.Date",
+            "java.sql.Time",
+            "java.time.DayOfWeek",
+            "java.time.Month",
+            "java.util.ArrayDeque",
+            "java.util.ArrayList",
+            "java.util.Arrays$ArrayList",
+            "java.util.Collections$EmptyList",
+            "java.util.Collections$EmptyMap",
+            "java.util.Collections$EmptySet",
+            "java.util.Collections$SingletonList",
+            "java.util.Collections$SingletonMap",
+            "java.util.Collections$SingletonSet",
+            "java.util.Collections$UnmodifiableList",
+            "java.util.Collections$UnmodifiableMap",
+            "java.util.Collections$UnmodifiableRandomAccessList",
+            "java.util.Collections$UnmodifiableSet",
+            "java.util.Currency",
+            "java.util.GregorianCalendar",
+            "java.util.HashMap",
+            "java.util.HashSet",
+            "java.util.LinkedHashMap",
+            "java.util.LinkedHashSet",
+            "java.util.LinkedList",
+            "java.util.Locale",
+            "java.util.Properties",
+            "java.util.TreeMap",
+            "java.util.TreeSet",
+            // gremlin-util depends on gremlin-core, so the V1 message 
envelope is named here without a class reference
+            "org.apache.tinkerpop.gremlin.util.message.RequestMessage");
+    private static final Set<String> GRAPHSON_1_0_ALLOWED_TYPE_NAMES = 
graphSON1dAllowedTypeNames();
     public static final int DEFAULT_MAX_NUMBER_LENGTH = 10000;
 
     private final List<SimpleModule> customModules;
@@ -71,6 +126,7 @@ public class GraphSONMapper implements Mapper<ObjectMapper> {
     private final GraphSONVersion version;
     private final TypeInfo typeInfo;
     private final StreamReadConstraints streamReadConstraints;
+    final List<String> allowedTypeIdNames;
 
     private GraphSONMapper(final Builder builder) {
         this.customModules = builder.customModules;
@@ -79,6 +135,7 @@ public class GraphSONMapper implements Mapper<ObjectMapper> {
         this.version = builder.version;
         this.streamReadConstraints = 
builder.streamReadConstraintsBuilder.build();
         this.typeInfo = builder.typeInfo;
+        this.allowedTypeIdNames = builder.allowedTypeIdNames;
     }
 
     @Override
@@ -128,8 +185,24 @@ public class GraphSONMapper implements 
Mapper<ObjectMapper> {
             om.setDefaultTyping(typer);
         } else if (version == GraphSONVersion.V1_0 || version == 
GraphSONVersion.V2_0) {
             if (typeInfo == TypeInfo.PARTIAL_TYPES) {
-                final TypeResolverBuilder<?> typer = new 
StdTypeResolverBuilder()
-                        .init(JsonTypeInfo.Id.CLASS, null)
+                final Set<String> allowedNames = new 
HashSet<>(GRAPHSON_1_0_ALLOWED_TYPE_NAMES);
+                allowedNames.addAll(allowedTypeIdNames);
+                final PolymorphicTypeValidator typeValidator = 
graphSON1dTypeValidator(allowedNames);
+                final TypeResolverBuilder<?> typer = new 
StdTypeResolverBuilder() {
+                    @Override
+                    public PolymorphicTypeValidator subTypeValidator(final 
MapperConfig<?> config) {
+                        return typeValidator;
+                    }
+
+                    @Override
+                    protected TypeIdResolver idResolver(final MapperConfig<?> 
config, final JavaType baseType,
+                                                        final 
PolymorphicTypeValidator subtypeValidator,
+                                                        final 
Collection<NamedType> subtypes,
+                                                        final boolean forSer, 
final boolean forDeser) {
+                        return new GraphSON1dScreeningIdResolver(
+                                super.idResolver(config, baseType, 
subtypeValidator, subtypes, forSer, forDeser));
+                    }
+                }.init(JsonTypeInfo.Id.CLASS, null)
                         .inclusion(JsonTypeInfo.As.PROPERTY)
                         .typeProperty(GraphSONTokens.CLASS);
                 om.setDefaultTyping(typer);
@@ -152,6 +225,141 @@ public class GraphSONMapper implements 
Mapper<ObjectMapper> {
         return om;
     }
 
+    /**
+     * A {@link PolymorphicTypeValidator} for GraphSON 1.0 embedded types that 
decides a simple type id from its name
+     * alone. Parameterized type ids are handled by {@link 
GraphSON1dScreeningIdResolver}, as the validator is not
+     * shown a type id's arguments.
+     */
+    private static PolymorphicTypeValidator graphSON1dTypeValidator(final 
Set<String> allowedNames) {
+        return new PolymorphicTypeValidator.Base() {
+            @Override
+            public Validity validateBaseType(final MapperConfig<?> config, 
final JavaType baseType) {
+                // must stay INDETERMINATE: on ALLOWED, Jackson substitutes 
LaissezFaireSubTypeValidator and the
+                // name set below is no longer consulted
+                return Validity.INDETERMINATE;
+            }
+
+            @Override
+            public Validity validateSubClassName(final MapperConfig<?> config, 
final JavaType baseType,
+                                                 final String subClassName) {
+                return isAllowedTypeName(subClassName, allowedNames) ? 
Validity.ALLOWED : Validity.DENIED;
+            }
+
+            @Override
+            public Validity validateSubType(final MapperConfig<?> config, 
final JavaType baseType,
+                                            final JavaType subType) {
+                // reached only when validateSubClassName returns 
INDETERMINATE, which it never does, so this is not
+                // where a name is decided today
+                return isAllowedTypeName(subType.getRawClass().getName(), 
allowedNames)
+                        ? Validity.ALLOWED : Validity.DENIED;
+            }
+        };
+    }
+
+    /**
+     * Wraps the class-name {@link TypeIdResolver} to refuse a parameterized 
GraphSON 1.0 type id, meaning one
+     * containing '{@code <}', rather than hand it to the delegate.
+     */
+    private static final class GraphSON1dScreeningIdResolver implements 
TypeIdResolver {
+        private final TypeIdResolver delegate;
+        private JavaType baseType;
+
+        private GraphSON1dScreeningIdResolver(final TypeIdResolver delegate) {
+            this.delegate = delegate;
+        }
+
+        @Override
+        public void init(final JavaType baseType) {
+            this.baseType = baseType;
+            delegate.init(baseType);
+        }
+
+        @Override
+        public JavaType typeFromId(final DatabindContext context, final String 
id) throws IOException {
+            if (id.indexOf('<') >= 0) {
+                if (context instanceof DeserializationContext)
+                    throw ((DeserializationContext) 
context).invalidTypeIdException(baseType, id,
+                            "GraphSON 1.0 does not permit a parameterized type 
id");
+                throw new IOException("GraphSON 1.0 does not permit a 
parameterized type id: " + id);
+            }
+            return delegate.typeFromId(context, id);
+        }
+
+        @Override
+        public String idFromValue(final Object value) {
+            return delegate.idFromValue(value);
+        }
+
+        @Override
+        public String idFromValueAndType(final Object value, final Class<?> 
suggestedType) {
+            return delegate.idFromValueAndType(value, suggestedType);
+        }
+
+        @Override
+        public String idFromBaseType() {
+            return delegate.idFromBaseType();
+        }
+
+        @Override
+        public String getDescForKnownTypeIds() {
+            return delegate.getDescForKnownTypeIds();
+        }
+
+        @Override
+        public JsonTypeInfo.Id getMechanism() {
+            return delegate.getMechanism();
+        }
+    }
+
+    // TypeInfo does not affect the (static) type-definition map, so NO_TYPES 
is passed
+    private static Set<Class> registeredV2V3Types() {
+        final Set<Class> registered = new 
LinkedHashSet<>(GRAPHSON_JAVA_BASE_TYPES);
+        registered.addAll(GraphSONVersion.V2_0.getBuilder().create(false, 
TypeInfo.NO_TYPES)
+                .getTypeDefinitions().keySet());
+        registered.addAll(GraphSONVersion.V3_0.getBuilder().create(false, 
TypeInfo.NO_TYPES)
+                .getTypeDefinitions().keySet());
+        registered.addAll(GraphSONXModuleV2.build().create(false, 
TypeInfo.NO_TYPES)
+                .getTypeDefinitions().keySet());
+        registered.addAll(GraphSONXModuleV3.build().create(false, 
TypeInfo.NO_TYPES)
+                .getTypeDefinitions().keySet());
+        return registered;
+    }
+
+    /**
+     * The exact class names GraphSON 1.0 embedded-type deserialization 
resolves by default.
+     */
+    private static Set<String> graphSON1dAllowedTypeNames() {
+        final Set<String> names = new 
HashSet<>(GRAPHSON_1_0_ALLOWED_EXTRA_TYPE_NAMES);
+        names.addAll(graphSON1dDerivedTypeNames());
+        return names;
+    }
+
+    /**
+     * The GraphSON 1.0 allowed names derived from {@link 
#registeredV2V3Types()}. Package private so a test can pin
+     * it, since a new type definition in any GraphSON 2.0/3.0 module widens 
this set as a side effect.
+     */
+    static Set<String> graphSON1dDerivedTypeNames() {
+        final Set<String> names = new HashSet<>();
+        for (final Class c : registeredV2V3Types()) {
+            // a java.lang.Class value resolves its own content as a class 
name, which GraphSON 1.0 does not need
+            if (Class.class != c)
+                names.add(c.getName());
+        }
+        return names;
+    }
+
+    private static boolean isAllowedTypeName(final String typeName, final 
Set<String> allowedNames) {
+        // unwrap array descriptors: "[Ljava.lang.String;" -> 
"java.lang.String", "[[B" -> primitive element
+        String name = typeName;
+        while (name.startsWith("["))
+            name = name.substring(1);
+        if (name.length() <= 1)
+            return true; // primitive array element (e.g. [B, [I) names no 
class
+        if (name.startsWith("L") && name.endsWith(";"))
+            name = name.substring(1, name.length() - 1);
+        return allowedNames.contains(name);
+    }
+
     public GraphSONVersion getVersion() {
         return this.version;
     }
@@ -174,6 +382,7 @@ public class GraphSONMapper implements Mapper<ObjectMapper> 
{
         builder.normalize = mapper.normalize;
         builder.typeInfo = mapper.typeInfo;
         builder.streamReadConstraintsBuilder = 
mapper.streamReadConstraints.rebuild();
+        builder.allowedTypeIdNames.addAll(mapper.allowedTypeIdNames);
 
         return builder;
     }
@@ -183,14 +392,8 @@ public class GraphSONMapper implements 
Mapper<ObjectMapper> {
     }
 
     private void registerJavaBaseTypes(final GraphSONTypeIdResolver 
graphSONTypeIdResolver) {
-        Arrays.asList(
-                UUID.class,
-                Class.class,
-                Calendar.class,
-                Date.class,
-                TimeZone.class,
-                Timestamp.class
-        ).forEach(e -> 
graphSONTypeIdResolver.addCustomType(String.format("%s:%s", 
GraphSONTokens.GREMLIN_TYPE_NAMESPACE, e.getSimpleName()), e));
+        GRAPHSON_JAVA_BASE_TYPES.forEach(e -> 
graphSONTypeIdResolver.addCustomType(
+                String.format("%s:%s", GraphSONTokens.GREMLIN_TYPE_NAMESPACE, 
e.getSimpleName()), e));
     }
 
     public static class Builder implements Mapper.Builder<Builder> {
@@ -204,6 +407,7 @@ public class GraphSONMapper implements Mapper<ObjectMapper> 
{
         private StreamReadConstraints.Builder streamReadConstraintsBuilder = 
StreamReadConstraints.builder()
                 .maxNumberLength(DEFAULT_MAX_NUMBER_LENGTH);
         private TypeInfo typeInfo = null;
+        private final List<String> allowedTypeIdNames = new ArrayList<>();
 
         private Builder() {
         }
@@ -288,6 +492,19 @@ public class GraphSONMapper implements 
Mapper<ObjectMapper> {
             return this;
         }
 
+        /**
+         * Adds a fully qualified class name that GraphSON 1.0 embedded-type 
deserialization resolves in a
+         * {@code @class} property, in addition to the default names. A name 
is matched by equality, so it covers the
+         * class named and its arrays, but not its subclasses or its package. 
Adding the same name more than once has
+         * no additional effect.
+         * <p/>
+         * Has no effect on GraphSON 2.0 or 3.0, which resolve types through a 
fixed registry rather than by name.
+         */
+        public Builder addAllowedTypeIdName(final String... names) {
+            this.allowedTypeIdNames.addAll(Arrays.asList(names));
+            return this;
+        }
+
         public Builder maxNumberLength(final int maxNumLength) {
             this.streamReadConstraintsBuilder.maxNumberLength(maxNumLength);
             return this;
diff --git 
a/gremlin-core/src/test/java/com/example/gadget/GraphSONTestGadgets.java 
b/gremlin-core/src/test/java/com/example/gadget/GraphSONTestGadgets.java
new file mode 100644
index 0000000000..3c112f436d
--- /dev/null
+++ b/gremlin-core/src/test/java/com/example/gadget/GraphSONTestGadgets.java
@@ -0,0 +1,127 @@
+/*
+ * 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 com.example.gadget;
+
+import java.util.Objects;
+
+/**
+ * Test-only types whose names are not among the GraphSON 1.0 allowed type 
names, used to exercise the GraphSON 1.0
+ * embedded-type rules and the {@code addAllowedTypeIdName(...)} opt-out.
+ */
+public final class GraphSONTestGadgets {
+
+    private GraphSONTestGadgets() {
+    }
+
+    /**
+     * Records that its static initializer ran through a system property, so a 
test can observe initialization
+     * without referencing the class, which would itself trigger it.
+     */
+    public static class StaticInitCanary {
+        public static final String FIRED_PROPERTY = 
"tinkerpop.test.graphson.staticInitCanary";
+        static {
+            System.setProperty(FIRED_PROPERTY, "fired");
+        }
+        public int x;
+    }
+
+    /**
+     * Counterpart of {@link StaticInitCanary} for a class named by a 
collection element.
+     * Each scenario needs a distinct canary because a class's static 
initializer runs only once per JVM.
+     */
+    public static class StaticInitCanaryElement {
+        public static final String FIRED_PROPERTY = 
"tinkerpop.test.graphson.staticInitCanaryElement";
+        static {
+            System.setProperty(FIRED_PROPERTY, "fired");
+        }
+        public int x;
+    }
+
+    /**
+     * A plain bean with no static-initializer side effect, used to check the 
opt-out.
+     */
+    public static class SamplePojo {
+        public int x;
+
+        public SamplePojo() {
+        }
+
+        public SamplePojo(final int x) {
+            this.x = x;
+        }
+
+        @Override
+        public boolean equals(final Object o) {
+            return o instanceof SamplePojo && ((SamplePojo) o).x == this.x;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(x);
+        }
+    }
+
+    /**
+     * A subclass of {@link SamplePojo}, used to verify that configuring the 
base class name does not permit its
+     * subclasses.
+     */
+    public static class SamplePojoSubclass extends SamplePojo {
+        public SamplePojoSubclass() {
+        }
+
+        public SamplePojoSubclass(final int x) {
+            super(x);
+        }
+    }
+
+    /**
+     * Counterpart of {@link StaticInitCanary} for an enum named as a generic 
type argument, which Jackson does not
+     * otherwise validate.
+     */
+    public enum StaticInitCanaryEnum {
+        A, B;
+        public static final String FIRED_PROPERTY = 
"tinkerpop.test.graphson.staticInitCanaryEnum";
+        static {
+            System.setProperty(FIRED_PROPERTY, "fired");
+        }
+    }
+
+    /**
+     * Counterpart of {@link StaticInitCanary} for a class named as a generic 
type argument.
+     * Each scenario needs a distinct canary because a class's static 
initializer runs only once per JVM.
+     */
+    public static class StaticInitCanaryArg {
+        public static final String FIRED_PROPERTY = 
"tinkerpop.test.graphson.staticInitCanaryArg";
+        static {
+            System.setProperty(FIRED_PROPERTY, "fired");
+        }
+        public int x;
+    }
+
+    /**
+     * Counterpart of {@link StaticInitCanary} for the class a {@code 
java.lang.Class} value names.
+     */
+    public static class StaticInitCanaryValue {
+        public static final String FIRED_PROPERTY = 
"tinkerpop.test.graphson.staticInitCanaryValue";
+        static {
+            System.setProperty(FIRED_PROPERTY, "fired");
+        }
+        public int x;
+    }
+}
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperBuilderTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperBuilderTest.java
new file mode 100644
index 0000000000..c978bd53bd
--- /dev/null
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperBuilderTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.junit.Test;
+
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+
+public class GraphSONMapperBuilderTest {
+
+    @Test
+    public void shouldPreserveAllowedTypeIdNamesWhenRebuilding() {
+        final GraphSONMapper original = GraphSONMapper.build()
+                .addAllowedTypeIdName("java.lang.Class")
+                .create();
+        final GraphSONMapper rebuilt = GraphSONMapper.build(original).create();
+
+        assertEquals(Collections.singletonList("java.lang.Class"), 
rebuilt.allowedTypeIdNames);
+        assertNotSame(original.allowedTypeIdNames, rebuilt.allowedTypeIdNames);
+    }
+}
diff --git 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperPartialEmbeddedTypeTest.java
 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperPartialEmbeddedTypeTest.java
index 523cdc95c8..34948e6f3d 100644
--- 
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperPartialEmbeddedTypeTest.java
+++ 
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperPartialEmbeddedTypeTest.java
@@ -23,8 +23,19 @@ import 
org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
 import org.apache.tinkerpop.gremlin.process.traversal.P;
 import org.apache.tinkerpop.gremlin.process.traversal.TextP;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.util.star.StarGraph;
 import org.apache.tinkerpop.shaded.jackson.databind.JsonMappingException;
+import com.example.gadget.GraphSONTestGadgets.SamplePojo;
+import com.example.gadget.GraphSONTestGadgets.SamplePojoSubclass;
+import com.example.gadget.GraphSONTestGadgets.StaticInitCanary;
+import com.example.gadget.GraphSONTestGadgets.StaticInitCanaryElement;
+import com.example.gadget.GraphSONTestGadgets.StaticInitCanaryEnum;
+import com.example.gadget.GraphSONTestGadgets.StaticInitCanaryValue;
 import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper;
+import org.apache.tinkerpop.shaded.jackson.databind.exc.InvalidFormatException;
+import org.apache.tinkerpop.shaded.jackson.databind.exc.InvalidTypeIdException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -33,21 +44,36 @@ import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.math.BigDecimal;
+import java.nio.ByteBuffer;
+import java.time.DayOfWeek;
 import java.time.Instant;
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.Currency;
+import java.util.EnumMap;
 import java.util.HashMap;
 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.Set;
+import java.util.TreeSet;
 import java.util.UUID;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
 import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.fail;
 
@@ -60,6 +86,11 @@ import static org.junit.Assert.fail;
 @RunWith(Parameterized.class)
 public class GraphSONMapperPartialEmbeddedTypeTest extends 
AbstractGraphSONTest {
 
+    /**
+     * The key the value under test is held under in the GraphSON 1.0 cases 
that nest it in a {@code Map}.
+     */
+    private static final String MAP_VALUE_KEY = "v";
+
     @Parameterized.Parameters(name = "{0}")
     public static Iterable<Object[]> data() {
         return Arrays.asList(new Object[][]{
@@ -248,6 +279,567 @@ public class GraphSONMapperPartialEmbeddedTypeTest 
extends AbstractGraphSONTest
         assertEquals(100L, read.get("test"));
     }
 
+    @Test
+    public void shouldRejectNetworkPackageTypeWithEmbedTypeSettingV1() {
+        // a name resolves only by being listed, so java.net.URL does not, 
even though the sibling
+        // java.net.InetAddress that GraphSON 2.0/3.0 register does
+        assertDeniedByTypeValidator(v1Typed(),
+                
"{\"@class\":\"java.util.HashMap\",\"v\":{\"@class\":\"java.net.URL\",\"u\":\"http://example.com\"}}";);
+    }
+
+    @Test
+    public void shouldRoundTripArraysWithEmbedTypeSettingV1() throws Exception 
{
+        // an array type id is decided by its component name, and a nested 
array carries one per level. A primitive
+        // component ("[B") names no class, while an object component 
("[Ljava.lang.String;") names one.
+        final ObjectMapper mapper = v1Typed();
+        assertArrayEquals(new byte[]{1, 2, 3}, (byte[]) roundTripInMap(mapper, 
new byte[]{1, 2, 3}));
+        assertArrayEquals(new Boolean[]{true, false},
+                (Boolean[]) roundTripInMap(mapper, new Boolean[]{true, 
false}));
+        assertArrayEquals(new String[]{"a", "b"}, (String[]) 
roundTripInMap(mapper, new String[]{"a", "b"}));
+        assertThat(Arrays.deepEquals(new String[][]{{"a"}, {"b"}},
+                (String[][]) roundTripInMap(mapper, new String[][]{{"a"}, 
{"b"}})), is(true));
+    }
+
+    @Test
+    public void shouldRoundTripSqlAndUtilValueTypesWithEmbedTypeSettingV1() 
throws Exception {
+        // java.sql.Time is written as its toString, which formats and parses 
back in the default time zone, so
+        // valueOf of a fixed literal round-trips anywhere
+        final ObjectMapper mapper = v1Typed();
+        assertRoundTripsInMap(mapper, Arrays.asList(
+                new java.sql.Timestamp(0L),
+                java.sql.Time.valueOf("12:34:56"),
+                Locale.US,
+                Currency.getInstance("USD")));
+
+        // java.util.ArrayDeque does not define value equality, so it is 
compared element-wise
+        final Object read = roundTripInMap(mapper, new 
ArrayDeque<>(Arrays.asList("a", "b")));
+        assertThat(read, instanceOf(ArrayDeque.class));
+        assertEquals(Arrays.asList("a", "b"), new ArrayList<>((ArrayDeque<?>) 
read));
+    }
+
+    @Test
+    public void shouldRejectArrayOfDisallowedComponentWithEmbedTypeSettingV1() 
{
+        // an array type id is decided by its component name, so an unlisted 
component does not resolve
+        assertDeniedByTypeValidator(v1Typed(),
+                
"{\"@class\":\"java.util.HashMap\",\"v\":[\"[Ljava.io.File;\",[\"/tmp/x\"]]}");
+    }
+
+    @Test
+    public void shouldAllowConfiguredTypeIdNameWithEmbedTypeSettingV1() throws 
Exception {
+        final String json = 
"{\"@class\":\"java.util.HashMap\",\"p\":{\"@class\":\"com.example.gadget.GraphSONTestGadgets$SamplePojo\",\"x\":42}}";
+
+        // not among the allowed names by default
+        assertDeniedByTypeValidator(v1Typed(), json);
+
+        final ObjectMapper mapper = 
GraphSONMapper.build().version(GraphSONVersion.V1_0).typeInfo(TypeInfo.PARTIAL_TYPES)
+                
.addAllowedTypeIdName("com.example.gadget.GraphSONTestGadgets$SamplePojo").create().createMapper();
+        final Map read = mapper.readValue(json, HashMap.class);
+        assertEquals(new SamplePojo(42), read.get("p"));
+    }
+
+    @Test
+    public void 
shouldAllowConfiguredTypeIdNameInsideCollectionWithEmbedTypeSettingV1() throws 
Exception {
+        final ObjectMapper mapper = 
GraphSONMapper.build().version(GraphSONVersion.V1_0)
+                .typeInfo(TypeInfo.PARTIAL_TYPES)
+                .addAllowedTypeIdName(SamplePojo.class.getName())
+                .create().createMapper();
+
+        final Object read = roundTripInMap(mapper, 
Collections.singletonList(new SamplePojo(42)));
+        assertEquals(Collections.singletonList(new SamplePojo(42)), read);
+        assertEquals(SamplePojo.class, ((List<?>) read).get(0).getClass());
+    }
+
+    @Test
+    public void 
shouldAllowArrayOfConfiguredTypeIdNameButRejectSubclassWithEmbedTypeSettingV1() 
throws Exception {
+        final ObjectMapper mapper = 
GraphSONMapper.build().version(GraphSONVersion.V1_0)
+                .typeInfo(TypeInfo.PARTIAL_TYPES)
+                .addAllowedTypeIdName(SamplePojo.class.getName())
+                .create().createMapper();
+
+        assertArrayEquals(new SamplePojo[]{new SamplePojo(42)},
+                (SamplePojo[]) roundTripInMap(mapper, new SamplePojo[]{new 
SamplePojo(42)}));
+
+        final String subclassTypeId = SamplePojoSubclass.class.getName();
+        assertTypeIdDeniedByTypeValidator(mapper,
+                "{\"@class\":\"java.util.HashMap\",\"p\":{\"@class\":\"" + 
subclassTypeId + "\",\"x\":42}}",
+                subclassTypeId);
+    }
+
+    @Test
+    public void 
shouldRejectDisallowedCollectionElementWithoutInitializingClassV1() {
+        final String typeId = StaticInitCanaryElement.class.getName();
+        final String json = 
"{\"@class\":\"java.util.HashMap\",\"v\":[\"java.util.ArrayList\",["
+                + "{\"@class\":\"" + typeId + "\",\"x\":1}]]}";
+
+        System.clearProperty(StaticInitCanaryElement.FIRED_PROPERTY);
+        assertTypeIdDeniedByTypeValidator(v1Typed(), json, typeId);
+        assertNull("type id resolution must not initialize the class named by 
a refused collection element",
+                System.getProperty(StaticInitCanaryElement.FIRED_PROPERTY));
+    }
+
+    @Test
+    public void shouldRejectEnumTypeParameterAndNotLoadItV1() {
+        System.clearProperty(StaticInitCanaryEnum.FIRED_PROPERTY);
+        assertDeniedByTypeValidator(v1Typed(),
+                
"{\"@class\":\"java.util.HashMap<com.example.gadget.GraphSONTestGadgets$StaticInitCanaryEnum,java.lang.String>\",\"A\":\"v\"}");
+        assertNull("type id resolution must not load the enum named as a type 
argument",
+                System.getProperty(StaticInitCanaryEnum.FIRED_PROPERTY));
+    }
+
+    @Test
+    public void shouldRejectClassValueAndNotLoadItV1() {
+        // java.lang.Class is held out of the derived names by 
graphSON1dDerivedTypeNames()
+        System.clearProperty(StaticInitCanaryValue.FIRED_PROPERTY);
+        assertDeniedByTypeValidator(v1Typed(),
+                
"{\"@class\":\"java.util.HashMap\",\"c\":[\"java.lang.Class\",\"com.example.gadget.GraphSONTestGadgets$StaticInitCanaryValue\"]}");
+        assertNull("type id resolution must not load the class named by a 
java.lang.Class value",
+                System.getProperty(StaticInitCanaryValue.FIRED_PROPERTY));
+    }
+
+    @Test
+    public void shouldNotLoadDisallowedClassWhenRefusingV1() {
+        // a @class outside the allowed names is decided from the name alone
+        System.clearProperty(StaticInitCanary.FIRED_PROPERTY);
+        assertDeniedByTypeValidator(v1Typed(),
+                
"{\"@class\":\"java.util.HashMap\",\"g\":{\"@class\":\"com.example.gadget.GraphSONTestGadgets$StaticInitCanary\",\"x\":1}}");
+        assertNull("type id resolution must not load the class named by a 
refused @class",
+                System.getProperty(StaticInitCanary.FIRED_PROPERTY));
+    }
+
+    @Test
+    public void shouldRoundTripInetAddressWithEmbedTypeSettingV1() throws 
Exception {
+        // java.net.InetAddress is a derived name, since GraphSON 2.0/3.0 
register it
+        final ObjectMapper mapper = v1Typed();
+        final Map<String, Object> m = new HashMap<>();
+        m.put("a", java.net.InetAddress.getByAddress(new byte[]{127, 0, 0, 
1}));
+
+        final Map read = mapper.readValue(mapper.writeValueAsString(m), 
HashMap.class);
+        assertEquals(java.net.InetAddress.getByAddress(new byte[]{127, 0, 0, 
1}), read.get("a"));
+    }
+
+    @Test
+    public void shouldRoundTripUriWithEmbedTypeSettingV1() throws Exception {
+        // java.net.URI is listed rather than derived, as GraphSON 2.0/3.0 do 
not register it
+        final ObjectMapper mapper = v1Typed();
+        final Map<String, Object> m = new HashMap<>();
+        m.put("u", new java.net.URI("http://example.com/x";));
+
+        final Map read = mapper.readValue(mapper.writeValueAsString(m), 
HashMap.class);
+        assertEquals(new java.net.URI("http://example.com/x";), read.get("u"));
+    }
+
+    @Test
+    public void shouldRoundTripBoxedPrimitivesWithEmbedTypeSettingV1() throws 
Exception {
+        // String, Integer, Double and Boolean are written bare, so only the 
boxed types JSON cannot represent
+        // natively carry a type id
+        assertRoundTripsInMap(v1Typed(), Arrays.asList(
+                Character.valueOf('c'),
+                BigDecimal.ONE));
+    }
+
+    @Test
+    public void shouldRoundTripCollectionTypesWithEmbedTypeSettingV1() throws 
Exception {
+        // the concrete collection class names GraphSON 1.0 writes for a 
Map-nested value. Each case checks the type
+        // id in the text written as well as the class read back, since 
AbstractMap.equals and AbstractList.equals
+        // are structural and would keep passing with no type id written at 
all.
+        final ObjectMapper mapper = v1Typed();
+        final Map<String, Object> entry = Collections.singletonMap("a", "b");
+        final List<String> one = Collections.singletonList("a");
+
+        assertTypedRoundTripInMap(mapper, new LinkedHashMap<>(entry));
+        assertTypedRoundTripInMap(mapper, new LinkedHashSet<>(one));
+
+        // Jackson cannot rebuild either class named, so it reads the type id 
back through a stand-in it can
+        // construct: Arrays.asList comes back as a plain ArrayList, and 
either unmodifiable list name comes back as a
+        // wrapper around an ArrayList. unmodifiableList writes 
Collections$UnmodifiableList over a LinkedList but
+        // Collections$UnmodifiableRandomAccessList over an ArrayList, so the 
allowed names carry both.
+        assertTypedRoundTripInMap(mapper, Arrays.asList("a", "b"), 
ArrayList.class);
+        assertTypedRoundTripInMap(mapper, Collections.unmodifiableList(new 
LinkedList<>(one)),
+                Collections.unmodifiableList(new ArrayList<>(one)).getClass());
+
+        // a Map in a List in a Map, so a type id is resolved at every depth
+        final Map<String, Object> nested = new HashMap<>();
+        nested.put("inner", new ArrayList<>(Collections.singletonList(new 
HashMap<String, Object>(entry))));
+        final String nestedJson = writeInMap(mapper, nested);
+        assertThat(nestedJson, 
containsString("\"inner\":[\"java.util.ArrayList\",["));
+
+        final Object readNested = readMapValue(mapper, nestedJson);
+        assertEquals(nested, readNested);
+        assertEquals(HashMap.class, readNested.getClass());
+        final Object readInner = ((Map<?, ?>) readNested).get("inner");
+        assertEquals(ArrayList.class, readInner.getClass());
+        assertEquals(HashMap.class, ((List<?>) readInner).get(0).getClass());
+    }
+
+    @Test
+    public void shouldRoundTripEnumTypesWithEmbedTypeSettingV1() throws 
Exception {
+        // T uses per-constant subclasses, whose type id is still the 
declaring enum. DayOfWeek is listed in
+        // GRAPHSON_1_0_ALLOWED_EXTRA_TYPE_NAMES rather than derived from the 
GraphSON 2.0/3.0 registry.
+        assertRoundTripsInMap(v1Typed(), Arrays.asList(
+                Direction.OUT,
+                T.id,
+                DayOfWeek.MONDAY));
+    }
+
+    @Test
+    public void shouldReadAllowedBaseTypeIdsWithEmbedTypeSettingV1() throws 
Exception {
+        // allowed names GraphSON 1.0 does not write itself, since it writes 
the concrete runtime class instead. A
+        // document may still name them, and what comes back is the concrete 
type Jackson picks for the base type.
+        final ObjectMapper mapper = v1Typed();
+
+        assertEquals(Collections.singletonMap("a", "b"),
+                readMapValue(mapper, 
"{\"@class\":\"java.util.HashMap\",\"v\":{\"@class\":\"java.util.Map\",\"a\":\"b\"}}"));
+        assertEquals(ByteBuffer.wrap(new byte[]{1, 2}),
+                readMapValue(mapper, 
"{\"@class\":\"java.util.HashMap\",\"v\":[\"java.nio.ByteBuffer\",\"AQI=\"]}"));
+    }
+
+    @Test
+    public void shouldRefuseStarGraphBecauseV1WritesItsStarVertexAsAMapV1() 
throws Exception {
+        // GraphSON 1.0 writes a StarGraph as a bean whose "starVertex" 
property carries the type id
+        // java.util.HashMap rather than a StarVertex, so Jackson cannot 
rebuild a StarGraph from what GraphSON 1.0
+        // writes and the name is not among the allowed names.
+        final ObjectMapper mapper = v1Typed();
+        final String json;
+        try (final StarGraph starGraph = StarGraph.open()) {
+            starGraph.addVertex("label", "person");
+            json = writeInMap(mapper, starGraph);
+        }
+
+        assertThat(json, containsString("\"" + GraphSONTokens.CLASS + "\":\"" 
+ StarGraph.class.getName() + "\""));
+        assertThat(json, containsString("\"starVertex\":{\"" + 
GraphSONTokens.CLASS + "\":\"java.util.HashMap\""));
+        assertTypeIdDeniedByTypeValidator(mapper, json, 
StarGraph.class.getName());
+    }
+
+    @Test
+    public void shouldRefuseByteBufferBecauseV1WritesItsConcreteHeapClassV1() 
throws Exception {
+        // java.nio.ByteBuffer is a derived name and does resolve (see
+        // shouldReadAllowedBaseTypeIdsWithEmbedTypeSettingV1), but what 
GraphSON 1.0 writes for wrap() or allocate()
+        // is the concrete java.nio.HeapByteBuffer, which the allowed names do 
not carry, so V1 cannot read what it
+        // writes.
+        final ObjectMapper mapper = v1Typed();
+
+        final String json = writeInMap(mapper, ByteBuffer.wrap(new byte[]{1, 
2}));
+        assertThat(json, containsString("[\"java.nio.HeapByteBuffer\","));
+        assertTypeIdDeniedByTypeValidator(mapper, json, 
"java.nio.HeapByteBuffer");
+    }
+
+    @Test
+    public void shouldRefuseEnumMapBecauseV1WritesAParameterizedTypeIdV1() 
throws Exception {
+        // GraphSON 1.0 writes an EnumMap as the parameterized type id 
"java.util.EnumMap<...,...>", which
+        // GraphSON1dScreeningIdResolver refuses, so an EnumMap no longer 
reads back. That is a round-trip regression
+        // introduced by this change. An EnumSet is written as a parameterized 
type id too, but it did not read back
+        // beforehand either, as jackson-databind#4849 leaves the type id 
EnumSet writes unresolvable.
+        final ObjectMapper mapper = v1Typed();
+        final String typeId = "java.util.EnumMap<" + Direction.class.getName() 
+ ",java.lang.Object>";
+
+        final EnumMap<Direction, String> value = new 
EnumMap<>(Direction.class);
+        value.put(Direction.OUT, "x");
+
+        final String json = writeInMap(mapper, value);
+        assertThat(json, containsString("\"" + GraphSONTokens.CLASS + "\":\"" 
+ typeId + "\""));
+        assertParameterizedTypeIdRefused(mapper, json, typeId);
+    }
+
+    @Test
+    public void 
shouldRejectUnresolvableHostnameWithoutLookupWithEmbedTypeSettingV1() {
+        // java.net.InetAddress is among the allowed names, so a document may 
name it, but a value that is not an IP
+        // address literal is refused on its syntax rather than looked up. The 
message is the evidence of that, as a
+        // lookup would report an UnknownHostException instead. It is 
Jackson's own wording, so an upgrade can move it.
+        final String json = "{\"@class\":\"java.util.HashMap\",\"v\":" +
+                
"[\"java.net.InetAddress\",\"this-name-should-not-resolve.invalid\"]}";
+        try {
+            v1Typed().readValue(json, HashMap.class);
+            fail("an InetAddress value that is not an IP address literal must 
be refused");
+        } catch (Exception e) {
+            assertThat(e, instanceOf(InvalidFormatException.class));
+            assertThat(e.getMessage(), containsString("Not a valid IP address 
string literal"));
+        }
+    }
+
+    @Test
+    public void shouldAllowConfiguredClassValueWithEmbedTypeSettingV1() throws 
Exception {
+        // java.lang.Class is held out of the derived names, and no separate 
list holds it out permanently, so a
+        // caller that wants GraphSON 1.0 to read one names it like any other 
name
+        final String json = 
"{\"@class\":\"java.util.HashMap\",\"c\":[\"java.lang.Class\",\"java.lang.String\"]}";
+        assertDeniedByTypeValidator(v1Typed(), json);
+
+        final ObjectMapper mapper = 
GraphSONMapper.build().version(GraphSONVersion.V1_0)
+                
.typeInfo(TypeInfo.PARTIAL_TYPES).addAllowedTypeIdName("java.lang.Class").create().createMapper();
+        assertEquals(String.class, mapper.readValue(json, 
HashMap.class).get("c"));
+    }
+
+    @Test
+    public void 
shouldAllowSeveralConfiguredTypeIdNamesWithEmbedTypeSettingV1() throws 
Exception {
+        // addAllowedTypeIdName takes several names at once, and successive 
calls add to earlier ones rather than
+        // replacing them
+        final String json = "{\"@class\":\"java.util.HashMap\","
+                + 
"\"p\":{\"@class\":\"com.example.gadget.GraphSONTestGadgets$SamplePojo\",\"x\":42},"
+                + "\"u\":[\"java.net.URL\",\"http://example.com/x\"]}";;
+
+        assertDeniedByTypeValidator(v1Typed(), json);
+
+        
assertConfiguredNamesResolve(GraphSONMapper.build().version(GraphSONVersion.V1_0)
+                .typeInfo(TypeInfo.PARTIAL_TYPES)
+                .addAllowedTypeIdName(SamplePojo.class.getName(), 
"java.net.URL")
+                .create().createMapper(), json);
+
+        
assertConfiguredNamesResolve(GraphSONMapper.build().version(GraphSONVersion.V1_0)
+                .typeInfo(TypeInfo.PARTIAL_TYPES)
+                .addAllowedTypeIdName(SamplePojo.class.getName())
+                .addAllowedTypeIdName("java.net.URL")
+                .create().createMapper(), json);
+    }
+
+    @Test
+    public void 
shouldIgnoreDuplicateConfiguredTypeIdNamesWithEmbedTypeSettingV1() throws 
Exception {
+        final String json = "{\"@class\":\"java.util.HashMap\","
+                + 
"\"p\":{\"@class\":\"com.example.gadget.GraphSONTestGadgets$SamplePojo\",\"x\":42}}";
+
+        final ObjectMapper mapper = 
GraphSONMapper.build().version(GraphSONVersion.V1_0)
+                .typeInfo(TypeInfo.PARTIAL_TYPES)
+                .addAllowedTypeIdName(SamplePojo.class.getName())
+                .addAllowedTypeIdName(SamplePojo.class.getName())
+                .create().createMapper();
+
+        assertEquals(new SamplePojo(42), mapper.readValue(json, 
HashMap.class).get("p"));
+    }
+
+    @Test
+    public void shouldDeriveTypeIdNamesFromTheRegisteredGraphSON2And3TypesV1() 
{
+        // a new put(...) in GraphSONModuleV2, GraphSONModuleV3, 
GraphSONXModuleV2 or GraphSONXModuleV3 widens what
+        // GraphSON 1.0 reads as a side effect, which pinning the derived 
names makes visible.
+        //
+        // The set is classpath dependent: 
GraphSONModule.tryLoadSparqlStrategy() contributes SparqlStrategy when
+        // sparql-gremlin is present, which it is not on the gremlin-core test 
classpath. A new name that is genuinely
+        // wanted belongs in the expected set below.
+        final Set<String> expected = new TreeSet<>(Arrays.asList(
+                "java.lang.Byte",
+                "java.lang.Character",
+                "java.lang.Double",
+                "java.lang.Float",
+                "java.lang.Integer",
+                "java.lang.Long",
+                "java.lang.Short",
+                "java.math.BigDecimal",
+                "java.math.BigInteger",
+                "java.net.InetAddress",
+                "java.nio.ByteBuffer",
+                "java.sql.Timestamp",
+                "java.time.Duration",
+                "java.time.Instant",
+                "java.time.LocalDate",
+                "java.time.LocalDateTime",
+                "java.time.LocalTime",
+                "java.time.MonthDay",
+                "java.time.OffsetDateTime",
+                "java.time.OffsetTime",
+                "java.time.Period",
+                "java.time.Year",
+                "java.time.YearMonth",
+                "java.time.ZoneOffset",
+                "java.time.ZonedDateTime",
+                "java.util.Calendar",
+                "java.util.Date",
+                "java.util.List",
+                "java.util.Map",
+                "java.util.Set",
+                "java.util.TimeZone",
+                "java.util.UUID",
+                
"org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy",
+                
"org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.optimization.GraphFilterStrategy",
+                "org.apache.tinkerpop.gremlin.process.traversal.Bytecode",
+                
"org.apache.tinkerpop.gremlin.process.traversal.Bytecode$Binding",
+                "org.apache.tinkerpop.gremlin.process.traversal.DT",
+                "org.apache.tinkerpop.gremlin.process.traversal.Merge",
+                "org.apache.tinkerpop.gremlin.process.traversal.Operator",
+                "org.apache.tinkerpop.gremlin.process.traversal.Order",
+                "org.apache.tinkerpop.gremlin.process.traversal.P",
+                "org.apache.tinkerpop.gremlin.process.traversal.Path",
+                "org.apache.tinkerpop.gremlin.process.traversal.Pick",
+                "org.apache.tinkerpop.gremlin.process.traversal.Pop",
+                
"org.apache.tinkerpop.gremlin.process.traversal.SackFunctions$Barrier",
+                "org.apache.tinkerpop.gremlin.process.traversal.Scope",
+                "org.apache.tinkerpop.gremlin.process.traversal.TextP",
+                "org.apache.tinkerpop.gremlin.process.traversal.Traverser",
+                
"org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet",
+                
"org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ConnectiveStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ElementIdStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.HaltedTraverserStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.OptionsStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.MatchAlgorithmStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.AdjacentToIncidentStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ByModulatorOptimizationStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.EarlyLimitStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.FilterRankingStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.IdentityRemovalStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.IncidentToAdjacentStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.InlineFilterStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.LazyBarrierStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.MatchPredicateStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.OrderLimitStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathProcessorStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathRetractionStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ComputerVerificationStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.LambdaRestrictionStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReservedKeysVerificationStrategy",
+                
"org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.StandardVerificationStrategy",
+                "org.apache.tinkerpop.gremlin.process.traversal.util.AndP",
+                "org.apache.tinkerpop.gremlin.process.traversal.util.Metrics",
+                "org.apache.tinkerpop.gremlin.process.traversal.util.OrP",
+                
"org.apache.tinkerpop.gremlin.process.traversal.util.TraversalExplanation",
+                
"org.apache.tinkerpop.gremlin.process.traversal.util.TraversalMetrics",
+                "org.apache.tinkerpop.gremlin.structure.Column",
+                "org.apache.tinkerpop.gremlin.structure.Direction",
+                "org.apache.tinkerpop.gremlin.structure.Edge",
+                "org.apache.tinkerpop.gremlin.structure.Property",
+                "org.apache.tinkerpop.gremlin.structure.T",
+                "org.apache.tinkerpop.gremlin.structure.Vertex",
+                "org.apache.tinkerpop.gremlin.structure.VertexProperty",
+                
"org.apache.tinkerpop.gremlin.structure.VertexProperty$Cardinality",
+                "org.apache.tinkerpop.gremlin.util.function.Lambda"));
+
+        final Set<String> derived = new 
TreeSet<>(GraphSONMapper.graphSON1dDerivedTypeNames());
+        final Set<String> added = new TreeSet<>(derived);
+        added.removeAll(expected);
+        final Set<String> dropped = new TreeSet<>(expected);
+        dropped.removeAll(derived);
+        assertEquals("derived but not expected: " + added + "; expected but 
not derived: " + dropped,
+                expected, derived);
+    }
+
+    private static ObjectMapper v1Typed() {
+        return 
GraphSONMapper.build().version(GraphSONVersion.V1_0).typeInfo(TypeInfo.PARTIAL_TYPES).create().createMapper();
+    }
+
+    /**
+     * Writes a value as a {@code Map} value and reads it back. That is the 
shape real payloads use, and it exercises
+     * the untyped {@code Object} value path rather than a declared concrete 
class.
+     */
+    private static Object roundTripInMap(final ObjectMapper mapper, final 
Object value) throws Exception {
+        return readMapValue(mapper, writeInMap(mapper, value));
+    }
+
+    /**
+     * Writes a value as a {@code Map} value and returns the text, so the type 
id GraphSON 1.0 emitted for it can be
+     * asserted on directly.
+     */
+    private static String writeInMap(final ObjectMapper mapper, final Object 
value) throws Exception {
+        final Map<String, Object> m = new HashMap<>();
+        m.put(MAP_VALUE_KEY, value);
+        return mapper.writeValueAsString(m);
+    }
+
+    private static void assertRoundTripsInMap(final ObjectMapper mapper, final 
List<Object> values) throws Exception {
+        for (final Object value : values) {
+            assertEquals(value.getClass().getName(), value, 
roundTripInMap(mapper, value));
+        }
+    }
+
+    private static void assertTypedRoundTripInMap(final ObjectMapper mapper, 
final Object value) throws Exception {
+        assertTypedRoundTripInMap(mapper, value, value.getClass());
+    }
+
+    /**
+     * Asserts that a {@code Map} or {@code Collection} value reads back equal 
and as {@code expectedClass}, and that
+     * the type id GraphSON 1.0 wrote for it is in the text written. Equality 
alone is no evidence of typing, as an
+     * equal value of any other concrete {@code Map} or {@code List} class 
satisfies it. {@code expectedClass} is what
+     * Jackson rebuilds for the type id written, which is not always the class 
that was written.
+     */
+    private static void assertTypedRoundTripInMap(final ObjectMapper mapper, 
final Object value,
+                                                  final Class<?> 
expectedClass) throws Exception {
+        final String name = value.getClass().getName();
+        final String json = writeInMap(mapper, value);
+        assertThat("no type id written for " + name + " in " + json, json, 
containsString(typeIdInMapOf(value)));
+
+        final Object read = readMapValue(mapper, json);
+        assertEquals(name, value, read);
+        assertEquals("concrete class read back for " + name, expectedClass, 
read.getClass());
+    }
+
+    /**
+     * The text GraphSON 1.0 writes for the type id of a {@code Map} or {@code 
Collection} held as a {@code Map}
+     * value: a {@code Map} carries it as an {@code "@class"} field of its own 
object, a {@code Collection} as the
+     * first element of a wrapper array. Both are anchored to the key the 
value sits under, so neither can be
+     * satisfied by the type id of the enclosing {@code Map}.
+     */
+    private static String typeIdInMapOf(final Object value) {
+        final String prefix = "\"" + MAP_VALUE_KEY + "\":";
+        final String name = value.getClass().getName();
+        return value instanceof Map
+                ? prefix + "{\"" + GraphSONTokens.CLASS + "\":\"" + name + "\""
+                : prefix + "[\"" + name + "\",";
+    }
+
+    private static Object readMapValue(final ObjectMapper mapper, final String 
json) throws Exception {
+        return mapper.readValue(json, HashMap.class).get(MAP_VALUE_KEY);
+    }
+
+    /**
+     * Asserts that both configured names resolved. A {@code java.net.URL} is 
compared as text, since
+     * {@code URL.equals} can consult the network.
+     */
+    private static void assertConfiguredNamesResolve(final ObjectMapper 
mapper, final String json) throws Exception {
+        final Map read = mapper.readValue(json, HashMap.class);
+        assertEquals(new SamplePojo(42), read.get("p"));
+        assertEquals("http://example.com/x";, read.get("u").toString());
+    }
+
+    private static void assertDeniedByTypeValidator(final ObjectMapper mapper, 
final String json) {
+        try {
+            mapper.readValue(json, HashMap.class);
+            fail("a @class outside the allowed names must not resolve");
+        } catch (InvalidTypeIdException expected) {
+        } catch (Exception other) {
+            throw new AssertionError("expected InvalidTypeIdException, got " + 
other, other);
+        }
+    }
+
+    /**
+     * Asserts that {@code typeId} in particular is what the read was refused 
on, and that the allowed names are what
+     * refused it. A listed type id that names no loadable class is also an 
{@code InvalidTypeIdException}, reported as
+     * "no such class found" rather than as a denial. Both strings matched are 
Jackson's own wording, so a Jackson
+     * upgrade can move them.
+     */
+    private static void assertTypeIdDeniedByTypeValidator(final ObjectMapper 
mapper, final String json,
+                                                          final String typeId) 
{
+        try {
+            mapper.readValue(json, HashMap.class);
+            fail("resolution of the type id " + typeId + " must be refused");
+        } catch (InvalidTypeIdException expected) {
+            assertThat(expected.getMessage(), containsString("Could not 
resolve type id '" + typeId + "'"));
+            assertThat(expected.getMessage(), containsString("denied 
resolution"));
+        } catch (Exception other) {
+            throw new AssertionError("expected InvalidTypeIdException, got " + 
other, other);
+        }
+    }
+
+    /**
+     * Asserts that {@code typeId} was refused for being parameterized, which 
{@code GraphSON1dScreeningIdResolver}
+     * does before the allowed names are consulted at all.
+     */
+    private static void assertParameterizedTypeIdRefused(final ObjectMapper 
mapper, final String json,
+                                                         final String typeId) {
+        try {
+            mapper.readValue(json, HashMap.class);
+            fail("a parameterized type id must be refused: " + typeId);
+        } catch (InvalidTypeIdException expected) {
+            assertThat(expected.getMessage(), containsString("Could not 
resolve type id '" + typeId + "'"));
+            assertThat(expected.getMessage(), containsString("GraphSON 1.0 
does not permit a parameterized type id"));
+        } catch (Exception other) {
+            throw new AssertionError("expected InvalidTypeIdException, got " + 
other, other);
+        }
+    }
+
     @Test
     public void shouldNotHandleMapWithTypesUsingEmbedTypeSettingV1() throws 
Exception {
         final ObjectMapper mapper = GraphSONMapper.build()
diff --git 
a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ClusterConfigTest.java
 
b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ClusterConfigTest.java
index f6a5dbfd00..418f883e1e 100644
--- 
a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ClusterConfigTest.java
+++ 
b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ClusterConfigTest.java
@@ -20,17 +20,63 @@ package org.apache.tinkerpop.gremlin.driver;
 
 import org.apache.commons.configuration2.BaseConfiguration;
 import org.apache.commons.configuration2.Configuration;
+import org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV1;
 import org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV2;
 import org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3;
 import org.apache.tinkerpop.shaded.jackson.core.StreamReadConstraints;
+import org.apache.tinkerpop.shaded.jackson.databind.exc.InvalidTypeIdException;
 import org.junit.Test;
 
+import java.awt.Point;
 import java.util.Arrays;
+import java.util.Map;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 public class ClusterConfigTest {
+    private static final String POINT_JSON =
+            
"{\"@class\":\"java.util.HashMap\",\"point\":{\"@class\":\"java.awt.Point\",\"x\":1.0,\"y\":2.0}}";
+
+    @Test
+    public void shouldPropagateAllowedTypeIdNamesForGraphSON1() throws 
Exception {
+        final Configuration config = createGraphSON1Config();
+        config.setProperty("serializer.config.allowedTypeIdNames", 
Arrays.asList("java.awt.Point"));
+
+        final Cluster cluster = Cluster.open(config);
+        try {
+            assertTrue(cluster.getSerializer() instanceof 
GraphSONMessageSerializerV1);
+            final GraphSONMessageSerializerV1 serializer =
+                    (GraphSONMessageSerializerV1) cluster.getSerializer();
+            final Map<String, Object> deserialized = 
serializer.getMapper().readValue(POINT_JSON, Map.class);
+
+            assertEquals(new Point(1, 2), deserialized.get("point"));
+        } finally {
+            cluster.close();
+        }
+    }
+
+    @Test
+    public void shouldRejectUnregisteredTypeIdNamesForGraphSON1() {
+        final Cluster cluster = Cluster.open(createGraphSON1Config());
+        try {
+            assertTrue(cluster.getSerializer() instanceof 
GraphSONMessageSerializerV1);
+            final GraphSONMessageSerializerV1 serializer =
+                    (GraphSONMessageSerializerV1) cluster.getSerializer();
+
+            try {
+                serializer.getMapper().readValue(POINT_JSON, Map.class);
+                fail("an unregistered type id name should not resolve");
+            } catch (Exception ex) {
+                assertThat(ex, instanceOf(InvalidTypeIdException.class));
+            }
+        } finally {
+            cluster.close();
+        }
+    }
 
     @Test
     public void shouldPropagateSerializerConstraintsForGraphSON3() {
@@ -69,4 +115,12 @@ public class ClusterConfigTest {
         assertEquals(123456, constraints.getMaxStringLength());
         assertEquals(55, constraints.getMaxNestingDepth());
     }
+
+    private static Configuration createGraphSON1Config() {
+        final Configuration config = new BaseConfiguration();
+        config.setProperty("serializer.className", 
GraphSONMessageSerializerV1.class.getCanonicalName());
+        // Cluster construction requires a host, but these tests do not 
connect to it.
+        config.setProperty("hosts", Arrays.asList("localhost"));
+        return config;
+    }
 }
diff --git 
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
 
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
index 5b83e2d4b2..afff9236fe 100644
--- 
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
+++ 
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
@@ -969,7 +969,21 @@ public class GremlinDriverIntegrateTest extends 
AbstractGremlinServerIntegration
     }
 
     @Test
-    public void shouldWorkWithGraphSONV1Serialization() throws Exception {
+    public void shouldWorkWithGraphSONV1TypedSerialization() throws Exception {
+        final Cluster cluster = 
TestClientFactory.build().serializer(Serializers.GRAPHSON_V1).create();
+        final Client client = cluster.connect();
+
+        try {
+            final List<Result> results = 
client.submit("g.inject(2)").all().join();
+            assertEquals(1, results.size());
+            assertEquals(2, results.get(0).getInt());
+        } finally {
+            cluster.close();
+        }
+    }
+
+    @Test
+    public void shouldWorkWithGraphSONV1UntypedSerialization() throws 
Exception {
         final Cluster cluster = 
TestClientFactory.build().serializer(Serializers.GRAPHSON_V1_UNTYPED).create();
         final Client client = cluster.connect();
 
diff --git 
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/SettingsTest.java
 
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/SettingsTest.java
index d399a5ad65..5642be5cce 100644
--- 
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/SettingsTest.java
+++ 
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/SettingsTest.java
@@ -18,13 +18,22 @@
  */
 package org.apache.tinkerpop.gremlin.server;
 
+import org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV1;
+import org.apache.tinkerpop.shaded.jackson.databind.exc.InvalidTypeIdException;
 import org.junit.Test;
 import org.yaml.snakeyaml.Yaml;
 import org.yaml.snakeyaml.constructor.Constructor;
 
+import java.awt.Dimension;
+import java.awt.Point;
 import java.io.InputStream;
+import java.util.Collections;
+import java.util.Map;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
 
 public class SettingsTest {
 
@@ -56,4 +65,39 @@ public class SettingsTest {
 
         assertEquals("localhost", settings.customValue);
     }
+
+    @Test
+    public void shouldConfigureAllowedTypeIdNamesFromYaml() throws Exception {
+        final GraphSONMessageSerializerV1 serializer = 
createGraphSONV1SerializerFromYaml();
+        final String json = 
"{\"@class\":\"java.util.HashMap\",\"point\":{\"@class\":\"java.awt.Point\"," +
+                "\"x\":1.0,\"y\":2.0}}";
+        final Map<String, Object> deserialized = 
serializer.getMapper().readValue(json, Map.class);
+
+        assertEquals(new Point(1, 2), deserialized.get("point"));
+    }
+
+    @Test
+    public void shouldRejectUnregisteredTypeIdNamesFromYaml() {
+        final GraphSONMessageSerializerV1 serializer = 
createGraphSONV1SerializerFromYaml();
+        final String json = 
"{\"@class\":\"java.util.HashMap\",\"dimension\":{\"@class\":\"" +
+                Dimension.class.getName() + "\",\"width\":1,\"height\":2}}";
+
+        try {
+            serializer.getMapper().readValue(json, Map.class);
+            fail("an unregistered type id name should not resolve");
+        } catch (Exception ex) {
+            assertThat(ex, instanceOf(InvalidTypeIdException.class));
+        }
+    }
+
+    private static GraphSONMessageSerializerV1 
createGraphSONV1SerializerFromYaml() {
+        final InputStream stream = 
SettingsTest.class.getResourceAsStream("gremlin-server-integration.yaml");
+        final Settings settings = Settings.read(stream);
+        final Settings.SerializerSettings serializerSettings = 
settings.serializers.stream()
+                .filter(s -> 
GraphSONMessageSerializerV1.class.getName().equals(s.className))
+                .findFirst().orElseThrow(IllegalStateException::new);
+        final GraphSONMessageSerializerV1 serializer = new 
GraphSONMessageSerializerV1();
+        serializer.configure(serializerSettings.config, 
Collections.emptyMap());
+        return serializer;
+    }
 }
diff --git 
a/gremlin-server/src/test/resources/org/apache/tinkerpop/gremlin/server/gremlin-server-integration.yaml
 
b/gremlin-server/src/test/resources/org/apache/tinkerpop/gremlin/server/gremlin-server-integration.yaml
index 15904893cd..23481cf3d1 100644
--- 
a/gremlin-server/src/test/resources/org/apache/tinkerpop/gremlin/server/gremlin-server-integration.yaml
+++ 
b/gremlin-server/src/test/resources/org/apache/tinkerpop/gremlin/server/gremlin-server-integration.yaml
@@ -53,6 +53,7 @@ serializers:
   - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3, config: { 
ioRegistries: 
[org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3] }}
   - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV2, config: { 
ioRegistries: 
[org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV2] }}
   - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphSONUntypedMessageSerializerV1, 
config: { ioRegistries: 
[org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV1] }}
+  - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV1, config: { 
ioRegistries: 
[org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV1], 
allowedTypeIdNames: [java.awt.Point] }}
   - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1 }
   - { className: 
org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1, config: { 
serializeResultToString: true }}
 processors:
diff --git 
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/io/IoCustomTest.java
 
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/io/IoCustomTest.java
index 882cd0ee4e..e70a297f2f 100644
--- 
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/io/IoCustomTest.java
+++ 
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/io/IoCustomTest.java
@@ -64,9 +64,10 @@ public class IoCustomTest extends AbstractGremlinTest {
         final SimpleModule modulev3 = new 
CustomId.CustomIdTinkerPopJacksonModuleV3();
 
         return Arrays.asList(new Object[][]{
+                // CustomId is not among the default GraphSON 1.0 type id 
names, so it is added
                 {"graphson-v1-embedded", true,
-                        (Function<Graph, GraphReader>) g -> 
g.io(GraphSONIo.build(GraphSONVersion.V1_0)).reader().mapper(g.io(GraphSONIo.build(GraphSONVersion.V1_0)).mapper().addCustomModule(moduleV1).typeInfo(TypeInfo.PARTIAL_TYPES).create()).create(),
-                        (Function<Graph, GraphWriter>) g -> 
g.io(GraphSONIo.build(GraphSONVersion.V1_0)).writer().mapper(g.io(GraphSONIo.build(GraphSONVersion.V1_0)).mapper().addCustomModule(moduleV1).typeInfo(TypeInfo.PARTIAL_TYPES).create()).create()},
+                        (Function<Graph, GraphReader>) g -> 
g.io(GraphSONIo.build(GraphSONVersion.V1_0)).reader().mapper(g.io(GraphSONIo.build(GraphSONVersion.V1_0)).mapper().addCustomModule(moduleV1).typeInfo(TypeInfo.PARTIAL_TYPES).addAllowedTypeIdName(CustomId.class.getName()).create()).create(),
+                        (Function<Graph, GraphWriter>) g -> 
g.io(GraphSONIo.build(GraphSONVersion.V1_0)).writer().mapper(g.io(GraphSONIo.build(GraphSONVersion.V1_0)).mapper().addCustomModule(moduleV1).typeInfo(TypeInfo.PARTIAL_TYPES).addAllowedTypeIdName(CustomId.class.getName()).create()).create()},
                 {"graphson-v2-embedded", true,
                         (Function<Graph, GraphReader>) g -> 
g.io(GraphSONIo.build(GraphSONVersion.V2_0)).reader().mapper(g.io(GraphSONIo.build(GraphSONVersion.V2_0)).mapper().addCustomModule(moduleV2).typeInfo(TypeInfo.PARTIAL_TYPES).create()).create(),
                         (Function<Graph, GraphWriter>) g -> 
g.io(GraphSONIo.build(GraphSONVersion.V2_0)).writer().mapper(g.io(GraphSONIo.build(GraphSONVersion.V2_0)).mapper().addCustomModule(moduleV2).typeInfo(TypeInfo.PARTIAL_TYPES).create()).create()},
diff --git 
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/io/IoTest.java
 
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/io/IoTest.java
index 6078b1459b..73046f3146 100644
--- 
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/io/IoTest.java
+++ 
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/io/IoTest.java
@@ -578,8 +578,10 @@ public class IoTest {
 
             final SimpleModule module = new SimpleModule();
             module.addSerializer(CustomId.class, new 
CustomId.CustomIdJacksonSerializerV1());
+            // CustomId is not among the default GraphSON 1.0 type id names, 
so it is added below and again on the
+            // reader
             final GraphWriter writer = graph.io(graphson).writer().mapper(
-                    
graph.io(graphson).mapper().version(GraphSONVersion.V1_0).addCustomModule(module).typeInfo(TypeInfo.PARTIAL_TYPES).create()).create();
+                    
graph.io(graphson).mapper().version(GraphSONVersion.V1_0).addCustomModule(module).typeInfo(TypeInfo.PARTIAL_TYPES).addAllowedTypeIdName(CustomId.class.getName()).create()).create();
 
             try (final ByteArrayOutputStream baos = new 
ByteArrayOutputStream()) {
                 writer.writeGraph(baos, graph);
@@ -598,7 +600,7 @@ public class IoTest {
 
                 try (final InputStream is = new 
ByteArrayInputStream(baos.toByteArray())) {
                     final GraphReader reader = graph.io(graphson).reader()
-                            
.mapper(graph.io(graphson).mapper().version(GraphSONVersion.V1_0).typeInfo(TypeInfo.PARTIAL_TYPES).addCustomModule(module).create()).create();
+                            
.mapper(graph.io(graphson).mapper().version(GraphSONVersion.V1_0).typeInfo(TypeInfo.PARTIAL_TYPES).addCustomModule(module).addAllowedTypeIdName(CustomId.class.getName()).create()).create();
                     reader.readGraph(is, g2);
                 }
 
diff --git 
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV1.java
 
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV1.java
index c2a0de62fd..da9d111d21 100644
--- 
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV1.java
+++ 
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractGraphSONMessageSerializerV1.java
@@ -44,6 +44,7 @@ import org.slf4j.LoggerFactory;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -51,6 +52,8 @@ import java.util.UUID;
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
 public abstract class AbstractGraphSONMessageSerializerV1 extends 
AbstractMessageSerializer<ObjectMapper> {
+    static final String TOKEN_ALLOWED_TYPE_ID_NAMES = "allowedTypeIdNames";
+
     private static final Logger logger = 
LoggerFactory.getLogger(AbstractGraphSONMessageSerializerV1.class);
 
     protected ObjectMapper mapper;
@@ -76,6 +79,7 @@ public abstract class AbstractGraphSONMessageSerializerV1 
extends AbstractMessag
         final GraphSONMapper.Builder initialBuilder = initBuilder(null);
         addIoRegistries(config, initialBuilder);
         applyMaxTokenLimits(initialBuilder, config);
+        applyAllowedTypeIdNames(initialBuilder, config);
         mapper = configureBuilder(initialBuilder).create().createMapper();
     }
 
@@ -183,6 +187,13 @@ public abstract class AbstractGraphSONMessageSerializerV1 
extends AbstractMessag
         return builder;
     }
 
+    private GraphSONMapper.Builder applyAllowedTypeIdNames(final 
GraphSONMapper.Builder builder,
+                                                           final Map<String, 
Object> config) {
+        final List<String> allowedTypeIdNames = 
getListStringFromConfig(TOKEN_ALLOWED_TYPE_ID_NAMES, config);
+        builder.addAllowedTypeIdName(allowedTypeIdNames.toArray(new 
String[0]));
+        return builder;
+    }
+
     @Override
     public ObjectMapper getMapper() {
         return this.mapper;
diff --git 
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractMessageSerializer.java
 
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractMessageSerializer.java
index 03f570aa5c..76139b0fdd 100644
--- 
a/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractMessageSerializer.java
+++ 
b/gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/AbstractMessageSerializer.java
@@ -88,9 +88,13 @@ public abstract class AbstractMessageSerializer<M> 
implements MessageSerializer<
      * Gets a {@link List} of strings from the configuration object.
      */
     protected List<String> getListStringFromConfig(final String token, final 
Map<String, Object> config) {
+        final Object value = config.getOrDefault(token, 
Collections.emptyList());
+        if (value instanceof String)
+            return Collections.singletonList((String) value);
+
         final List<String> classNameList;
         try {
-            classNameList = (List<String>) config.getOrDefault(token, 
Collections.emptyList());
+            classNameList = (List<String>) value;
         } catch (Exception ex) {
             throw new IllegalStateException(String.format("Invalid 
configuration value of [%s] for [%s] setting on %s serialization configuration",
                     config.getOrDefault(token, ""), token, 
this.getClass().getName()), ex);
@@ -98,4 +102,4 @@ public abstract class AbstractMessageSerializer<M> 
implements MessageSerializer<
 
         return classNameList;
     }
-}
\ No newline at end of file
+}
diff --git 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/GraphSONMessageSerializerV1Test.java
 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/GraphSONMessageSerializerV1Test.java
index 62bbdf1c3c..cae571835c 100644
--- 
a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/GraphSONMessageSerializerV1Test.java
+++ 
b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/GraphSONMessageSerializerV1Test.java
@@ -22,6 +22,7 @@ import io.netty.buffer.ByteBuf;
 import io.netty.buffer.ByteBufAllocator;
 import io.netty.buffer.UnpooledByteBufAllocator;
 import org.apache.tinkerpop.gremlin.util.MessageSerializer;
+import org.apache.tinkerpop.gremlin.util.message.RequestMessage;
 import org.apache.tinkerpop.gremlin.util.message.ResponseMessage;
 import org.apache.tinkerpop.gremlin.util.message.ResponseStatusCode;
 import 
org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
@@ -35,10 +36,12 @@ import 
org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory;
 import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper;
+import org.apache.tinkerpop.shaded.jackson.databind.exc.InvalidTypeIdException;
 import org.apache.tinkerpop.shaded.jackson.databind.util.StdDateFormat;
 import org.junit.Test;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -48,6 +51,7 @@ import java.util.UUID;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 /**
@@ -63,6 +67,58 @@ public class GraphSONMessageSerializerV1Test {
 
     public MessageSerializer<ObjectMapper> serializer = new 
GraphSONMessageSerializerV1();
 
+    @Test
+    public void shouldRejectUnregisteredTypeIdNamesAtMessageBoundaries() 
throws Exception {
+        final GraphSONMessageSerializerV1 defaultSerializer = new 
GraphSONMessageSerializerV1();
+        final RequestMessage request = 
RequestMessage.build("eval").addArg("sample", new SamplePojo(123)).create();
+        final ByteBuf requestBuffer = 
defaultSerializer.serializeRequestAsBinary(request, allocator);
+        // The server decoder removes the MIME header before passing the JSON 
payload to deserializeRequest().
+        final int mimeLength = requestBuffer.readByte();
+        requestBuffer.skipBytes(mimeLength);
+
+        try {
+            defaultSerializer.deserializeRequest(requestBuffer);
+            fail("an unregistered request type id name should not resolve");
+        } catch (SerializationException expected) {
+            assertEquals(InvalidTypeIdException.class, 
expected.getCause().getClass());
+            
assertTrue(expected.getCause().getMessage().contains(SamplePojo.class.getName()));
+        }
+
+        final ResponseMessage response = 
ResponseMessage.build(requestId).result(new SamplePojo(123)).create();
+        final ByteBuf responseBuffer = 
defaultSerializer.serializeResponseAsBinary(response, allocator);
+        try {
+            defaultSerializer.deserializeResponse(responseBuffer);
+            fail("an unregistered response type id name should not resolve");
+        } catch (SerializationException expected) {
+            assertEquals(InvalidTypeIdException.class, 
expected.getCause().getClass());
+            
assertTrue(expected.getCause().getMessage().contains(SamplePojo.class.getName()));
+        }
+    }
+
+    @Test
+    public void shouldConfigureAllowedTypeIdNamesAtMessageBoundaries() throws 
Exception {
+        final GraphSONMessageSerializerV1 configuredSerializer = new 
GraphSONMessageSerializerV1();
+        final Map<String, Object> config = new HashMap<>();
+        
config.put(AbstractGraphSONMessageSerializerV1.TOKEN_ALLOWED_TYPE_ID_NAMES,
+                Collections.singletonList(SamplePojo.class.getName()));
+        configuredSerializer.configure(config, null);
+
+        final RequestMessage request = 
RequestMessage.build("eval").addArg("sample", new SamplePojo(123)).create();
+        final ByteBuf requestBuffer = 
configuredSerializer.serializeRequestAsBinary(request, allocator);
+        // The server decoder removes the MIME header before passing the JSON 
payload to deserializeRequest().
+        final int mimeLength = requestBuffer.readByte();
+        requestBuffer.skipBytes(mimeLength);
+        final SamplePojo requestSample = 
configuredSerializer.deserializeRequest(requestBuffer).getArg("sample");
+
+        final ResponseMessage response = 
ResponseMessage.build(requestId).result(new SamplePojo(123)).create();
+        final ByteBuf responseBuffer = 
configuredSerializer.serializeResponseAsBinary(response, allocator);
+        final SamplePojo responseSample = (SamplePojo) 
configuredSerializer.deserializeResponse(responseBuffer)
+                .getResult().getData();
+
+        assertEquals(123, requestSample.value);
+        assertEquals(123, responseSample.value);
+    }
+
     @Test
     public void shouldSerializeIterable() throws Exception {
         final ArrayList<Integer> list = new ArrayList<>();
@@ -332,4 +388,15 @@ public class GraphSONMessageSerializerV1Test {
         final ByteBuf bb = 
serializer.serializeResponseAsBinary(responseMessageBuilder.result(toSerialize).create(),
 allocator);
         return serializer.deserializeResponse(bb);
     }
+
+    public static class SamplePojo {
+        public int value;
+
+        public SamplePojo() {
+        }
+
+        public SamplePojo(final int value) {
+            this.value = value;
+        }
+    }
 }

Reply via email to