This is an automated email from the ASF dual-hosted git repository. jorgebg pushed a commit to branch TINKERPOP-1942 in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
The following commit(s) were added to refs/heads/TINKERPOP-1942 by this push: new e59a282 Support interfaces e59a282 is described below commit e59a282274e4b0d788157d229ecdf0b23d766e62 Author: Jorge Bay Gondra <jorgebaygon...@gmail.com> AuthorDate: Fri Nov 23 11:44:55 2018 +0100 Support interfaces --- docs/src/dev/io/graphbinary.asciidoc | 86 ++++++++++++---------- .../driver/ser/GraphBinaryMessageSerializerV1.java | 17 ++++- .../driver/ser/binary/GraphBinaryReader.java | 6 +- .../driver/ser/binary/GraphBinaryWriter.java | 6 +- .../driver/ser/binary/TypeSerializerRegistry.java | 37 ++++++++-- .../driver/ser/binary/types/ListSerializer.java | 6 +- .../driver/ser/binary/types/MapSerializer.java | 7 +- .../ser/binary/types/RequestMessageSerializer.java | 6 +- .../binary/GraphBinaryMessageSerializerV1Test.java | 15 ++++ .../GraphBinaryReaderWriterRoundTripTest.java | 3 +- 10 files changed, 128 insertions(+), 61 deletions(-) diff --git a/docs/src/dev/io/graphbinary.asciidoc b/docs/src/dev/io/graphbinary.asciidoc index fd16c97..2a30845 100644 --- a/docs/src/dev/io/graphbinary.asciidoc +++ b/docs/src/dev/io/graphbinary.asciidoc @@ -19,8 +19,9 @@ limitations under the License. [[graphbinary]] = GraphBinary -GraphBinary is a binary serialization format that is designed to reduce serialization overhead on both the client -and the server, as well as limiting the size of the payload that is transmitted over the wire. +GraphBinary is a binary serialization format suitable for object trees, designed to reduce serialization +overhead on both the client and the server, as well as limiting the size of the payload that is transmitted over the +wire. It describes arbitrary object graphs with a fully-qualified format: @@ -57,42 +58,6 @@ The serialization format supports new types being added without the need to intr Changes to existing types require new revision. -=== Request Message - -Represents a message from the client to the server. - -Format: `{version}{request_id}{op}{processor}{args}` - -Where: - -- `{version}` is a `Byte` representing the protocol version, with the most significant bit set to one. For this version -of the protocol, the value expected is `0x81` (`10000001`). -- `{request_id}` is a `UUID`. -- `{op}` is a `String`. -- `{processor}` is a `String`. -- `{args}` is a `Map`. - -The total length is not part of the message as the transport layer will provide it. For example: WebSockets, -as a framing protocol, defines payload length. - -=== Response Message - -Format: `{version}{id_present}{request_id}{status_code}{status_message}{status_attributes}{result_meta}{result_data}` - -Where: - -- `{version}` is a `Byte` representing the protocol version, with the most significant bit set to one. For this version -of the protocol, the value expected is `0x81` (`10000001`). -- `{id_present}` is a single `Byte` representing whether a request id is present with only two possible values 0 and 1. -- `{request_id}` is a `UUID`. -- `{status_code}` is an `Int`. -- `{status_message}` is a `String`. -- `{status_attributes}` is a `Map`. -- `{result_meta}` is a `Map`. -- `{result_data}` is a fully qualified typed value composed of `{type_code}{type_info}{value_flag}{value}`. - -The total length is not part of the message as the transport layer will provide it. - === Data Type Codes ==== Core Data Types @@ -271,7 +236,9 @@ Format: `{length}{item_0}...{item_n}` Where: - `{length}` is an `Int` describing the length of the map. -- `{item_0}...{item_n}` are the items of the map. `{item_i}` is sequence of 2 fully qualified typed values one representing the key and the following representing the value, each composed composed of `{type_code}{type_info}{value_flag}{value}`. +- `{item_0}...{item_n}` are the items of the map. `{item_i}` is sequence of 2 fully qualified typed values one +representing the key and the following representing the value, each composed composed of +`{type_code}{type_info}{value_flag}{value}`. ==== UUID @@ -659,3 +626,44 @@ Where: A time-zone offset from Greenwich/UTC, such as +02:00. Format: An `Int` representing total zone offset in seconds. + +=== Request and Response Messages + +Request and response messages are special containers types used to represent messages from client to the server and the +other way around. These messages are independent from the transport layer. + +==== Request Message + +Represents a message from the client to the server. + +Format: `{version}{request_id}{op}{processor}{args}` + +Where: + +- `{version}` is a `Byte` representing the specification version, with the most significant bit set to one. For this +version of the format, the value expected is `0x81` (`10000001`). +- `{request_id}` is a `UUID`. +- `{op}` is a `String`. +- `{processor}` is a `String`. +- `{args}` is a `Map`. + +The total length is not part of the message as the transport layer will provide it. For example: WebSockets, +as a framing protocol, defines payload length. + +==== Response Message + +Format: `{version}{id_present}{request_id}{status_code}{status_message}{status_attributes}{result_meta}{result_data}` + +Where: + +- `{version}` is a `Byte` representing the protocol version, with the most significant bit set to one. For this version +of the protocol, the value expected is `0x81` (`10000001`). +- `{id_present}` is a single `Byte` representing whether a request id is present with only two possible values 0 and 1. +- `{request_id}` is a `UUID`. +- `{status_code}` is an `Int`. +- `{status_message}` is a `String`. +- `{status_attributes}` is a `Map`. +- `{result_meta}` is a `Map`. +- `{result_data}` is a fully qualified typed value composed of `{type_code}{type_info}{value_flag}{value}`. + +The total length is not part of the message as the transport layer will provide it. \ No newline at end of file diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphBinaryMessageSerializerV1.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphBinaryMessageSerializerV1.java index dbc3a05..1cc2f3f 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphBinaryMessageSerializerV1.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphBinaryMessageSerializerV1.java @@ -22,20 +22,30 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import org.apache.tinkerpop.gremlin.driver.message.RequestMessage; import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage; +import org.apache.tinkerpop.gremlin.driver.ser.binary.GraphBinaryReader; +import org.apache.tinkerpop.gremlin.driver.ser.binary.GraphBinaryWriter; import org.apache.tinkerpop.gremlin.driver.ser.binary.TypeSerializerRegistry; +import org.apache.tinkerpop.gremlin.driver.ser.binary.types.RequestMessageSerializer; public class GraphBinaryMessageSerializerV1 extends AbstractMessageSerializer { private static final String MIME_TYPE = SerTokens.MIME_GRAPHBINARY_V1D0; + private final GraphBinaryReader reader; + private final GraphBinaryWriter writer; + private final RequestMessageSerializer requestSerializer; + /** * Creates a new instance of the message serializer using the default type serializers. */ public GraphBinaryMessageSerializerV1() { - + this(TypeSerializerRegistry.INSTANCE); } public GraphBinaryMessageSerializerV1(TypeSerializerRegistry registry) { + reader = new GraphBinaryReader(registry); + writer = new GraphBinaryWriter(registry); + requestSerializer = new RequestMessageSerializer(); } @Override @@ -45,14 +55,13 @@ public class GraphBinaryMessageSerializerV1 extends AbstractMessageSerializer { @Override public ByteBuf serializeRequestAsBinary(RequestMessage requestMessage, ByteBufAllocator allocator) throws SerializationException { - + //TODO: Implement return null; } @Override public RequestMessage deserializeRequest(ByteBuf msg) throws SerializationException { - //TODO: Use BinaryReader - return null; + return requestSerializer.readValue(msg, reader, false); } @Override diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReader.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReader.java index 0915d65..7297dcb 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReader.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReader.java @@ -25,7 +25,11 @@ public class GraphBinaryReader { private final TypeSerializerRegistry registry; public GraphBinaryReader() { - registry = TypeSerializerRegistry.INSTANCE; + this(TypeSerializerRegistry.INSTANCE); + } + + public GraphBinaryReader(TypeSerializerRegistry registry) { + this.registry = registry; } /** diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryWriter.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryWriter.java index 78c09b3..50af406 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryWriter.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryWriter.java @@ -30,7 +30,11 @@ public class GraphBinaryWriter { private final static byte[] unspecifiedNull = new byte[] { DataType.UNSPECIFIED_NULL.getCodeByte(), 0x01}; public GraphBinaryWriter() { - registry = TypeSerializerRegistry.INSTANCE; + this(TypeSerializerRegistry.INSTANCE); + } + + public GraphBinaryWriter(TypeSerializerRegistry registry) { + this.registry = registry; } /** diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/TypeSerializerRegistry.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/TypeSerializerRegistry.java index 730d3d4..48a0e57 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/TypeSerializerRegistry.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/TypeSerializerRegistry.java @@ -29,6 +29,7 @@ public class TypeSerializerRegistry { public static final TypeSerializerRegistry INSTANCE = new TypeSerializerRegistry(); private final Map<Class<?>, TypeSerializer<?>> serializers = new HashMap<>(); + private final Map<Class<?>, TypeSerializer<?>> serializersByInterface = new HashMap<>(); private final Map<DataType, TypeSerializer<?>> serializersByDataType = new HashMap<>(); private TypeSerializerRegistry() { @@ -38,9 +39,8 @@ public class TypeSerializerRegistry { put(String.class, DataType.STRING, new StringSerializer()); put(UUID.class, DataType.UUID, new UUIDSerializer()); - // TODO: provide mechanism to look for interfaces - put(HashMap.class, DataType.MAP, new MapSerializer()); - put(ArrayList.class, DataType.LIST, new ListSerializer()); + put(Map.class, DataType.MAP, new MapSerializer()); + put(List.class, DataType.LIST, new ListSerializer()); put(Integer.class, DataType.INT, SingleTypeSerializer.IntSerializer); put(Long.class, DataType.LONG, SingleTypeSerializer.LongSerializer); @@ -54,7 +54,22 @@ public class TypeSerializerRegistry { } public <T> TypeSerializerRegistry put(Class<T> type, DataType dataType, TypeSerializer<T> instance) { - serializers.put(type, instance); + if (type == null) { + throw new IllegalArgumentException("Type can not be null"); + } + + if (instance == null) { + throw new IllegalArgumentException("Serializer instance can not be null"); + } + + if (!type.isInterface()) { + // Direct class match + serializers.put(type, instance); + } else { + // Interface can be assigned by provided type + serializersByInterface.put(type, instance); + } + if (dataType != null) { serializersByDataType.put(dataType, instance); @@ -64,7 +79,19 @@ public class TypeSerializerRegistry { } public <T> TypeSerializer<T> getSerializer(Class<T> type) throws SerializationException { - return validateInstance(serializers.get(type), type.getTypeName()); + TypeSerializer<?> serializer = serializers.get(type); + + if (serializer == null) { + // Find by interface + for (Map.Entry<Class<?>, TypeSerializer<?>> entry : serializersByInterface.entrySet()) { + if (entry.getKey().isAssignableFrom(type)) { + serializer = entry.getValue(); + break; + } + } + } + + return validateInstance(serializer, type.getTypeName()); } public <T> TypeSerializer<T> getSerializer(DataType dataType) throws SerializationException { diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/ListSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/ListSerializer.java index 6985b2c..e123b0e 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/ListSerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/ListSerializer.java @@ -11,9 +11,9 @@ import org.apache.tinkerpop.gremlin.driver.ser.binary.GraphBinaryWriter; import java.util.ArrayList; import java.util.List; -public class ListSerializer extends SimpleTypeSerializer<ArrayList> { +public class ListSerializer extends SimpleTypeSerializer<List> { @Override - public ArrayList readValue(ByteBuf buffer, GraphBinaryReader context) throws SerializationException { + public List readValue(ByteBuf buffer, GraphBinaryReader context) throws SerializationException { final int length = buffer.readInt(); ArrayList result = new ArrayList(length); @@ -30,7 +30,7 @@ public class ListSerializer extends SimpleTypeSerializer<ArrayList> { } @Override - public ByteBuf writeValueSequence(ArrayList value, ByteBufAllocator allocator, GraphBinaryWriter context) throws SerializationException { + public ByteBuf writeValueSequence(List value, ByteBufAllocator allocator, GraphBinaryWriter context) throws SerializationException { CompositeByteBuf result = allocator.compositeBuffer(1 + value.size()); result.addComponent(true, allocator.buffer(4).writeInt(value.size())); diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/MapSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/MapSerializer.java index 253b4e4..278a4e4 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/MapSerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/MapSerializer.java @@ -27,10 +27,11 @@ import org.apache.tinkerpop.gremlin.driver.ser.binary.GraphBinaryReader; import org.apache.tinkerpop.gremlin.driver.ser.binary.GraphBinaryWriter; import java.util.HashMap; +import java.util.Map; -public class MapSerializer extends SimpleTypeSerializer<HashMap> { +public class MapSerializer extends SimpleTypeSerializer<Map> { @Override - public HashMap readValue(ByteBuf buffer, GraphBinaryReader context) throws SerializationException { + public Map readValue(ByteBuf buffer, GraphBinaryReader context) throws SerializationException { final int length = buffer.readInt(); HashMap result = new HashMap<>(length); @@ -47,7 +48,7 @@ public class MapSerializer extends SimpleTypeSerializer<HashMap> { } @Override - public ByteBuf writeValueSequence(HashMap value, ByteBufAllocator allocator, GraphBinaryWriter context) throws SerializationException { + public ByteBuf writeValueSequence(Map value, ByteBufAllocator allocator, GraphBinaryWriter context) throws SerializationException { CompositeByteBuf result = allocator.compositeBuffer(1 + value.size() * 2); result.addComponent(true, allocator.buffer(4).writeInt(value.size())); diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/RequestMessageSerializer.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/RequestMessageSerializer.java index 92f7d3e..010f0d8 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/RequestMessageSerializer.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/binary/types/RequestMessageSerializer.java @@ -48,9 +48,9 @@ public class RequestMessageSerializer implements TypeSerializer<RequestMessage> } @Override - public ByteBuf write(RequestMessage value, ByteBufAllocator allocator, GraphBinaryWriter context) { - //TODO: Implement - return null; + public ByteBuf write(RequestMessage value, ByteBufAllocator allocator, GraphBinaryWriter context) throws SerializationException { + // There is no type code / information for the request message itself. + throw new SerializationException("RequestMessageSerializer can not be written with type information"); } @Override diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryMessageSerializerV1Test.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryMessageSerializerV1Test.java new file mode 100644 index 0000000..e8c66a8 --- /dev/null +++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryMessageSerializerV1Test.java @@ -0,0 +1,15 @@ +package org.apache.tinkerpop.gremlin.driver.ser.binary; + +import org.junit.Test; + +public class GraphBinaryMessageSerializerV1Test { + @Test + public void shouldSerializeAndDeserializeRequest() { + //TODO + } + + @Test + public void shouldSerializeAndDeserializeRequestWithNullProcessor() { + //TODO + } +} diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphBinaryReaderWriterRoundTripTest.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReaderWriterRoundTripTest.java similarity index 95% rename from gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphBinaryReaderWriterRoundTripTest.java rename to gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReaderWriterRoundTripTest.java index 4819849..60d3bc6 100644 --- a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphBinaryReaderWriterRoundTripTest.java +++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/binary/GraphBinaryReaderWriterRoundTripTest.java @@ -16,12 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.tinkerpop.gremlin.driver.ser; +package org.apache.tinkerpop.gremlin.driver.ser.binary; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import org.apache.tinkerpop.gremlin.process.traversal.Bytecode; -import org.apache.tinkerpop.gremlin.driver.ser.binary.*; import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource; import org.junit.Assert; import org.junit.Test;