http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperV2d0Test.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperV2d0Test.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperV2d0Test.java new file mode 100644 index 0000000..0fea292 --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperV2d0Test.java @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.structure.io.graphson; + +import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalExplanation; +import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper; +import org.junit.Test; + +import java.time.*; +import java.util.*; + +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.__; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +/** + * Tests non-typed serialization/deserialization for GraphSON 2.0. + */ + +public class GraphSONMapperV2d0Test { + private final ObjectMapper mapper = GraphSONMapper.build() + .version(GraphSONVersion.V2_0) + .typeInfo(GraphSONMapper.TypeInfo.NO_TYPES) + .create() + .createMapper(); + + @Test + public void shouldHandleTraversalExplanation() throws Exception { + final TraversalExplanation te = __().out().outV().outE().explain(); + final String json = mapper.writeValueAsString(te); + assertEquals("{\"original\":[\"InjectStep([])\",\"VertexStep(OUT,vertex)\",\"EdgeVertexStep(OUT)\",\"VertexStep(OUT,edge)\"],\"intermediate\":[],\"final\":[\"InjectStep([])\",\"VertexStep(OUT,vertex)\",\"EdgeVertexStep(OUT)\",\"VertexStep(OUT,edge)\"]}", json); + } + + @Test + public void shouldHandleDuration() throws Exception { + final Duration o = Duration.ZERO; + final String json = mapper.writeValueAsString(o); + assertEquals("\"" + o.toString() + "\"", json); + } + + @Test + public void shouldHandleInstant() throws Exception { + final Instant o = Instant.ofEpochMilli(System.currentTimeMillis()); + final String json = mapper.writeValueAsString(o); + assertEquals("\"" + o.toString() + "\"", json); + } + + @Test + public void shouldHandleLocalDate() throws Exception { + final LocalDate o = LocalDate.now(); + final String json = mapper.writeValueAsString(o); + assertEquals("\"" + o.toString() + "\"", json); + } + + @Test + public void shouldHandleLocalDateTime() throws Exception { + final LocalDateTime o = LocalDateTime.now(); + final String json = mapper.writeValueAsString(o); + assertEquals("\"" + o.toString() + "\"", json); + } + + @Test + public void shouldHandleLocalTime() throws Exception { + final LocalTime o = LocalTime.now(); + final String json = mapper.writeValueAsString(o); + assertEquals("\"" + o.toString() + "\"", json); + } + + @Test + public void shouldHandleMonthDay() throws Exception { + final MonthDay o = MonthDay.now(); + final String json = mapper.writeValueAsString(o); + assertEquals("\"" + o.toString() + "\"", json); + } + + @Test + public void shouldHandleOffsetDateTime() throws Exception { + final OffsetDateTime o = OffsetDateTime.now(); + final String json = mapper.writeValueAsString(o); + assertEquals("\"" + o.toString() + "\"", json); + } + + @Test + public void shouldHandleOffsetTime() throws Exception { + final OffsetTime o = OffsetTime.now(); + final String json = mapper.writeValueAsString(o); + assertEquals("\"" + o.toString() + "\"", json); + } + + @Test + public void shouldHandlePeriod() throws Exception { + final Period o = Period.ofDays(3); + final String json = mapper.writeValueAsString(o); + assertEquals("\"" + o.toString() + "\"", json); + } + + @Test + public void shouldHandleYear() throws Exception { + final Year o = Year.now(); + final String json = mapper.writeValueAsString(o); + assertEquals("\"" + o.toString() + "\"", json); + } + + @Test + public void shouldHandleYearMonth() throws Exception { + final YearMonth o = YearMonth.now(); + final String json = mapper.writeValueAsString(o); + assertEquals("\"" + o.toString() + "\"", json); + } + + @Test + public void shouldHandleZonedDateTime() throws Exception { + final ZonedDateTime o = ZonedDateTime.now(); + final String json = mapper.writeValueAsString(o); + assertEquals("\"" + o.toString() + "\"", json); + } + + @Test + public void shouldHandleZoneOffset() throws Exception { + final ZoneOffset o = ZonedDateTime.now().getOffset(); + final String json = mapper.writeValueAsString(o); + assertEquals("\"" + o.toString() + "\"", json); + } + + @Test + public void shouldSerializeDeserializeNestedCollectionsAndMapAndTypedCollectionsCorrectly() throws Exception { + UUID uuid = UUID.randomUUID(); + List myList = new ArrayList<>(); + + List myList2 = new ArrayList<>(); + myList2.add(UUID.randomUUID()); + myList2.add(33L); + myList2.add(84); + Map map2 = new HashMap<>(); + map2.put("eheh", UUID.randomUUID()); + map2.put("normal", "normal"); + myList2.add(map2); + + Map<String, Object> map1 = new HashMap<>(); + map1.put("hello", "world"); + map1.put("test", uuid); + map1.put("hehe", myList2); + myList.add(map1); + + myList.add("kjkj"); + myList.add(UUID.randomUUID()); + + String json = mapper.writeValueAsString(myList); + Object read = mapper.readValue(json, Object.class); + + // Not equals because of type loss + assertNotEquals(myList, read); + } + +}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/AbstractGraphSONMessageSerializerV2d0.java ---------------------------------------------------------------------- diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/AbstractGraphSONMessageSerializerV2d0.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/AbstractGraphSONMessageSerializerV2d0.java new file mode 100644 index 0000000..d0303eb --- /dev/null +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/AbstractGraphSONMessageSerializerV2d0.java @@ -0,0 +1,248 @@ +/* + * 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.driver.ser; + +import groovy.json.JsonBuilder; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.util.ReferenceCountUtil; +import org.apache.tinkerpop.gremlin.driver.message.RequestMessage; +import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage; +import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONIo; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONUtil; +import org.apache.tinkerpop.shaded.jackson.core.JsonGenerationException; +import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator; +import org.apache.tinkerpop.shaded.jackson.core.type.TypeReference; +import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper; +import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer; +import org.apache.tinkerpop.shaded.jackson.databind.module.SimpleModule; +import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Map; +import java.util.UUID; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public abstract class AbstractGraphSONMessageSerializerV2d0 extends AbstractMessageSerializer { + private static final Logger logger = LoggerFactory.getLogger(AbstractGraphSONMessageSerializerV2d0.class); + + protected ObjectMapper mapper; + + protected static final String TOKEN_USE_MAPPER_FROM_GRAPH = "useMapperFromGraph"; + + protected final TypeReference<Map<String, Object>> mapTypeReference = new TypeReference<Map<String, Object>>() { + }; + + public AbstractGraphSONMessageSerializerV2d0() { + final GraphSONMapper.Builder builder = configureBuilder(GraphSONMapper.build()); + mapper = builder.create().createMapper(); + } + + public AbstractGraphSONMessageSerializerV2d0(final GraphSONMapper mapper) { + this.mapper = mapper.createMapper(); + } + + abstract byte[] obtainHeader(); + + abstract GraphSONMapper.Builder configureBuilder(final GraphSONMapper.Builder builder); + + @Override + public void configure(final Map<String, Object> config, final Map<String, Graph> graphs) { + final GraphSONMapper.Builder initialBuilder; + final Object graphToUseForMapper = config.get(TOKEN_USE_MAPPER_FROM_GRAPH); + if (graphToUseForMapper != null) { + if (null == graphs) throw new IllegalStateException(String.format( + "No graphs have been provided to the serializer and therefore %s is not a valid configuration", TOKEN_USE_MAPPER_FROM_GRAPH)); + + final Graph g = graphs.get(graphToUseForMapper.toString()); + if (null == g) throw new IllegalStateException(String.format( + "There is no graph named [%s] configured to be used in the %s setting", + graphToUseForMapper, TOKEN_USE_MAPPER_FROM_GRAPH)); + + // a graph was found so use the mapper it constructs. this allows graphson to be auto-configured with any + // custom classes that the implementation allows for + initialBuilder = g.io(GraphSONIo.build()).mapper(); + } else { + // no graph was supplied so just use the default - this will likely be the case when using a graph + // with no custom classes or a situation where the user needs complete control like when using two + // distinct implementations each with their own custom classes. + initialBuilder = GraphSONMapper.build(); + } + + addIoRegistries(config, initialBuilder); + + mapper = configureBuilder(initialBuilder).create().createMapper(); + } + + @Override + public ByteBuf serializeResponseAsBinary(final ResponseMessage responseMessage, final ByteBufAllocator allocator) throws SerializationException { + ByteBuf encodedMessage = null; + try { + final byte[] payload = mapper.writeValueAsBytes(responseMessage); + encodedMessage = allocator.buffer(payload.length); + encodedMessage.writeBytes(payload); + + return encodedMessage; + } catch (Exception ex) { + if (encodedMessage != null) ReferenceCountUtil.release(encodedMessage); + + logger.warn("Response [{}] could not be serialized by {}.", responseMessage.toString(), AbstractGraphSONMessageSerializerV2d0.class.getName()); + throw new SerializationException(ex); + } + } + + @Override + public ByteBuf serializeRequestAsBinary(final RequestMessage requestMessage, final ByteBufAllocator allocator) throws SerializationException { + ByteBuf encodedMessage = null; + try { + final byte[] header = obtainHeader(); + final byte[] payload = mapper.writeValueAsBytes(requestMessage); + + encodedMessage = allocator.buffer(header.length + payload.length); + encodedMessage.writeBytes(header); + encodedMessage.writeBytes(payload); + + return encodedMessage; + } catch (Exception ex) { + if (encodedMessage != null) ReferenceCountUtil.release(encodedMessage); + + logger.warn("Request [{}] could not be serialized by {}.", requestMessage.toString(), AbstractGraphSONMessageSerializerV2d0.class.getName()); + throw new SerializationException(ex); + } + } + + @Override + public RequestMessage deserializeRequest(final ByteBuf msg) throws SerializationException { + try { + final byte[] payload = new byte[msg.readableBytes()]; + msg.readBytes(payload); + return mapper.readValue(payload, RequestMessage.class); + } catch (Exception ex) { + logger.warn("Request [{}] could not be deserialized by {}.", msg, AbstractGraphSONMessageSerializerV2d0.class.getName()); + throw new SerializationException(ex); + } + } + + @Override + public ResponseMessage deserializeResponse(final ByteBuf msg) throws SerializationException { + try { + final byte[] payload = new byte[msg.readableBytes()]; + msg.readBytes(payload); + final Map<String, Object> responseData = mapper.readValue(payload, mapTypeReference); + final Map<String, Object> status = (Map<String, Object>) responseData.get(SerTokens.TOKEN_STATUS); + final Map<String, Object> result = (Map<String, Object>) responseData.get(SerTokens.TOKEN_RESULT); + return ResponseMessage.build(UUID.fromString(responseData.get(SerTokens.TOKEN_REQUEST).toString())) + .code(ResponseStatusCode.getFromValue((Integer) status.get(SerTokens.TOKEN_CODE))) + .statusMessage(status.get(SerTokens.TOKEN_MESSAGE).toString()) + .statusAttributes((Map<String, Object>) status.get(SerTokens.TOKEN_ATTRIBUTES)) + .result(result.get(SerTokens.TOKEN_DATA)) + .responseMetaData((Map<String, Object>) result.get(SerTokens.TOKEN_META)) + .create(); + } catch (Exception ex) { + logger.warn("Response [{}] could not be deserialized by {}.", msg, AbstractGraphSONMessageSerializerV2d0.class.getName()); + throw new SerializationException(ex); + } + } + + public final static class GremlinServerModule extends SimpleModule { + public GremlinServerModule() { + super("graphson-gremlin-server"); + addSerializer(JsonBuilder.class, new JsonBuilderJacksonSerializer()); + addSerializer(ResponseMessage.class, new ResponseMessageSerializer()); + } + } + + public final static class JsonBuilderJacksonSerializer extends StdSerializer<JsonBuilder> { + public JsonBuilderJacksonSerializer() { + super(JsonBuilder.class); + } + + @Override + public void serialize(final JsonBuilder json, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) + throws IOException, JsonGenerationException { + // the JSON from the builder will already be started/ended as array or object...just need to surround it + // with appropriate chars to fit into the serialization pattern. + jsonGenerator.writeRaw(":"); + jsonGenerator.writeRaw(json.toString()); + jsonGenerator.writeRaw(","); + } + } + + public final static class ResponseMessageSerializer extends StdSerializer<ResponseMessage> { + public ResponseMessageSerializer() { + super(ResponseMessage.class); + } + + @Override + public void serialize(final ResponseMessage responseMessage, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider) throws IOException { + ser(responseMessage, jsonGenerator, serializerProvider, null); + } + + @Override + public void serializeWithType(final ResponseMessage responseMessage, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, + final TypeSerializer typeSerializer) throws IOException { + ser(responseMessage, jsonGenerator, serializerProvider, typeSerializer); + } + + public void ser(final ResponseMessage responseMessage, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, + final TypeSerializer typeSerializer) throws IOException { + GraphSONUtil.writeStartObject(responseMessage, jsonGenerator, typeSerializer); + + jsonGenerator.writeStringField(SerTokens.TOKEN_REQUEST, responseMessage.getRequestId() != null ? responseMessage.getRequestId().toString() : null); + jsonGenerator.writeFieldName(SerTokens.TOKEN_STATUS); + + GraphSONUtil.writeStartObject(responseMessage, jsonGenerator, typeSerializer); + jsonGenerator.writeStringField(SerTokens.TOKEN_MESSAGE, responseMessage.getStatus().getMessage()); + jsonGenerator.writeNumberField(SerTokens.TOKEN_CODE, responseMessage.getStatus().getCode().getValue()); + jsonGenerator.writeObjectField(SerTokens.TOKEN_ATTRIBUTES, responseMessage.getStatus().getAttributes()); + GraphSONUtil.writeEndObject(responseMessage, jsonGenerator, typeSerializer); + + jsonGenerator.writeFieldName(SerTokens.TOKEN_RESULT); + + GraphSONUtil.writeStartObject(responseMessage, jsonGenerator, typeSerializer); + + if (null == responseMessage.getResult().getData()) + { + jsonGenerator.writeNullField(SerTokens.TOKEN_DATA); + } + else + { + jsonGenerator.writeFieldName(SerTokens.TOKEN_DATA); + Object result = responseMessage.getResult().getData(); + serializerProvider.findTypedValueSerializer(result.getClass(), true, null).serialize(result, jsonGenerator, serializerProvider); + } + + jsonGenerator.writeObjectField(SerTokens.TOKEN_META, responseMessage.getResult().getMeta()); + GraphSONUtil.writeEndObject(responseMessage, jsonGenerator, typeSerializer); + + GraphSONUtil.writeEndObject(responseMessage, jsonGenerator, typeSerializer); + } + } +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerGremlinV2d0.java ---------------------------------------------------------------------- diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerGremlinV2d0.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerGremlinV2d0.java new file mode 100644 index 0000000..3c2fd3f --- /dev/null +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerGremlinV2d0.java @@ -0,0 +1,68 @@ +/* + * 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.driver.ser; + +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONVersion; + +import java.nio.ByteBuffer; + +/** + * Serialize results to JSON with version 2.0.x schema. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public final class GraphSONMessageSerializerGremlinV2d0 extends AbstractGraphSONMessageSerializerV2d0 { + + private static final String MIME_TYPE = SerTokens.MIME_GRAPHSON_V1D0; + + private static byte[] header; + + static { + final ByteBuffer buffer = ByteBuffer.allocate(MIME_TYPE.length() + 1); + buffer.put((byte) MIME_TYPE.length()); + buffer.put(MIME_TYPE.getBytes()); + header = buffer.array(); + } + + public GraphSONMessageSerializerGremlinV2d0() { + super(); + } + + public GraphSONMessageSerializerGremlinV2d0(final GraphSONMapper mapper) { + super(mapper); + } + + @Override + public String[] mimeTypesSupported() { + return new String[]{MIME_TYPE}; + } + + @Override + byte[] obtainHeader() { + return header; + } + + @Override + GraphSONMapper.Builder configureBuilder(final GraphSONMapper.Builder builder) { + return builder.version(GraphSONVersion.V2_0) + .addCustomModule(new GremlinServerModule()) + .typeInfo(GraphSONMapper.TypeInfo.PARTIAL_TYPES); + } +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0.java ---------------------------------------------------------------------- diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0.java new file mode 100644 index 0000000..85d1c5b --- /dev/null +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0.java @@ -0,0 +1,124 @@ +/* + * 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.driver.ser; + +import org.apache.tinkerpop.gremlin.driver.message.RequestMessage; +import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage; +import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONVersion; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.ByteBuffer; +import java.util.Map; +import java.util.UUID; + +/** + * Serialize results to JSON with version 2.0.x schema. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public final class GraphSONMessageSerializerV2d0 extends AbstractGraphSONMessageSerializerV2d0 implements MessageTextSerializer { + private static final Logger logger = LoggerFactory.getLogger(GraphSONMessageSerializerV2d0.class); + private static final String MIME_TYPE = SerTokens.MIME_JSON; + + private static byte[] header; + + static { + final ByteBuffer buffer = ByteBuffer.allocate(MIME_TYPE.length() + 1); + buffer.put((byte) MIME_TYPE.length()); + buffer.put(MIME_TYPE.getBytes()); + header = buffer.array(); + } + + public GraphSONMessageSerializerV2d0() { + super(); + } + + public GraphSONMessageSerializerV2d0(final GraphSONMapper mapper) { + super(mapper); + } + + @Override + public String[] mimeTypesSupported() { + return new String[]{MIME_TYPE}; + } + + @Override + GraphSONMapper.Builder configureBuilder(final GraphSONMapper.Builder builder) { + return builder.version(GraphSONVersion.V2_0) + .addCustomModule(new GremlinServerModule()) + .typeInfo(GraphSONMapper.TypeInfo.PARTIAL_TYPES); + } + + @Override + byte[] obtainHeader() { + return header; + } + + @Override + public ResponseMessage deserializeResponse(final String msg) throws SerializationException { + try { + final Map<String, Object> responseData = mapper.readValue(msg, mapTypeReference); + final Map<String, Object> status = (Map<String, Object>) responseData.get(SerTokens.TOKEN_STATUS); + final Map<String, Object> result = (Map<String, Object>) responseData.get(SerTokens.TOKEN_RESULT); + return ResponseMessage.build(UUID.fromString(responseData.get(SerTokens.TOKEN_REQUEST).toString())) + .code(ResponseStatusCode.getFromValue((Integer) status.get(SerTokens.TOKEN_CODE))) + .statusMessage(status.get(SerTokens.TOKEN_MESSAGE).toString()) + .statusAttributes((Map<String, Object>) status.get(SerTokens.TOKEN_ATTRIBUTES)) + .result(result.get(SerTokens.TOKEN_DATA)) + .responseMetaData((Map<String, Object>) result.get(SerTokens.TOKEN_META)) + .create(); + } catch (Exception ex) { + logger.warn("Response [{}] could not be deserialized by {}.", msg, AbstractGraphSONMessageSerializerV2d0.class.getName()); + throw new SerializationException(ex); + } + } + + @Override + public String serializeResponseAsString(final ResponseMessage responseMessage) throws SerializationException { + try { + return mapper.writeValueAsString(responseMessage); + } catch (Exception ex) { + logger.warn("Response [{}] could not be serialized by {}.", responseMessage.toString(), AbstractGraphSONMessageSerializerV2d0.class.getName()); + throw new SerializationException(ex); + } + } + + @Override + public RequestMessage deserializeRequest(final String msg) throws SerializationException { + try { + return mapper.readValue(msg, RequestMessage.class); + } catch (Exception ex) { + logger.warn("Request [{}] could not be deserialized by {}.", msg, AbstractGraphSONMessageSerializerV2d0.class.getName()); + throw new SerializationException(ex); + } + } + + @Override + public String serializeRequestAsString(final RequestMessage requestMessage) throws SerializationException { + try { + return mapper.writeValueAsString(requestMessage); + } catch (Exception ex) { + logger.warn("Request [{}] could not be serialized by {}.", requestMessage.toString(), AbstractGraphSONMessageSerializerV2d0.class.getName()); + throw new SerializationException(ex); + } + } +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerGremlinV2d0Test.java ---------------------------------------------------------------------- diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerGremlinV2d0Test.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerGremlinV2d0Test.java new file mode 100644 index 0000000..7f21586 --- /dev/null +++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerGremlinV2d0Test.java @@ -0,0 +1,321 @@ +/* + * 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.driver.ser; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.UnpooledByteBufAllocator; +import org.apache.tinkerpop.gremlin.driver.MessageSerializer; +import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage; +import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.structure.*; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTokens; +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.util.StdDateFormat; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.*; + +/** + * Serializer tests that cover non-lossy serialization/deserialization methods for GraphSONMessage. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class GraphSONMessageSerializerGremlinV2d0Test { + private UUID requestId = UUID.fromString("6457272A-4018-4538-B9AE-08DD5DDC0AA1"); + private ResponseMessage.Builder responseMessageBuilder = ResponseMessage.build(requestId); + private static ByteBufAllocator allocator = UnpooledByteBufAllocator.DEFAULT; + + public MessageSerializer serializer = new GraphSONMessageSerializerGremlinV2d0(); + + @Test + public void shouldSerializeIterable() throws Exception { + final ArrayList<Integer> list = new ArrayList<>(); + list.add(1); + list.add(100); + + final ResponseMessage response = convert(list); + assertCommon(response); + + final List<Integer> deserializedFunList = (List<Integer>) response.getResult().getData(); + assertEquals(2, deserializedFunList.size()); + assertEquals(new Integer(1), deserializedFunList.get(0)); + assertEquals(new Integer(100), deserializedFunList.get(1)); + } + + @Test + public void shouldSerializeIterableWithNull() throws Exception { + final ArrayList<Integer> list = new ArrayList<>(); + list.add(1); + list.add(null); + list.add(100); + + final ResponseMessage response = convert(list); + assertCommon(response); + + final List<Integer> deserializedFunList = (List<Integer>) response.getResult().getData(); + assertEquals(3, deserializedFunList.size()); + assertEquals(new Integer(1), deserializedFunList.get(0)); + assertNull(deserializedFunList.get(1)); + assertEquals(new Integer(100), deserializedFunList.get(2)); + } + + @Test + public void shouldSerializeMap() throws Exception { + final Map<String, Object> map = new HashMap<>(); + final Map<String, String> innerMap = new HashMap<>(); + innerMap.put("a", "b"); + + map.put("x", 1); + map.put("y", "some"); + map.put("z", innerMap); + + final ResponseMessage response = convert(map); + assertCommon(response); + + final Map<String, Object> deserializedMap = (Map<String, Object>) response.getResult().getData(); + assertEquals(3, deserializedMap.size()); + assertEquals(1, deserializedMap.get("x")); + assertEquals("some", deserializedMap.get("y")); + + final Map<String, String> deserializedInnerMap = (Map<String, String>) deserializedMap.get("z"); + assertEquals(1, deserializedInnerMap.size()); + assertEquals("b", deserializedInnerMap.get("a")); + } + + @Test + public void shouldSerializeMapEntries() throws Exception { + final Graph graph = TinkerGraph.open(); + final Vertex v1 = graph.addVertex(); + final Date d = new Date(); + + final Map<Object, Object> map = new HashMap<>(); + map.put("x", 1); + map.put(v1, 100); + map.put(d, "test"); + + final ResponseMessage response = convert(IteratorUtils.asList(map.entrySet())); + assertCommon(response); + + final List<Map<String, Object>> deserializedEntries = (List<Map<String, Object>>) response.getResult().getData(); + assertEquals(3, deserializedEntries.size()); + deserializedEntries.forEach(e -> { + if (e.containsKey("x")) + assertEquals(1, e.get("x")); + else if (e.containsKey(v1.id().toString())) + assertEquals(100, e.get(v1.id().toString())); + else if (e.containsKey(StdDateFormat.instance.format(d))) + assertEquals("test", e.get(StdDateFormat.instance.format(d))); + else + fail("Map entries contains a key that is not part of what was serialized"); + }); + } + + @Test + public void shouldSerializeEdge() throws Exception { + final Graph graph = TinkerGraph.open(); + final Vertex v1 = graph.addVertex(); + final Vertex v2 = graph.addVertex(); + final Edge e = v1.addEdge("test", v2); + e.property("abc", 123); + + final Iterable<Edge> iterable = IteratorUtils.list(graph.edges()); + + final ResponseMessage response = convert(iterable); + assertCommon(response); + + final List<Map<String, Object>> edgeList = (List<Map<String, Object>>) response.getResult().getData(); + assertEquals(1, edgeList.size()); + + final Map<String, Object> deserializedEdge = edgeList.get(0); + assertEquals(e.id(), deserializedEdge.get(GraphSONTokens.ID)); + assertEquals(v1.id(), deserializedEdge.get(GraphSONTokens.OUT)); + assertEquals(v2.id(), deserializedEdge.get(GraphSONTokens.IN)); + assertEquals(v1.label(), deserializedEdge.get(GraphSONTokens.OUT_LABEL)); + assertEquals(v2.label(), deserializedEdge.get(GraphSONTokens.IN_LABEL)); + assertEquals(e.label(), deserializedEdge.get(GraphSONTokens.LABEL)); + assertEquals(GraphSONTokens.EDGE, deserializedEdge.get(GraphSONTokens.TYPE)); + + final Map<String, Object> properties = (Map<String, Object>) deserializedEdge.get(GraphSONTokens.PROPERTIES); + assertNotNull(properties); + assertEquals(123, properties.get("abc")); + + } + + @Test + public void shouldSerializeEdgeProperty() throws Exception { + final Graph graph = TinkerGraph.open(); + final Vertex v1 = graph.addVertex(); + final Vertex v2 = graph.addVertex(); + final Edge e = v1.addEdge("test", v2); + e.property("abc", 123); + + final Iterable<Property<Object>> iterable = IteratorUtils.list(e.properties("abc")); + final ResponseMessage response = convert(iterable); + assertCommon(response); + + final List<Map<String, Object>> propertyList = (List<Map<String, Object>>) response.getResult().getData(); + assertEquals(1, propertyList.size()); + assertEquals(123, propertyList.get(0).get("value")); + } + + @Test + public void shouldSerializeVertexWithEmbeddedMap() throws Exception { + final Graph graph = TinkerGraph.open(); + final Vertex v = graph.addVertex(); + final Map<String, Object> map = new HashMap<>(); + map.put("x", 500); + map.put("y", "some"); + + final ArrayList<Object> friends = new ArrayList<>(); + friends.add("x"); + friends.add(5); + friends.add(map); + + v.property(VertexProperty.Cardinality.single, "friends", friends); + + final List list = IteratorUtils.list(graph.vertices()); + + final ResponseMessage response = convert(list); + assertCommon(response); + + final List<Map<String, Object>> vertexList = (List<Map<String, Object>>) response.getResult().getData(); + assertEquals(1, vertexList.size()); + + final Map<String, Object> deserializedVertex = vertexList.get(0); + assertEquals(v.id(), deserializedVertex.get(GraphSONTokens.ID)); + assertEquals(Vertex.DEFAULT_LABEL, deserializedVertex.get(GraphSONTokens.LABEL)); + + final Map<String, Object> properties = ((Map<String, Object>) deserializedVertex.get(GraphSONTokens.PROPERTIES)); + assertEquals(1, properties.size()); + + final List<Map<String,Object>> friendsProperties = (List<Map<String,Object>>) properties.get("friends"); + assertEquals(1, friendsProperties.size()); + + final List<Object> deserializedInnerList = (List<Object>) friendsProperties.get(0).get(GraphSONTokens.VALUE); + assertEquals(3, deserializedInnerList.size()); + assertEquals("x", deserializedInnerList.get(0)); + assertEquals(5, deserializedInnerList.get(1)); + + final Map<String, Object> deserializedInnerInnerMap = (Map<String, Object>) deserializedInnerList.get(2); + assertEquals(2, deserializedInnerInnerMap.size()); + assertEquals(500, deserializedInnerInnerMap.get("x")); + assertEquals("some", deserializedInnerInnerMap.get("y")); + } + + @Test + public void shouldSerializeToJsonMapWithElementForKey() throws Exception { + final TinkerGraph graph = TinkerFactory.createClassic(); + final GraphTraversalSource g = graph.traversal(); + final Map<Vertex, Integer> map = new HashMap<>(); + map.put(g.V().has("name", "marko").next(), 1000); + + final ResponseMessage response = convert(map); + assertCommon(response); + + final Map<String, Integer> deserializedMap = (Map<String, Integer>) response.getResult().getData(); + assertEquals(1, deserializedMap.size()); + + // with no embedded types the key (which is a vertex) simply serializes out to an id + // {"result":{"1":1000},"code":200,"requestId":"2d62161b-9544-4f39-af44-62ec49f9a595","type":0} + assertEquals(new Integer(1000), deserializedMap.get("1")); + } + + @Test + public void shouldSerializeToJsonTree() throws Exception { + final TinkerGraph graph = TinkerFactory.createClassic(); + final GraphTraversalSource g = graph.traversal(); + final Map t = g.V(1).out().properties("name").tree().next(); + + final ResponseMessage response = convert(t); + assertCommon(response); + + final Map<String, Map<String, Map>> deserializedMap = (Map<String, Map<String, Map>>) response.getResult().getData(); + + assertEquals(1, deserializedMap.size()); + + //check the first object and it's properties + Map<String,Object> vertex = deserializedMap.get("1").get("key"); + Map<String,List<Map>> vertexProperties = (Map<String, List<Map>>)vertex.get("properties"); + assertEquals(1, (int)vertex.get("id")); + assertEquals("marko", vertexProperties.get("name").get(0).get("value")); + + //check objects tree structure + //check Vertex property + Map<String, Map<String, Map>> subTreeMap = deserializedMap.get("1").get("value"); + Map<String, Map<String, Map>> subTreeMap2 = subTreeMap.get("2").get("value"); + Map<String, String> vertexPropertiesDeep = subTreeMap2.get("3").get("key"); + assertEquals("vadas", vertexPropertiesDeep.get("value")); + assertEquals("name", vertexPropertiesDeep.get("label")); + + // check subitem + Map<String,Object> vertex2 = subTreeMap.get("3").get("key"); + Map<String,List<Map>> vertexProperties2 = (Map<String, List<Map>>)vertex2.get("properties"); + + assertEquals("lop", vertexProperties2.get("name").get(0).get("value")); + } + + @Test + public void shouldSerializeFullResponseMessage() throws Exception { + final UUID id = UUID.randomUUID(); + + final Map<String, Object> metaData = new HashMap<>(); + metaData.put("test", "this"); + metaData.put("one", 1); + + final Map<String, Object> attributes = new HashMap<>(); + attributes.put("test", "that"); + attributes.put("two", 2); + + final ResponseMessage response = ResponseMessage.build(id) + .responseMetaData(metaData) + .code(ResponseStatusCode.SUCCESS) + .result("some-result") + .statusAttributes(attributes) + .statusMessage("worked") + .create(); + + final ByteBuf bb = serializer.serializeResponseAsBinary(response, allocator); + final ResponseMessage deserialized = serializer.deserializeResponse(bb); + + assertEquals(id, deserialized.getRequestId()); + assertEquals("this", deserialized.getResult().getMeta().get("test")); + assertEquals(1, deserialized.getResult().getMeta().get("one")); + assertEquals("some-result", deserialized.getResult().getData()); + assertEquals("that", deserialized.getStatus().getAttributes().get("test")); + assertEquals(2, deserialized.getStatus().getAttributes().get("two")); + assertEquals(ResponseStatusCode.SUCCESS.getValue(), deserialized.getStatus().getCode().getValue()); + assertEquals("worked", deserialized.getStatus().getMessage()); + } + + private void assertCommon(final ResponseMessage response) { + assertEquals(requestId, response.getRequestId()); + assertEquals(ResponseStatusCode.SUCCESS, response.getStatus().getCode()); + } + + private ResponseMessage convert(final Object toSerialize) throws SerializationException { + final ByteBuf bb = serializer.serializeResponseAsBinary(responseMessageBuilder.result(toSerialize).create(), allocator); + return serializer.deserializeResponse(bb); + } +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0Test.java ---------------------------------------------------------------------- diff --git a/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0Test.java b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0Test.java new file mode 100644 index 0000000..0e07d44 --- /dev/null +++ b/gremlin-driver/src/test/java/org/apache/tinkerpop/gremlin/driver/ser/GraphSONMessageSerializerV2d0Test.java @@ -0,0 +1,474 @@ +/* + * 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.driver.ser; + +import org.apache.tinkerpop.gremlin.driver.message.RequestMessage; +import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage; +import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; +import org.apache.tinkerpop.gremlin.structure.*; +import org.apache.tinkerpop.gremlin.structure.io.AbstractIoRegistry; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONIo; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTokens; +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.core.JsonGenerationException; +import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator; +import org.apache.tinkerpop.shaded.jackson.databind.JsonNode; +import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper; +import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider; +import org.apache.tinkerpop.shaded.jackson.databind.module.SimpleModule; +import org.apache.tinkerpop.shaded.jackson.databind.node.NullNode; +import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer; +import org.apache.tinkerpop.shaded.jackson.databind.util.StdDateFormat; +import org.junit.Test; + +import java.awt.*; +import java.io.IOException; +import java.util.*; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.*; + +/** + * These tests focus on message serialization and not "result" serialization as test specific to results (e.g. + * vertices, edges, annotated values, etc.) are handled in the IO packages. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class GraphSONMessageSerializerV2d0Test { + + public static final GraphSONMessageSerializerV2d0 SERIALIZER = new GraphSONMessageSerializerV2d0(); + private static final RequestMessage msg = RequestMessage.build("op") + .overrideRequestId(UUID.fromString("2D62161B-9544-4F39-AF44-62EC49F9A595")).create(); + private static final ObjectMapper mapper = new ObjectMapper(); + + @Test + public void shouldConfigureIoRegistry() throws Exception { + final GraphSONMessageSerializerV1d0 serializer = new GraphSONMessageSerializerV1d0(); + final Map<String, Object> config = new HashMap<String, Object>() {{ + put(GryoMessageSerializerV1d0.TOKEN_IO_REGISTRIES, Arrays.asList(ColorIoRegistry.class.getName())); + }}; + + serializer.configure(config, null); + + final ResponseMessage toSerialize = ResponseMessage.build(UUID.fromString("2D62161B-9544-4F39-AF44-62EC49F9A595")) + .result(Color.RED).create(); + final String results = serializer.serializeResponseAsString(toSerialize); + final JsonNode json = mapper.readTree(results); + assertNotNull(json); + assertThat(json.get(SerTokens.TOKEN_RESULT).get(SerTokens.TOKEN_DATA).booleanValue(), is(true)); + } + + @Test + public void shouldSerializeToJsonNullResultReturnsNull() throws Exception { + final ResponseMessage message = ResponseMessage.build(msg).create(); + final String results = SERIALIZER.serializeResponseAsString(message); + final JsonNode json = mapper.readTree(results); + assertNotNull(json); + assertEquals(msg.getRequestId().toString(), json.path(SerTokens.TOKEN_REQUEST).asText()); + assertEquals(NullNode.getInstance(), json.get(SerTokens.TOKEN_RESULT).get(SerTokens.TOKEN_DATA)); + } + + @Test + public void shouldSerializeToJsonIterable() throws Exception { + final ArrayList<FunObject> funList = new ArrayList<>(); + funList.add(new FunObject("x")); + funList.add(new FunObject("y")); + + final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList).create()); + final JsonNode json = mapper.readTree(results); + + assertNotNull(json); + assertEquals(msg.getRequestId().toString(), json.get(SerTokens.TOKEN_REQUEST).asText()); + final JsonNode converted = json.get(SerTokens.TOKEN_RESULT).get(SerTokens.TOKEN_DATA); + + assertEquals(2, converted.size()); + + assertEquals("x", converted.get(0).asText()); + assertEquals("y", converted.get(1).asText()); + } + + @Test + public void shouldSerializeToJsonIterator() throws Exception { + final ArrayList<FunObject> funList = new ArrayList<>(); + funList.add(new FunObject("x")); + funList.add(new FunObject("y")); + + final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList).create()); + final JsonNode json = mapper.readTree(results); + + assertNotNull(json); + assertEquals(msg.getRequestId().toString(), json.get(SerTokens.TOKEN_REQUEST).asText()); + final JsonNode converted = json.get(SerTokens.TOKEN_RESULT).get(SerTokens.TOKEN_DATA); + + assertEquals(2, converted.size()); + + assertEquals("x", converted.get(0).asText()); + assertEquals("y", converted.get(1).asText()); + } + + @Test + public void shouldSerializeToJsonIteratorNullElement() throws Exception { + + final ArrayList<FunObject> funList = new ArrayList<>(); + funList.add(new FunObject("x")); + funList.add(null); + funList.add(new FunObject("y")); + + final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(funList.iterator()).create()); + final JsonNode json = mapper.readTree(results); + + assertNotNull(json); + assertEquals(msg.getRequestId().toString(), json.get(SerTokens.TOKEN_REQUEST).asText()); + final JsonNode converted = json.get(SerTokens.TOKEN_RESULT).get(SerTokens.TOKEN_DATA); + + assertEquals(3, converted.size()); + + assertEquals("x", converted.get(0).asText()); + assertEquals(NullNode.getInstance(), converted.get(1)); + assertEquals("y", converted.get(2).asText()); + } + + @Test + public void shouldSerializeToJsonMap() throws Exception { + final Map<String, Object> map = new HashMap<>(); + final Map<String, String> innerMap = new HashMap<>(); + innerMap.put("a", "b"); + + map.put("x", new FunObject("x")); + map.put("y", "some"); + map.put("z", innerMap); + + final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(map).create()); + final JsonNode json = mapper.readTree(results); + + assertNotNull(json); + assertEquals(msg.getRequestId().toString(), json.get(SerTokens.TOKEN_REQUEST).asText()); + final JsonNode jsonObject = json.get(SerTokens.TOKEN_RESULT).get(SerTokens.TOKEN_DATA); + + assertNotNull(jsonObject); + assertEquals("some", jsonObject.get("y").asText()); + assertEquals("x", jsonObject.get("x").asText()); + + final JsonNode innerJsonObject = jsonObject.get("z"); + assertNotNull(innerJsonObject); + assertEquals("b", innerJsonObject.get("a").asText()); + } + + @Test + public void shouldShouldSerializeMapEntries() throws Exception { + final Graph graph = TinkerGraph.open(); + final Vertex v1 = graph.addVertex(); + final Date d = new Date(); + + final Map<Object, Object> map = new HashMap<>(); + map.put("x", 1); + map.put(v1, 100); + map.put(d, "test"); + + final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(IteratorUtils.asList(map)).create()); + final JsonNode json = mapper.readTree(results); + + assertNotNull(json); + assertEquals(msg.getRequestId().toString(), json.get(SerTokens.TOKEN_REQUEST).asText()); + final JsonNode jsonObject = json.get(SerTokens.TOKEN_RESULT).get(SerTokens.TOKEN_DATA); + jsonObject.elements().forEachRemaining(e -> { + if (e.has("x")) + assertEquals(1, e.get("x").asInt()); + else if (e.has(v1.id().toString())) + assertEquals(100, e.get(v1.id().toString()).asInt()); + else if (e.has(StdDateFormat.instance.format(d))) + assertEquals("test", e.get(StdDateFormat.instance.format(d)).asText()); + else + fail("Map entries contains a key that is not part of what was serialized"); + }); + } + + @Test + public void shouldSerializeEdge() throws Exception { + final Graph g = TinkerGraph.open(); + final Vertex v1 = g.addVertex(); + final Vertex v2 = g.addVertex(); + final Edge e = v1.addEdge("test", v2); + e.property("abc", 123); + + final Iterable<Edge> iterable = IteratorUtils.list(g.edges()); + final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create()); + + final JsonNode json = mapper.readTree(results); + + assertNotNull(json); + assertEquals(msg.getRequestId().toString(), json.get(SerTokens.TOKEN_REQUEST).asText()); + final JsonNode converted = json.get(SerTokens.TOKEN_RESULT).get(SerTokens.TOKEN_DATA); + + assertNotNull(converted); + assertEquals(1, converted.size()); + + final JsonNode edgeAsJson = converted.get(0); + assertNotNull(edgeAsJson); + + assertEquals(((Long) e.id()).longValue(), edgeAsJson.get(GraphSONTokens.ID).get(1).asLong()); // lossy + assertEquals(((Long) v1.id()).longValue(), edgeAsJson.get(GraphSONTokens.OUT).get(1).asLong());// lossy + assertEquals(((Long) v2.id()).longValue(), edgeAsJson.get(GraphSONTokens.IN).get(1).asLong()); // lossy + assertEquals(e.label(), edgeAsJson.get(GraphSONTokens.LABEL).asText()); + assertEquals(GraphSONTokens.EDGE, edgeAsJson.get(GraphSONTokens.TYPE).asText()); + + final JsonNode properties = edgeAsJson.get(GraphSONTokens.PROPERTIES); + assertNotNull(properties); + assertEquals(123, properties.get("abc").asInt()); + } + + @Test + public void shouldSerializeEdgeProperty() throws Exception { + final Graph g = TinkerGraph.open(); + final Vertex v1 = g.addVertex(); + final Vertex v2 = g.addVertex(); + final Edge e = v1.addEdge("test", v2); + e.property("abc", 123); + + final Iterable<Property<Object>> iterable = IteratorUtils.list(e.properties("abc")); + final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create()); + + final JsonNode json = mapper.readTree(results); + + assertNotNull(json); + assertEquals(msg.getRequestId().toString(), json.get(SerTokens.TOKEN_REQUEST).asText()); + final JsonNode converted = json.get(SerTokens.TOKEN_RESULT).get(SerTokens.TOKEN_DATA); + + assertNotNull(converted); + assertEquals(1, converted.size()); + + final JsonNode propertyAsJson = converted.get(0); + assertNotNull(propertyAsJson); + + assertEquals(123, propertyAsJson.get("value").asInt()); + } + + @Test + public void shouldSerializeToJsonIteratorWithEmbeddedMap() throws Exception { + final Graph g = TinkerGraph.open(); + final Vertex v = g.addVertex(); + final Map<String, Object> map = new HashMap<>(); + map.put("x", 500); + map.put("y", "some"); + + final ArrayList<Object> friends = new ArrayList<>(); + friends.add("x"); + friends.add(5); + friends.add(map); + + v.property(VertexProperty.Cardinality.single, "friends", friends); + + final Iterable iterable = IteratorUtils.list(g.vertices()); + final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(iterable).create()); + final JsonNode json = mapper.readTree(results); + + assertNotNull(json); + assertEquals(msg.getRequestId().toString(), json.get(SerTokens.TOKEN_REQUEST).asText()); + final JsonNode converted = json.get(SerTokens.TOKEN_RESULT).get(SerTokens.TOKEN_DATA); + + assertNotNull(converted); + assertEquals(1, converted.size()); + + final JsonNode vertexAsJson = converted.get(0); + assertNotNull(vertexAsJson); + + final JsonNode properties = vertexAsJson.get(GraphSONTokens.PROPERTIES); + assertNotNull(properties); + assertEquals(1, properties.size()); + + final JsonNode friendProperties = properties.get("friends"); + assertEquals(1, friendProperties.size()); + final JsonNode friendsProperty = friendProperties.get(0); + assertNotNull(friendsProperty); + assertEquals(3, friends.size()); + + final String object1 = friendsProperty.get(GraphSONTokens.VALUE).get(0).asText(); + assertEquals("x", object1); + + final int object2 = friendsProperty.get(GraphSONTokens.VALUE).get(1).asInt(); + assertEquals(5, object2); + + final JsonNode object3 = friendsProperty.get(GraphSONTokens.VALUE).get(2); + assertEquals(500, object3.get("x").asInt()); + assertEquals("some", object3.get("y").asText()); + } + + @Test + public void shouldSerializeToJsonMapWithElementForKey() throws Exception { + final TinkerGraph graph = TinkerFactory.createClassic(); + final GraphTraversalSource g = graph.traversal(); + final Map<Vertex, Integer> map = new HashMap<>(); + map.put(g.V().has("name", "marko").next(), 1000); + + final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(map).create()); + final JsonNode json = mapper.readTree(results); + + assertNotNull(json); + assertEquals(msg.getRequestId().toString(), json.get(SerTokens.TOKEN_REQUEST).asText()); + final JsonNode converted = json.get(SerTokens.TOKEN_RESULT).get(SerTokens.TOKEN_DATA); + + assertNotNull(converted); + + // with no embedded types the key (which is a vertex) simply serializes out to an id + // {"result":{"1":1000},"code":200,"requestId":"2d62161b-9544-4f39-af44-62ec49f9a595","type":0} + assertEquals(1000, converted.get("1").asInt()); + } + + @Test + public void shouldDeserializeRequestNicelyWithNoArgs() throws Exception { + final UUID request = UUID.fromString("011CFEE9-F640-4844-AC93-034448AC0E80"); + final RequestMessage m = SERIALIZER.deserializeRequest(String.format("{\"requestId\":\"%s\",\"op\":\"eval\"}", request)); + assertEquals(request, m.getRequestId()); + assertEquals("eval", m.getOp()); + assertNotNull(m.getArgs()); + assertEquals(0, m.getArgs().size()); + } + + @Test + public void shouldDeserializeRequestNicelyWithArgs() throws Exception { + final UUID request = UUID.fromString("011CFEE9-F640-4844-AC93-034448AC0E80"); + final RequestMessage m = SERIALIZER.deserializeRequest(String.format("{\"requestId\":\"%s\",\"op\":\"eval\",\"args\":{\"x\":\"y\"}}", request)); + assertEquals(request, m.getRequestId()); + assertEquals("eval", m.getOp()); + assertNotNull(m.getArgs()); + assertEquals("y", m.getArgs().get("x")); + } + + @Test(expected = SerializationException.class) + public void shouldDeserializeRequestParseMessage() throws Exception { + SERIALIZER.deserializeRequest("{\"requestId\":\"%s\",\"op\":\"eval\",\"args\":{\"x\":\"y\"}}"); + } + + @Test + public void shouldSerializeFullResponseMessage() throws Exception { + final UUID id = UUID.randomUUID(); + + final Map<String, Object> metaData = new HashMap<>(); + metaData.put("test", "this"); + metaData.put("one", 1); + + final Map<String, Object> attributes = new HashMap<>(); + attributes.put("test", "that"); + attributes.put("two", 2); + + final ResponseMessage response = ResponseMessage.build(id) + .responseMetaData(metaData) + .code(ResponseStatusCode.SUCCESS) + .result("some-result") + .statusAttributes(attributes) + .statusMessage("worked") + .create(); + + final String results = SERIALIZER.serializeResponseAsString(response); + final ResponseMessage deserialized = SERIALIZER.deserializeResponse(results); + + assertEquals(id, deserialized.getRequestId()); + assertEquals("this", deserialized.getResult().getMeta().get("test")); + assertEquals(1, deserialized.getResult().getMeta().get("one")); + assertEquals("some-result", deserialized.getResult().getData()); + assertEquals("that", deserialized.getStatus().getAttributes().get("test")); + assertEquals(2, deserialized.getStatus().getAttributes().get("two")); + assertEquals(ResponseStatusCode.SUCCESS.getValue(), deserialized.getStatus().getCode().getValue()); + assertEquals("worked", deserialized.getStatus().getMessage()); + } + + @Test + public void shouldSerializeToJsonTree() throws Exception { + final TinkerGraph graph = TinkerFactory.createClassic(); + final GraphTraversalSource g = graph.traversal(); + final Tree t = g.V(1).out().properties("name").tree().next(); + + + final String results = SERIALIZER.serializeResponseAsString(ResponseMessage.build(msg).result(t).create()); + + final JsonNode json = mapper.readTree(results); + + assertNotNull(json); + assertEquals(msg.getRequestId().toString(), json.get(SerTokens.TOKEN_REQUEST).asText()); + final JsonNode converted = json.get(SerTokens.TOKEN_RESULT).get(SerTokens.TOKEN_DATA); + assertNotNull(converted); + + //check the first object and it's properties + assertEquals(1, converted.get("1").get("key").get("id").asInt()); + assertEquals("marko", converted.get("1").get("key").get("properties").get("name").get(0).get("value").asText()); + + //check objects tree structure + //check Vertex property + assertEquals("vadas", converted.get("1") + .get("value") + .get("2") + .get("value") + .get("3").get("key").get("value").asText()); + assertEquals("name", converted.get("1") + .get("value") + .get("2") + .get("value") + .get("3").get("key").get("label").asText()); + + // check subitem + assertEquals("lop", converted.get("1") + .get("value") + .get("3") + .get("key") + .get("properties").get("name").get(0).get("value").asText()); + } + + private static class FunObject { + private String val; + + public FunObject(String val) { + this.val = val; + } + + public String toString() { + return this.val; + } + } + + public static class ColorIoRegistry extends AbstractIoRegistry { + public ColorIoRegistry() { + register(GraphSONIo.class, null, new ColorSimpleModule()); + } + } + + public static class ColorSimpleModule extends SimpleModule { + public ColorSimpleModule() { + super("color-fun"); + addSerializer(Color.class, new ColorSerializer()); + + } + } + + public static class ColorSerializer extends StdSerializer<Color> { + public ColorSerializer() { + super(Color.class); + } + + @Override + public void serialize(final Color color, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider) throws IOException, JsonGenerationException { + jsonGenerator.writeBoolean(color.equals(Color.RED)); + } + } +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistryV2d0.java ---------------------------------------------------------------------- diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistryV2d0.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistryV2d0.java new file mode 100644 index 0000000..9923905 --- /dev/null +++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerIoRegistryV2d0.java @@ -0,0 +1,227 @@ +/* + * 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.tinkergraph.structure; + +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.io.AbstractIoRegistry; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONIo; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTokens; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONUtil; +import org.apache.tinkerpop.gremlin.structure.io.graphson.TinkerPopJacksonModule; +import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoIo; +import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoReader; +import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoWriter; +import org.apache.tinkerpop.gremlin.structure.util.Attachable; +import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge; +import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex; +import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator; +import org.apache.tinkerpop.shaded.jackson.core.JsonParser; +import org.apache.tinkerpop.shaded.jackson.core.JsonProcessingException; +import org.apache.tinkerpop.shaded.jackson.databind.DeserializationContext; +import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider; +import org.apache.tinkerpop.shaded.jackson.databind.deser.std.StdDeserializer; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer; +import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer; +import org.apache.tinkerpop.shaded.kryo.Kryo; +import org.apache.tinkerpop.shaded.kryo.Serializer; +import org.apache.tinkerpop.shaded.kryo.io.Input; +import org.apache.tinkerpop.shaded.kryo.io.Output; +import org.javatuples.Pair; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * An implementation of the {@link org.apache.tinkerpop.gremlin.structure.io.IoRegistry} interface that provides serializers with custom configurations for + * implementation specific classes that might need to be serialized. This registry allows a {@link org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph} to + * be serialized directly which is useful for moving small graphs around on the network. + * <p/> + * Most providers need not implement this kind of custom serializer as they will deal with much larger graphs that + * wouldn't be practical to serialize in this fashion. This is a bit of a special case for TinkerGraph given its + * in-memory status. Typical implementations would create serializers for a complex vertex identifier or a + * custom data class like a "geographic point". + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public final class TinkerIoRegistryV2d0 extends AbstractIoRegistry { + + private static final TinkerIoRegistryV2d0 INSTANCE = new TinkerIoRegistryV2d0(); + + private TinkerIoRegistryV2d0() { + register(GryoIo.class, TinkerGraph.class, new TinkerGraphGryoSerializer()); + register(GraphSONIo.class, null, new TinkerModuleV2d0()); + } + + public static TinkerIoRegistryV2d0 getInstance() { + return INSTANCE; + } + + /** + * Provides a method to serialize an entire {@link org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph} into itself for Gryo. This is useful when + * shipping small graphs around through Gremlin Server. Reuses the existing Kryo instance for serialization. + */ + final static class TinkerGraphGryoSerializer extends Serializer<TinkerGraph> { + @Override + public void write(final Kryo kryo, final Output output, final TinkerGraph graph) { + try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) { + GryoWriter.build().mapper(() -> kryo).create().writeGraph(stream, graph); + final byte[] bytes = stream.toByteArray(); + output.writeInt(bytes.length); + output.write(bytes); + } catch (Exception io) { + throw new RuntimeException(io); + } + } + + @Override + public TinkerGraph read(final Kryo kryo, final Input input, final Class<TinkerGraph> tinkerGraphClass) { + final TinkerGraph graph = TinkerGraph.open(); + final int len = input.readInt(); + final byte[] bytes = input.readBytes(len); + try (final ByteArrayInputStream stream = new ByteArrayInputStream(bytes)) { + GryoReader.build().mapper(() -> kryo).create().readGraph(stream, graph); + } catch (Exception io) { + throw new RuntimeException(io); + } + + return graph; + } + } + + /** + * Provides a method to serialize an entire {@link org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph} into itself for GraphSON. This is useful when + * shipping small graphs around through Gremlin Server. + */ + final static class TinkerModuleV2d0 extends TinkerPopJacksonModule { + public TinkerModuleV2d0() { + super("tinkergraph-2.0"); + addSerializer(TinkerGraph.class, new TinkerGraphJacksonSerializer()); + addDeserializer(TinkerGraph.class, new TinkerGraphJacksonDeserializer()); + } + } + + /** + * Serializes the graph into an edge list format. Edge list is a better choices than adjacency list (which is + * typically standard from the {@link org.apache.tinkerpop.gremlin.structure.io.GraphReader} and {@link org.apache.tinkerpop.gremlin.structure.io.GraphWriter} perspective) in this case because + * the use case for this isn't around massive graphs. The use case is for "small" subgraphs that are being + * shipped over the wire from Gremlin Server. Edge list format is a bit easier for non-JVM languages to work + * with as a format and doesn't require a cache for loading (as vertex labels are not serialized in adjacency + * list). + */ + final static class TinkerGraphJacksonSerializer extends StdSerializer<TinkerGraph> { + + public TinkerGraphJacksonSerializer() { + super(TinkerGraph.class); + } + + @Override + public void serialize(final TinkerGraph graph, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) + throws IOException { + ser(graph, jsonGenerator, serializerProvider, null); + } + + @Override + public void serializeWithType(final TinkerGraph graph, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException { + ser(graph, jsonGenerator, serializerProvider, typeSerializer); + } + + private void ser(final TinkerGraph graph, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, final TypeSerializer typeSerializer) throws IOException { + GraphSONUtil.writeStartObject(graph, jsonGenerator, typeSerializer); + jsonGenerator.writeFieldName(GraphSONTokens.VERTICES); + GraphSONUtil.writeStartArray(graph, jsonGenerator, typeSerializer); + + final Iterator<Vertex> vertices = graph.vertices(); + while (vertices.hasNext()) { + serializerProvider.defaultSerializeValue(vertices.next(), jsonGenerator); + } + + GraphSONUtil.writeEndArray(graph, jsonGenerator, typeSerializer); + jsonGenerator.writeFieldName(GraphSONTokens.EDGES); + GraphSONUtil.writeStartArray(graph, jsonGenerator, typeSerializer); + + final Iterator<Edge> edges = graph.edges(); + while (edges.hasNext()) { + serializerProvider.defaultSerializeValue(edges.next(), jsonGenerator); + } + + GraphSONUtil.writeEndArray(graph, jsonGenerator, typeSerializer); + GraphSONUtil.writeEndObject(graph, jsonGenerator, typeSerializer); + } + } + + /** + * Deserializes the edge list format. + */ + static class TinkerGraphJacksonDeserializer extends StdDeserializer<TinkerGraph> { + public TinkerGraphJacksonDeserializer() { + super(TinkerGraph.class); + } + + @Override + public TinkerGraph deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { + final TinkerGraph graph = TinkerGraph.open(); + + final List<Map<String, Object>> edges; + final List<Map<String, Object>> vertices; + if (!jsonParser.getCurrentToken().isStructStart()) { + if (!jsonParser.getCurrentName().equals(GraphSONTokens.VERTICES)) + throw new IOException(String.format("Expected a '%s' key", GraphSONTokens.VERTICES)); + + jsonParser.nextToken(); + + vertices = deserializationContext.readValue(jsonParser, List.class); + jsonParser.nextToken(); + + if (!jsonParser.getCurrentName().equals(GraphSONTokens.EDGES)) + throw new IOException(String.format("Expected a '%s' key", GraphSONTokens.EDGES)); + + jsonParser.nextToken(); + edges = (List<Map<String, Object>>) deserializationContext.readValue(jsonParser, List.class); + + } else { + final Map<String, Object> graphData = deserializationContext.readValue(jsonParser, Map.class); + vertices = (List<Map<String,Object>>) graphData.get(GraphSONTokens.VERTICES); + edges = (List<Map<String,Object>>) graphData.get(GraphSONTokens.EDGES); + } + + for (Map<String, Object> vertexData : vertices) { + final DetachedVertex detached = new DetachedVertex(vertexData.get(GraphSONTokens.ID), + vertexData.get(GraphSONTokens.LABEL).toString(), (Map<String,Object>) vertexData.get(GraphSONTokens.PROPERTIES)); + detached.attach(Attachable.Method.getOrCreate(graph)); + } + + for (Map<String, Object> edgeData : edges) { + final DetachedEdge detached = new DetachedEdge(edgeData.get(GraphSONTokens.ID), + edgeData.get(GraphSONTokens.LABEL).toString(), (Map<String,Object>) edgeData.get(GraphSONTokens.PROPERTIES), + Pair.with(edgeData.get(GraphSONTokens.OUT), edgeData.get(GraphSONTokens.OUT_LABEL).toString()), + Pair.with(edgeData.get(GraphSONTokens.IN), edgeData.get(GraphSONTokens.IN_LABEL).toString())); + detached.attach(Attachable.Method.getOrCreate(graph)); + } + + return graph; + } + } +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b44ec666/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/IoDataGenerationTest.java ---------------------------------------------------------------------- diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/IoDataGenerationTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/IoDataGenerationTest.java index 86fc733..b4356b6 100644 --- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/IoDataGenerationTest.java +++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/IoDataGenerationTest.java @@ -23,33 +23,31 @@ import org.apache.tinkerpop.gremlin.AbstractGremlinTest; import org.apache.tinkerpop.gremlin.TestHelper; import org.apache.tinkerpop.gremlin.algorithm.generator.DistributionGenerator; import org.apache.tinkerpop.gremlin.algorithm.generator.PowerLawDistribution; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Graph; -import org.apache.tinkerpop.gremlin.structure.T; -import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.*; import org.apache.tinkerpop.gremlin.structure.io.GraphReader; import org.apache.tinkerpop.gremlin.structure.io.graphml.GraphMLWriter; import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONReader; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONVersion; import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONWriter; import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoReader; import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoWriter; import org.junit.BeforeClass; import org.junit.Test; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; +import java.util.Iterator; import java.util.stream.IntStream; +import static org.junit.Assert.assertEquals; + /** * Less of a test of functionality and more of a tool to help generate data files for TinkerPop. * * @author Stephen Mallette (http://stephen.genoprime.com) */ public class IoDataGenerationTest { - private static String tempPath; + private static final String tempPath; static { tempPath = TestHelper.makeTestDataPath(TinkerGraphTest.class, "tinkerpop-io").getPath() + File.separator; @@ -173,6 +171,23 @@ public class IoDataGenerationTest { os.close(); } + @Test + public void shouldWriteAndReadClassicGraphAsGraphSONV2d0WithTypes() throws IOException { + Graph g = TinkerFactory.createClassic(); + final OutputStream os = new FileOutputStream(tempPath + "tinkerpop-classic-V2d0-typed.json"); + GraphSONWriter.build().mapper(GraphSONMapper.build().version(GraphSONVersion.V2_0).typeInfo(GraphSONMapper.TypeInfo.PARTIAL_TYPES).create()) + .create().writeGraph(os, g); + os.close(); + + Graph readG = TinkerGraph.open(); + final InputStream is = new FileInputStream(tempPath + "tinkerpop-classic-V2d0-typed.json"); + GraphSONReader.build().mapper(GraphSONMapper.build().version(GraphSONVersion.V2_0).typeInfo(GraphSONMapper.TypeInfo.PARTIAL_TYPES).create()).create().readGraph(is, readG); + is.close(); + + assertEquals(approximateGraphsCheck(g, readG), true); + } + + /** * No assertions. Just write out the graph for convenience. */ @@ -275,4 +290,78 @@ public class IoDataGenerationTest { GraphSONWriter.build().mapper(GraphSONMapper.build().embedTypes(true).create()).create().writeGraph(os4, g); os4.close(); } + + @Test + public void shouldWriteGratefulDeadGraphSONV2d0() throws IOException { + final TinkerGraph g = TinkerGraph.open(); + final TinkerGraph readG = TinkerGraph.open(); + + final GraphReader reader = GryoReader.build().create(); + try (final InputStream stream = AbstractGremlinTest.class.getResourceAsStream("/org/apache/tinkerpop/gremlin/structure/io/gryo/grateful-dead.kryo")) { + reader.readGraph(stream, g); + } + final OutputStream os2 = new FileOutputStream(tempPath + "grateful-dead-V2d0-typed.json"); + GraphSONWriter.build().mapper(GraphSONMapper.build().version(GraphSONVersion.V2_0).typeInfo(GraphSONMapper.TypeInfo.PARTIAL_TYPES).create()).create().writeGraph(os2, g); + os2.close(); + + final InputStream is = new FileInputStream(tempPath + "grateful-dead-V2d0-typed.json"); + GraphSONReader.build().mapper(GraphSONMapper.build().version(GraphSONVersion.V2_0).typeInfo(GraphSONMapper.TypeInfo.PARTIAL_TYPES).create()).create().readGraph(is, readG); + is.close(); + + assertEquals(approximateGraphsCheck(g, readG), true); + } + + /** + * Checks sequentially vertices and egdes of both graphs. Will check sequentially Vertex IDs, Vertex Properties IDs + * and values and classes. Then same for edges. To use when serializing a Graph and deserializing the supposedly + * same Graph. + */ + private boolean approximateGraphsCheck(Graph g1, Graph g2) { + Iterator<Vertex> itV = g1.vertices(); + Iterator<Vertex> itVRead = g2.vertices(); + + while (itV.hasNext()) { + Vertex v = itV.next(); + Vertex vRead = itVRead.next(); + // Will only check IDs but that's 'good' enough. + if (!v.equals(vRead)) { + return false; + } + + Iterator itVP = v.properties(); + Iterator itVPRead = vRead.properties(); + while (itVP.hasNext()) { + VertexProperty vp = (VertexProperty) itVP.next(); + VertexProperty vpRead = (VertexProperty) itVPRead.next(); + if (!vp.value().equals(vpRead.value())) { + return false; + } + } + } + + + Iterator<Edge> itE = g1.edges(); + Iterator<Edge> itERead = g2.edges(); + + while (itE.hasNext()) { + Edge e = itE.next(); + Edge eRead = itERead.next(); + // Will only check IDs but that's good enough. + if (!e.equals(eRead)) { + return false; + } + + Iterator itEP = e.properties(); + Iterator itEPRead = eRead.properties(); + while (itEP.hasNext()) { + Property ep = (Property) itEP.next(); + Property epRead = (Property) itEPRead.next(); + if (!ep.value().equals(epRead.value()) + || !ep.equals(epRead)) { + return false; + } + } + } + return true; + } }
