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:37 UTC

[11/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/Transaction.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Transaction.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Transaction.java
new file mode 100644
index 0000000..7720885
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Transaction.java
@@ -0,0 +1,384 @@
+/*
+ * 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;
+
+import java.io.Closeable;
+import java.util.Collections;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * A set of methods that allow for control of transactional behavior of a {@link Graph} instance.
+ *
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ * @author TinkerPop Community (http://tinkerpop.com)
+ */
+public interface Transaction extends Closeable {
+
+    /**
+     * Opens a transaction.
+     */
+    public void open();
+
+    /**
+     * Commits a transaction.
+     */
+    public void commit();
+
+    /**
+     * Rolls back a transaction.
+     */
+    public void rollback();
+
+    /**
+     * Submit a unit of work that represents a transaction returning a {@link Workload} that can be automatically
+     * retried in the event of failure.
+     */
+    public <R> Workload<R> submit(final Function<Graph, R> work);
+
+    /**
+     * Creates a transaction that can be executed across multiple threads.
+     */
+    public <G extends Graph> G create();
+
+    /**
+     * Determines if a transaction is currently open.
+     */
+    public boolean isOpen();
+
+    /**
+     * An internal function that signals a read or a write has occurred - not meant to be called directly by end users.
+     */
+    public void readWrite();
+
+    /**
+     * Closes the transaction where the default close behavior will be executed.
+     */
+    @Override
+    public void close();
+
+    /**
+     * Describes how a transaction is started when a read or a write occurs.  This value can be set using standard
+     * behaviors defined in {@link READ_WRITE_BEHAVIOR} or a mapper {@link Consumer} function.
+     */
+    public Transaction onReadWrite(final Consumer<Transaction> consumer);
+
+    /**
+     * Describes what happens to a transaction on a call to {@link com.tinkerpop.gremlin.structure.Graph#close()}.
+     * This value can be set using standard behavior defined in {@link CLOSE_BEHAVIOR} or a mapper {@link Consumer}
+     * function.
+     */
+    public Transaction onClose(final Consumer<Transaction> consumer);
+
+    public static class Exceptions {
+        public static IllegalStateException transactionAlreadyOpen() {
+            return new IllegalStateException("Stop the current transaction before opening another");
+        }
+
+        public static IllegalStateException transactionMustBeOpenToReadWrite() {
+            return new IllegalStateException("Open a transaction before attempting to read/write the transaction");
+        }
+
+        public static IllegalStateException openTransactionsOnClose() {
+            return new IllegalStateException("Commit or rollback all outstanding transactions before closing the transaction");
+        }
+
+        public static UnsupportedOperationException threadedTransactionsNotSupported() {
+            return new UnsupportedOperationException("Graph does not support threaded transactions");
+        }
+
+        public static IllegalArgumentException onCloseBehaviorCannotBeNull() {
+            return new IllegalArgumentException("Transaction behavior for onClose cannot be null");
+        }
+
+        public static IllegalArgumentException onReadWriteBehaviorCannotBeNull() {
+            return new IllegalArgumentException("Transaction behavior for onReadWrite cannot be null");
+        }
+    }
+
+    /**
+     * Behaviors to supply to the {@link #onClose(java.util.function.Consumer)}.
+     */
+    public static enum CLOSE_BEHAVIOR implements Consumer<Transaction> {
+        /**
+         * Any open transaction will commit on close.
+         */
+        COMMIT {
+            @Override
+            public void accept(final Transaction transaction) {
+                if (transaction.isOpen()) transaction.commit();
+            }
+        },
+
+        /**
+         * Any open transaction will rollback on close.
+         */
+        ROLLBACK {
+            @Override
+            public void accept(final Transaction transaction) {
+                if (transaction.isOpen()) transaction.rollback();
+            }
+        },
+
+        /**
+         * Open transactions on close will throw an exception
+         */
+        MANUAL {
+            @Override
+            public void accept(final Transaction transaction) {
+                if (transaction.isOpen()) throw Exceptions.openTransactionsOnClose();
+            }
+        }
+    }
+
+    /**
+     * Behaviors to supply to the {@link #onReadWrite(java.util.function.Consumer)}.
+     */
+    public static enum READ_WRITE_BEHAVIOR implements Consumer<Transaction> {
+        /**
+         * Transactions are automatically started when a read or a write occurs.
+         */
+        AUTO {
+            @Override
+            public void accept(final Transaction transaction) {
+                if (!transaction.isOpen()) transaction.open();
+            }
+        },
+
+        /**
+         * Transactions must be explicitly opened for operations to occur on the graph.
+         */
+        MANUAL {
+            @Override
+            public void accept(final Transaction transaction) {
+                if (!transaction.isOpen()) throw Exceptions.transactionMustBeOpenToReadWrite();
+            }
+        }
+    }
+
+    /**
+     * A {@link Workload} represents a unit of work constructed by the
+     * {@link Workload#submit(java.util.function.Function)} method on the {@link Transaction} interface.
+     * The unit of work is a {@link java.util.function.Function} typically containing mutations to the {@link Graph}.  The
+     * {@link Workload} is responsible for executing the unit of work in the context of a retry strategy, such that a
+     * failed unit of work is rolled back and executed again until retries are exhausted or the unit of work succeeds
+     * with a commit operation.
+     *
+     * @param <R> The type of the result from the unit of work.
+     */
+    public static class Workload<R> {
+        public static final long DEFAULT_DELAY_MS = 20;
+        public static final int DEFAULT_TRIES = 8;
+
+        private final Function<Graph, R> workToDo;
+        private final Graph g;
+
+        /**
+         * Creates a new {@link Workload} that will be tried to be executed within a transaction.
+         *
+         * @param g    The {@link Graph} instance on which the work will be performed.
+         * @param work The work to be executed on the Graph instance which will optionally return a value.
+         */
+        public Workload(final Graph g, final Function<Graph, R> work) {
+            this.g = g;
+            this.workToDo = work;
+        }
+
+        /**
+         * Try to execute a {@link Workload} with a mapper retry strategy.
+         *
+         * @param retryStrategy The first argument to this function is the Graph instance and the second is
+         *                      the encapsulated work to be performed.  The function should ultimately return the
+         *                      result of the encapsulated work function.
+         * @return The result of the encapsulated work.
+         */
+        public R attempt(final BiFunction<Graph, Function<Graph, R>, R> retryStrategy) {
+            return retryStrategy.apply(g, workToDo);
+        }
+
+        /**
+         * Executes the {@link Workload} committing if possible and rolling back on failure.  On failure, an exception
+         * is reported.
+         */
+        public R oneAndDone() {
+            return attempt((g, w) -> {
+                try {
+                    R result = w.apply(g);
+                    g.tx().commit();
+
+                    return result;
+                } catch (Throwable t) {
+                    g.tx().rollback();
+                    throw new RuntimeException(t);
+                }
+            });
+        }
+
+        /**
+         * Executes the {@link Workload} committing if possible and rolling back on failure.  On failure no exception
+         * is reported.
+         */
+        public R fireAndForget() {
+            return attempt((g, w) -> {
+                R result = null;
+                try {
+                    result = w.apply(g);
+                    g.tx().commit();
+                } catch (Throwable t) {
+                    g.tx().rollback();
+                }
+
+                return result;
+            });
+        }
+
+        /**
+         * Executes the {@link Workload} with the default number of retries and with the default number of
+         * milliseconds delay between each try.
+         */
+        public R retry() {
+            return retry(DEFAULT_TRIES);
+        }
+
+        /**
+         * Executes the {@link Workload} with a number of retries and with the default number of milliseconds delay
+         * between each try.
+         */
+        public R retry(final int tries) {
+            return retry(tries, DEFAULT_DELAY_MS);
+        }
+
+        /**
+         * Executes the {@link Workload} with a number of retries and with a number of milliseconds delay between
+         * each try.
+         */
+        public R retry(final int tries, final long delay) {
+            return retry(tries, delay, Collections.emptySet());
+        }
+
+        /**
+         * Executes the {@link Workload} with a number of retries and with a number of milliseconds delay between each
+         * try and will only retry on the set of supplied exceptions.  Exceptions outside of that set will generate a
+         * RuntimeException and immediately fail.
+         */
+        public R retry(final int tries, final long delay, final Set<Class> exceptionsToRetryOn) {
+            return attempt(retry(tries, exceptionsToRetryOn, i -> delay));
+        }
+
+        /**
+         * Executes the {@link Workload} with the default number of retries and with a exponentially increasing
+         * number of milliseconds between each retry using the default retry delay.
+         */
+        public R exponentialBackoff() {
+            return exponentialBackoff(DEFAULT_TRIES);
+        }
+
+        /**
+         * Executes the {@link Workload} with a number of retries and with a exponentially increasing number of
+         * milliseconds between each retry using the default retry delay.
+         */
+        public R exponentialBackoff(final int tries) {
+            return exponentialBackoff(tries, DEFAULT_DELAY_MS);
+        }
+
+        /**
+         * Executes the {@link Workload} with a number of retries and with a exponentially increasing number of
+         * milliseconds between each retry.
+         */
+        public R exponentialBackoff(final int tries, final long initialDelay) {
+            return exponentialBackoff(tries, initialDelay, Collections.emptySet());
+        }
+
+        /**
+         * Executes the {@link Workload} with a number of retries and with a exponentially increasing number of
+         * milliseconds between each retry.  It will only retry on the set of supplied exceptions.  Exceptions outside
+         * of that set will generate a {@link RuntimeException} and immediately fail.
+         */
+        public R exponentialBackoff(final int tries, final long initialDelay, final Set<Class> exceptionsToRetryOn) {
+            return attempt(retry(tries, exceptionsToRetryOn, retryCount -> (long) (initialDelay * Math.pow(2, retryCount))));
+        }
+
+        /**
+         * Creates a generic retry function to be passed to the {@link Workload#attempt(java.util.function.BiFunction)}
+         * method.
+         */
+        private static <R> BiFunction<Graph, Function<Graph, R>, R> retry(final int tries,
+                                                                          final Set<Class> exceptionsToRetryOn,
+                                                                          final Function<Integer, Long> delay) {
+            return (g, w) -> {
+                R returnValue;
+
+                // this is the default exception...it may get reassgined during retries
+                Exception previousException = new RuntimeException("Exception initialized when trying commit");
+
+                // try to commit a few times
+                for (int ix = 0; ix < tries; ix++) {
+
+                    // increase time after each failed attempt though there is no delay on the first try
+                    if (ix > 0)
+                        try {
+                            Thread.sleep(delay.apply(ix));
+                        } catch (InterruptedException ignored) {
+                        }
+
+                    try {
+                        // ensure that a transaction is open for this try. even if there was an open transaction
+                        // from the first try it would be rolled back on failure and unless automatic transactions
+                        // are used, the transaction would be closed on the next retry.
+                        if (!g.tx().isOpen()) g.tx().open();
+
+                        returnValue = w.apply(g);
+                        g.tx().commit();
+
+                        // need to exit the function here so that retries don't happen
+                        return returnValue;
+                    } catch (Exception ex) {
+                        g.tx().rollback();
+
+                        // retry if this is an allowed exception otherwise, just throw and go
+                        boolean retry = false;
+                        if (exceptionsToRetryOn.size() == 0)
+                            retry = true;
+                        else {
+                            for (Class exceptionToRetryOn : exceptionsToRetryOn) {
+                                if (ex.getClass().equals(exceptionToRetryOn)) {
+                                    retry = true;
+                                    break;
+                                }
+                            }
+                        }
+
+                        if (!retry) {
+                            throw new RuntimeException(ex);
+                        }
+
+                        previousException = ex;
+                    }
+                }
+
+                // the exception just won't go away after all the retries
+                throw new RuntimeException(previousException);
+            };
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Vertex.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Vertex.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Vertex.java
new file mode 100644
index 0000000..b2fb418
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Vertex.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.tinkerpop.gremlin.structure;
+
+import com.tinkerpop.gremlin.process.graph.traversal.VertexTraversal;
+import com.tinkerpop.gremlin.structure.util.ElementHelper;
+
+import java.util.Iterator;
+import java.util.Optional;
+
+/**
+ * A {@link Vertex} maintains pointers to both a set of incoming and outgoing {@link Edge} objects. The outgoing edges
+ * are those edges for  which the {@link Vertex} is the tail. The incoming edges are those edges for which the
+ * {@link Vertex} is the head.
+ * <p/>
+ * Diagrammatically:
+ * <pre>
+ * ---inEdges---> vertex ---outEdges--->.
+ * </pre>
+ *
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public interface Vertex extends Element, VertexTraversal {
+
+    /**
+     * The default label to use for a vertex.
+     */
+    public static final String DEFAULT_LABEL = "vertex";
+
+    /**
+     * Add an outgoing edge to the vertex with provided label and edge properties as key/value pairs.
+     * These key/values must be provided in an even number where the odd numbered arguments are {@link String}
+     * property keys and the even numbered arguments are the related property values.
+     *
+     * @param label     The label of the edge
+     * @param inVertex  The vertex to receive an incoming edge from the current vertex
+     * @param keyValues The key/value pairs to turn into edge properties
+     * @return the newly created edge
+     */
+    public Edge addEdge(final String label, final Vertex inVertex, final Object... keyValues);
+
+    @Override
+    public default <V> VertexProperty<V> property(final String key) {
+        final Iterator<VertexProperty<V>> iterator = this.iterators().propertyIterator(key);
+        if (iterator.hasNext()) {
+            final VertexProperty<V> property = iterator.next();
+            if (iterator.hasNext())
+                throw Vertex.Exceptions.multiplePropertiesExistForProvidedKey(key);
+            else
+                return property;
+        } else {
+            return VertexProperty.<V>empty();
+        }
+    }
+
+    @Override
+    public <V> VertexProperty<V> property(final String key, final V value);
+
+    public default <V> VertexProperty<V> property(final String key, final V value, final Object... keyValues) {
+        ElementHelper.legalPropertyKeyValueArray(keyValues);
+        final Optional<Object> optionalId = ElementHelper.getIdValue(keyValues);
+        if (optionalId.isPresent() && !graph().features().vertex().properties().supportsUserSuppliedIds())
+            throw VertexProperty.Exceptions.userSuppliedIdsNotSupported();
+
+        final VertexProperty<V> vertexProperty = this.property(key, value);
+        ElementHelper.attachProperties(vertexProperty, keyValues);
+        return vertexProperty;
+    }
+
+    public default <V> VertexProperty<V> property(final VertexProperty.Cardinality cardinality, final String key, final V value, final Object... keyValues) {
+        if (cardinality.equals(VertexProperty.Cardinality.list))
+            return this.property(key, value, keyValues);
+        else if (cardinality.equals(VertexProperty.Cardinality.single)) {
+            this.iterators().propertyIterator(key).forEachRemaining(VertexProperty::remove);
+            return this.property(key, value, keyValues);
+        } else if (cardinality.equals(VertexProperty.Cardinality.set)) {
+            final Iterator<VertexProperty<V>> iterator = this.iterators().propertyIterator(key);
+            while (iterator.hasNext()) {
+                final VertexProperty<V> property = iterator.next();
+                if (property.value().equals(value)) {
+                    ElementHelper.attachProperties(property, keyValues);
+                    return property;
+                }
+            }
+            return this.property(key, value, keyValues);
+        } else {
+            throw new IllegalArgumentException("The provided cardinality is unknown: " + cardinality);
+        }
+    }
+
+    /**
+     * Get the {@link Vertex.Iterators} implementation associated with this {@code Vertex}.
+     * <p/>
+     * {@inheritDoc}
+     */
+    @Override
+    public Vertex.Iterators iterators();
+
+    /**
+     * An interface that provides access to iterators over {@link VertexProperty} objects, {@link Edge} objects
+     * and adjacent vertices, associated with the {@code Vertex}, without constructing a
+     * {@link com.tinkerpop.gremlin.process.Traversal} object.
+     */
+    public interface Iterators extends Element.Iterators {
+        /**
+         * Gets an {@link Iterator} of incident edges.
+         *
+         * @param direction  The incident direction of the edges to retrieve off this vertex
+         * @param edgeLabels The labels of the edges to retrieve. If no labels are provided, then get all edges.
+         * @return An iterator of edges meeting the provided specification
+         */
+        public Iterator<Edge> edgeIterator(final Direction direction, final String... edgeLabels);
+
+        /**
+         * Gets an {@link Iterator} of adjacent vertices.
+         *
+         * @param direction  The adjacency direction of the vertices to retrieve off this vertex
+         * @param edgeLabels The labels of the edges associated with the vertices to retrieve. If no labels are provided, then get all edges.
+         * @return An iterator of vertices meeting the provided specification
+         */
+        public Iterator<Vertex> vertexIterator(final Direction direction, final String... edgeLabels);
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public <V> Iterator<VertexProperty<V>> propertyIterator(final String... propertyKeys);
+    }
+
+    /**
+     * Common exceptions to use with a vertex.
+     */
+    public static class Exceptions {
+        public static UnsupportedOperationException userSuppliedIdsNotSupported() {
+            return new UnsupportedOperationException("Vertex does not support user supplied identifiers");
+        }
+
+        public static UnsupportedOperationException userSuppliedIdsOfThisTypeNotSupported() {
+            return new UnsupportedOperationException("Vertex does not support user supplied identifiers of this type");
+        }
+
+        public static IllegalStateException vertexRemovalNotSupported() {
+            return new IllegalStateException("Vertex removal are not supported");
+        }
+
+        public static IllegalStateException edgeAdditionsNotSupported() {
+            return new IllegalStateException("Edge additions not supported");
+        }
+
+        public static IllegalStateException multiplePropertiesExistForProvidedKey(final String propertyKey) {
+            return new IllegalStateException("Multiple properties exist for the provided key, use Vertex.properties(" + propertyKey + ')');
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java
new file mode 100644
index 0000000..0eba9e6
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/VertexProperty.java
@@ -0,0 +1,118 @@
+/*
+ * 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;
+
+import com.tinkerpop.gremlin.process.graph.traversal.VertexPropertyTraversal;
+import com.tinkerpop.gremlin.structure.util.empty.EmptyVertexProperty;
+
+import java.util.Iterator;
+
+/**
+ * A {@code VertexProperty} is similar to a {@link Property} in that it denotes a key/value pair associated with an
+ * {@link Vertex}, however it is different in the sense that it also represents an entity that it is an {@link Element}
+ * that can have properties of its own.
+ * <br/>
+ * A property is much like a Java8 {@link java.util.Optional} in that a property can be not present (i.e. empty).
+ * The key of a property is always a String and the value of a property is an arbitrary Java object.
+ * Each underlying graph engine will typically have constraints on what Java objects are allowed to be used as values.
+ *
+ * @author Matthias Broecheler (me@matthiasb.com)
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public interface VertexProperty<V> extends Property<V>, Element, VertexPropertyTraversal {
+
+    public static final String DEFAULT_LABEL = "vertexProperty";
+
+    public enum Cardinality {
+        single, list, set
+    }
+
+    /**
+     * Gets the {@link Vertex} that owns this {@code VertexProperty}.
+     */
+    @Override
+    public Vertex element();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    @Graph.Helper
+    public default Graph graph() {
+        return this.element().graph();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    @Graph.Helper
+    public default String label() {
+        return this.key();
+    }
+
+    /**
+     * Constructs an empty {@code VertexProperty}.
+     */
+    public static <V> VertexProperty<V> empty() {
+        return EmptyVertexProperty.instance();
+    }
+
+    /**
+     * Gets the {@link VertexProperty.Iterators} set.
+     * <p/>
+     * {@inheritDoc}
+     */
+    @Override
+    public VertexProperty.Iterators iterators();
+
+    /**
+     * An interface that provides access to iterators over properties, without constructing a
+     * {@link com.tinkerpop.gremlin.process.Traversal} object.
+     */
+    public interface Iterators extends Element.Iterators {
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public <U> Iterator<Property<U>> propertyIterator(final String... propertyKeys);
+    }
+
+    /**
+     * Common exceptions to use with a property.
+     */
+    public static class Exceptions {
+        public static UnsupportedOperationException userSuppliedIdsNotSupported() {
+            return new UnsupportedOperationException("VertexProperty does not support user supplied identifiers");
+        }
+
+        public static UnsupportedOperationException userSuppliedIdsOfThisTypeNotSupported() {
+            return new UnsupportedOperationException("VertexProperty does not support user supplied identifiers of this type");
+        }
+
+        public static UnsupportedOperationException multiPropertiesNotSupported() {
+            return new UnsupportedOperationException("Multiple properties on a vertex is not supported");
+        }
+
+        public static UnsupportedOperationException metaPropertiesNotSupported() {
+            return new UnsupportedOperationException("Properties on a vertex property is not supported");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/DefaultIo.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/DefaultIo.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/DefaultIo.java
new file mode 100644
index 0000000..3013a76
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/DefaultIo.java
@@ -0,0 +1,82 @@
+/*
+ * 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;
+
+import com.tinkerpop.gremlin.structure.Graph;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Default implementation of the {@link Graph.Io} interface which overrides none of the default methods.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class DefaultIo implements Graph.Io {
+    private final Graph g;
+
+    public DefaultIo(final Graph g) {
+        this.g = g;
+    }
+
+    @Override
+    public void writeKryo(final String file) throws IOException {
+        try (final OutputStream out = new FileOutputStream(file)) {
+            kryoWriter().create().writeGraph(out, g);
+        }
+    }
+
+    @Override
+    public void readKryo(final String file) throws IOException {
+        try (final InputStream in = new FileInputStream(file)) {
+            kryoReader().create().readGraph(in, g);
+        }
+    }
+
+    @Override
+    public void writeGraphML(final String file) throws IOException {
+        try (final OutputStream out = new FileOutputStream(file)) {
+            graphMLWriter().create().writeGraph(out, g);
+        }
+    }
+
+    @Override
+    public void readGraphML(final String file) throws IOException {
+        try (final InputStream in = new FileInputStream(file)) {
+            graphMLReader().create().readGraph(in, g);
+        }
+    }
+
+    @Override
+    public void writeGraphSON(final String file) throws IOException {
+        try (final OutputStream out = new FileOutputStream(file)) {
+            graphSONWriter().create().writeGraph(out, g);
+        }
+    }
+
+    @Override
+    public void readGraphSON(final String file) throws IOException {
+        try (final InputStream in = new FileInputStream(file)) {
+            graphSONReader().create().readGraph(in, g);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphMigrator.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphMigrator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphMigrator.java
new file mode 100644
index 0000000..b923093
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphMigrator.java
@@ -0,0 +1,94 @@
+/*
+ * 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;
+
+import com.tinkerpop.gremlin.structure.Graph;
+import com.tinkerpop.gremlin.structure.io.kryo.KryoReader;
+import com.tinkerpop.gremlin.structure.io.kryo.KryoWriter;
+
+import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+
+/**
+ * {@link GraphMigrator} takes the data in one graph and pipes it to another graph.  Uses the {@link KryoReader}
+ * and {@link KryoWriter} by default.
+ *
+ * @author Alex Averbuch (alex.averbuch@gmail.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class GraphMigrator {
+
+    private static final KryoReader defaultKryoReader = KryoReader.build().create();
+    private static final KryoWriter defaultKryoWriter = KryoWriter.build().create();
+
+    /**
+     * Use Kryo to pipe the data from one graph to another graph.  Uses all default settings for reader/writers.
+     * Refer to {@link KryoReader} and {@link KryoWriter} for those settings.  To use features like incremental
+     * loading, construct the reader/writers manually and utilize
+     * {@link #migrateGraph(com.tinkerpop.gremlin.structure.Graph, com.tinkerpop.gremlin.structure.Graph, GraphReader, GraphWriter)}
+     */
+    public static void migrateGraph(final Graph fromGraph, final Graph toGraph) throws IOException {
+        migrateGraph(fromGraph, toGraph, defaultKryoReader, defaultKryoWriter);
+    }
+
+    /**
+     * Pipe the data from one graph to another graph.  It is important that the reader and writer utilize the
+     * same format.
+     *
+     * @param fromGraph the graph to take data from
+     * @param toGraph   the graph to take data to
+     * @param reader    reads from the graph written by the writer
+     * @param writer    writes the graph to be read by the reader
+     * @throws java.io.IOException thrown if there is an error in steam between the two graphs
+     */
+    public static void migrateGraph(final Graph fromGraph, final Graph toGraph,
+                                    final GraphReader reader, final GraphWriter writer) throws IOException {
+        final PipedInputStream inPipe = new PipedInputStream(1024);
+
+        final PipedOutputStream outPipe = new PipedOutputStream(inPipe) {
+            @Override
+            public void close() throws IOException {
+                while (inPipe.available() > 0) {
+                    try {
+                        Thread.sleep(500);
+                    } catch (InterruptedException e) {
+                        // do nothing
+                    }
+                }
+                super.close();
+            }
+        };
+
+        new Thread(() -> {
+            try {
+                writer.writeGraph(outPipe, fromGraph);
+                outPipe.flush();
+                outPipe.close();
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            } finally {
+                if (fromGraph.features().graph().supportsTransactions()) fromGraph.tx().rollback();
+                if (toGraph.features().graph().supportsTransactions()) toGraph.tx().rollback();
+            }
+        }).start();
+
+        reader.readGraph(inPipe, toGraph);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphReader.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphReader.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphReader.java
new file mode 100644
index 0000000..73d218d
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphReader.java
@@ -0,0 +1,99 @@
+/*
+ * 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;
+
+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.util.detached.DetachedEdge;
+import com.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.function.Function;
+
+/**
+ * Functions for reading a graph and its graph elements from a different format.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public interface GraphReader {
+
+    /**
+     * Reads an entire graph from an {@link InputStream}.
+     *
+     * @param inputStream a stream containing a single vertex as defined by the accompanying {@link GraphWriter}
+     */
+    public void readGraph(final InputStream inputStream, final Graph graphToWriteTo) throws IOException;
+
+    /**
+     * Reads a single vertex from an {@link InputStream}.  This method will read vertex properties but not edges.
+     *
+     * @param inputStream a stream containing a single vertex as defined by the accompanying {@link GraphWriter}
+     * @param vertexMaker a function to create a vertex where the first argument is the vertex identifier, the
+     *                    second argument is vertex label and the last is the list of properties for it
+     */
+    public Vertex readVertex(final InputStream inputStream, final Function<DetachedVertex, Vertex> vertexMaker) throws IOException;
+
+    /**
+     * Reads a single vertex from an {@link InputStream}.  This method will read vertex properties as well as edges
+     * given the direction supplied as an argument.
+     *
+     * @param inputStream a stream containing a single vertex as defined by the accompanying {@link GraphWriter}
+     * @param direction   the direction of edges to read.
+     * @param vertexMaker a function to create a vertex where the first argument is the vertex identifier, the
+     *                    second argument is vertex label and the last is the list of properties for it
+     * @param edgeMaker   a function that creates an edge from the stream where the first argument is the edge
+     *                    identifier, the second argument is the out vertex id, the third is the in vertex id,
+     *                    the fourth is the label, and the fifth is the list of properties as key/value pairs.
+     */
+    public Vertex readVertex(final InputStream inputStream, final Direction direction,
+                             final Function<DetachedVertex, Vertex> vertexMaker,
+                             final Function<DetachedEdge, Edge> edgeMaker) throws IOException;
+
+    /**
+     * Reads a set of vertices from an {@link InputStream} which were written by
+     * {@link GraphWriter#writeVertices(java.io.OutputStream, com.tinkerpop.gremlin.process.Traversal)}.  This method
+     * will read vertex properties as well as edges given the direction supplied as an argument.
+     *
+     * @param inputStream a stream containing a single vertex as defined by the accompanying {@link GraphWriter}
+     * @param direction   the direction of edges to read.
+     * @param vertexMaker a function to create a vertex where the first argument is the vertex identifier, the
+     *                    second argument is vertex label and the last is the list of properties for it
+     * @param edgeMaker   a function that creates an edge from the stream where the first argument is the edge
+     *                    identifier, the second argument is the out vertex id, the third is the in vertex id,
+     *                    the fourth is the label, and the fifth is the list of properties as key/value pairs.
+     */
+    public Iterator<Vertex> readVertices(final InputStream inputStream, final Direction direction,
+                                         final Function<DetachedVertex, Vertex> vertexMaker,
+                                         final Function<DetachedEdge, Edge> edgeMaker) throws IOException;
+
+    /**
+     * Reads a single edge from an {@link InputStream}.
+     *
+     * @param inputStream a stream containing a single vertex as defined by the accompanying {@link GraphWriter}
+     * @param edgeMaker   a function that creates an edge from the stream where the first argument is the edge
+     *                    identifier, the second argument is the out vertex id, the third is the in vertex id,
+     *                    the fourth is the label, and the fifth is the list of properties as key/value pairs.
+     */
+    public Edge readEdge(final InputStream inputStream, final Function<DetachedEdge, Edge> edgeMaker) throws IOException;
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphWriter.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphWriter.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphWriter.java
new file mode 100644
index 0000000..e5ffa9c
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/GraphWriter.java
@@ -0,0 +1,89 @@
+/*
+ * 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;
+
+import com.tinkerpop.gremlin.process.Traversal;
+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 java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Functions for writing a graph and its elements to a different format.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public interface GraphWriter {
+    /**
+     * Write the entire graph to a stream.
+     */
+    public void writeGraph(final OutputStream outputStream, final Graph g) throws IOException;
+
+    /**
+     * Write a vertex to a stream with its associated edges.  Only write edges as defined by the requested direction.
+     *
+     * @param outputStream The stream to write to.
+     * @param v            The vertex to write.
+     * @param direction    If direction is null then no edges are written.
+     */
+    public void writeVertex(final OutputStream outputStream, final Vertex v, final Direction direction) throws IOException;
+
+    /**
+     * Write a vertex to a stream without writing its edges.
+     *
+     * @param outputStream The stream to write to.
+     * @param v            The vertex to write.
+     */
+    public void writeVertex(final OutputStream outputStream, final Vertex v) throws IOException;
+
+
+    /**
+     * Write a list of vertices from a {@link Traversal} to a stream with its associated edges.  Only write edges as
+     * defined by the requested direction.
+     *
+     * @param outputStream The stream to write to.
+     * @param traversal    A traversal that returns a list of vertices.
+     * @param direction    If direction is null then no edges are written.
+     */
+    public default void writeVertices(final OutputStream outputStream, final Traversal<?, Vertex> traversal, final Direction direction) throws IOException {
+        while (traversal.hasNext()) {
+            writeVertex(outputStream, traversal.next(), direction);
+        }
+    }
+
+    /**
+     * Write a vertex to a stream without writing its edges.
+     *
+     * @param outputStream The stream to write to.
+     * @param traversal    A traversal that returns a list of vertices.
+     */
+    public default void writeVertices(final OutputStream outputStream, final Traversal<?, Vertex> traversal) throws IOException {
+        while (traversal.hasNext()) {
+            writeVertex(outputStream, traversal.next());
+        }
+    }
+
+    /**
+     * Write an edge to a stream.
+     */
+    public void writeEdge(final OutputStream outputStream, final Edge e) throws IOException;
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/Mapper.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/Mapper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/Mapper.java
new file mode 100644
index 0000000..32b5404
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/Mapper.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+/**
+ * Represents a low-level serialization class that can be used to map classes to serializers.  These implementation
+ * create instances of serializers from other libraries (e.g. creating a {@code Kryo} instance).
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public interface Mapper<T> {
+    /**
+     * Create a new instance of the internal object mapper that an implementation represents.
+     */
+    public T createMapper();
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLReader.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLReader.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLReader.java
new file mode 100644
index 0000000..226b2ff
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLReader.java
@@ -0,0 +1,312 @@
+/*
+ * 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.graphml;
+
+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 javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.stream.events.XMLEvent;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+/**
+ * GraphMLReader writes the data from a GraphML stream to a graph.  Note that this format is lossy, in the sense that data
+ * types and features of Gremlin Structure not supported by GraphML are not serialized.  This format is meant for
+ * external export of a graph to tools outside of Gremlin Structure graphs.  Note that GraphML does not support
+ * the notion of multi-properties or properties on properties.
+ *
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Alex Averbuch (alex.averbuch@gmail.com)
+ * @author Joshua Shinavier (http://fortytwo.net)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class GraphMLReader implements GraphReader {
+    private final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
+
+    private final String vertexIdKey;
+    private final String edgeIdKey;
+    private final String edgeLabelKey;
+    private final String vertexLabelKey;
+    private final long batchSize;
+
+    private GraphMLReader(final String vertexIdKey, final String edgeIdKey,
+                          final String edgeLabelKey, final String vertexLabelKey,
+                          final long batchSize) {
+        this.vertexIdKey = vertexIdKey;
+        this.edgeIdKey = edgeIdKey;
+        this.edgeLabelKey = edgeLabelKey;
+        this.batchSize = batchSize;
+        this.vertexLabelKey = vertexLabelKey;
+    }
+
+    @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("GraphML does not allow for a partial structure");
+    }
+
+    @Override
+    public Edge readEdge(final InputStream inputStream, final Function<DetachedEdge, Edge> edgeMaker) throws IOException {
+        throw new UnsupportedOperationException("GraphML does not allow for a partial structure");
+    }
+
+    @Override
+    public Vertex readVertex(final InputStream inputStream, final Function<DetachedVertex, Vertex> vertexMaker) throws IOException {
+        throw new UnsupportedOperationException("GraphML does not allow for a partial structure");
+    }
+
+    @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("GraphML does not allow for a partial structure");
+    }
+
+    @Override
+    public void readGraph(final InputStream graphInputStream, final Graph graphToWriteTo) throws IOException {
+        final BatchGraph graph;
+        try {
+            // will throw an exception if not constructed properly
+            graph = BatchGraph.build(graphToWriteTo)
+                    .vertexIdKey(vertexIdKey)
+                    .bufferSize(batchSize).create();
+        } catch (Exception ex) {
+            throw new IOException("Could not instantiate BatchGraph wrapper", ex);
+        }
+
+        try {
+            final XMLStreamReader reader = inputFactory.createXMLStreamReader(graphInputStream);
+            final Map<String, String> keyIdMap = new HashMap<>();
+            final Map<String, String> keyTypesMaps = new HashMap<>();
+
+            // Buffered Vertex Data
+            String vertexId = null;
+            String vertexLabel = null;
+            Map<String, Object> vertexProps = null;
+            boolean isInVertex = false;
+
+            // Buffered Edge Data
+            String edgeId = null;
+            String edgeLabel = null;
+            Vertex edgeInVertex = null;
+            Vertex edgeOutVertex = null;
+            Map<String, Object> edgeProps = null;
+            boolean isInEdge = false;
+
+            while (reader.hasNext()) {
+                final Integer eventType = reader.next();
+                if (eventType.equals(XMLEvent.START_ELEMENT)) {
+                    final String elementName = reader.getName().getLocalPart();
+
+                    switch (elementName) {
+                        case GraphMLTokens.KEY:
+                            final String id = reader.getAttributeValue(null, GraphMLTokens.ID);
+                            final String attributeName = reader.getAttributeValue(null, GraphMLTokens.ATTR_NAME);
+                            final String attributeType = reader.getAttributeValue(null, GraphMLTokens.ATTR_TYPE);
+                            keyIdMap.put(id, attributeName);
+                            keyTypesMaps.put(id, attributeType);
+                            break;
+                        case GraphMLTokens.NODE:
+                            vertexId = reader.getAttributeValue(null, GraphMLTokens.ID);
+                            isInVertex = true;
+                            vertexProps = new HashMap<>();
+                            break;
+                        case GraphMLTokens.EDGE:
+                            edgeId = reader.getAttributeValue(null, GraphMLTokens.ID);
+
+                            final String vertexIdOut = reader.getAttributeValue(null, GraphMLTokens.SOURCE);
+                            final String vertexIdIn = reader.getAttributeValue(null, GraphMLTokens.TARGET);
+
+                            // graphml allows edges and vertices to be mixed in terms of how they are positioned
+                            // in the xml therefore it is possible that an edge is created prior to its definition
+                            // as a vertex.
+                            Iterator<Vertex> iterator = graph.iterators().vertexIterator(vertexIdOut);
+                            edgeOutVertex = iterator.hasNext() ? iterator.next() : graph.addVertex(T.id, vertexIdOut);
+                            iterator = graph.iterators().vertexIterator(vertexIdIn);
+                            edgeInVertex = iterator.hasNext() ? iterator.next() : graph.addVertex(T.id, vertexIdIn);
+
+                            isInEdge = true;
+                            edgeProps = new HashMap<>();
+
+                            break;
+                        case GraphMLTokens.DATA:
+                            final String key = reader.getAttributeValue(null, GraphMLTokens.KEY);
+                            final String dataAttributeName = keyIdMap.get(key);
+
+                            if (dataAttributeName != null) {
+                                final String value = reader.getElementText();
+
+                                if (isInVertex) {
+                                    if (key.equals(vertexLabelKey))
+                                        vertexLabel = value;
+                                    else
+                                        vertexProps.put(dataAttributeName, typeCastValue(key, value, keyTypesMaps));
+                                } else if (isInEdge) {
+                                    if (key.equals(edgeLabelKey))
+                                        edgeLabel = value;
+                                    else if (key.equals(edgeIdKey))
+                                        edgeId = value;
+                                    else
+                                        edgeProps.put(dataAttributeName, typeCastValue(key, value, keyTypesMaps));
+                                }
+                            }
+
+                            break;
+                    }
+                } else if (eventType.equals(XMLEvent.END_ELEMENT)) {
+                    final String elementName = reader.getName().getLocalPart();
+
+                    if (elementName.equals(GraphMLTokens.NODE)) {
+                        final String currentVertexId = vertexId;
+                        final String currentVertexLabel = Optional.ofNullable(vertexLabel).orElse(Vertex.DEFAULT_LABEL);
+                        final Object[] propsAsArray = vertexProps.entrySet().stream().flatMap(e -> Stream.of(e.getKey(), e.getValue())).toArray();
+
+                        // if incremental loading is on in batchgraph it handles graphml spec where it states that
+                        // order of edges/vertices may be mixed such that an edge may be created before an vertex.
+                        graph.addVertex(Stream.concat(Stream.of(T.id, currentVertexId, T.label, currentVertexLabel),
+                                Stream.of(propsAsArray)).toArray());
+
+                        vertexId = null;
+                        vertexLabel = null;
+                        vertexProps = null;
+                        isInVertex = false;
+                    } else if (elementName.equals(GraphMLTokens.EDGE)) {
+                        final Object[] propsAsArray = edgeProps.entrySet().stream().flatMap(e -> Stream.of(e.getKey(), e.getValue())).toArray();
+                        edgeOutVertex.addEdge(edgeLabel, edgeInVertex, Stream.concat(Stream.of(T.id, edgeId),
+                                Stream.of(propsAsArray)).toArray());
+
+                        edgeId = null;
+                        edgeLabel = null;
+                        edgeOutVertex = null;
+                        edgeInVertex = null;
+                        edgeProps = null;
+                        isInEdge = false;
+                    }
+
+                }
+            }
+
+            graph.tx().commit();
+        } catch (XMLStreamException xse) {
+            // rollback whatever portion failed
+            graph.tx().rollback();
+            throw new IOException(xse);
+        }
+    }
+
+    private static Object typeCastValue(final String key, final String value, final Map<String, String> keyTypes) {
+        final String type = keyTypes.get(key);
+        if (null == type || type.equals(GraphMLTokens.STRING))
+            return value;
+        else if (type.equals(GraphMLTokens.FLOAT))
+            return Float.valueOf(value);
+        else if (type.equals(GraphMLTokens.INT))
+            return Integer.valueOf(value);
+        else if (type.equals(GraphMLTokens.DOUBLE))
+            return Double.valueOf(value);
+        else if (type.equals(GraphMLTokens.BOOLEAN))
+            return Boolean.valueOf(value);
+        else if (type.equals(GraphMLTokens.LONG))
+            return Long.valueOf(value);
+        else
+            return value;
+    }
+
+    public static Builder build() {
+        return new Builder();
+    }
+
+    /**
+     * Allows configuration and construction of the GraphMLReader instance.
+     */
+    public static final class Builder {
+        private String vertexIdKey = T.id.getAccessor();
+        private String edgeIdKey = T.id.getAccessor();
+        private String edgeLabelKey = GraphMLTokens.LABEL_E;
+        private String vertexLabelKey = GraphMLTokens.LABEL_V;
+        private long batchSize = BatchGraph.DEFAULT_BUFFER_SIZE;
+
+        private Builder() {
+        }
+
+        /**
+         * 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 key to use as the edge label.
+         */
+        public Builder edgeLabelKey(final String edgeLabelKey) {
+            this.edgeLabelKey = edgeLabelKey;
+            return this;
+        }
+
+        /**
+         * the key to use as the vertex label.
+         */
+        public Builder vertexLabelKey(final String vertexLabelKey) {
+            this.vertexLabelKey = vertexLabelKey;
+            return this;
+        }
+
+        /**
+         * Number of mutations to perform before a commit is executed.
+         */
+        public Builder batchSize(final long batchSize) {
+            this.batchSize = batchSize;
+            return this;
+        }
+
+        public GraphMLReader create() {
+            return new GraphMLReader(vertexIdKey, edgeIdKey, edgeLabelKey, vertexLabelKey, batchSize);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLTokens.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLTokens.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLTokens.java
new file mode 100644
index 0000000..bb1298d
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLTokens.java
@@ -0,0 +1,56 @@
+/*
+ * 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.graphml;
+
+/**
+ * A collection of tokens used for GraphML related data.
+ *
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+class GraphMLTokens {
+    public static final String XML_SCHEMA_NAMESPACE_TAG = "xsi";
+    public static final String DEFAULT_GRAPHML_SCHEMA_LOCATION = "http://graphml.graphdrawing.org/xmlns/1.1/graphml.xsd";
+    public static final String XML_SCHEMA_LOCATION_ATTRIBUTE = "schemaLocation";
+    public static final String GRAPHML = "graphml";
+    public static final String XMLNS = "xmlns";
+    public static final String GRAPHML_XMLNS = "http://graphml.graphdrawing.org/xmlns";
+    public static final String G = "G";
+    public static final String EDGEDEFAULT = "edgedefault";
+    public static final String DIRECTED = "directed";
+    public static final String KEY = "key";
+    public static final String FOR = "for";
+    public static final String ID = "id";
+    public static final String ATTR_NAME = "attr.name";
+    public static final String ATTR_TYPE = "attr.type";
+    public static final String GRAPH = "graph";
+    public static final String NODE = "node";
+    public static final String EDGE = "edge";
+    public static final String SOURCE = "source";
+    public static final String TARGET = "target";
+    public static final String DATA = "data";
+    public static final String LABEL_E = "labelE";
+    public static final String LABEL_V = "labelV";
+    public static final String STRING = "string";
+    public static final String FLOAT = "float";
+    public static final String DOUBLE = "double";
+    public static final String LONG = "long";
+    public static final String BOOLEAN = "boolean";
+    public static final String INT = "int";
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/1545201f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriter.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriter.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriter.java
new file mode 100644
index 0000000..1cdbca4
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLWriter.java
@@ -0,0 +1,445 @@
+/*
+ * 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.graphml;
+
+import com.tinkerpop.gremlin.process.Traversal;
+import com.tinkerpop.gremlin.structure.*;
+import com.tinkerpop.gremlin.structure.io.GraphWriter;
+import com.tinkerpop.gremlin.structure.util.Comparators;
+import com.tinkerpop.gremlin.util.iterator.IteratorUtils;
+
+import javax.xml.XMLConstants;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.*;
+
+/**
+ * GraphMLWriter writes a Graph to a GraphML OutputStream. Note that this format is lossy, in the sense that data
+ * types and features of Gremlin Structure not supported by GraphML are not serialized.  This format is meant for
+ * external export of a graph to tools outside of Gremlin Structure graphs.  Note that GraphML does not support
+ * the notion of multi-properties or properties on properties and will throw an exception when writing a
+ * graph elements that have such things.
+ *
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Joshua Shinavier (http://fortytwo.net)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class GraphMLWriter implements GraphWriter {
+    private final XMLOutputFactory inputFactory = XMLOutputFactory.newInstance();
+    private boolean normalize = false;
+
+    private final Optional<Map<String, String>> vertexKeyTypes;
+    private final Optional<Map<String, String>> edgeKeyTypes;
+    private final Optional<String> xmlSchemaLocation;
+    private final String edgeLabelKey;
+    private final String vertexLabelKey;
+
+    private GraphMLWriter(final boolean normalize, final Map<String, String> vertexKeyTypes,
+                          final Map<String, String> edgeKeyTypes, final String xmlSchemaLocation,
+                          final String edgeLabelKey, final String vertexLabelKey) {
+        this.normalize = normalize;
+        this.vertexKeyTypes = Optional.ofNullable(vertexKeyTypes);
+        this.edgeKeyTypes = Optional.ofNullable(edgeKeyTypes);
+        this.xmlSchemaLocation = Optional.ofNullable(xmlSchemaLocation);
+        this.edgeLabelKey = edgeLabelKey;
+        this.vertexLabelKey = vertexLabelKey;
+    }
+
+    @Override
+    public void writeVertex(final OutputStream outputStream, final Vertex v, Direction direction) throws IOException {
+        throw new UnsupportedOperationException("GraphML does not allow for a partial structure");
+    }
+
+    @Override
+    public void writeVertex(final OutputStream outputStream, final Vertex v) throws IOException {
+        throw new UnsupportedOperationException("GraphML does not allow for a partial structure");
+    }
+
+    @Override
+    public void writeEdge(final OutputStream outputStream, final Edge e) throws IOException {
+        throw new UnsupportedOperationException("GraphML does not allow for a partial structure");
+    }
+
+    @Override
+    public void writeVertices(final OutputStream outputStream, Traversal<?, Vertex> traversal, final Direction direction) throws IOException {
+        throw new UnsupportedOperationException("GraphML does not allow for a partial structure");
+    }
+
+    @Override
+    public void writeVertices(final OutputStream outputStream, final Traversal<?, Vertex> traversal) throws IOException {
+        throw new UnsupportedOperationException("GraphML does not allow for a partial structure");
+    }
+
+    /**
+     * Write the data in a Graph to a GraphML OutputStream.
+     *
+     * @param outputStream the GraphML OutputStream to write the Graph data to
+     * @throws java.io.IOException thrown if there is an error generating the GraphML data
+     */
+    @Override
+    public void writeGraph(final OutputStream outputStream, final Graph g) throws IOException {
+        final Map<String, String> identifiedVertexKeyTypes = this.vertexKeyTypes.orElseGet(() -> GraphMLWriter.determineVertexTypes(g));
+        final Map<String, String> identifiedEdgeKeyTypes = this.edgeKeyTypes.orElseGet(() -> GraphMLWriter.determineEdgeTypes(g));
+
+        if (identifiedEdgeKeyTypes.containsKey(this.edgeLabelKey))
+            throw new IllegalStateException(String.format("The edgeLabelKey value of[%s] conflicts with the name of an existing property key to be included in the GraphML", this.edgeLabelKey));
+        if (identifiedEdgeKeyTypes.containsKey(this.edgeLabelKey))
+            throw new IllegalStateException(String.format("The vertexLabelKey value of[%s] conflicts with the name of an existing property key to be included in the GraphML", this.vertexLabelKey));
+
+        identifiedEdgeKeyTypes.put(this.edgeLabelKey, GraphMLTokens.STRING);
+        identifiedVertexKeyTypes.put(this.vertexLabelKey, GraphMLTokens.STRING);
+
+        try {
+            final XMLStreamWriter writer;
+            writer = configureWriter(outputStream);
+
+            writer.writeStartDocument();
+            writer.writeStartElement(GraphMLTokens.GRAPHML);
+            writeXmlNsAndSchema(writer);
+
+            writeTypes(identifiedVertexKeyTypes, identifiedEdgeKeyTypes, writer);
+
+            writer.writeStartElement(GraphMLTokens.GRAPH);
+            writer.writeAttribute(GraphMLTokens.ID, GraphMLTokens.G);
+            writer.writeAttribute(GraphMLTokens.EDGEDEFAULT, GraphMLTokens.DIRECTED);
+
+            writeVertices(writer, g);
+            writeEdges(writer, g);
+
+            writer.writeEndElement(); // graph
+            writer.writeEndElement(); // graphml
+            writer.writeEndDocument();
+
+            writer.flush();
+            writer.close();
+        } catch (XMLStreamException xse) {
+            throw new IOException(xse);
+        }
+    }
+
+    private XMLStreamWriter configureWriter(final OutputStream outputStream) throws XMLStreamException {
+        final XMLStreamWriter utf8Writer = inputFactory.createXMLStreamWriter(outputStream, "UTF8");
+        if (normalize) {
+            final XMLStreamWriter writer = new GraphMLWriterHelper.IndentingXMLStreamWriter(utf8Writer);
+            ((GraphMLWriterHelper.IndentingXMLStreamWriter) writer).setIndentStep("    ");
+            return writer;
+        } else
+            return utf8Writer;
+    }
+
+    private void writeTypes(final Map<String, String> identifiedVertexKeyTypes,
+                            final Map<String, String> identifiedEdgeKeyTypes,
+                            final XMLStreamWriter writer) throws XMLStreamException {
+        // <key id="weight" for="edge" attr.name="weight" attr.type="float"/>
+        final Collection<String> vertexKeySet = getVertexKeysAndNormalizeIfRequired(identifiedVertexKeyTypes);
+        for (String key : vertexKeySet) {
+            writer.writeStartElement(GraphMLTokens.KEY);
+            writer.writeAttribute(GraphMLTokens.ID, key);
+            writer.writeAttribute(GraphMLTokens.FOR, GraphMLTokens.NODE);
+            writer.writeAttribute(GraphMLTokens.ATTR_NAME, key);
+            writer.writeAttribute(GraphMLTokens.ATTR_TYPE, identifiedVertexKeyTypes.get(key));
+            writer.writeEndElement();
+        }
+
+        final Collection<String> edgeKeySet = getEdgeKeysAndNormalizeIfRequired(identifiedEdgeKeyTypes);
+        for (String key : edgeKeySet) {
+            writer.writeStartElement(GraphMLTokens.KEY);
+            writer.writeAttribute(GraphMLTokens.ID, key);
+            writer.writeAttribute(GraphMLTokens.FOR, GraphMLTokens.EDGE);
+            writer.writeAttribute(GraphMLTokens.ATTR_NAME, key);
+            writer.writeAttribute(GraphMLTokens.ATTR_TYPE, identifiedEdgeKeyTypes.get(key));
+            writer.writeEndElement();
+        }
+    }
+
+    private void writeEdges(final XMLStreamWriter writer, final Graph graph) throws XMLStreamException {
+        if (normalize) {
+            final List<Edge> edges = IteratorUtils.list(graph.iterators().edgeIterator());
+            Collections.sort(edges, Comparators.ELEMENT_COMPARATOR);
+
+            for (Edge edge : edges) {
+                writer.writeStartElement(GraphMLTokens.EDGE);
+                writer.writeAttribute(GraphMLTokens.ID, edge.id().toString());
+                writer.writeAttribute(GraphMLTokens.SOURCE, edge.outV().next().id().toString());
+                writer.writeAttribute(GraphMLTokens.TARGET, edge.inV().next().id().toString());
+
+                writer.writeStartElement(GraphMLTokens.DATA);
+                writer.writeAttribute(GraphMLTokens.KEY, this.edgeLabelKey);
+                writer.writeCharacters(edge.label());
+                writer.writeEndElement();
+
+                final List<String> keys = new ArrayList<>(edge.keys());
+                Collections.sort(keys);
+
+                for (String key : keys) {
+                    writer.writeStartElement(GraphMLTokens.DATA);
+                    writer.writeAttribute(GraphMLTokens.KEY, key);
+                    // technically there can't be a null here as gremlin structure forbids that occurrence even if Graph
+                    // implementations support it, but out to empty string just in case.
+                    writer.writeCharacters(edge.property(key).orElse("").toString());
+                    writer.writeEndElement();
+                }
+                writer.writeEndElement();
+            }
+        } else {
+            final Iterator<Edge> iterator = graph.iterators().edgeIterator();
+            while (iterator.hasNext()) {
+                final Edge edge = iterator.next();
+                writer.writeStartElement(GraphMLTokens.EDGE);
+                writer.writeAttribute(GraphMLTokens.ID, edge.id().toString());
+                writer.writeAttribute(GraphMLTokens.SOURCE, edge.outV().next().id().toString());
+                writer.writeAttribute(GraphMLTokens.TARGET, edge.inV().next().id().toString());
+
+                writer.writeStartElement(GraphMLTokens.DATA);
+                writer.writeAttribute(GraphMLTokens.KEY, this.edgeLabelKey);
+                writer.writeCharacters(edge.label());
+                writer.writeEndElement();
+
+                for (String key : edge.keys()) {
+                    writer.writeStartElement(GraphMLTokens.DATA);
+                    writer.writeAttribute(GraphMLTokens.KEY, key);
+                    // technically there can't be a null here as gremlin structure forbids that occurrence even if Graph
+                    // implementations support it, but out to empty string just in case.
+                    writer.writeCharacters(edge.property(key).orElse("").toString());
+                    writer.writeEndElement();
+                }
+                writer.writeEndElement();
+            }
+        }
+    }
+
+    private void writeVertices(final XMLStreamWriter writer, final Graph graph) throws XMLStreamException {
+        final Iterable<Vertex> vertices = getVerticesAndNormalizeIfRequired(graph);
+        for (Vertex vertex : vertices) {
+            writer.writeStartElement(GraphMLTokens.NODE);
+            writer.writeAttribute(GraphMLTokens.ID, vertex.id().toString());
+            final Collection<String> keys = getElementKeysAndNormalizeIfRequired(vertex);
+
+            writer.writeStartElement(GraphMLTokens.DATA);
+            writer.writeAttribute(GraphMLTokens.KEY, this.vertexLabelKey);
+            writer.writeCharacters(vertex.label());
+            writer.writeEndElement();
+
+            for (String key : keys) {
+                writer.writeStartElement(GraphMLTokens.DATA);
+                writer.writeAttribute(GraphMLTokens.KEY, key);
+                // technically there can't be a null here as gremlin structure forbids that occurrence even if Graph
+                // implementations support it, but out to empty string just in case.
+                writer.writeCharacters(vertex.property(key).orElse("").toString());
+                writer.writeEndElement();
+            }
+            writer.writeEndElement();
+        }
+    }
+
+    private Collection<String> getElementKeysAndNormalizeIfRequired(final Element element) {
+        final Collection<String> keys;
+        if (normalize) {
+            keys = new ArrayList<>();
+            keys.addAll(element.keys());
+            Collections.sort((List<String>) keys);
+        } else
+            keys = element.keys();
+
+        return keys;
+    }
+
+    private Iterable<Vertex> getVerticesAndNormalizeIfRequired(final Graph graph) {
+        final Iterable<Vertex> vertices;
+        if (normalize) {
+            vertices = new ArrayList<>();
+            for (Vertex v : graph.V().toList()) {
+                ((Collection<Vertex>) vertices).add(v);
+            }
+            Collections.sort((List<Vertex>) vertices, Comparators.ELEMENT_COMPARATOR);
+        } else
+            vertices = IteratorUtils.list(graph.iterators().vertexIterator());
+
+        return vertices;
+    }
+
+    private Collection<String> getEdgeKeysAndNormalizeIfRequired(final Map<String, String> identifiedEdgeKeyTypes) {
+        final Collection<String> edgeKeySet;
+        if (normalize) {
+            edgeKeySet = new ArrayList<>();
+            edgeKeySet.addAll(identifiedEdgeKeyTypes.keySet());
+            Collections.sort((List<String>) edgeKeySet);
+        } else
+            edgeKeySet = identifiedEdgeKeyTypes.keySet();
+
+        return edgeKeySet;
+    }
+
+    private Collection<String> getVertexKeysAndNormalizeIfRequired(final Map<String, String> identifiedVertexKeyTypes) {
+        final Collection<String> keyset;
+        if (normalize) {
+            keyset = new ArrayList<>();
+            keyset.addAll(identifiedVertexKeyTypes.keySet());
+            Collections.sort((List<String>) keyset);
+        } else
+            keyset = identifiedVertexKeyTypes.keySet();
+
+        return keyset;
+    }
+
+    private void writeXmlNsAndSchema(final XMLStreamWriter writer) throws XMLStreamException {
+        writer.writeAttribute(GraphMLTokens.XMLNS, GraphMLTokens.GRAPHML_XMLNS);
+
+        //XML Schema instance namespace definition (xsi)
+        writer.writeAttribute(XMLConstants.XMLNS_ATTRIBUTE + ':' + GraphMLTokens.XML_SCHEMA_NAMESPACE_TAG,
+                XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
+        //XML Schema location
+        writer.writeAttribute(GraphMLTokens.XML_SCHEMA_NAMESPACE_TAG + ':' + GraphMLTokens.XML_SCHEMA_LOCATION_ATTRIBUTE,
+                GraphMLTokens.GRAPHML_XMLNS + ' ' + this.xmlSchemaLocation.orElse(GraphMLTokens.DEFAULT_GRAPHML_SCHEMA_LOCATION));
+    }
+
+    private static Map<String, String> determineVertexTypes(final Graph graph) {
+        final Map<String, String> vertexKeyTypes = new HashMap<>();
+        for (Vertex vertex : graph.V().toList()) {
+            for (String key : vertex.keys()) {
+                if (!vertexKeyTypes.containsKey(key)) {
+                    vertexKeyTypes.put(key, GraphMLWriter.getStringType(vertex.property(key).value()));
+                }
+            }
+        }
+
+        return vertexKeyTypes;
+    }
+
+    private static Map<String, String> determineEdgeTypes(final Graph graph) {
+        final Map<String, String> edgeKeyTypes = new HashMap<>();
+        for (Edge edge : graph.E().toList()) {
+            for (String key : edge.keys()) {
+                if (!edgeKeyTypes.containsKey(key))
+                    edgeKeyTypes.put(key, GraphMLWriter.getStringType(edge.property(key).value()));
+            }
+        }
+
+        return edgeKeyTypes;
+    }
+
+    private static String getStringType(final Object object) {
+        if (object instanceof String)
+            return GraphMLTokens.STRING;
+        else if (object instanceof Integer)
+            return GraphMLTokens.INT;
+        else if (object instanceof Long)
+            return GraphMLTokens.LONG;
+        else if (object instanceof Float)
+            return GraphMLTokens.FLOAT;
+        else if (object instanceof Double)
+            return GraphMLTokens.DOUBLE;
+        else if (object instanceof Boolean)
+            return GraphMLTokens.BOOLEAN;
+        else
+            return GraphMLTokens.STRING;
+    }
+
+    public static Builder build() {
+        return new Builder();
+    }
+
+    public static final class Builder {
+        private boolean normalize = false;
+        private Map<String, String> vertexKeyTypes = null;
+        private Map<String, String> edgeKeyTypes = null;
+
+        private String xmlSchemaLocation = null;
+        private String edgeLabelKey = GraphMLTokens.LABEL_E;
+        private String vertexLabelKey = GraphMLTokens.LABEL_V;
+
+        private Builder() {
+        }
+
+        /**
+         * Normalized output is deterministic with respect to the order of elements and properties in the resulting
+         * XML document, and is compatible with line diff-based tools such as Git. Note: normalized output is
+         * sideEffects-intensive and is not appropriate for very large graphs.
+         *
+         * @param normalize whether to normalize the output.
+         */
+        public Builder normalize(final boolean normalize) {
+            this.normalize = normalize;
+            return this;
+        }
+
+        /**
+         * Map of the data types of the vertex keys.  It is best to specify this Map if possible as the only
+         * other way to attain it is to iterate all vertices to find all the property keys.
+         */
+        public Builder vertexKeyTypes(final Map<String, String> vertexKeyTypes) {
+            this.vertexKeyTypes = vertexKeyTypes;
+            return this;
+        }
+
+        /**
+         * Map of the data types of the edge keys.  It is best to specify this Map if possible as the only
+         * other way to attain it is to iterate all vertices to find all the property keys.
+         */
+        public Builder edgeKeyTypes(final Map<String, String> edgeKeyTypes) {
+            this.edgeKeyTypes = edgeKeyTypes;
+            return this;
+        }
+
+        /**
+         * Location of the GraphML schema which is defaulted to
+         * {@link com.tinkerpop.gremlin.structure.io.graphml.GraphMLTokens#DEFAULT_GRAPHML_SCHEMA_LOCATION}.
+         */
+        public Builder xmlSchemaLocation(final String xmlSchemaLocation) {
+            this.xmlSchemaLocation = xmlSchemaLocation;
+            return this;
+        }
+
+        /**
+         * Set the name of the edge label in the GraphML. This value is defaulted to {@link GraphMLTokens#LABEL_E}.
+         * The value of {@link com.tinkerpop.gremlin.structure.Edge#label()} is written as a data element on the edge
+         * and the appropriate key element is added to define it in the GraphML.  It is important that when reading
+         * this GraphML back in with the reader that this label key is set appropriately to properly read the edge
+         * labels.
+         *
+         * @param edgeLabelKey if the label of an edge will be handled by the data property.
+         */
+        public Builder edgeLabelKey(final String edgeLabelKey) {
+            this.edgeLabelKey = edgeLabelKey;
+            return this;
+        }
+
+        /**
+         * Set the name of the vertex label in the GraphML. This value is defaulted to {@link GraphMLTokens#LABEL_V}.
+         * The value of {@link com.tinkerpop.gremlin.structure.Vertex#label()} is written as a data element on the
+         * vertex and the appropriate key element is added to define it in the GraphML.  It is important that when
+         * reading this GraphML back in with the reader that this label key is set appropriately to properly read the
+         * vertex labels.
+         *
+         * @param vertexLabelKey if the label of an vertex will be handled by the data property.
+         */
+        public Builder vertexLabelKey(final String vertexLabelKey) {
+            this.vertexLabelKey = vertexLabelKey;
+            return this;
+        }
+
+        public GraphMLWriter create() {
+            return new GraphMLWriter(normalize, vertexKeyTypes, edgeKeyTypes, xmlSchemaLocation, edgeLabelKey, vertexLabelKey);
+        }
+    }
+}
+