You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2015/02/12 14:01:35 UTC
[09/77] [partial] incubator-tinkerpop git commit: moved com/tinkerpop
directories to org/apache/tinkerpop
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/LegacyGraphSONReader.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/LegacyGraphSONReader.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/LegacyGraphSONReader.java
new file mode 100644
index 0000000..53b12fd
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/LegacyGraphSONReader.java
@@ -0,0 +1,347 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.tinkerpop.gremlin.structure.io.graphson;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.tinkerpop.gremlin.process.T;
+import com.tinkerpop.gremlin.structure.Direction;
+import com.tinkerpop.gremlin.structure.Edge;
+import com.tinkerpop.gremlin.structure.Graph;
+import com.tinkerpop.gremlin.structure.Vertex;
+import com.tinkerpop.gremlin.structure.io.GraphReader;
+import com.tinkerpop.gremlin.structure.util.batch.BatchGraph;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedEdge;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+/**
+ * A @{link GraphReader} that constructs a graph from a JSON-based representation of a graph and its elements given
+ * the "legacy" Blueprints 2.x version of GraphSON. This implementation is specifically for aiding in migration
+ * of graphs from TinkerPop 2.x to TinkerPop 3.x. This reader only reads GraphSON from TinkerPop 2.x that was
+ * generated in {@code GraphSONMode.EXTENDED}.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class LegacyGraphSONReader implements GraphReader {
+ private final ObjectMapper mapper;
+ private final long batchSize;
+
+ public LegacyGraphSONReader(final ObjectMapper mapper, final long batchSize) {
+ this.mapper = mapper;
+ this.batchSize = batchSize;
+ }
+
+ @Override
+ public void readGraph(final InputStream inputStream, final Graph graphToWriteTo) throws IOException {
+ final BatchGraph graph;
+ try {
+ // will throw an exception if not constructed properly
+ graph = BatchGraph.build(graphToWriteTo)
+ .bufferSize(batchSize).create();
+ } catch (Exception ex) {
+ throw new IOException("Could not instantiate BatchGraph wrapper", ex);
+ }
+
+ final JsonFactory factory = mapper.getFactory();
+ final GraphSONUtility graphson = new GraphSONUtility(graph);
+
+ try (JsonParser parser = factory.createParser(inputStream)) {
+ if (parser.nextToken() != JsonToken.START_OBJECT)
+ throw new IOException("Expected data to start with an Object");
+
+ while (parser.nextToken() != JsonToken.END_OBJECT) {
+ final String fieldName = parser.getCurrentName() == null ? "" : parser.getCurrentName();
+ switch (fieldName) {
+ case GraphSONTokens.MODE:
+ parser.nextToken();
+ final String mode = parser.getText();
+ if (!mode.equals("EXTENDED"))
+ throw new IllegalStateException("The legacy GraphSON must be generated with GraphSONMode.EXTENDED");
+ break;
+ case GraphSONTokens.VERTICES:
+ parser.nextToken();
+ while (parser.nextToken() != JsonToken.END_ARRAY) {
+ final JsonNode node = parser.readValueAsTree();
+ graphson.vertexFromJson(node);
+ }
+ break;
+ case GraphSONTokens.EDGES:
+ parser.nextToken();
+ while (parser.nextToken() != JsonToken.END_ARRAY) {
+ final JsonNode node = parser.readValueAsTree();
+ final Vertex inV = graph.iterators().vertexIterator(GraphSONUtility.getTypedValueFromJsonNode(node.get(GraphSONTokens._IN_V))).next();
+ final Vertex outV = graph.iterators().vertexIterator(GraphSONUtility.getTypedValueFromJsonNode(node.get(GraphSONTokens._OUT_V))).next();
+ GraphSONUtility.edgeFromJson(node, outV, inV);
+ }
+ break;
+ default:
+ throw new IllegalStateException(String.format("Unexpected token in GraphSON - %s", fieldName));
+ }
+ }
+
+ graph.tx().commit();
+ } catch (Exception ex) {
+ throw new IOException(ex);
+ }
+
+ }
+
+ @Override
+ public Iterator<Vertex> readVertices(final InputStream inputStream, final Direction direction,
+ final Function<DetachedVertex, Vertex> vertexMaker,
+ final Function<DetachedEdge, Edge> edgeMaker) throws IOException {
+ throw new UnsupportedOperationException("This reader only reads an entire Graph");
+ }
+
+ @Override
+ public Edge readEdge(final InputStream inputStream, final Function<DetachedEdge, Edge> edgeMaker) throws IOException {
+ throw new UnsupportedOperationException("This reader only reads an entire Graph");
+ }
+
+ @Override
+ public Vertex readVertex(final InputStream inputStream, final Function<DetachedVertex, Vertex> vertexMaker) throws IOException {
+ throw new UnsupportedOperationException("This reader only reads an entire Graph");
+ }
+
+ @Override
+ public Vertex readVertex(final InputStream inputStream, final Direction direction, final Function<DetachedVertex, Vertex> vertexMaker, final Function<DetachedEdge, Edge> edgeMaker) throws IOException {
+ throw new UnsupportedOperationException("This reader only reads an entire Graph");
+ }
+
+ public static Builder build() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private boolean loadCustomModules = false;
+ private List<SimpleModule> customModules = new ArrayList<>();
+ private long batchSize = BatchGraph.DEFAULT_BUFFER_SIZE;
+ private boolean embedTypes = false;
+
+ private Builder() {
+ }
+
+ /**
+ * Supply a mapper module for serialization/deserialization.
+ */
+ public Builder addCustomModule(final SimpleModule custom) {
+ this.customModules.add(custom);
+ return this;
+ }
+
+ /**
+ * Try to load {@code SimpleModule} instances from the current classpath. These are loaded in addition to
+ * the one supplied to the {@link #addCustomModule(com.fasterxml.jackson.databind.module.SimpleModule)};
+ */
+ public Builder loadCustomModules(final boolean loadCustomModules) {
+ this.loadCustomModules = loadCustomModules;
+ return this;
+ }
+
+ /**
+ * Number of mutations to perform before a commit is executed.
+ */
+ public Builder batchSize(final long batchSize) {
+ this.batchSize = batchSize;
+ return this;
+ }
+
+ public LegacyGraphSONReader create() {
+ final GraphSONMapper.Builder builder = GraphSONMapper.build();
+ customModules.forEach(builder::addCustomModule);
+ final GraphSONMapper mapper = builder.embedTypes(embedTypes)
+ .loadCustomModules(loadCustomModules).create();
+ return new LegacyGraphSONReader(mapper.createMapper(), batchSize);
+ }
+ }
+
+ public static class GraphSONUtility {
+ private static final String EMPTY_STRING = "";
+ private final Graph g;
+
+ public GraphSONUtility(final Graph g) {
+ this.g = g;
+ }
+
+ public Vertex vertexFromJson(final JsonNode json) throws IOException {
+ final Map<String, Object> props = readProperties(json);
+
+ final Object vertexId = getTypedValueFromJsonNode(json.get(GraphSONTokens._ID));
+ final Vertex v = g.addVertex(T.id, vertexId);
+
+ for (Map.Entry<String, Object> entry : props.entrySet()) {
+ v.property(entry.getKey(), entry.getValue());
+ }
+
+ return v;
+ }
+
+ public static Edge edgeFromJson(final JsonNode json, final Vertex out, final Vertex in) throws IOException {
+ final Map<String, Object> props = GraphSONUtility.readProperties(json);
+
+ final Object edgeId = getTypedValueFromJsonNode(json.get(GraphSONTokens._ID));
+ final JsonNode nodeLabel = json.get(GraphSONTokens._LABEL);
+ final String label = nodeLabel == null ? EMPTY_STRING : nodeLabel.textValue();
+
+ final Edge e = out.addEdge(label, in, T.id, edgeId);
+ for (Map.Entry<String, Object> entry : props.entrySet()) {
+ e.property(entry.getKey(), entry.getValue());
+ }
+
+ return e;
+ }
+
+ static Map<String, Object> readProperties(final JsonNode node) {
+ final Map<String, Object> map = new HashMap<>();
+
+ final Iterator<Map.Entry<String, JsonNode>> iterator = node.fields();
+ while (iterator.hasNext()) {
+ final Map.Entry<String, JsonNode> entry = iterator.next();
+
+ if (!isReservedKey(entry.getKey())) {
+ // it generally shouldn't be as such but graphson containing null values can't be shoved into
+ // element property keys or it will result in error
+ final Object o = readProperty(entry.getValue());
+ if (o != null) {
+ map.put(entry.getKey(), o);
+ }
+ }
+ }
+
+ return map;
+ }
+
+ private static boolean isReservedKey(final String key) {
+ return key.equals(GraphSONTokens._ID) || key.equals(GraphSONTokens._TYPE) || key.equals(GraphSONTokens._LABEL)
+ || key.equals(GraphSONTokens._OUT_V) || key.equals(GraphSONTokens._IN_V);
+ }
+
+ private static Object readProperty(final JsonNode node) {
+ final Object propertyValue;
+
+ if (node.get(GraphSONTokens.TYPE).textValue().equals(GraphSONTokens.TYPE_UNKNOWN)) {
+ propertyValue = null;
+ } else if (node.get(GraphSONTokens.TYPE).textValue().equals(GraphSONTokens.TYPE_BOOLEAN)) {
+ propertyValue = node.get(GraphSONTokens.VALUE).booleanValue();
+ } else if (node.get(GraphSONTokens.TYPE).textValue().equals(GraphSONTokens.TYPE_FLOAT)) {
+ propertyValue = Float.parseFloat(node.get(GraphSONTokens.VALUE).asText());
+ } else if (node.get(GraphSONTokens.TYPE).textValue().equals(GraphSONTokens.TYPE_BYTE)) {
+ propertyValue = Byte.parseByte(node.get(GraphSONTokens.VALUE).asText());
+ } else if (node.get(GraphSONTokens.TYPE).textValue().equals(GraphSONTokens.TYPE_SHORT)) {
+ propertyValue = Short.parseShort(node.get(GraphSONTokens.VALUE).asText());
+ } else if (node.get(GraphSONTokens.TYPE).textValue().equals(GraphSONTokens.TYPE_DOUBLE)) {
+ propertyValue = node.get(GraphSONTokens.VALUE).doubleValue();
+ } else if (node.get(GraphSONTokens.TYPE).textValue().equals(GraphSONTokens.TYPE_INTEGER)) {
+ propertyValue = node.get(GraphSONTokens.VALUE).intValue();
+ } else if (node.get(GraphSONTokens.TYPE).textValue().equals(GraphSONTokens.TYPE_LONG)) {
+ propertyValue = node.get(GraphSONTokens.VALUE).longValue();
+ } else if (node.get(GraphSONTokens.TYPE).textValue().equals(GraphSONTokens.TYPE_STRING)) {
+ propertyValue = node.get(GraphSONTokens.VALUE).textValue();
+ } else if (node.get(GraphSONTokens.TYPE).textValue().equals(GraphSONTokens.TYPE_LIST)) {
+ propertyValue = readProperties(node.get(GraphSONTokens.VALUE).elements());
+ } else if (node.get(GraphSONTokens.TYPE).textValue().equals(GraphSONTokens.TYPE_MAP)) {
+ propertyValue = readProperties(node.get(GraphSONTokens.VALUE));
+ } else {
+ propertyValue = node.textValue();
+ }
+
+ return propertyValue;
+ }
+
+ private static List readProperties(final Iterator<JsonNode> listOfNodes) {
+ final List<Object> array = new ArrayList<>();
+
+ while (listOfNodes.hasNext()) {
+ array.add(readProperty(listOfNodes.next()));
+ }
+
+ return array;
+ }
+
+ static Object getTypedValueFromJsonNode(final JsonNode node) {
+ Object theValue = null;
+
+ if (node != null && !node.isNull()) {
+ if (node.isBoolean()) {
+ theValue = node.booleanValue();
+ } else if (node.isDouble()) {
+ theValue = node.doubleValue();
+ } else if (node.isFloatingPointNumber()) {
+ theValue = node.floatValue();
+ } else if (node.isInt()) {
+ theValue = node.intValue();
+ } else if (node.isLong()) {
+ theValue = node.longValue();
+ } else if (node.isTextual()) {
+ theValue = node.textValue();
+ } else if (node.isArray()) {
+ // this is an array so just send it back so that it can be
+ // reprocessed to its primitive components
+ theValue = node;
+ } else if (node.isObject()) {
+ // this is an object so just send it back so that it can be
+ // reprocessed to its primitive components
+ theValue = node;
+ } else {
+ theValue = node.textValue();
+ }
+ }
+
+ return theValue;
+ }
+ }
+
+ public static class GraphSONTokens {
+ public static final String _ID = "_id";
+ public static final String _LABEL = "_label";
+ public static final String _TYPE = "_type";
+ public static final String _OUT_V = "_outV";
+ public static final String _IN_V = "_inV";
+ public static final String VALUE = "value";
+ public static final String TYPE = "type";
+ public static final String TYPE_LIST = "list";
+ public static final String TYPE_STRING = "string";
+ public static final String TYPE_DOUBLE = "double";
+ public static final String TYPE_INTEGER = "integer";
+ public static final String TYPE_FLOAT = "float";
+ public static final String TYPE_MAP = "map";
+ public static final String TYPE_BOOLEAN = "boolean";
+ public static final String TYPE_LONG = "long";
+ public static final String TYPE_SHORT = "short";
+ public static final String TYPE_BYTE = "byte";
+ public static final String TYPE_UNKNOWN = "unknown";
+
+ public static final String VERTICES = "vertices";
+ public static final String EDGES = "edges";
+ public static final String MODE = "mode";
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/EdgeTerminator.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/EdgeTerminator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/EdgeTerminator.java
new file mode 100644
index 0000000..d409d16
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/EdgeTerminator.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 com.tinkerpop.gremlin.structure.io.kryo;
+
+/**
+ * Represents the end of an edge list in a serialization stream.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+class EdgeTerminator {
+ public static final EdgeTerminator INSTANCE = new EdgeTerminator();
+ private final boolean terminal;
+
+ private EdgeTerminator() {
+ this.terminal = true;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ final EdgeTerminator that = (EdgeTerminator) o;
+
+ return terminal == that.terminal;
+ }
+
+ @Override
+ public int hashCode() {
+ return (terminal ? 1 : 0);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/EntrySerializer.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/EntrySerializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/EntrySerializer.java
new file mode 100644
index 0000000..a089af9
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/EntrySerializer.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.tinkerpop.gremlin.structure.io.kryo;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+import java.util.AbstractMap;
+import java.util.Map;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+class EntrySerializer extends Serializer<Map.Entry> {
+ @Override
+ public void write(final Kryo kryo, final Output output, final Map.Entry entry) {
+ kryo.writeClassAndObject(output, entry.getKey());
+ kryo.writeClassAndObject(output, entry.getValue());
+ }
+
+ @Override
+ public Map.Entry read(final Kryo kryo, final Input input, final Class<Map.Entry> entryClass) {
+ return new AbstractMap.SimpleEntry(kryo.readClassAndObject(input), kryo.readClassAndObject(input));
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/GraphSerializer.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/GraphSerializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/GraphSerializer.java
new file mode 100644
index 0000000..eb964b6
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/GraphSerializer.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.tinkerpop.gremlin.structure.io.kryo;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+import com.tinkerpop.gremlin.process.Path;
+import com.tinkerpop.gremlin.process.Traverser;
+import com.tinkerpop.gremlin.structure.Edge;
+import com.tinkerpop.gremlin.structure.Property;
+import com.tinkerpop.gremlin.structure.Vertex;
+import com.tinkerpop.gremlin.structure.VertexProperty;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedEdge;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedPath;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedProperty;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty;
+
+/**
+ * Class used to serialize graph-based objects such as vertices, edges, properties, and paths.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+class GraphSerializer {
+ /**
+ * Serializes any {@link Edge} implementation encountered to a {@link DetachedEdge}.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+ static class EdgeSerializer extends Serializer<Edge> {
+ @Override
+ public void write(final Kryo kryo, final Output output, final Edge edge) {
+ kryo.writeClassAndObject(output, DetachedFactory.detach(edge, true));
+ }
+
+ @Override
+ public Edge read(final Kryo kryo, final Input input, final Class<Edge> edgeClass) {
+ final Object o = kryo.readClassAndObject(input);
+ return (Edge) o;
+ }
+ }
+
+ /**
+ * Serializes any {@link Vertex} implementation encountered to an {@link DetachedVertex}.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+ static class VertexSerializer extends Serializer<Vertex> {
+ public VertexSerializer() {
+ }
+
+ @Override
+ public void write(final Kryo kryo, final Output output, final Vertex vertex) {
+ kryo.writeClassAndObject(output, DetachedFactory.detach(vertex, true));
+ }
+
+ @Override
+ public Vertex read(final Kryo kryo, final Input input, final Class<Vertex> vertexClass) {
+ return (Vertex) kryo.readClassAndObject(input);
+ }
+ }
+
+ /**
+ * Serializes any {@link Property} implementation encountered to an {@link DetachedProperty}.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+ static class PropertySerializer extends Serializer<Property> {
+ public PropertySerializer() {
+ }
+
+ @Override
+ public void write(final Kryo kryo, final Output output, final Property property) {
+ kryo.writeClassAndObject(output, DetachedFactory.detach(property));
+ }
+
+ @Override
+ public Property read(final Kryo kryo, final Input input, final Class<Property> propertyClass) {
+ return (Property) kryo.readClassAndObject(input);
+ }
+ }
+
+ /**
+ * Serializes any {@link VertexProperty} implementation encountered to an {@link DetachedVertexProperty}.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+ static class VertexPropertySerializer extends Serializer<VertexProperty> {
+ public VertexPropertySerializer() {
+ }
+
+ @Override
+ public void write(final Kryo kryo, final Output output, final VertexProperty vertexProperty) {
+ kryo.writeClassAndObject(output, DetachedFactory.detach(vertexProperty, true));
+ }
+
+ @Override
+ public VertexProperty read(final Kryo kryo, final Input input, final Class<VertexProperty> vertexPropertyClass) {
+ return (VertexProperty) kryo.readClassAndObject(input);
+ }
+ }
+
+ /**
+ * Serializes any {@link Path} implementation encountered to an {@link DetachedPath}.
+ *
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+ static class PathSerializer extends Serializer<Path> {
+ public PathSerializer() {
+ }
+
+ @Override
+ public void write(final Kryo kryo, final Output output, final Path path) {
+ kryo.writeClassAndObject(output, DetachedFactory.detach(path, false));
+ }
+
+ @Override
+ public Path read(final Kryo kryo, final Input input, final Class<Path> pathClass) {
+ return (Path) kryo.readClassAndObject(input);
+ }
+
+ }
+
+ /**
+ * Serializes any {@link Traverser} implementation encountered via pre-processing with {@link Traverser.Admin#detach()}.
+ *
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+ /*static class TraverserSerializer extends Serializer<Traverser.Admin> {
+ public TraverserSerializer() {
+ }
+
+ @Override
+ public void write(final Kryo kryo, final Output output, final Traverser.Admin traverser) {
+ kryo.writeClassAndObject(output, traverser.asAdmin().detach());
+ }
+
+ @Override
+ public Traverser.Admin read(final Kryo kryo, final Input input, final Class<Traverser.Admin> traverser) {
+ return (Traverser.Admin) kryo.readClassAndObject(input);
+ }
+
+ }*/
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/GremlinClassResolver.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/GremlinClassResolver.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/GremlinClassResolver.java
new file mode 100644
index 0000000..f9d04de
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/GremlinClassResolver.java
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.tinkerpop.gremlin.structure.io.kryo;
+
+import com.esotericsoftware.kryo.ClassResolver;
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.KryoException;
+import com.esotericsoftware.kryo.Registration;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+import com.esotericsoftware.kryo.util.IdentityObjectIntMap;
+import com.esotericsoftware.kryo.util.IntMap;
+import com.esotericsoftware.kryo.util.ObjectMap;
+import com.tinkerpop.gremlin.process.Path;
+import com.tinkerpop.gremlin.structure.Edge;
+import com.tinkerpop.gremlin.structure.Property;
+import com.tinkerpop.gremlin.structure.Vertex;
+import com.tinkerpop.gremlin.structure.VertexProperty;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedEdge;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedPath;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedProperty;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty;
+
+import static com.esotericsoftware.kryo.util.Util.getWrapperClass;
+
+/**
+ * This mapper implementation of the {@code ClassResolver} helps ensure that all Vertex and Edge concrete classes
+ * get properly serialized and deserialized by stripping them of their concrete class name so that they are treated
+ * generically.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+class GremlinClassResolver implements ClassResolver {
+ static public final byte NAME = -1;
+
+ protected Kryo kryo;
+
+ protected final IntMap<Registration> idToRegistration = new IntMap<>();
+ protected final ObjectMap<Class, Registration> classToRegistration = new ObjectMap<>();
+
+ protected IdentityObjectIntMap<Class> classToNameId;
+ protected IntMap<Class> nameIdToClass;
+ protected ObjectMap<String, Class> nameToClass;
+ protected int nextNameId;
+
+ private int memoizedClassId = -1;
+ private Registration memoizedClassIdValue;
+ private Class memoizedClass;
+ private Registration memoizedClassValue;
+
+ @Override
+ public void setKryo(Kryo kryo) {
+ this.kryo = kryo;
+ }
+
+ @Override
+ public Registration register(final Registration registration) {
+ if (null == registration) throw new IllegalArgumentException("Registration cannot be null.");
+ if (registration.getId() != NAME) idToRegistration.put(registration.getId(), registration);
+
+ classToRegistration.put(registration.getType(), registration);
+ if (registration.getType().isPrimitive())
+ classToRegistration.put(getWrapperClass(registration.getType()), registration);
+ return registration;
+ }
+
+ @Override
+ public Registration registerImplicit(final Class type) {
+ return register(new Registration(type, kryo.getDefaultSerializer(type), NAME));
+ }
+
+ @Override
+ public Registration getRegistration(final Class clazz) {
+ // force all instances of Vertex, Edge, VertexProperty, etc. to their respective interface
+ final Class type;
+ if (!DetachedVertex.class.isAssignableFrom(clazz) && Vertex.class.isAssignableFrom(clazz))
+ type = Vertex.class;
+ else if (!DetachedEdge.class.isAssignableFrom(clazz) && Edge.class.isAssignableFrom(clazz))
+ type = Edge.class;
+ else if (!DetachedVertexProperty.class.isAssignableFrom(clazz) && VertexProperty.class.isAssignableFrom(clazz))
+ type = VertexProperty.class;
+ else if (!DetachedProperty.class.isAssignableFrom(clazz) && !DetachedVertexProperty.class.isAssignableFrom(clazz) && Property.class.isAssignableFrom(clazz))
+ type = Property.class;
+ else if (!DetachedPath.class.isAssignableFrom(clazz) && Path.class.isAssignableFrom(clazz))
+ type = Path.class;
+ else
+ type = clazz;
+
+ if (type == memoizedClass) return memoizedClassValue;
+ final Registration registration = classToRegistration.get(type);
+ if (registration != null) {
+ memoizedClass = type;
+ memoizedClassValue = registration;
+ }
+
+ return registration;
+ }
+
+ @Override
+ public Registration getRegistration(final int classID) {
+ return idToRegistration.get(classID);
+ }
+
+ @Override
+ public Registration writeClass(final Output output, final Class type) {
+ if (null == type) {
+ output.writeVarInt(Kryo.NULL, true);
+ return null;
+ }
+
+ final Registration registration = kryo.getRegistration(type);
+ if (registration.getId() == NAME)
+ writeName(output, type);
+ else
+ output.writeVarInt(registration.getId() + 2, true);
+
+ return registration;
+ }
+
+ protected void writeName(final Output output, final Class type) {
+ output.writeVarInt(NAME + 2, true);
+ if (classToNameId != null) {
+ final int nameId = classToNameId.get(type, -1);
+ if (nameId != -1) {
+ output.writeVarInt(nameId, true);
+ return;
+ }
+ }
+ // Only write the class name the first time encountered in object graph.
+ final int nameId = nextNameId++;
+ if (classToNameId == null) classToNameId = new IdentityObjectIntMap<>();
+ classToNameId.put(type, nameId);
+ output.writeVarInt(nameId, true);
+ output.writeString(type.getName());
+ }
+
+ @Override
+ public Registration readClass(final Input input) {
+ final int classID = input.readVarInt(true);
+ switch (classID) {
+ case Kryo.NULL:
+ return null;
+ case NAME + 2: // Offset for NAME and NULL.
+ return readName(input);
+ }
+
+ if (classID == memoizedClassId) return memoizedClassIdValue;
+ final Registration registration = idToRegistration.get(classID - 2);
+ if (registration == null) throw new KryoException("Encountered unregistered class ID: " + (classID - 2));
+ memoizedClassId = classID;
+ memoizedClassIdValue = registration;
+ return registration;
+ }
+
+ protected Registration readName(final Input input) {
+ final int nameId = input.readVarInt(true);
+ if (nameIdToClass == null) nameIdToClass = new IntMap<>();
+ Class type = nameIdToClass.get(nameId);
+ if (type == null) {
+ // Only read the class name the first time encountered in object graph.
+ final String className = input.readString();
+ type = getTypeByName(className);
+ if (type == null) {
+ try {
+ type = Class.forName(className, false, kryo.getClassLoader());
+ } catch (ClassNotFoundException ex) {
+ throw new KryoException("Unable to find class: " + className, ex);
+ }
+ if (nameToClass == null) nameToClass = new ObjectMap<>();
+ nameToClass.put(className, type);
+ }
+ nameIdToClass.put(nameId, type);
+ }
+ return kryo.getRegistration(type);
+ }
+
+ protected Class<?> getTypeByName(final String className) {
+ return nameToClass != null ? nameToClass.get(className) : null;
+ }
+
+ @Override
+ public void reset() {
+ if (!kryo.isRegistrationRequired()) {
+ if (classToNameId != null) classToNameId.clear();
+ if (nameIdToClass != null) nameIdToClass.clear();
+ nextNameId = 0;
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/KryoMapper.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/KryoMapper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/KryoMapper.java
new file mode 100644
index 0000000..bb31f0f
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/KryoMapper.java
@@ -0,0 +1,402 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.tinkerpop.gremlin.structure.io.kryo;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.KryoSerializable;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+import com.esotericsoftware.kryo.util.DefaultStreamFactory;
+import com.esotericsoftware.kryo.util.MapReferenceResolver;
+import com.tinkerpop.gremlin.process.Path;
+import com.tinkerpop.gremlin.process.T;
+import com.tinkerpop.gremlin.process.computer.MapReduce;
+import com.tinkerpop.gremlin.process.computer.util.MapMemory;
+import com.tinkerpop.gremlin.process.graph.util.Tree;
+import com.tinkerpop.gremlin.process.traverser.B_O_PA_S_SE_SL_Traverser;
+import com.tinkerpop.gremlin.process.traverser.B_O_P_PA_S_SE_SL_Traverser;
+import com.tinkerpop.gremlin.process.traverser.B_O_Traverser;
+import com.tinkerpop.gremlin.process.traverser.O_Traverser;
+import com.tinkerpop.gremlin.process.util.BulkSet;
+import com.tinkerpop.gremlin.process.util.metric.MutableMetrics;
+import com.tinkerpop.gremlin.process.util.metric.StandardTraversalMetrics;
+import com.tinkerpop.gremlin.process.util.TraverserSet;
+import com.tinkerpop.gremlin.structure.Contains;
+import com.tinkerpop.gremlin.structure.Direction;
+import com.tinkerpop.gremlin.structure.Edge;
+import com.tinkerpop.gremlin.structure.Property;
+import com.tinkerpop.gremlin.structure.Vertex;
+import com.tinkerpop.gremlin.structure.VertexProperty;
+import com.tinkerpop.gremlin.structure.io.Mapper;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedEdge;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedPath;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedProperty;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty;
+import org.javatuples.Triplet;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URI;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiPredicate;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+/**
+ * A {@link Mapper} implementation for Kryo.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class KryoMapper implements Mapper<Kryo> {
+ static final byte[] GIO = "gio".getBytes();
+ private final List<Triplet<Class, Function<Kryo, Serializer>, Integer>> serializationList;
+ private final HeaderWriter headerWriter;
+ private final HeaderReader headerReader;
+ private final byte[] versionedHeader;
+
+ public static final byte DEFAULT_EXTENDED_VERSION = Byte.MIN_VALUE;
+
+ private KryoMapper(final List<Triplet<Class, Function<Kryo, Serializer>, Integer>> serializationList,
+ final HeaderWriter headerWriter,
+ final HeaderReader headerReader) {
+ this.serializationList = serializationList;
+ this.headerWriter = headerWriter;
+ this.headerReader = headerReader;
+
+ final Output out = new Output(32);
+ try {
+ this.headerWriter.write(createMapper(), out);
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ this.versionedHeader = out.toBytes();
+ }
+
+ @Override
+ public Kryo createMapper() {
+ final Kryo kryo = new Kryo(new GremlinClassResolver(), new MapReferenceResolver(), new DefaultStreamFactory());
+ kryo.addDefaultSerializer(Map.Entry.class, new EntrySerializer());
+ kryo.setRegistrationRequired(true);
+ serializationList.forEach(p -> {
+ final Function<Kryo, Serializer> serializer = p.getValue1();
+ if (null == serializer)
+ kryo.register(p.getValue0(), kryo.getDefaultSerializer(p.getValue0()), p.getValue2());
+ else
+ kryo.register(p.getValue0(), serializer.apply(kryo), p.getValue2());
+ });
+ return kryo;
+ }
+
+ public HeaderWriter getHeaderWriter() {
+ return headerWriter;
+ }
+
+ public HeaderReader getHeaderReader() {
+ return headerReader;
+ }
+
+ /**
+ * Gets the header for a Gremlin Kryo file, which is based on the version of Gremlin Kryo that is constructed
+ * via the builder classes.
+ */
+ public byte[] getVersionedHeader() {
+ return versionedHeader;
+ }
+
+ @FunctionalInterface
+ public interface HeaderReader {
+ public void read(final Kryo kryo, final Input input) throws IOException;
+ }
+
+ @FunctionalInterface
+ public interface HeaderWriter {
+ public void write(final Kryo kryo, final Output output) throws IOException;
+ }
+
+ /**
+ * Use a specific version of Gremlin Kryo.
+ */
+ public static Builder build(final Version version) {
+ return version.getBuilder();
+ }
+
+ /**
+ * Use the most current version of Gremlin Kryo.
+ */
+ public static Builder build() {
+ return Version.V_1_0_0.getBuilder();
+ }
+
+ public static interface Builder {
+ /**
+ * Add mapper classes to serializes with kryo using standard serialization.
+ */
+ public Builder addCustom(final Class... custom);
+
+ /**
+ * Add mapper class to serializes with mapper serialization.
+ */
+ public Builder addCustom(final Class clazz, final Serializer serializer);
+
+ /**
+ * Add mapper class to serializes with mapper serialization as returned from a {@link Function}.
+ */
+ public Builder addCustom(final Class clazz, final Function<Kryo, Serializer> serializer);
+
+ /**
+ * If using mapper classes it might be useful to tag the version stamped to the serialization with a mapper
+ * value, such that Kryo serialization at 1.0.0 would have a fourth byte for an extended version. The user
+ * supplied fourth byte can then be used to ensure the right deserializer is used to read the data. If this
+ * value is not supplied then it is written as {@link Byte#MIN_VALUE}. The value supplied here should be greater
+ * than or equal to zero.
+ */
+ public Builder extendedVersion(final byte extendedVersion);
+
+ /**
+ * By default the {@link #extendedVersion(byte)} is checked against what is read from an input source and if
+ * those values are equal the version being read is considered "compliant". To alter this behavior, supply a
+ * mapper compliance {@link Predicate} to evaluate the value read from the input source (i.e. first argument)
+ * and the value marked in the {@code GremlinKryo} instance {i.e. second argument}. Supplying this function is
+ * useful when versions require backward compatibility or other more complex checks. This function is only used
+ * if the {@link #extendedVersion(byte)} is set to something other than its default.
+ */
+ public Builder compliant(final BiPredicate<Byte, Byte> compliant);
+
+ public KryoMapper create();
+ }
+
+ public enum Version {
+ V_1_0_0(BuilderV1d0.class);
+
+ private final Class<? extends Builder> builder;
+
+ private Version(final Class<? extends Builder> builder) {
+ this.builder = builder;
+ }
+
+ Builder getBuilder() {
+ try {
+ return builder.newInstance();
+ } catch (Exception x) {
+ throw new RuntimeException("GremlinKryo Builder implementation cannot be instantiated", x);
+ }
+ }
+ }
+
+ public static class BuilderV1d0 implements Builder {
+
+ /**
+ * Map with one entry that is used so that it is possible to get the class of LinkedHashMap.Entry.
+ */
+ private static final LinkedHashMap m = new LinkedHashMap() {{
+ put("junk", "dummy");
+ }};
+
+ private static final Class LINKED_HASH_MAP_ENTRY_CLASS = m.entrySet().iterator().next().getClass();
+
+ /**
+ * Note that the following are pre-registered boolean, Boolean, byte, Byte, char, Character, double, Double,
+ * int, Integer, float, Float, long, Long, short, Short, String, void.
+ */
+ private final List<Triplet<Class, Function<Kryo, Serializer>, Integer>> serializationList = new ArrayList<Triplet<Class, Function<Kryo, Serializer>, Integer>>() {{
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(byte[].class, null, 25));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(char[].class, null, 26));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(short[].class, null, 27));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(int[].class, null, 28));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(long[].class, null, 29));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(float[].class, null, 30));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(double[].class, null, 31));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(String[].class, null, 32));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Object[].class, null, 33));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(ArrayList.class, null, 10));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(BigInteger.class, null, 34));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(BigDecimal.class, null, 35));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Calendar.class, null, 39));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Class.class, null, 41));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Collection.class, null, 37));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Collections.EMPTY_LIST.getClass(), null, 51));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Collections.EMPTY_MAP.getClass(), null, 52));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Collections.EMPTY_SET.getClass(), null, 53));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Collections.singleton(null).getClass(), null, 54));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Collections.singletonList(null).getClass(), null, 24));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Collections.singletonMap(null, null).getClass(), null, 23));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Contains.class, null, 49));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Currency.class, null, 40));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Date.class, null, 38));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Direction.class, null, 12));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(DetachedEdge.class, null, 21));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(DetachedVertexProperty.class, null, 20));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(DetachedProperty.class, null, 18));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(DetachedVertex.class, null, 19));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(DetachedPath.class, null, 60));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(EdgeTerminator.class, null, 14));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(EnumSet.class, null, 46));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(HashMap.class, null, 11));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(HashMap.Entry.class, null, 16));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(KryoSerializable.class, null, 36));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(LinkedHashMap.class, null, 47));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(LinkedHashSet.class, null, 71));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(LINKED_HASH_MAP_ENTRY_CLASS, null, 15));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Locale.class, null, 22));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(StringBuffer.class, null, 43));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(StringBuilder.class, null, 44));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(T.class, null, 48));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(TimeZone.class, null, 42));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(TreeMap.class, null, 45));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(TreeSet.class, null, 50));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(UUID.class, kryo -> new UUIDSerializer(), 17));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(URI.class, kryo -> new URISerializer(), 72));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(VertexTerminator.class, null, 13));
+
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Edge.class, kryo -> new GraphSerializer.EdgeSerializer(), 65));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Vertex.class, kryo -> new GraphSerializer.VertexSerializer(), 66));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Property.class, kryo -> new GraphSerializer.PropertySerializer(), 67));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(VertexProperty.class, kryo -> new GraphSerializer.VertexPropertySerializer(), 68));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Path.class, kryo -> new GraphSerializer.PathSerializer(), 59));
+ // HACK!
+ //add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Traverser.Admin.class, kryo -> new GraphSerializer.TraverserSerializer(), 55));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(B_O_Traverser.class, null, 75));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(O_Traverser.class, null, 76));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(B_O_P_PA_S_SE_SL_Traverser.class, null, 77));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(B_O_PA_S_SE_SL_Traverser.class, null, 78)); // ***LAST ID***
+
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(TraverserSet.class, null, 58));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(Tree.class, null, 61));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(HashSet.class, null, 62));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(BulkSet.class, null, 64));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(MutableMetrics.class, null, 69));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(StandardTraversalMetrics.class, null, 70));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(MapMemory.class, null, 73));
+ add(Triplet.<Class, Function<Kryo, Serializer>, Integer>with(MapReduce.NullObject.class, null, 74));
+ }};
+
+ private static final byte major = 1;
+ private static final byte minor = 0;
+ private static final byte patchLevel = 0;
+
+ private byte extendedVersion = DEFAULT_EXTENDED_VERSION;
+ private BiPredicate<Byte, Byte> compliant = (readExt, serExt) -> readExt.equals(serExt);
+
+ /**
+ * Starts numbering classes for Kryo serialization at 65536 to leave room for future usage by TinkerPop.
+ */
+ private final AtomicInteger currentSerializationId = new AtomicInteger(65536);
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Builder addCustom(final Class... custom) {
+ if (custom != null && custom.length > 0)
+ serializationList.addAll(Arrays.asList(custom).stream()
+ .map(c -> Triplet.<Class, Function<Kryo, Serializer>, Integer>with(c, null, currentSerializationId.getAndIncrement()))
+ .collect(Collectors.<Triplet<Class, Function<Kryo, Serializer>, Integer>>toList()));
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Builder addCustom(final Class clazz, final Serializer serializer) {
+ serializationList.add(Triplet.with(clazz, kryo -> serializer, currentSerializationId.getAndIncrement()));
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Builder addCustom(final Class clazz, final Function<Kryo, Serializer> serializer) {
+ serializationList.add(Triplet.with(clazz, serializer, currentSerializationId.getAndIncrement()));
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Builder extendedVersion(final byte extendedVersion) {
+ if (extendedVersion > DEFAULT_EXTENDED_VERSION && extendedVersion < 0)
+ throw new IllegalArgumentException("The extendedVersion must be greater than zero");
+
+ this.extendedVersion = extendedVersion;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Builder compliant(final BiPredicate<Byte, Byte> compliant) {
+ if (null == compliant)
+ throw new IllegalArgumentException("compliant");
+
+ this.compliant = compliant;
+ return this;
+ }
+
+ @Override
+ public KryoMapper create() {
+ return new KryoMapper(serializationList, this::writeHeader, this::readHeader);
+ }
+
+ private void writeHeader(final Kryo kryo, final Output output) throws IOException {
+ // 32 byte header total
+ output.writeBytes(GIO);
+
+ // some space for later
+ output.writeBytes(new byte[25]);
+
+ // version x.y.z
+ output.writeByte(major);
+ output.writeByte(minor);
+ output.writeByte(patchLevel);
+ output.writeByte(extendedVersion);
+ }
+
+ private void readHeader(final Kryo kryo, final Input input) throws IOException {
+ if (!Arrays.equals(GIO, input.readBytes(3)))
+ throw new IOException("Invalid format - first three bytes of header do not match expected value");
+
+ // skip the next 25 bytes in v1
+ input.readBytes(25);
+
+ // final three bytes of header are the version which should be 1.0.0
+ final byte[] version = input.readBytes(3);
+ final byte extension = input.readByte();
+
+ // direct match on version for now
+ if (version[0] != major || version[1] != minor || version[2] != patchLevel)
+ throw new IOException(String.format(
+ "The version [%s.%s.%s] in the stream cannot be understood by this reader",
+ version[0], version[1], version[2]));
+
+ if (extendedVersion >= 0 && !compliant.test(extension, extendedVersion))
+ throw new IOException(String.format(
+ "The extension [%s] in the input source is not compliant with this configuration of GremlinKryo - [%s]",
+ extension, extendedVersion));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/KryoReader.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/KryoReader.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/KryoReader.java
new file mode 100644
index 0000000..25df118
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/KryoReader.java
@@ -0,0 +1,400 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.tinkerpop.gremlin.structure.io.kryo;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+import com.tinkerpop.gremlin.process.T;
+import com.tinkerpop.gremlin.structure.*;
+import com.tinkerpop.gremlin.structure.io.GraphReader;
+import com.tinkerpop.gremlin.structure.util.batch.BatchGraph;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedEdge;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Function;
+
+/**
+ * The {@link GraphReader} for the Gremlin Structure serialization format based on Kryo. The format is meant to be
+ * non-lossy in terms of Gremlin Structure to Gremlin Structure migrations (assuming both structure implementations
+ * support the same graph features).
+ * <br/>
+ * This implementation is not thread-safe.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public class KryoReader implements GraphReader {
+ private final Kryo kryo;
+ private final KryoMapper.HeaderReader headerReader;
+
+ private final long batchSize;
+ private final String vertexIdKey;
+ private final String edgeIdKey;
+
+ private final File tempFile;
+
+ final AtomicLong counter = new AtomicLong(0);
+
+ private KryoReader(final File tempFile, final long batchSize,
+ final String vertexIdKey, final String edgeIdKey,
+ final KryoMapper kryoMapper) {
+ this.kryo = kryoMapper.createMapper();
+ this.headerReader = kryoMapper.getHeaderReader();
+ this.vertexIdKey = vertexIdKey;
+ this.edgeIdKey = edgeIdKey;
+ this.tempFile = tempFile;
+ this.batchSize = batchSize;
+ }
+
+ @Override
+ public Iterator<Vertex> readVertices(final InputStream inputStream, final Direction direction,
+ final Function<DetachedVertex, Vertex> vertexMaker,
+ final Function<DetachedEdge, Edge> edgeMaker) throws IOException {
+ final Input input = new Input(inputStream);
+ return new Iterator<Vertex>() {
+ @Override
+ public boolean hasNext() {
+ return !input.eof();
+ }
+
+ @Override
+ public Vertex next() {
+ try {
+ final Vertex v = readVertex(direction, vertexMaker, edgeMaker, input);
+
+ // read the vertex terminator
+ kryo.readClassAndObject(input);
+
+ return v;
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ };
+ }
+
+ @Override
+ public Edge readEdge(final InputStream inputStream, final Function<DetachedEdge, Edge> edgeMaker) throws IOException {
+ final Input input = new Input(inputStream);
+ this.headerReader.read(kryo, input);
+ final Object o = kryo.readClassAndObject(input);
+ return edgeMaker.apply((DetachedEdge) o);
+ }
+
+ @Override
+ public Vertex readVertex(final InputStream inputStream, final Function<DetachedVertex, Vertex> vertexMaker) throws IOException {
+ return readVertex(inputStream, null, vertexMaker, null);
+ }
+
+ @Override
+ public Vertex readVertex(final InputStream inputStream, final Direction direction, Function<DetachedVertex, Vertex> vertexMaker, final Function<DetachedEdge, Edge> edgeMaker) throws IOException {
+ final Input input = new Input(inputStream);
+ return readVertex(direction, vertexMaker, edgeMaker, input);
+ }
+
+ @Override
+ public void readGraph(final InputStream inputStream, final Graph graphToWriteTo) throws IOException {
+ this.counter.set(0);
+ final Input input = new Input(inputStream);
+ this.headerReader.read(kryo, input);
+
+ final BatchGraph graph;
+ try {
+ // will throw an exception if not constructed properly
+ graph = BatchGraph.build(graphToWriteTo)
+ .vertexIdKey(vertexIdKey)
+ .edgeIdKey(edgeIdKey)
+ .bufferSize(batchSize).create();
+ } catch (Exception ex) {
+ throw new IOException("Could not instantiate BatchGraph wrapper", ex);
+ }
+
+ try (final Output output = new Output(new FileOutputStream(tempFile))) {
+ final boolean supportedMemory = input.readBoolean();
+ if (supportedMemory) {
+ // if the graph that serialized the data supported sideEffects then the sideEffects needs to be read
+ // to advance the reader forward. if the graph being read into doesn't support the sideEffects
+ // then we just setting the data to sideEffects.
+ final Map<String, Object> memMap = (Map<String, Object>) kryo.readObject(input, HashMap.class);
+ if (graphToWriteTo.features().graph().variables().supportsVariables()) {
+ final Graph.Variables variables = graphToWriteTo.variables();
+ memMap.forEach(variables::set);
+ }
+ }
+
+ final boolean hasSomeVertices = input.readBoolean();
+ if (hasSomeVertices) {
+ final List<Object> vertexArgs = new ArrayList<>();
+ while (!input.eof()) {
+ final DetachedVertex current = (DetachedVertex) kryo.readClassAndObject(input);
+ appendToArgList(vertexArgs, T.id, current.id());
+ appendToArgList(vertexArgs, T.label, current.label());
+
+ final Vertex v = graph.addVertex(vertexArgs.toArray());
+ vertexArgs.clear();
+ current.iterators().propertyIterator().forEachRemaining(p -> createVertexProperty(graphToWriteTo, v, p, false));
+
+ // the gio file should have been written with a direction specified
+ final boolean hasDirectionSpecified = input.readBoolean();
+ final Direction directionInStream = kryo.readObject(input, Direction.class);
+ final Direction directionOfEdgeBatch = kryo.readObject(input, Direction.class);
+
+ // graph serialization requires that a direction be specified in the stream and that the
+ // direction of the edges be OUT
+ if (!hasDirectionSpecified || directionInStream != Direction.OUT || directionOfEdgeBatch != Direction.OUT)
+ throw new IllegalStateException(String.format("Stream must specify edge direction and that direction must be %s", Direction.OUT));
+
+ // if there are edges then read them to end and write to temp, otherwise read what should be
+ // the vertex terminator
+ if (!input.readBoolean())
+ kryo.readClassAndObject(input);
+ else
+ readToEndOfEdgesAndWriteToTemp(input, output);
+ }
+ }
+ } catch (Exception ex) {
+ throw new IOException(ex);
+ }
+ // done writing to temp
+
+ // start reading in the edges now from the temp file
+ try (final Input edgeInput = new Input(new FileInputStream(tempFile))) {
+ readFromTempEdges(edgeInput, graph);
+ graph.tx().commit();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ throw new IOException(ex);
+ } finally {
+ deleteTempFileSilently();
+ }
+ }
+
+ private static void createVertexProperty(final Graph graphToWriteTo, final Vertex v, final VertexProperty<Object> p, final boolean hidden) {
+ final List<Object> propertyArgs = new ArrayList<>();
+ if (graphToWriteTo.features().vertex().properties().supportsUserSuppliedIds())
+ appendToArgList(propertyArgs, T.id, p.id());
+ p.iterators().propertyIterator().forEachRemaining(it -> appendToArgList(propertyArgs, it.key(), it.value()));
+ v.property(p.key(), p.value(), propertyArgs.toArray());
+ }
+
+ private static void appendToArgList(final List<Object> propertyArgs, final Object key, final Object val) {
+ propertyArgs.add(key);
+ propertyArgs.add(val);
+ }
+
+ private Vertex readVertex(final Direction directionRequested, final Function<DetachedVertex, Vertex> vertexMaker,
+ final Function<DetachedEdge, Edge> edgeMaker, final Input input) throws IOException {
+ if (null != directionRequested && null == edgeMaker)
+ throw new IllegalArgumentException("If a directionRequested is specified then an edgeAdder function should also be specified");
+
+ this.headerReader.read(kryo, input);
+
+ final DetachedVertex detachedVertex = (DetachedVertex) kryo.readClassAndObject(input);
+ final Vertex v = vertexMaker.apply(detachedVertex);
+
+ final boolean streamContainsEdgesInSomeDirection = input.readBoolean();
+ if (!streamContainsEdgesInSomeDirection && directionRequested != null)
+ throw new IllegalStateException(String.format("The direction %s was requested but no attempt was made to serialize edges into this stream", directionRequested));
+
+ // if there are edges in the stream and the direction is not present then the rest of the stream is
+ // simply ignored
+ if (directionRequested != null) {
+ final Direction directionsInStream = kryo.readObject(input, Direction.class);
+ if (directionsInStream != Direction.BOTH && directionsInStream != directionRequested)
+ throw new IllegalStateException(String.format("Stream contains %s edges, but requesting %s", directionsInStream, directionRequested));
+
+ final Direction firstDirection = kryo.readObject(input, Direction.class);
+ if (firstDirection == Direction.OUT && (directionRequested == Direction.BOTH || directionRequested == Direction.OUT))
+ readEdges(input, edgeMaker);
+ else {
+ // requested direction in, but BOTH must be serialized so skip this. the illegalstateexception
+ // prior to this IF should have caught a problem where IN is not supported at all
+ if (firstDirection == Direction.OUT && directionRequested == Direction.IN)
+ skipEdges(input);
+ }
+
+ if (directionRequested == Direction.BOTH || directionRequested == Direction.IN) {
+ // if the first direction was OUT then it was either read or skipped. in that case, the marker
+ // of the stream is currently ready to read the IN direction. otherwise it's in the perfect place
+ // to start reading edges
+ if (firstDirection == Direction.OUT)
+ kryo.readObject(input, Direction.class);
+
+ readEdges(input, edgeMaker);
+ }
+ }
+
+ return v;
+ }
+
+ private void readEdges(final Input input, final Function<DetachedEdge, Edge> edgeMaker) {
+ if (input.readBoolean()) {
+ Object next = kryo.readClassAndObject(input);
+ while (!next.equals(EdgeTerminator.INSTANCE)) {
+ final DetachedEdge detachedEdge = (DetachedEdge) next;
+ edgeMaker.apply(detachedEdge);
+ next = kryo.readClassAndObject(input);
+ }
+ }
+ }
+
+ private void skipEdges(final Input input) {
+ if (input.readBoolean()) {
+ Object next = kryo.readClassAndObject(input);
+ while (!next.equals(EdgeTerminator.INSTANCE)) {
+ // next edge to skip or the terminator
+ next = kryo.readClassAndObject(input);
+ }
+ }
+ }
+
+ /**
+ * Reads through the all the edges for a vertex and writes the edges to a temp file which will be read later.
+ */
+ private void readToEndOfEdgesAndWriteToTemp(final Input input, final Output output) throws IOException {
+ Object next = kryo.readClassAndObject(input);
+ while (!next.equals(EdgeTerminator.INSTANCE)) {
+ kryo.writeClassAndObject(output, next);
+
+ // next edge or terminator
+ next = kryo.readClassAndObject(input);
+ }
+
+ // this should be the vertex terminator
+ kryo.readClassAndObject(input);
+
+ kryo.writeClassAndObject(output, EdgeTerminator.INSTANCE);
+ kryo.writeClassAndObject(output, VertexTerminator.INSTANCE);
+ }
+
+
+ /**
+ * Read the edges from the temp file and load them to the graph.
+ */
+ private void readFromTempEdges(final Input input, final Graph graphToWriteTo) {
+ final List<Object> edgeArgs = new ArrayList<>();
+ while (!input.eof()) {
+ // in this case the outId is the id assigned by the graph
+ Object next = kryo.readClassAndObject(input);
+ while (!next.equals(EdgeTerminator.INSTANCE)) {
+ final DetachedEdge detachedEdge = (DetachedEdge) next;
+ final Vertex vOut = graphToWriteTo.iterators().vertexIterator(detachedEdge.iterators().vertexIterator(Direction.OUT).next().id()).next();
+ final Vertex inV = graphToWriteTo.iterators().vertexIterator(detachedEdge.iterators().vertexIterator(Direction.IN).next().id()).next();
+
+ detachedEdge.iterators().propertyIterator().forEachRemaining(p -> edgeArgs.addAll(Arrays.asList(p.key(), p.value())));
+
+ appendToArgList(edgeArgs, T.id, detachedEdge.id());
+
+ vOut.addEdge(detachedEdge.label(), inV, edgeArgs.toArray());
+
+ edgeArgs.clear();
+ next = kryo.readClassAndObject(input);
+ }
+
+ // vertex terminator
+ kryo.readClassAndObject(input);
+ }
+ }
+
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ private void deleteTempFileSilently() {
+ try {
+ tempFile.delete();
+ } catch (Exception ignored) {
+ }
+ }
+
+ public static Builder build() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private File tempFile;
+ private long batchSize = BatchGraph.DEFAULT_BUFFER_SIZE;
+ private String vertexIdKey = T.id.getAccessor();
+ private String edgeIdKey = T.id.getAccessor();
+
+ /**
+ * Always use the most recent kryo version by default
+ */
+ private KryoMapper kryoMapper = KryoMapper.build().create();
+
+ private Builder() {
+ this.tempFile = new File(UUID.randomUUID() + ".tmp");
+ }
+
+ /**
+ * Set the size between commits when reading into the {@link Graph} instance. This value defaults to
+ * {@link BatchGraph#DEFAULT_BUFFER_SIZE}.
+ */
+ public Builder batchSize(final long batchSize) {
+ this.batchSize = batchSize;
+ return this;
+ }
+
+ /**
+ * Supply a mapper {@link KryoMapper} instance to use as the serializer for the {@code KryoWriter}.
+ */
+ public Builder mapper(final KryoMapper kryoMapper) {
+ this.kryoMapper = kryoMapper;
+ return this;
+ }
+
+ /**
+ * The name of the key to supply to
+ * {@link com.tinkerpop.gremlin.structure.util.batch.BatchGraph.Builder#vertexIdKey} when reading data into
+ * the {@link Graph}.
+ */
+ public Builder vertexIdKey(final String vertexIdKey) {
+ this.vertexIdKey = vertexIdKey;
+ return this;
+ }
+
+ /**
+ * The name of the key to supply to
+ * {@link com.tinkerpop.gremlin.structure.util.batch.BatchGraph.Builder#edgeIdKey} when reading data into
+ * the {@link Graph}.
+ */
+ public Builder edgeIdKey(final String edgeIdKey) {
+ this.edgeIdKey = edgeIdKey;
+ return this;
+ }
+
+ /**
+ * The reader requires a working directory to write temp files to. If this value is not set, it will write
+ * the temp file to the local directory.
+ */
+ public Builder workingDirectory(final String workingDirectory) {
+ final File f = new File(workingDirectory);
+ if (!f.exists() || !f.isDirectory())
+ throw new IllegalArgumentException(String.format("%s is not a directory or does not exist", workingDirectory));
+
+ tempFile = new File(workingDirectory + File.separator + UUID.randomUUID() + ".tmp");
+ return this;
+ }
+
+ public KryoReader create() {
+ return new KryoReader(tempFile, batchSize, this.vertexIdKey, this.edgeIdKey, this.kryoMapper);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/KryoWriter.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/KryoWriter.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/KryoWriter.java
new file mode 100644
index 0000000..b1af298
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/KryoWriter.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.tinkerpop.gremlin.structure.io.kryo;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.io.Output;
+import com.tinkerpop.gremlin.structure.Direction;
+import com.tinkerpop.gremlin.structure.Edge;
+import com.tinkerpop.gremlin.structure.Element;
+import com.tinkerpop.gremlin.structure.Graph;
+import com.tinkerpop.gremlin.structure.Vertex;
+import com.tinkerpop.gremlin.structure.io.GraphWriter;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.UUID;
+
+/**
+ * The {@link GraphWriter} for the Gremlin Structure serialization format based on Kryo. The format is meant to be
+ * non-lossy in terms of Gremlin Structure to Gremlin Structure migrations (assuming both structure implementations
+ * support the same graph features).
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class KryoWriter implements GraphWriter {
+ private Kryo kryo;
+ private final KryoMapper.HeaderWriter headerWriter;
+ private static final UUID delimiter = UUID.fromString("2DEE3ABF-9963-4546-A578-C1C48690D7F7");
+ public static final byte[] DELIMITER = new byte[16];
+
+ static {
+ final ByteBuffer bb = ByteBuffer.wrap(DELIMITER);
+ bb.putLong(delimiter.getMostSignificantBits());
+ bb.putLong(delimiter.getLeastSignificantBits());
+ }
+
+ private KryoWriter(final KryoMapper kryoMapper) {
+ this.kryo = kryoMapper.createMapper();
+ this.headerWriter = kryoMapper.getHeaderWriter();
+ }
+
+ @Override
+ public void writeGraph(final OutputStream outputStream, final Graph g) throws IOException {
+ final Output output = new Output(outputStream);
+ this.headerWriter.write(kryo, output);
+
+ final boolean supportsGraphMemory = g.features().graph().variables().supportsVariables();
+ output.writeBoolean(supportsGraphMemory);
+ if (supportsGraphMemory)
+ kryo.writeObject(output, new HashMap(g.variables().asMap()));
+
+ final Iterator<Vertex> vertices = g.iterators().vertexIterator();
+ final boolean hasSomeVertices = vertices.hasNext();
+ output.writeBoolean(hasSomeVertices);
+ while (vertices.hasNext()) {
+ final Vertex v = vertices.next();
+ writeVertexToOutput(output, v, Direction.OUT);
+ }
+
+ output.flush();
+ }
+
+ @Override
+ public void writeVertex(final OutputStream outputStream, final Vertex v, final Direction direction) throws IOException {
+ final Output output = new Output(outputStream);
+ this.headerWriter.write(kryo, output);
+ writeVertexToOutput(output, v, direction);
+ output.flush();
+ }
+
+ @Override
+ public void writeVertex(final OutputStream outputStream, final Vertex v) throws IOException {
+ final Output output = new Output(outputStream);
+ this.headerWriter.write(kryo, output);
+ writeVertexWithNoEdgesToOutput(output, v);
+ output.flush();
+ }
+
+ @Override
+ public void writeEdge(final OutputStream outputStream, final Edge e) throws IOException {
+ final Output output = new Output(outputStream);
+ this.headerWriter.write(kryo, output);
+ kryo.writeClassAndObject(output, DetachedFactory.detach(e, true));
+ output.flush();
+ }
+
+ private void writeEdgeToOutput(final Output output, final Edge e) {
+ this.writeElement(output, e, null);
+ }
+
+ private void writeVertexWithNoEdgesToOutput(final Output output, final Vertex v) {
+ writeElement(output, v, null);
+ }
+
+ private void writeVertexToOutput(final Output output, final Vertex v, final Direction direction) {
+ this.writeElement(output, v, direction);
+ }
+
+ private void writeElement(final Output output, final Element e, final Direction direction) {
+ kryo.writeClassAndObject(output, e);
+
+ if (e instanceof Vertex) {
+ output.writeBoolean(direction != null);
+ if (direction != null) {
+ final Vertex v = (Vertex) e;
+ kryo.writeObject(output, direction);
+ if (direction == Direction.BOTH || direction == Direction.OUT)
+ writeDirectionalEdges(output, Direction.OUT, v.iterators().edgeIterator(Direction.OUT));
+
+ if (direction == Direction.BOTH || direction == Direction.IN)
+ writeDirectionalEdges(output, Direction.IN, v.iterators().edgeIterator(Direction.IN));
+ }
+
+ kryo.writeClassAndObject(output, VertexTerminator.INSTANCE);
+ }
+ }
+
+ private void writeDirectionalEdges(final Output output, final Direction d, final Iterator<Edge> vertexEdges) {
+ final boolean hasEdges = vertexEdges.hasNext();
+ kryo.writeObject(output, d);
+ output.writeBoolean(hasEdges);
+
+ while (vertexEdges.hasNext()) {
+ final Edge edgeToWrite = vertexEdges.next();
+ writeEdgeToOutput(output, edgeToWrite);
+ }
+
+ if (hasEdges)
+ kryo.writeClassAndObject(output, EdgeTerminator.INSTANCE);
+ }
+
+ public static Builder build() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ /**
+ * Always creates the most current version available.
+ */
+ private KryoMapper kryoMapper = KryoMapper.build().create();
+
+ private Builder() {
+ }
+
+ /**
+ * Supply a mapper {@link KryoMapper} instance to use as the serializer for the {@code KryoWriter}.
+ */
+ public Builder mapper(final KryoMapper kryoMapper) {
+ this.kryoMapper = kryoMapper;
+ return this;
+ }
+
+ /**
+ * Create the {@code KryoWriter}.
+ */
+ public KryoWriter create() {
+ return new KryoWriter(this.kryoMapper);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/URISerializer.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/URISerializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/URISerializer.java
new file mode 100644
index 0000000..fd8a59d
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/URISerializer.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.tinkerpop.gremlin.structure.io.kryo;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+import java.net.URI;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+class URISerializer extends Serializer<URI> {
+
+ public URISerializer() {
+ setImmutable(true);
+ }
+
+ @Override
+ public void write(final Kryo kryo, final Output output, final URI uri) {
+ output.writeString(uri.toString());
+ }
+
+ @Override
+ public URI read(final Kryo kryo, final Input input, final Class<URI> uriClass) {
+ return URI.create(input.readString());
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/UUIDSerializer.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/UUIDSerializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/UUIDSerializer.java
new file mode 100644
index 0000000..f43e503
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/kryo/UUIDSerializer.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.tinkerpop.gremlin.structure.io.kryo;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+import java.util.UUID;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+class UUIDSerializer extends Serializer<UUID> {
+ public UUIDSerializer() {
+ setImmutable(true);
+ }
+
+ @Override
+ public void write(final Kryo kryo, final Output output, final UUID uuid) {
+ output.writeLong(uuid.getMostSignificantBits());
+ output.writeLong(uuid.getLeastSignificantBits());
+ }
+
+ @Override
+ public UUID read(final Kryo kryo, final Input input, final Class<UUID> uuidClass) {
+ return new UUID(input.readLong(), input.readLong());
+ }
+}