Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 82978200B7E for ; Mon, 22 Aug 2016 20:30:43 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 816CB160AB3; Mon, 22 Aug 2016 18:30:43 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 6BE1D160ACE for ; Mon, 22 Aug 2016 20:30:40 +0200 (CEST) Received: (qmail 73592 invoked by uid 500); 22 Aug 2016 18:30:39 -0000 Mailing-List: contact commits-help@tinkerpop.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@tinkerpop.apache.org Delivered-To: mailing list commits@tinkerpop.apache.org Received: (qmail 73412 invoked by uid 99); 22 Aug 2016 18:30:39 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 22 Aug 2016 18:30:39 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 1FA32E69B5; Mon, 22 Aug 2016 18:30:39 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: spmallette@apache.org To: commits@tinkerpop.apache.org Date: Mon, 22 Aug 2016 18:30:45 -0000 Message-Id: <11038bb6c0974d56957bd65785a6743a@git.apache.org> In-Reply-To: <389edb2f8f7c45118b0b5c89a77589d5@git.apache.org> References: <389edb2f8f7c45118b0b5c89a77589d5@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [08/19] tinkerpop git commit: TINKERPOP-1274: GraphSON 2.0. archived-at: Mon, 22 Aug 2016 18:30:43 -0000 http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/115eb3c7/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TinkerPopJacksonModule.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TinkerPopJacksonModule.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TinkerPopJacksonModule.java new file mode 100644 index 0000000..847027c --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TinkerPopJacksonModule.java @@ -0,0 +1,48 @@ +/* + * 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.shaded.jackson.databind.module.SimpleModule; + +import java.util.Map; + +/** + * A {@link SimpleModule} extension that does the necessary work to make the automatic typed deserialization + * without full canonical class names work. + * + * Users of custom modules with the GraphSONMapper that want their objects to be deserialized automatically by the + * mapper, must extend this class with their module. It is the only required step. + * + * Using this basis module allows the serialization and deserialization of typed objects without having the whole + * canonical name of the serialized classes included in the Json payload. This is also necessary because Java + * does not provide an agnostic way to search in a classpath a find a class by its simple name. Although that could + * be done with an external library, later if we deem it necessary. + * + * @author Kevin Gallardo (https://kgdo.me) + */ +public abstract class TinkerPopJacksonModule extends SimpleModule { + + public TinkerPopJacksonModule(String name) { + super(name); + } + + public abstract Map getTypeDefinitions(); + + public abstract String getTypeNamespace(); +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/115eb3c7/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/ToStringGraphSONSerializer.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/ToStringGraphSONSerializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/ToStringGraphSONSerializer.java new file mode 100644 index 0000000..fbd9e00 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/ToStringGraphSONSerializer.java @@ -0,0 +1,41 @@ +/* + * 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.shaded.jackson.core.JsonGenerator; +import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer; +import org.apache.tinkerpop.shaded.jackson.databind.ser.std.ToStringSerializer; + +import java.io.IOException; + +/** + * A different implementation of the {@link ToStringSerializer} that does not serialize types by calling + * `typeSerializer.writeTypePrefixForScalar()` for unknown objects, because it doesn't make sense when there is a + * custom types mechanism in place. + * + * @author Kevin Gallardo (https://kgdo.me) + */ +public class ToStringGraphSONSerializer extends ToStringSerializer { + @Override + public void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer) throws IOException { + this.serialize(value, gen, provider); + } + +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/115eb3c7/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TypeInfo.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TypeInfo.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TypeInfo.java new file mode 100644 index 0000000..34e0d2b --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TypeInfo.java @@ -0,0 +1,29 @@ +/* + * 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; + +/** + * Defines how data types are handled in GraphSON through the {@link GraphSONMapper}. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public enum TypeInfo { + NO_TYPES, + PARTIAL_TYPES +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/115eb3c7/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedEdge.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedEdge.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedEdge.java index 776d313..ba05cca 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedEdge.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedEdge.java @@ -79,12 +79,17 @@ public class DetachedEdge extends DetachedElement implements Edge { super(id, label); this.outVertex = new DetachedVertex(outV.getValue0(), outV.getValue1(), Collections.emptyMap()); this.inVertex = new DetachedVertex(inV.getValue0(), inV.getValue1(), Collections.emptyMap()); - if (!properties.isEmpty()) { + if (properties != null && !properties.isEmpty()) { this.properties = new HashMap<>(); - properties.entrySet().stream().forEach(entry -> this.properties.put(entry.getKey(), Collections.singletonList(new DetachedProperty<>(entry.getKey(), entry.getValue(), this)))); + properties.entrySet().stream().forEach(entry -> { + if (Property.class.isAssignableFrom(entry.getValue().getClass())) { + this.properties.put(entry.getKey(), Collections.singletonList((Property)entry.getValue())); + } else { + this.properties.put(entry.getKey(), Collections.singletonList(new DetachedProperty<>(entry.getKey(), entry.getValue(), this))); + } + }); } } - @Override public String toString() { return StringFactory.edgeString(this); http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/115eb3c7/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedVertex.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedVertex.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedVertex.java index 371c091..70cce37 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedVertex.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedVertex.java @@ -76,12 +76,14 @@ public class DetachedVertex extends DetachedElement implements Vertex { public DetachedVertex(final Object id, final String label, final Map properties) { super(id, label); - if (!properties.isEmpty()) { + if (properties != null && !properties.isEmpty()) { this.properties = new HashMap<>(); properties.entrySet().stream().forEach( - entry -> this.properties.put(entry.getKey(), ((List>) entry.getValue()).stream() - .map(m -> new DetachedVertexProperty<>(m.get(ID), entry.getKey(), m.get(VALUE), (Map) m.getOrDefault(PROPERTIES, new HashMap<>()), this)) - .collect(Collectors.toList()))); + entry -> this.properties.put(entry.getKey(), (List) ((List) entry.getValue()).stream() + .map(m -> VertexProperty.class.isAssignableFrom(m.getClass()) + ? m + : new DetachedVertexProperty<>(((Map) m).get(ID), entry.getKey(), ((Map) m).get(VALUE), (Map) ((Map) m).getOrDefault(PROPERTIES, new HashMap<>()), this)) + .collect(Collectors.toList()))); } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/115eb3c7/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/DirectionalStarGraph.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/DirectionalStarGraph.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/DirectionalStarGraph.java new file mode 100644 index 0000000..ed7b58c --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/DirectionalStarGraph.java @@ -0,0 +1,39 @@ +/* + * 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.util.star; + +import org.apache.tinkerpop.gremlin.structure.Direction; + +public class DirectionalStarGraph { + private final Direction direction; + private final StarGraph starGraphToSerialize; + + public DirectionalStarGraph(final StarGraph starGraphToSerialize, final Direction direction) { + this.direction = direction; + this.starGraphToSerialize = starGraphToSerialize; + } + + public Direction getDirection() { + return direction; + } + + public StarGraph getStarGraphToSerialize() { + return starGraphToSerialize; + } +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/115eb3c7/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONDeserializer.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONDeserializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONDeserializer.java new file mode 100644 index 0000000..84a3fd4 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONDeserializer.java @@ -0,0 +1,91 @@ +/* + * 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.util.star; + +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTokens; +import org.apache.tinkerpop.gremlin.structure.util.Attachable; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +/** + * Provides helper functions for reading vertex and edges from their serialized GraphSON forms. + */ +public class StarGraphGraphSONDeserializer { + + /** + * A helper function for reading vertex edges from a serialized {@link org.apache.tinkerpop.gremlin.structure.util.star.StarGraph} (i.e. a {@link java.util.Map}) generated by + * {@link org.apache.tinkerpop.gremlin.structure.util.star.StarGraphGraphSONSerializerV1d0}. + */ + public static void readStarGraphEdges(final Function, Edge> edgeMaker, + final StarGraph starGraph, + final Map vertexData, + final String direction) throws IOException { + final Map>> edgeDatas = (Map>>) vertexData.get(direction); + for (Map.Entry>> edgeData : edgeDatas.entrySet()) { + for (Map inner : edgeData.getValue()) { + final StarGraph.StarEdge starEdge; + if (direction.equals(GraphSONTokens.OUT_E)) + starEdge = (StarGraph.StarEdge) starGraph.getStarVertex().addOutEdge(edgeData.getKey(), starGraph.addVertex(T.id, inner.get(GraphSONTokens.IN)), T.id, inner.get(GraphSONTokens.ID)); + else + starEdge = (StarGraph.StarEdge) starGraph.getStarVertex().addInEdge(edgeData.getKey(), starGraph.addVertex(T.id, inner.get(GraphSONTokens.OUT)), T.id, inner.get(GraphSONTokens.ID)); + + if (inner.containsKey(GraphSONTokens.PROPERTIES)) { + final Map edgePropertyData = (Map) inner.get(GraphSONTokens.PROPERTIES); + for (Map.Entry epd : edgePropertyData.entrySet()) { + starEdge.property(epd.getKey(), epd.getValue()); + } + } + + if (edgeMaker != null) edgeMaker.apply(starEdge); + } + } + } + + /** + * A helper function for reading a serialized {@link org.apache.tinkerpop.gremlin.structure.util.star.StarGraph} from a {@link java.util.Map} generated by + * {@link org.apache.tinkerpop.gremlin.structure.util.star.StarGraphGraphSONSerializerV1d0}. + */ + public static StarGraph readStarGraphVertex(final Map vertexData) throws IOException { + final StarGraph starGraph = StarGraph.open(); + starGraph.addVertex(T.id, vertexData.get(GraphSONTokens.ID), T.label, vertexData.get(GraphSONTokens.LABEL)); + if (vertexData.containsKey(GraphSONTokens.PROPERTIES)) { + final Map>> properties = (Map>>) vertexData.get(GraphSONTokens.PROPERTIES); + for (Map.Entry>> property : properties.entrySet()) { + for (Map p : property.getValue()) { + final StarGraph.StarVertexProperty vp = (StarGraph.StarVertexProperty) starGraph.getStarVertex().property(VertexProperty.Cardinality.list, property.getKey(), p.get(GraphSONTokens.VALUE), T.id, p.get(GraphSONTokens.ID)); + if (p.containsKey(GraphSONTokens.PROPERTIES)) { + final Map edgePropertyData = (Map) p.get(GraphSONTokens.PROPERTIES); + for (Map.Entry epd : edgePropertyData.entrySet()) { + vp.property(epd.getKey(), epd.getValue()); + } + } + } + } + } + + return starGraph; + } + +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/115eb3c7/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONSerializer.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONSerializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONSerializer.java deleted file mode 100644 index 3d1a16a..0000000 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONSerializer.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * 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.util.star; - -import org.apache.tinkerpop.gremlin.structure.Direction; -import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Property; -import org.apache.tinkerpop.gremlin.structure.T; -import org.apache.tinkerpop.gremlin.structure.VertexProperty; -import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTokens; -import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONUtil; -import org.apache.tinkerpop.gremlin.structure.util.Attachable; -import org.apache.tinkerpop.gremlin.structure.util.Comparators; -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.core.JsonProcessingException; -import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider; -import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer; -import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; -import java.util.function.Function; - -/** - * @author Stephen Mallette (http://stephen.genoprime.com) - */ -public class StarGraphGraphSONSerializer extends StdSerializer { - private final boolean normalize; - public StarGraphGraphSONSerializer(final boolean normalize) { - super(DirectionalStarGraph.class); - this.normalize = normalize; - } - - @Override - public void serialize(final DirectionalStarGraph starGraph, final JsonGenerator jsonGenerator, - final SerializerProvider serializerProvider) throws IOException, JsonGenerationException { - ser(starGraph, jsonGenerator, serializerProvider, null); - } - - @Override - public void serializeWithType(final DirectionalStarGraph starGraph, final JsonGenerator jsonGenerator, - final SerializerProvider serializerProvider, - final TypeSerializer typeSerializer) throws IOException, JsonProcessingException { - ser(starGraph, jsonGenerator, serializerProvider, typeSerializer); - } - - private void ser(final DirectionalStarGraph directionalStarGraph, final JsonGenerator jsonGenerator, - final SerializerProvider serializerProvider, - final TypeSerializer typeSerializer) throws IOException, JsonProcessingException { - final StarGraph starGraph = directionalStarGraph.getStarGraphToSerialize(); - jsonGenerator.writeStartObject(); - if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); - GraphSONUtil.writeWithType(GraphSONTokens.ID, starGraph.starVertex.id, jsonGenerator, serializerProvider, typeSerializer); - jsonGenerator.writeStringField(GraphSONTokens.LABEL, starGraph.starVertex.label); - if (directionalStarGraph.direction != null) writeEdges(directionalStarGraph, jsonGenerator, serializerProvider, typeSerializer, Direction.IN); - if (directionalStarGraph.direction != null) writeEdges(directionalStarGraph, jsonGenerator, serializerProvider, typeSerializer, Direction.OUT); - if (starGraph.starVertex.vertexProperties != null && !starGraph.starVertex.vertexProperties.isEmpty()) { - jsonGenerator.writeObjectFieldStart(GraphSONTokens.PROPERTIES); - if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); - final Set keys = normalize ? new TreeSet<>(starGraph.starVertex.vertexProperties.keySet()) : starGraph.starVertex.vertexProperties.keySet(); - for (final String k : keys) { - final List vp = starGraph.starVertex.vertexProperties.get(k); - jsonGenerator.writeArrayFieldStart(k); - if (typeSerializer != null) { - jsonGenerator.writeString(ArrayList.class.getName()); - jsonGenerator.writeStartArray(); - } - - final List vertexProperties = normalize ?sort(vp, Comparators.PROPERTY_COMPARATOR) : vp; - for (final VertexProperty property : vertexProperties) { - jsonGenerator.writeStartObject(); - if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); - GraphSONUtil.writeWithType(GraphSONTokens.ID, property.id(), jsonGenerator, serializerProvider, typeSerializer); - GraphSONUtil.writeWithType(GraphSONTokens.VALUE, property.value(), jsonGenerator, serializerProvider, typeSerializer); - - final Iterator metaProperties = normalize ? - IteratorUtils.list(property.properties(), Comparators.PROPERTY_COMPARATOR).iterator() : property.properties(); - if (metaProperties.hasNext()) { - jsonGenerator.writeObjectFieldStart(GraphSONTokens.PROPERTIES); - if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); - while (metaProperties.hasNext()) { - final Property meta = metaProperties.next(); - GraphSONUtil.writeWithType(meta.key(), meta.value(), jsonGenerator, serializerProvider, typeSerializer); - } - jsonGenerator.writeEndObject(); - } - jsonGenerator.writeEndObject(); - } - jsonGenerator.writeEndArray(); - if (typeSerializer != null) jsonGenerator.writeEndArray(); - } - jsonGenerator.writeEndObject(); - } - } - - private void writeEdges(final DirectionalStarGraph directionalStarGraph, final JsonGenerator jsonGenerator, - final SerializerProvider serializerProvider, - final TypeSerializer typeSerializer, - final Direction direction) throws IOException, JsonProcessingException { - // only write edges if there are some AND if the user requested them to be serialized AND if they match - // the direction being serialized by the format - final StarGraph starGraph = directionalStarGraph.getStarGraphToSerialize(); - final Direction edgeDirectionToSerialize = directionalStarGraph.getDirection(); - final Map> starEdges = direction.equals(Direction.OUT) ? starGraph.starVertex.outEdges : starGraph.starVertex.inEdges; - final boolean writeEdges = null != starEdges && edgeDirectionToSerialize != null - && (edgeDirectionToSerialize == direction || edgeDirectionToSerialize == Direction.BOTH); - if (writeEdges) { - jsonGenerator.writeObjectFieldStart(direction == Direction.IN ? GraphSONTokens.IN_E : GraphSONTokens.OUT_E); - if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); - final Set keys = normalize ? new TreeSet<>(starEdges.keySet()) : starEdges.keySet(); - for (final String k : keys) { - final List edges = starEdges.get(k); - jsonGenerator.writeArrayFieldStart(k); - if (typeSerializer != null) { - jsonGenerator.writeString(ArrayList.class.getName()); - jsonGenerator.writeStartArray(); - } - - final List edgesToWrite = normalize ? sort(edges, Comparators.EDGE_COMPARATOR) : edges; - for (final Edge edge : edgesToWrite) { - jsonGenerator.writeStartObject(); - if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); - GraphSONUtil.writeWithType(GraphSONTokens.ID, edge.id(), jsonGenerator, serializerProvider, typeSerializer); - GraphSONUtil.writeWithType(direction.equals(Direction.OUT) ? GraphSONTokens.IN : GraphSONTokens.OUT, - direction.equals(Direction.OUT) ? edge.inVertex().id() : edge.outVertex().id(), - jsonGenerator, serializerProvider, typeSerializer); - - final Iterator> edgeProperties = normalize ? - IteratorUtils.list(edge.properties(), Comparators.PROPERTY_COMPARATOR).iterator() : edge.properties(); - if (edgeProperties.hasNext()) { - jsonGenerator.writeObjectFieldStart(GraphSONTokens.PROPERTIES); - if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); - while (edgeProperties.hasNext()) { - final Property meta = edgeProperties.next(); - GraphSONUtil.writeWithType(meta.key(), meta.value(), jsonGenerator, serializerProvider, typeSerializer); - } - jsonGenerator.writeEndObject(); - } - jsonGenerator.writeEndObject(); - } - jsonGenerator.writeEndArray(); - if (typeSerializer != null) jsonGenerator.writeEndArray(); - } - jsonGenerator.writeEndObject(); - } - } - - /** - * A helper function for reading vertex edges from a serialized {@link StarGraph} (i.e. a {@link Map}) generated by - * {@link StarGraphGraphSONSerializer}. - */ - public static void readStarGraphEdges(final Function, Edge> edgeMaker, - final StarGraph starGraph, - final Map vertexData, - final String direction) throws IOException { - final Map>> edgeDatas = (Map>>) vertexData.get(direction); - for (Map.Entry>> edgeData : edgeDatas.entrySet()) { - for (Map inner : edgeData.getValue()) { - final StarGraph.StarEdge starEdge; - if (direction.equals(GraphSONTokens.OUT_E)) - starEdge = (StarGraph.StarEdge) starGraph.getStarVertex().addOutEdge(edgeData.getKey(), starGraph.addVertex(T.id, inner.get(GraphSONTokens.IN)), T.id, inner.get(GraphSONTokens.ID)); - else - starEdge = (StarGraph.StarEdge) starGraph.getStarVertex().addInEdge(edgeData.getKey(), starGraph.addVertex(T.id, inner.get(GraphSONTokens.OUT)), T.id, inner.get(GraphSONTokens.ID)); - - if (inner.containsKey(GraphSONTokens.PROPERTIES)) { - final Map edgePropertyData = (Map) inner.get(GraphSONTokens.PROPERTIES); - for (Map.Entry epd : edgePropertyData.entrySet()) { - starEdge.property(epd.getKey(), epd.getValue()); - } - } - - if (edgeMaker != null) edgeMaker.apply(starEdge); - } - } - } - - /** - * A helper function for reading a serialized {@link StarGraph} from a {@link Map} generated by - * {@link StarGraphGraphSONSerializer}. - */ - public static StarGraph readStarGraphVertex(final Map vertexData) throws IOException { - final StarGraph starGraph = StarGraph.open(); - starGraph.addVertex(T.id, vertexData.get(GraphSONTokens.ID), T.label, vertexData.get(GraphSONTokens.LABEL)); - if (vertexData.containsKey(GraphSONTokens.PROPERTIES)) { - final Map>> properties = (Map>>) vertexData.get(GraphSONTokens.PROPERTIES); - for (Map.Entry>> property : properties.entrySet()) { - for (Map p : property.getValue()) { - final StarGraph.StarVertexProperty vp = (StarGraph.StarVertexProperty) starGraph.getStarVertex().property(VertexProperty.Cardinality.list, property.getKey(), p.get(GraphSONTokens.VALUE), T.id, p.get(GraphSONTokens.ID)); - if (p.containsKey(GraphSONTokens.PROPERTIES)) { - final Map edgePropertyData = (Map) p.get(GraphSONTokens.PROPERTIES); - for (Map.Entry epd : edgePropertyData.entrySet()) { - vp.property(epd.getKey(), epd.getValue()); - } - } - } - } - } - - return starGraph; - } - - private static List sort(final List listToSort, final Comparator comparator) { - Collections.sort(listToSort, comparator); - return listToSort; - } - - public static class DirectionalStarGraph { - private final Direction direction; - private final StarGraph starGraphToSerialize; - - public DirectionalStarGraph(final StarGraph starGraphToSerialize, final Direction direction) { - this.direction = direction; - this.starGraphToSerialize = starGraphToSerialize; - } - - public Direction getDirection() { - return direction; - } - - public StarGraph getStarGraphToSerialize() { - return starGraphToSerialize; - } - } -} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/115eb3c7/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONSerializerV1d0.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONSerializerV1d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONSerializerV1d0.java new file mode 100644 index 0000000..01cb6d6 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONSerializerV1d0.java @@ -0,0 +1,178 @@ +/* + * 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.util.star; + +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTokens; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONUtil; +import org.apache.tinkerpop.gremlin.structure.util.Comparators; +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.core.JsonProcessingException; +import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer; +import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class StarGraphGraphSONSerializerV1d0 extends StdSerializer { + private final boolean normalize; + public StarGraphGraphSONSerializerV1d0(final boolean normalize) { + super(DirectionalStarGraph.class); + this.normalize = normalize; + } + + @Override + public void serialize(final DirectionalStarGraph starGraph, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider) throws IOException, JsonGenerationException { + ser(starGraph, jsonGenerator, serializerProvider, null); + } + + @Override + public void serializeWithType(final DirectionalStarGraph starGraph, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, + final TypeSerializer typeSerializer) throws IOException, JsonProcessingException { + ser(starGraph, jsonGenerator, serializerProvider, typeSerializer); + } + + private void ser(final DirectionalStarGraph directionalStarGraph, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, + final TypeSerializer typeSerializer) throws IOException, JsonProcessingException { + final StarGraph starGraph = directionalStarGraph.getStarGraphToSerialize(); + jsonGenerator.writeStartObject(); + if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); + GraphSONUtil.writeWithType(GraphSONTokens.ID, starGraph.starVertex.id, jsonGenerator, serializerProvider, typeSerializer); + jsonGenerator.writeStringField(GraphSONTokens.LABEL, starGraph.starVertex.label); + if (directionalStarGraph.getDirection() != null) writeEdges(directionalStarGraph, jsonGenerator, serializerProvider, typeSerializer, Direction.IN); + if (directionalStarGraph.getDirection() != null) writeEdges(directionalStarGraph, jsonGenerator, serializerProvider, typeSerializer, Direction.OUT); + if (starGraph.starVertex.vertexProperties != null && !starGraph.starVertex.vertexProperties.isEmpty()) { + jsonGenerator.writeObjectFieldStart(GraphSONTokens.PROPERTIES); + if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); + final Set keys = normalize ? new TreeSet<>(starGraph.starVertex.vertexProperties.keySet()) : starGraph.starVertex.vertexProperties.keySet(); + for (final String k : keys) { + final List vp = starGraph.starVertex.vertexProperties.get(k); + jsonGenerator.writeArrayFieldStart(k); + if (typeSerializer != null) { + jsonGenerator.writeString(ArrayList.class.getName()); + jsonGenerator.writeStartArray(); + } + + final List vertexProperties = normalize ? sort(vp, Comparators.PROPERTY_COMPARATOR) : vp; + for (final VertexProperty property : vertexProperties) { + jsonGenerator.writeStartObject(); + if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); + GraphSONUtil.writeWithType(GraphSONTokens.ID, property.id(), jsonGenerator, serializerProvider, typeSerializer); + GraphSONUtil.writeWithType(GraphSONTokens.VALUE, property.value(), jsonGenerator, serializerProvider, typeSerializer); + + final Iterator metaProperties = normalize ? + IteratorUtils.list(property.properties(), Comparators.PROPERTY_COMPARATOR).iterator() : property.properties(); + if (metaProperties.hasNext()) { + jsonGenerator.writeObjectFieldStart(GraphSONTokens.PROPERTIES); + if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); + while (metaProperties.hasNext()) { + final Property meta = metaProperties.next(); + GraphSONUtil.writeWithType(meta.key(), meta.value(), jsonGenerator, serializerProvider, typeSerializer); + } + jsonGenerator.writeEndObject(); + } + jsonGenerator.writeEndObject(); + } + jsonGenerator.writeEndArray(); + if (typeSerializer != null) jsonGenerator.writeEndArray(); + } + jsonGenerator.writeEndObject(); + } + // For some reason, this wasn't closed. + jsonGenerator.writeEndObject(); + } + + private void writeEdges(final DirectionalStarGraph directionalStarGraph, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, + final TypeSerializer typeSerializer, + final Direction direction) throws IOException, JsonProcessingException { + // only write edges if there are some AND if the user requested them to be serialized AND if they match + // the direction being serialized by the format + final StarGraph starGraph = directionalStarGraph.getStarGraphToSerialize(); + final Direction edgeDirectionToSerialize = directionalStarGraph.getDirection(); + final Map> starEdges = direction.equals(Direction.OUT) ? starGraph.starVertex.outEdges : starGraph.starVertex.inEdges; + final boolean writeEdges = null != starEdges && edgeDirectionToSerialize != null + && (edgeDirectionToSerialize == direction || edgeDirectionToSerialize == Direction.BOTH); + if (writeEdges) { + jsonGenerator.writeObjectFieldStart(direction == Direction.IN ? GraphSONTokens.IN_E : GraphSONTokens.OUT_E); + if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); + final Set keys = normalize ? new TreeSet<>(starEdges.keySet()) : starEdges.keySet(); + for (final String k : keys) { + final List edges = starEdges.get(k); + jsonGenerator.writeArrayFieldStart(k); + if (typeSerializer != null) { + jsonGenerator.writeString(ArrayList.class.getName()); + jsonGenerator.writeStartArray(); + } + + final List edgesToWrite = normalize ? sort(edges, Comparators.EDGE_COMPARATOR) : edges; + for (final Edge edge : edgesToWrite) { + jsonGenerator.writeStartObject(); + if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); + GraphSONUtil.writeWithType(GraphSONTokens.ID, edge.id(), jsonGenerator, serializerProvider, typeSerializer); + GraphSONUtil.writeWithType(direction.equals(Direction.OUT) ? GraphSONTokens.IN : GraphSONTokens.OUT, + direction.equals(Direction.OUT) ? edge.inVertex().id() : edge.outVertex().id(), + jsonGenerator, serializerProvider, typeSerializer); + + final Iterator> edgeProperties = normalize ? + IteratorUtils.list(edge.properties(), Comparators.PROPERTY_COMPARATOR).iterator() : edge.properties(); + if (edgeProperties.hasNext()) { + jsonGenerator.writeObjectFieldStart(GraphSONTokens.PROPERTIES); + if (typeSerializer != null) jsonGenerator.writeStringField(GraphSONTokens.CLASS, HashMap.class.getName()); + while (edgeProperties.hasNext()) { + final Property meta = edgeProperties.next(); + GraphSONUtil.writeWithType(meta.key(), meta.value(), jsonGenerator, serializerProvider, typeSerializer); + } + jsonGenerator.writeEndObject(); + } + jsonGenerator.writeEndObject(); + } + jsonGenerator.writeEndArray(); + if (typeSerializer != null) jsonGenerator.writeEndArray(); + } + jsonGenerator.writeEndObject(); + } + } + + static List sort(final List listToSort, final Comparator comparator) { + Collections.sort(listToSort, comparator); + return listToSort; + } + +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/115eb3c7/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONSerializerV2d0.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONSerializerV2d0.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONSerializerV2d0.java new file mode 100644 index 0000000..04d65c5 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/star/StarGraphGraphSONSerializerV2d0.java @@ -0,0 +1,165 @@ +/* + * 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.util.star; + +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTokens; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONUtil; +import org.apache.tinkerpop.gremlin.structure.util.Comparators; +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.core.JsonProcessingException; +import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider; +import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer; +import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class StarGraphGraphSONSerializerV2d0 extends StdSerializer { + private final boolean normalize; + public StarGraphGraphSONSerializerV2d0(final boolean normalize) { + super(DirectionalStarGraph.class); + this.normalize = normalize; + } + + @Override + public void serialize(final DirectionalStarGraph starGraph, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider) throws IOException, JsonGenerationException { + ser(starGraph, jsonGenerator, serializerProvider, null); + } + + @Override + public void serializeWithType(final DirectionalStarGraph starGraph, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, + final TypeSerializer typeSerializer) throws IOException, JsonProcessingException { + ser(starGraph, jsonGenerator, serializerProvider, typeSerializer); + } + + private void ser(final DirectionalStarGraph directionalStarGraph, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, + final TypeSerializer typeSerializer) throws IOException, JsonProcessingException { + final StarGraph starGraph = directionalStarGraph.getStarGraphToSerialize(); + GraphSONUtil.writeStartObject(starGraph, jsonGenerator, typeSerializer); + GraphSONUtil.writeWithType(GraphSONTokens.ID, starGraph.starVertex.id, jsonGenerator, serializerProvider, typeSerializer); + jsonGenerator.writeStringField(GraphSONTokens.LABEL, starGraph.starVertex.label); + if (directionalStarGraph.getDirection() != null) writeEdges(directionalStarGraph, jsonGenerator, serializerProvider, typeSerializer, Direction.IN); + if (directionalStarGraph.getDirection() != null) writeEdges(directionalStarGraph, jsonGenerator, serializerProvider, typeSerializer, Direction.OUT); + if (starGraph.starVertex.vertexProperties != null && !starGraph.starVertex.vertexProperties.isEmpty()) { + jsonGenerator.writeFieldName(GraphSONTokens.PROPERTIES); + GraphSONUtil.writeStartObject(starGraph, jsonGenerator, typeSerializer); + final Set keys = normalize ? new TreeSet<>(starGraph.starVertex.vertexProperties.keySet()) : starGraph.starVertex.vertexProperties.keySet(); + for (final String k : keys) { + final List vp = starGraph.starVertex.vertexProperties.get(k); + jsonGenerator.writeFieldName(k); + GraphSONUtil.writeStartArray(k, jsonGenerator, typeSerializer); + + final List vertexProperties = normalize ?sort(vp, Comparators.PROPERTY_COMPARATOR) : vp; + for (final VertexProperty property : vertexProperties) { + GraphSONUtil.writeStartObject(property, jsonGenerator, typeSerializer); + GraphSONUtil.writeWithType(GraphSONTokens.ID, property.id(), jsonGenerator, serializerProvider, typeSerializer); + GraphSONUtil.writeWithType(GraphSONTokens.VALUE, property.value(), jsonGenerator, serializerProvider, typeSerializer); + + final Iterator metaProperties = normalize ? + IteratorUtils.list(property.properties(), Comparators.PROPERTY_COMPARATOR).iterator() : property.properties(); + if (metaProperties.hasNext()) { + jsonGenerator.writeFieldName(GraphSONTokens.PROPERTIES); + GraphSONUtil.writeStartObject(metaProperties, jsonGenerator, typeSerializer); + + while (metaProperties.hasNext()) { + final Property meta = metaProperties.next(); + GraphSONUtil.writeWithType(meta.key(), meta.value(), jsonGenerator, serializerProvider, typeSerializer); + } + GraphSONUtil.writeEndObject(metaProperties, jsonGenerator, typeSerializer); + } + GraphSONUtil.writeEndObject(property, jsonGenerator, typeSerializer); + } + GraphSONUtil.writeEndArray(k, jsonGenerator, typeSerializer); + } + GraphSONUtil.writeEndObject(starGraph, jsonGenerator, typeSerializer); + } + GraphSONUtil.writeEndObject(starGraph, jsonGenerator, typeSerializer); + } + + private void writeEdges(final DirectionalStarGraph directionalStarGraph, final JsonGenerator jsonGenerator, + final SerializerProvider serializerProvider, + final TypeSerializer typeSerializer, + final Direction direction) throws IOException, JsonProcessingException { + // only write edges if there are some AND if the user requested them to be serialized AND if they match + // the direction being serialized by the format + final StarGraph starGraph = directionalStarGraph.getStarGraphToSerialize(); + final Direction edgeDirectionToSerialize = directionalStarGraph.getDirection(); + final Map> starEdges = direction.equals(Direction.OUT) ? starGraph.starVertex.outEdges : starGraph.starVertex.inEdges; + final boolean writeEdges = null != starEdges && edgeDirectionToSerialize != null + && (edgeDirectionToSerialize == direction || edgeDirectionToSerialize == Direction.BOTH); + if (writeEdges) { + jsonGenerator.writeFieldName(direction == Direction.IN ? GraphSONTokens.IN_E : GraphSONTokens.OUT_E); + GraphSONUtil.writeStartObject(directionalStarGraph, jsonGenerator, typeSerializer); + final Set keys = normalize ? new TreeSet<>(starEdges.keySet()) : starEdges.keySet(); + for (final String k : keys) { + final List edges = starEdges.get(k); + jsonGenerator.writeFieldName(k); + GraphSONUtil.writeStartArray(k, jsonGenerator, typeSerializer); + + final List edgesToWrite = normalize ? sort(edges, Comparators.EDGE_COMPARATOR) : edges; + for (final Edge edge : edgesToWrite) { + GraphSONUtil.writeStartObject(edge, jsonGenerator, typeSerializer); + GraphSONUtil.writeWithType(GraphSONTokens.ID, edge.id(), jsonGenerator, serializerProvider, typeSerializer); + GraphSONUtil.writeWithType(direction.equals(Direction.OUT) ? GraphSONTokens.IN : GraphSONTokens.OUT, + direction.equals(Direction.OUT) ? edge.inVertex().id() : edge.outVertex().id(), + jsonGenerator, serializerProvider, typeSerializer); + + final Iterator> edgeProperties = normalize ? + IteratorUtils.list(edge.properties(), Comparators.PROPERTY_COMPARATOR).iterator() : edge.properties(); + if (edgeProperties.hasNext()) { + jsonGenerator.writeFieldName(GraphSONTokens.PROPERTIES); + GraphSONUtil.writeStartObject(edge, jsonGenerator, typeSerializer); + while (edgeProperties.hasNext()) { + final Property meta = edgeProperties.next(); + GraphSONUtil.writeWithType(meta.key(), meta.value(), jsonGenerator, serializerProvider, typeSerializer); + } + GraphSONUtil.writeEndObject(edge, jsonGenerator, typeSerializer); + } + GraphSONUtil.writeEndObject(edge, jsonGenerator, typeSerializer); + } + GraphSONUtil.writeEndArray(k, jsonGenerator, typeSerializer); + } + GraphSONUtil.writeEndObject(directionalStarGraph, jsonGenerator, typeSerializer); + } + } + + private static List sort(final List listToSort, final Comparator comparator) { + Collections.sort(listToSort, comparator); + return listToSort; + } + +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/115eb3c7/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java index c7549f9..4980adf 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperEmbeddedTypeTest.java @@ -19,9 +19,9 @@ package org.apache.tinkerpop.gremlin.structure.io.graphson; import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper; -import org.apache.tinkerpop.shaded.kryo.io.Input; -import org.apache.tinkerpop.shaded.kryo.io.Output; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -39,11 +39,26 @@ import java.time.Year; import java.time.YearMonth; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import static org.junit.Assert.assertEquals; +@RunWith(Parameterized.class) public class GraphSONMapperEmbeddedTypeTest { - private final ObjectMapper mapper = GraphSONMapper.build().embedTypes(true).create().createMapper(); + + @Parameterized.Parameters(name = "{0}") + public static Iterable data() { + return Arrays.asList(new Object[][]{ + {GraphSONMapper.build().version(GraphSONVersion.V1_0).embedTypes(true).create().createMapper()}, + {GraphSONMapper.build().version(GraphSONVersion.V2_0).typeInfo(TypeInfo.PARTIAL_TYPES).create() + .createMapper()}, + }); + } + + @Parameterized.Parameter + public ObjectMapper mapper; @Test public void shouldHandleDuration()throws Exception { @@ -122,6 +137,40 @@ public class GraphSONMapperEmbeddedTypeTest { assertEquals(o, serializeDeserialize(o, ZoneOffset.class)); } + @Test + public void shouldHandleMapWithTypesUsingEmbedTypeSetting() throws Exception { + final ObjectMapper mapper = GraphSONMapper.build() + .version(GraphSONVersion.V1_0) + .typeInfo(TypeInfo.PARTIAL_TYPES) + .create() + .createMapper(); + + final Map m = new HashMap<>(); + m.put("test", 100L); + + final String json = mapper.writeValueAsString(m); + final Map read = mapper.readValue(json, HashMap.class); + + assertEquals(100L, read.get("test")); + } + + @Test + public void shouldNotHandleMapWithTypesUsingEmbedTypeSetting() throws Exception { + final ObjectMapper mapper = GraphSONMapper.build() + .version(GraphSONVersion.V1_0) + .typeInfo(TypeInfo.NO_TYPES) + .create() + .createMapper(); + + final Map m = new HashMap<>(); + m.put("test", 100L); + + final String json = mapper.writeValueAsString(m); + final Map read = mapper.readValue(json, HashMap.class); + + assertEquals(100, read.get("test")); + } + public T serializeDeserialize(final Object o, final Class clazz) throws Exception { try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) { mapper.writeValue(stream, o); http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/115eb3c7/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperTest.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperTest.java index 41e24c6..4b2e98f 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperTest.java @@ -21,6 +21,8 @@ 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 org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import java.time.Duration; import java.time.Instant; @@ -35,12 +37,25 @@ import java.time.Year; import java.time.YearMonth; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.Arrays; import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.__; import static org.junit.Assert.assertEquals; +@RunWith(Parameterized.class) public class GraphSONMapperTest { - private final ObjectMapper mapper = GraphSONMapper.build().embedTypes(false).create().createMapper(); + + @Parameterized.Parameters(name = "{0}") + public static Iterable data() { + return Arrays.asList(new Object[][]{ + {GraphSONMapper.build().version(GraphSONVersion.V1_0).embedTypes(false).create().createMapper()}, + {GraphSONMapper.build().version(GraphSONVersion.V2_0).typeInfo(TypeInfo.NO_TYPES).create().createMapper()}, + }); + } + + @Parameterized.Parameter + public ObjectMapper mapper; + @Test public void shouldHandleTraversalExplanation() throws Exception { http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/115eb3c7/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperV2d0PartialEmbeddedTypeTest.java ---------------------------------------------------------------------- diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperV2d0PartialEmbeddedTypeTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperV2d0PartialEmbeddedTypeTest.java new file mode 100644 index 0000000..c2ad05e --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONMapperV2d0PartialEmbeddedTypeTest.java @@ -0,0 +1,344 @@ +/* + * 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.shaded.jackson.databind.ObjectMapper; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.MonthDay; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.Period; +import java.time.Year; +import java.time.YearMonth; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.fail; + +/** + * Tests automatic typed serialization/deserialization for GraphSON 2.0. + */ +public class GraphSONMapperV2d0PartialEmbeddedTypeTest { + + private final ObjectMapper mapper = GraphSONMapper.build() + .version(GraphSONVersion.V2_0) + .typeInfo(TypeInfo.PARTIAL_TYPES) + .create() + .createMapper(); + + @Test + public void shouldHandleDurationAuto() throws Exception { + final Duration o = Duration.ZERO; + assertEquals(o, serializeDeserializeAuto(o)); + } + + @Test + public void shouldHandleInstantAuto() throws Exception { + final Instant o = Instant.ofEpochMilli(System.currentTimeMillis()); + assertEquals(o, serializeDeserializeAuto(o)); + } + + @Test + public void shouldHandleLocalDateAuto() throws Exception { + final LocalDate o = LocalDate.now(); + assertEquals(o, serializeDeserializeAuto(o)); + } + + @Test + public void shouldHandleLocalDateTimeAuto() throws Exception { + final LocalDateTime o = LocalDateTime.now(); + assertEquals(o, serializeDeserializeAuto(o)); + } + + @Test + public void shouldHandleLocalTimeAuto() throws Exception { + final LocalTime o = LocalTime.now(); + assertEquals(o, serializeDeserializeAuto(o)); + } + + @Test + public void shouldHandleMonthDayAuto() throws Exception { + final MonthDay o = MonthDay.now(); + assertEquals(o, serializeDeserializeAuto(o)); + } + + @Test + public void shouldHandleOffsetDateTimeAuto() throws Exception { + final OffsetDateTime o = OffsetDateTime.now(); + assertEquals(o, serializeDeserializeAuto(o)); + } + + @Test + public void shouldHandleOffsetTimeAuto() throws Exception { + final OffsetTime o = OffsetTime.now(); + assertEquals(o, serializeDeserializeAuto(o)); + } + + @Test + public void shouldHandlePeriodAuto() throws Exception { + final Period o = Period.ofDays(3); + assertEquals(o, serializeDeserializeAuto(o)); + } + + @Test + public void shouldHandleYearAuto() throws Exception { + final Year o = Year.now(); + assertEquals(o, serializeDeserializeAuto(o)); + } + + @Test + public void shouldHandleYearMonthAuto() throws Exception { + final YearMonth o = YearMonth.now(); + assertEquals(o, serializeDeserializeAuto(o)); + } + + @Test + public void shouldHandleZonedDateTimeAuto() throws Exception { + final ZonedDateTime o = ZonedDateTime.now(); + assertEquals(o, serializeDeserializeAuto(o)); + } + + @Test + public void shouldHandleZonedOffsetAuto() throws Exception { + final ZoneOffset o = ZonedDateTime.now().getOffset(); + assertEquals(o, serializeDeserializeAuto(o)); + } + + @Test + // Trying to fail the TypeDeserializer type detection + public void shouldSerializeDeserializeNestedCollectionsAndMapAndTypedValuesCorrectly() 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 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()); + assertEquals(myList, serializeDeserializeAuto(myList)); + + // no "@value" property + String s = "{\""+GraphSONTokens.VALUETYPE+"\":\""+GraphSONTokens.GREMLIN_TYPE_NAMESPACE +":uuid\", \"test\":2}"; + Map map = new LinkedHashMap<>(); + map.put(GraphSONTokens.VALUETYPE, "gremlin:uuid"); + map.put("test", 2); + Object res = mapper.readValue(s, Object.class); + assertEquals(map, res); + + // "@value" and "@type" property reversed + s = "{\""+GraphSONTokens.VALUEPROP+"\":2, \""+ GraphSONTokens.VALUETYPE+"\":\""+GraphSONTokens.GREMLIN_TYPE_NAMESPACE +":int64\"}"; + res = mapper.readValue(s, Object.class); + assertEquals(res, 2L); + assertEquals(res.getClass(), Long.class); + + // no "@type" property. + s = "{\""+GraphSONTokens.VALUEPROP+"\":2, \"id\":2}"; + map = new LinkedHashMap<>(); + map.put(GraphSONTokens.VALUEPROP, 2); + map.put("id", 2); + res = mapper.readValue(s, Object.class); + assertEquals(res, map); + } + + @Test + public void shouldFailIfMoreThanTwoPropertiesInATypePattern() { + String s = "{\"" + GraphSONTokens.VALUEPROP + "\":2, \"" + GraphSONTokens.VALUETYPE + "\":\""+GraphSONTokens.GREMLIN_TYPE_NAMESPACE +":int64\", \"hello\": \"world\"}"; + try { + mapper.readValue(s, Object.class); + fail("Should have failed deserializing because there's more than properties in the type."); + } catch (IOException e) { + assertThat(e.getMessage(), containsString("Detected the type pattern in the JSON payload but the map containing the types and values contains other fields. This is not allowed by the deserializer.")); + } + s = "{\"" + GraphSONTokens.VALUETYPE + "\":\""+GraphSONTokens.GREMLIN_TYPE_NAMESPACE +":int64\",\"" + GraphSONTokens.VALUEPROP + "\":2, \"hello\": \"world\"}"; + try { + mapper.readValue(s, Object.class); + fail("Should have failed deserializing because there's more than properties in the type."); + } catch (IOException e) { + assertThat(e.getMessage(), containsString("Detected the type pattern in the JSON payload but the map containing the types and values contains other fields. This is not allowed by the deserializer.")); + } + } + + @Test + public void shouldFailIfTypeSpecifiedIsNotSameTypeInPayload() { + final ZoneOffset o = ZonedDateTime.now().getOffset(); + final ByteArrayOutputStream stream = new ByteArrayOutputStream(); + try { + mapper.writeValue(stream, o); + final InputStream inputStream = new ByteArrayInputStream(stream.toByteArray()); + // What has been serialized is a ZoneOffset with the type, but the user explicitly requires another type. + mapper.readValue(inputStream, Instant.class); + fail("Should have failed decoding the value"); + } catch (Exception e) { + assertThat(e.getMessage(), containsString("Could not deserialize the JSON value as required. Nested exception: java.lang.InstantiationException: Cannot deserialize the value with the detected type contained in the JSON ('"+GraphSONTokens.GREMLIN_TYPE_NAMESPACE +":zoneoffset') to the type specified in parameter to the object mapper (class java.time.Instant). Those types are incompatible.")); + } + } + + @Test + public void shouldHandleRawPOJOs() throws Exception { + final FunObject funObject = new FunObject(); + funObject.setVal("test"); + assertEquals(funObject.toString(), serializeDeserialize(funObject, FunObject.class).toString()); + assertEquals(funObject.getClass(), serializeDeserialize(funObject, FunObject.class).getClass()); + } + + @Test + public void shouldHandleMapWithTypesUsingEmbedTypeSetting() throws Exception { + final ObjectMapper mapper = GraphSONMapper.build() + .version(GraphSONVersion.V2_0) + .embedTypes(true) + .create() + .createMapper(); + + final Map m = new HashMap<>(); + m.put("test", 100L); + + final String json = mapper.writeValueAsString(m); + final Map read = mapper.readValue(json, HashMap.class); + + assertEquals(100L, read.get("test")); + } + + @Test + public void shouldNotHandleMapWithTypesUsingEmbedTypeSetting() throws Exception { + final ObjectMapper mapper = GraphSONMapper.build() + .version(GraphSONVersion.V2_0) + .embedTypes(false) + .create() + .createMapper(); + + final Map m = new HashMap<>(); + m.put("test", 100L); + + final String json = mapper.writeValueAsString(m); + final Map read = mapper.readValue(json, HashMap.class); + + assertEquals(100, read.get("test")); + } + + @Test + public void shouldLooseTypesInfoWithGraphSONNoType() throws Exception { + ObjectMapper mapper = GraphSONMapper.build() + .version(GraphSONVersion.V2_0) + .typeInfo(TypeInfo.NO_TYPES) + .create() + .createMapper(); + + 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 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); + } + + + // Class needs to be defined as statics as it's a nested class. + public static class FunObject { + private String val; + + public FunObject() { + } + + public String getVal() { + return this.val; + } + + public void setVal(String s) { + this.val = s; + } + + public String toString() { + return this.val; + } + } + + public T serializeDeserialize(final Object o, final Class clazz) throws Exception { + try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) { + mapper.writeValue(stream, o); + + try (final InputStream inputStream = new ByteArrayInputStream(stream.toByteArray())) { + return mapper.readValue(inputStream, clazz); + } + } + } + + public T serializeDeserializeAuto(final Object o) throws Exception { + try (final ByteArrayOutputStream stream = new ByteArrayOutputStream()) { + mapper.writeValue(stream, o); + + try (final InputStream inputStream = new ByteArrayInputStream(stream.toByteArray())) { + // Object.class is the wildcard that triggers the auto discovery. + return (T)mapper.readValue(inputStream, Object.class); + } + } + } + +} http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/115eb3c7/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> mapTypeReference = new TypeReference>() { + }; + + 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 config, final Map 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 responseData = mapper.readValue(payload, mapTypeReference); + final Map status = (Map) responseData.get(SerTokens.TOKEN_STATUS); + final Map result = (Map) 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) status.get(SerTokens.TOKEN_ATTRIBUTES)) + .result(result.get(SerTokens.TOKEN_DATA)) + .responseMetaData((Map) 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 { + 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 { + 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); + } + } +}