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 2016/12/16 15:55:09 UTC

[08/50] tinkerpop git commit: TINKERPOP-1562 Added gremlin-console plugins under the new model.

TINKERPOP-1562 Added gremlin-console plugins under the new model.

Had to rework the PluggedIn adapters a bit so that they could better handle Console environment variable.s


Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/a91fb80e
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/a91fb80e
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/a91fb80e

Branch: refs/heads/TINKERPOP-1490
Commit: a91fb80e64ceb2e5c97b5f8c2ef20205d075ec18
Parents: bb5b47d
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Tue Nov 22 16:35:16 2016 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Fri Dec 2 06:28:51 2016 -0500

----------------------------------------------------------------------
 .../tinkerpop/gremlin/console/Console.groovy    |   4 +-
 .../console/jsr223/GephiRemoteAcceptor.groovy   | 372 +++++++++++++++++++
 .../gremlin/console/plugin/PluggedIn.groovy     |  14 +-
 .../groovy/plugin/DriverGremlinPlugin.java      |   2 +
 .../groovy/plugin/DriverRemoteAcceptor.java     |   2 +
 .../groovy/plugin/GephiGremlinPlugin.java       |   1 +
 .../groovy/plugin/UtilitiesGremlinPlugin.java   |   1 +
 .../console/jsr223/DriverGremlinPlugin.java     | 106 ++++++
 .../console/jsr223/DriverRemoteAcceptor.java    | 238 ++++++++++++
 .../console/jsr223/GephiGremlinPlugin.java      |  45 +++
 .../console/jsr223/UtilitiesGremlinPlugin.java  | 106 ++++++
 ...pache.tinkerpop.gremlin.jsr223.GremlinPlugin |   3 +
 .../jsr223/UtilitiesGremlinPluginScript.groovy  |  52 +++
 .../groovy/plugin/GremlinPluginAdapterTest.java |   5 +-
 14 files changed, 940 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a91fb80e/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy
----------------------------------------------------------------------
diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy
index cb6e90f..d39e085 100644
--- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy
+++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy
@@ -131,9 +131,9 @@ class Console {
                 def pluggedIn
 
                 if (Mediator.useV3d3) {
-                    pluggedIn = new PluggedIn(new PluggedIn.GremlinPluginAdapter(plugin), groovy, io, false)
+                    pluggedIn = new PluggedIn(new PluggedIn.GremlinPluginAdapter((org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin) plugin, groovy, io), groovy, io, false)
                 } else {
-                    pluggedIn = new PluggedIn(plugin, groovy, io, false)
+                    pluggedIn = new PluggedIn((GremlinPlugin) plugin, groovy, io, false)
                 }
 
                 mediator.availablePlugins.put(plugin.class.name, pluggedIn)

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a91fb80e/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy
----------------------------------------------------------------------
diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy
new file mode 100644
index 0000000..dbc1156
--- /dev/null
+++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy
@@ -0,0 +1,372 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.console.jsr223
+
+import groovy.json.JsonOutput
+import groovy.json.JsonSlurper
+import groovy.transform.CompileStatic
+import org.apache.http.client.methods.CloseableHttpResponse
+import org.apache.http.client.methods.HttpUriRequest
+import org.apache.http.client.methods.RequestBuilder
+import org.apache.http.entity.StringEntity
+import org.apache.http.impl.client.CloseableHttpClient
+import org.apache.http.impl.client.HttpClients
+import org.apache.http.util.EntityUtils
+import org.apache.tinkerpop.gremlin.console.plugin.GephiTraversalVisualizationStrategy
+import org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor
+import org.apache.tinkerpop.gremlin.jsr223.console.RemoteException
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource
+import org.apache.tinkerpop.gremlin.structure.Edge
+import org.apache.tinkerpop.gremlin.structure.Graph
+import org.apache.tinkerpop.gremlin.structure.Vertex
+import org.codehaus.groovy.tools.shell.Groovysh
+import org.codehaus.groovy.tools.shell.IO
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ * @author Randall Barnhart (randompi@gmail.com)
+ */
+class GephiRemoteAcceptor implements RemoteAcceptor {
+
+    private String host = "localhost"
+    private int port = 8080
+    private String workspace = "workspace1"
+
+    private final Groovysh shell
+    private final IO io
+
+    private final Random rand = new Random();
+    boolean traversalSubmittedForViz = false
+    long vizStepDelay
+    private float[] vizStartRGBColor
+    private float[] vizDefaultRGBColor
+    private char vizColorToFade
+    private float vizColorFadeRate
+    private float vizStartSize
+    private float vizSizeDecrementRate
+    private Map vertexAttributes = [:]
+
+    private CloseableHttpClient httpclient = HttpClients.createDefault();
+
+    public GephiRemoteAcceptor(final Groovysh shell, final IO io) {
+        this.shell = shell
+        this.io = io
+
+        // traversal visualization defaults
+        vizStepDelay = 1000;                 // 1 second pause between viz of steps
+        vizStartRGBColor = [0.0f, 1.0f, 0.5f]  // light aqua green
+        vizDefaultRGBColor = [0.6f, 0.6f, 0.6f]  // light grey
+        vizColorToFade = 'g'                 // will fade so blue is strongest
+        vizColorFadeRate = 0.7               // the multiplicative rate to fade visited vertices
+        vizStartSize = 10
+        vizSizeDecrementRate = 0.33f
+    }
+
+    @Override
+    connect(final List<String> args) throws RemoteException {
+        if (args.size() >= 1)
+            workspace = args[0]
+
+        if (args.size() >= 2)
+            host = args[1]
+
+        if (args.size() >= 3) {
+            try {
+                port = Integer.parseInt(args[2])
+            } catch (Exception ex) {
+                throw new RemoteException("Port must be an integer value")
+            }
+        }
+
+        def vizConfig = " with stepDelay:$vizStepDelay, startRGBColor:$vizStartRGBColor, " +
+                "colorToFade:$vizColorToFade, colorFadeRate:$vizColorFadeRate, startSize:$vizStartSize," +
+                "sizeDecrementRate:$vizSizeDecrementRate"
+
+        return "Connection to Gephi - http://$host:$port/$workspace" + vizConfig
+    }
+
+    @Override
+    Object configure(final List<String> args) throws RemoteException {
+        if (args.size() < 2)
+            throw new RemoteException("Invalid config arguments - check syntax")
+
+        if (args[0] == "host")
+            host = args[1]
+        else if (args[0] == "port") {
+            try {
+                port = Integer.parseInt(args[1])
+            } catch (Exception ignored) {
+                throw new RemoteException("Port must be an integer value")
+            }
+        } else if (args[0] == "workspace")
+            workspace = args[1]
+        else if (args[0] == "stepDelay")
+            parseVizStepDelay(args[1])
+        else if (args[0] == "startRGBColor")
+            parseVizStartRGBColor(args[1])
+        else if (args[0] == "colorToFade")
+            parseVizColorToFade(args[1])
+        else if (args[0] == "colorFadeRate")
+            parseVizColorFadeRate(args[1])
+        else if (args[0] == "sizeDecrementRate")
+            parseVizSizeDecrementRate(args[1])
+        else if (args[0] == "startSize")
+            parseVizStartSize(args[1])
+        else if (args[0] == "visualTraversal") {
+            def graphVar = shell.interp.context.getVariable(args[1])
+            if (!(graphVar instanceof Graph))
+                throw new RemoteException("Invalid argument to 'visualTraversal' - first parameter must be a Graph instance")
+
+            def gVar = args.size() == 3 ? args[2] : "vg"
+            def theG = GraphTraversalSource.build().with(new GephiTraversalVisualizationStrategy(this)).create(graphVar)
+            shell.interp.context.setVariable(gVar, theG)
+        } else
+            throw new RemoteException("Invalid config arguments - check syntax")
+
+        return "Connection to Gephi - http://$host:$port/$workspace" +
+                " with stepDelay:$vizStepDelay, startRGBColor:$vizStartRGBColor, " +
+                "colorToFade:$vizColorToFade, colorFadeRate:$vizColorFadeRate, startSize:$vizStartSize," +
+                "sizeDecrementRate:$vizSizeDecrementRate"
+    }
+
+    @Override
+    @CompileStatic
+    Object submit(final List<String> args) throws RemoteException {
+        final String line = String.join(" ", args)
+        if (line.trim() == "clear") {
+            clearGraph()
+            io.out.println("Gephi workspace cleared")
+            return
+        }
+
+        // need to clear the vertex attributes
+        vertexAttributes.clear()
+
+        // this tells the GraphTraversalVisualizationStrategy that if the line eval's to a traversal it should
+        // try to visualize it
+        traversalSubmittedForViz = true
+
+        // get the colors/sizes back to basics before trying visualize
+        resetColorsSizes()
+
+        final Object o = shell.execute(line)
+        if (o instanceof Graph) {
+            clearGraph()
+            def graph = (Graph) o
+            def g = graph.traversal()
+            g.V().sideEffect { addVertexToGephi(g, it.get()) }.iterate()
+        }
+
+        traversalSubmittedForViz = false
+    }
+
+    @Override
+    void close() throws IOException {
+        httpclient.close()
+    }
+
+    /**
+     * Visits the last set of vertices traversed and degrades their color and size.
+     */
+    def updateVisitedVertices(def List except = []) {
+        vertexAttributes.keySet().findAll{ vertexId -> !except.contains(vertexId) }.each { String vertexId ->
+            def attrs = vertexAttributes[vertexId]
+            float currentColor = attrs.color
+            currentColor *= vizColorFadeRate
+
+            int currentSize = attrs["size"]
+            currentSize = Math.max(vizStartSize, currentSize - (currentSize * vizSizeDecrementRate))
+
+            vertexAttributes.get(vertexId).color = currentColor
+            vertexAttributes.get(vertexId).size = currentSize
+
+            changeVertexAttributes(vertexId)
+        }
+    }
+
+    def changeVertexAttributes(def String vertexId) {
+        def props = [:]
+        props.put(vizColorToFade.toString(), vertexAttributes[vertexId].color)
+        props.put("size", vertexAttributes[vertexId].size)
+        updateGephiGraph([cn: [(vertexId): props]])
+    }
+
+    /**
+     * Visit a vertex traversed and initialize its color and size.
+     */
+    def visitVertexInGephi(def Vertex v) {
+        def props = [:]
+        props.put('r', vizStartRGBColor[0])
+        props.put('g', vizStartRGBColor[1])
+        props.put('b', vizStartRGBColor[2])
+        props.put('size', vizStartSize * 2.5)
+        props.put('visited', 1)
+
+        updateGephiGraph([cn: [(v.id().toString()): props]])
+
+        vertexAttributes[v.id().toString()] = [color: vizStartRGBColor[fadeColorIndex()], size: vizStartSize * 2.5, touch: 1]
+    }
+
+    def fadeColorIndex() {
+        if (vizColorToFade == 'r')
+            return 0
+        else if (vizColorToFade == 'g')
+            return 1
+        else if (vizColorToFade == 'b')
+            return 2
+    }
+
+    def addVertexToGephi(def GraphTraversalSource g, def Vertex v, def boolean ignoreEdges = false) {
+        // grab the first property value from the strategies of values
+        def props = g.V(v).valueMap().next().collectEntries { kv -> [(kv.key): kv.value[0]] }
+        props << [label: v.label()]
+        props.put('r', vizDefaultRGBColor[0])
+        props.put('g', vizDefaultRGBColor[1])
+        props.put('b', vizDefaultRGBColor[2])
+        props.put('x', rand.nextFloat())
+        props.put('y', rand.nextFloat())
+        props.put('size', 10)
+        props.put('visited', 0)
+
+        // only add if it does not exist in graph already
+        if (!getFromGephiGraph([operation: "getNode", id: v.id().toString()]).isPresent())
+            updateGephiGraph([an: [(v.id().toString()): props]])
+
+        if (!ignoreEdges) {
+            g.V(v).outE().sideEffect {
+                addEdgeToGephi(g, it.get())
+            }.iterate()
+        }
+    }
+
+    @CompileStatic
+    def addEdgeToGephi(def GraphTraversalSource g, def Edge e) {
+        def props = g.E(e).valueMap().next()
+        props.put('label', e.label())
+        props.put('source', e.outVertex().id().toString())
+        props.put('target', e.inVertex().id().toString())
+        props.put('directed', true)
+        props.put('visited', 0)
+
+        // make sure the in vertex is there but don't add its edges - that will happen later as we are looping
+        // all vertices in the graph
+        addVertexToGephi(g, e.inVertex(), true)
+
+        // both vertices are definitely there now, so add the edge
+        updateGephiGraph([ae: [(e.id().toString()): props]])
+    }
+
+    def clearGraph() {
+        updateGephiGraph([dn: [filter: "ALL"]])
+    }
+
+    def resetColorsSizes() {
+        updateGephiGraph([cn: [filter: [nodeAttribute: [attribute: "visited", value: 1]],
+                               attributes: [size: 1, r: vizDefaultRGBColor[0], g: vizDefaultRGBColor[1], b: vizDefaultRGBColor[2]]]])
+        updateGephiGraph([ce: [filter: [edgeAttribute: [attribute: "visited", value: 1]],
+                               attributes: [size: 1, r: vizDefaultRGBColor[0], g: vizDefaultRGBColor[1], b: vizDefaultRGBColor[2]]]])
+    }
+
+    def getFromGephiGraph(def Map queryArgs) {
+        def requestBuilder = RequestBuilder.get("http://$host:$port/$workspace")
+        queryArgs.each { requestBuilder = requestBuilder.addParameter(it.key, it.value) }
+
+        def httpResp = makeRequest(requestBuilder.build())
+        def resp = EntityUtils.toString(httpResp.entity)
+
+        // gephi streaming plugin does not set the content type or respect the Accept header - treat as text
+        if (resp.isEmpty())
+            return Optional.empty()
+        else
+            return Optional.of(new JsonSlurper().parseText(resp))
+    }
+
+    def updateGephiGraph(def Map postBody) {
+        def requestBuilder = RequestBuilder.post("http://$host:$port/$workspace")
+                                           .addParameter("format", "JSON")
+                                           .addParameter("operation", "updateGraph")
+                                           .setEntity(new StringEntity(JsonOutput.toJson(postBody)))
+        EntityUtils.consume(makeRequest(requestBuilder.build()).entity)
+    }
+
+    private CloseableHttpResponse makeRequest(HttpUriRequest request) {
+        def httpResp = httpclient.execute(request)
+        if (httpResp.getStatusLine().getStatusCode() == 200) {
+            return httpResp
+        } else {
+            def resp = EntityUtils.toString(httpResp.entity)
+            throw new RuntimeException("Unsuccessful request to Gephi - [${httpResp.getStatusLine().getStatusCode()}] ${httpResp.getStatusLine().getReasonPhrase()} - $resp")
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "Gephi - [$workspace]"
+    }
+
+    private void parseVizStepDelay(String arg) {
+        try {
+            vizStepDelay = Long.parseLong(arg)
+        } catch (Exception ignored) {
+            throw new RemoteException("The stepDelay must be a long value")
+        }
+    }
+
+    private void parseVizStartRGBColor(String arg) {
+        try {
+            vizStartRGBColor = arg[1..-2].tokenize(',')*.toFloat()
+            assert (vizStartRGBColor.length == 3)
+        } catch (Exception ignored) {
+            throw new RemoteException("The vizStartRGBColor must be an array of 3 float values, e.g. [0.0,1.0,0.5]")
+        }
+    }
+
+    private void parseVizColorToFade(String arg) {
+        try {
+            vizColorToFade = arg.charAt(0).toLowerCase();
+            assert (vizColorToFade == 'r' || vizColorToFade == 'g' || vizColorToFade == 'b')
+        } catch (Exception ignored) {
+            throw new RemoteException("The vizColorToFade must be one character value among: r, g, b, R, G, B")
+        }
+    }
+
+    private void parseVizColorFadeRate(String arg) {
+        try {
+            vizColorFadeRate = Float.parseFloat(arg)
+        } catch (Exception ignored) {
+            throw new RemoteException("The colorFadeRate must be a float value")
+        }
+    }
+
+    private void parseVizSizeDecrementRate(String arg) {
+        try {
+            vizSizeDecrementRate = Float.parseFloat(arg)
+        } catch (Exception ignored) {
+            throw new RemoteException("The sizeDecrementRate must be a float value")
+        }
+    }
+
+    private void parseVizStartSize(String arg) {
+        try {
+            vizStartSize = Float.parseFloat(arg)
+        } catch (Exception ignored) {
+            throw new RemoteException("The startSize must be a float value")
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a91fb80e/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy
----------------------------------------------------------------------
diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy
index dc63a2f..7a08a9d 100644
--- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy
+++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy
@@ -68,9 +68,13 @@ class PluggedIn {
 
     public static class GremlinPluginAdapter implements GremlinPlugin {
         org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin corePlugin
+        private final Groovysh shell
+        private final IO io
 
-        public GremlinPluginAdapter(final org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin corePlugin) {
+        public GremlinPluginAdapter(final org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin corePlugin, final Groovysh shell, final IO io) {
             this.corePlugin = corePlugin
+            this.shell = shell
+            this.io = io
         }
 
         @Override
@@ -103,11 +107,11 @@ class PluggedIn {
         @Override
         Optional<RemoteAcceptor> remoteAcceptor() {
             // find a consoleCustomizer if available
-            if (!corePlugin.getCustomizers("gremlin-groovy").any{ it instanceof ConsoleCustomizer })
-                Optional.empty()
+            if (!corePlugin.getCustomizers("gremlin-groovy").isPresent() || !corePlugin.getCustomizers("gremlin-groovy").get().any{ it instanceof ConsoleCustomizer })
+                return Optional.empty()
 
-            ConsoleCustomizer customizer = (ConsoleCustomizer) corePlugin.getCustomizers("gremlin-groovy").find{ it instanceof ConsoleCustomizer }
-            return Optional.of(new RemoteAcceptorAdapter(customizer.remoteAcceptor))
+            ConsoleCustomizer customizer = (ConsoleCustomizer) corePlugin.getCustomizers("gremlin-groovy").get().find{ it instanceof ConsoleCustomizer }
+            return Optional.of(new RemoteAcceptorAdapter(customizer.getRemoteAcceptor([(ConsoleCustomizer.ENV_CONSOLE_SHELL): shell, (ConsoleCustomizer.ENV_CONSOLE_IO): io])))
         }
     }
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a91fb80e/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverGremlinPlugin.java
----------------------------------------------------------------------
diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverGremlinPlugin.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverGremlinPlugin.java
index 2c52bd5..2f8fc75 100644
--- a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverGremlinPlugin.java
+++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverGremlinPlugin.java
@@ -35,7 +35,9 @@ import java.util.Set;
 
 /**
  * @author Stephen Mallette (http://stephen.genoprime.com)
+ * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.console.jsr223.DriverGremlinPlugin}.
  */
+@Deprecated
 public class DriverGremlinPlugin extends AbstractGremlinPlugin {
 
     private static final Set<String> IMPORTS = new HashSet<String>() {{

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a91fb80e/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverRemoteAcceptor.java
----------------------------------------------------------------------
diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverRemoteAcceptor.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverRemoteAcceptor.java
index c346540..80e8194 100644
--- a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverRemoteAcceptor.java
+++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverRemoteAcceptor.java
@@ -50,7 +50,9 @@ import java.util.stream.Stream;
  *
  * @author Stephen Mallette (http://stephen.genoprime.com)
  * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.console.jsr223.DriverRemoteAcceptor}.
  */
+@Deprecated
 public class DriverRemoteAcceptor implements RemoteAcceptor {
     public static final int NO_TIMEOUT = 0;
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a91fb80e/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GephiGremlinPlugin.java
----------------------------------------------------------------------
diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GephiGremlinPlugin.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GephiGremlinPlugin.java
index 6977aa8..df0541f 100644
--- a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GephiGremlinPlugin.java
+++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GephiGremlinPlugin.java
@@ -29,6 +29,7 @@ import java.util.Optional;
 
 /**
  * @author Stephen Mallette (http://stephen.genoprime.com)
+ * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.console.jsr223.GephiGremlinPlugin}.
  */
 public class GephiGremlinPlugin extends AbstractGremlinPlugin {
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a91fb80e/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/UtilitiesGremlinPlugin.java
----------------------------------------------------------------------
diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/UtilitiesGremlinPlugin.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/UtilitiesGremlinPlugin.java
index 59c2b1a..d1c853d 100644
--- a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/UtilitiesGremlinPlugin.java
+++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/UtilitiesGremlinPlugin.java
@@ -34,6 +34,7 @@ import java.util.Set;
 
 /**
  * @author Stephen Mallette (http://stephen.genoprime.com)
+ * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.console.jsr223.UtilitiesGremlinPlugin}.
  */
 public class UtilitiesGremlinPlugin extends AbstractGremlinPlugin {
 

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a91fb80e/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverGremlinPlugin.java
----------------------------------------------------------------------
diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverGremlinPlugin.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverGremlinPlugin.java
new file mode 100644
index 0000000..89cec10
--- /dev/null
+++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverGremlinPlugin.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.console.jsr223;
+
+import org.apache.tinkerpop.gremlin.driver.Client;
+import org.apache.tinkerpop.gremlin.driver.Cluster;
+import org.apache.tinkerpop.gremlin.driver.Host;
+import org.apache.tinkerpop.gremlin.driver.LoadBalancingStrategy;
+import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
+import org.apache.tinkerpop.gremlin.driver.Result;
+import org.apache.tinkerpop.gremlin.driver.ResultSet;
+import org.apache.tinkerpop.gremlin.driver.Tokens;
+import org.apache.tinkerpop.gremlin.driver.exception.ConnectionException;
+import org.apache.tinkerpop.gremlin.driver.exception.ResponseException;
+import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
+import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
+import org.apache.tinkerpop.gremlin.driver.message.ResponseResult;
+import org.apache.tinkerpop.gremlin.driver.message.ResponseStatus;
+import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
+import org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection;
+import org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteTraversal;
+import org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteTraversalSideEffects;
+import org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerGremlinV1d0;
+import org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerGremlinV2d0;
+import org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV1d0;
+import org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV2d0;
+import org.apache.tinkerpop.gremlin.driver.ser.GryoLiteMessageSerializerV1d0;
+import org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0;
+import org.apache.tinkerpop.gremlin.driver.ser.JsonBuilderGryoSerializer;
+import org.apache.tinkerpop.gremlin.driver.ser.MessageTextSerializer;
+import org.apache.tinkerpop.gremlin.driver.ser.SerTokens;
+import org.apache.tinkerpop.gremlin.driver.ser.SerializationException;
+import org.apache.tinkerpop.gremlin.driver.ser.Serializers;
+import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin;
+import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer;
+import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer;
+import org.apache.tinkerpop.gremlin.jsr223.console.ConsoleCustomizer;
+import org.codehaus.groovy.tools.shell.Groovysh;
+
+import java.util.Map;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class DriverGremlinPlugin extends AbstractGremlinPlugin {
+
+    private static final String NAME = "tinkerpop.server";
+
+    private static final ImportCustomizer imports = DefaultImportCustomizer.build()
+            .addClassImports(Cluster.class,
+                    Client.class,
+                    Host.class,
+                    LoadBalancingStrategy.class,
+                    MessageSerializer.class,
+                    Result.class,
+                    ResultSet.class,
+                    Tokens.class,
+                    ConnectionException.class,
+                    ResponseException.class,
+                    RequestMessage.class,
+                    ResponseMessage.class,
+                    ResponseResult.class,
+                    ResponseStatus.class,
+                    ResponseStatusCode.class,
+                    GraphSONMessageSerializerGremlinV1d0.class,
+                    GraphSONMessageSerializerGremlinV2d0.class,
+                    GraphSONMessageSerializerV1d0.class,
+                    GraphSONMessageSerializerV2d0.class,
+                    GryoLiteMessageSerializerV1d0.class,
+                    GryoMessageSerializerV1d0.class,
+                    JsonBuilderGryoSerializer.class,
+                    MessageTextSerializer.class,
+                    SerializationException.class,
+                    Serializers.class,
+                    SerTokens.class,
+                    DriverRemoteConnection.class,
+                    DriverRemoteTraversal.class,
+                    DriverRemoteTraversalSideEffects.class).create();
+
+    public DriverGremlinPlugin() {
+        super(NAME, imports, new DriverConsoleCustomizer());
+    }
+
+    private static class DriverConsoleCustomizer implements ConsoleCustomizer {
+        @Override
+        public org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor getRemoteAcceptor(final Map<String, Object> environment) {
+            return new DriverRemoteAcceptor((Groovysh) environment.get(ConsoleCustomizer.ENV_CONSOLE_SHELL));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a91fb80e/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverRemoteAcceptor.java
----------------------------------------------------------------------
diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverRemoteAcceptor.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverRemoteAcceptor.java
new file mode 100644
index 0000000..93ac184
--- /dev/null
+++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverRemoteAcceptor.java
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.console.jsr223;
+
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.apache.tinkerpop.gremlin.driver.Client;
+import org.apache.tinkerpop.gremlin.driver.Cluster;
+import org.apache.tinkerpop.gremlin.driver.Result;
+import org.apache.tinkerpop.gremlin.driver.ResultSet;
+import org.apache.tinkerpop.gremlin.driver.exception.ResponseException;
+import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
+import org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor;
+import org.apache.tinkerpop.gremlin.jsr223.console.RemoteException;
+import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
+import org.codehaus.groovy.tools.shell.Groovysh;
+
+import javax.security.sasl.SaslException;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.stream.Stream;
+
+/**
+ * A {@link RemoteAcceptor} that takes input from the console and sends it to Gremlin Server over the standard
+ * Java driver.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public class DriverRemoteAcceptor implements RemoteAcceptor {
+    public static final int NO_TIMEOUT = 0;
+
+    private Cluster currentCluster;
+    private Client currentClient;
+    private int timeout = NO_TIMEOUT;
+    private Map<String,String> aliases = new HashMap<>();
+    private Optional<String> session = Optional.empty();
+
+    private static final String TOKEN_RESET = "reset";
+    private static final String TOKEN_SHOW = "show";
+
+    /**
+     * @deprecated As of 3.1.3, replaced by "none" option
+     */
+    @Deprecated
+    private static final String TOKEN_MAX = "max";
+    private static final String TOKEN_NONE = "none";
+    private static final String TOKEN_TIMEOUT = "timeout";
+    private static final String TOKEN_ALIAS = "alias";
+    private static final String TOKEN_SESSION = "session";
+    private static final String TOKEN_SESSION_MANAGED = "session-managed";
+    private static final List<String> POSSIBLE_TOKENS = Arrays.asList(TOKEN_TIMEOUT, TOKEN_ALIAS);
+
+    private final Groovysh shell;
+
+    public DriverRemoteAcceptor(final Groovysh shell) {
+        this.shell = shell;
+    }
+
+    @Override
+    public Object connect(final List<String> args) throws RemoteException {
+        if (args.size() < 1) throw new RemoteException("Expects the location of a configuration file as an argument");
+
+        try {
+            this.currentCluster = Cluster.open(args.get(0));
+            final boolean useSession = args.size() >= 2 && (args.get(1).equals(TOKEN_SESSION) || args.get(1).equals(TOKEN_SESSION_MANAGED));
+            if (useSession) {
+                final String sessionName = args.size() == 3 ? args.get(2) : UUID.randomUUID().toString();
+                session = Optional.of(sessionName);
+
+                final boolean managed = args.get(1).equals(TOKEN_SESSION_MANAGED);
+
+                this.currentClient = this.currentCluster.connect(sessionName, managed);
+            } else {
+                this.currentClient = this.currentCluster.connect();
+            }
+            this.currentClient.init();
+            return String.format("Configured %s", this.currentCluster) + getSessionStringSegment();
+        } catch (final FileNotFoundException ignored) {
+            throw new RemoteException("The 'connect' option must be accompanied by a valid configuration file");
+        } catch (final Exception ex) {
+            throw new RemoteException("Error during 'connect' - " + ex.getMessage(), ex);
+        }
+    }
+
+    @Override
+    public Object configure(final List<String> args) throws RemoteException {
+        final String option = args.size() == 0 ? "" : args.get(0);
+        if (!POSSIBLE_TOKENS.contains(option))
+            throw new RemoteException(String.format("The 'config' option expects one of ['%s'] as an argument", String.join(",", POSSIBLE_TOKENS)));
+
+        final List<String> arguments = args.subList(1, args.size());
+
+        if (option.equals(TOKEN_TIMEOUT)) {
+            final String errorMessage = "The timeout option expects a positive integer representing milliseconds or 'none' as an argument";
+            if (arguments.size() != 1) throw new RemoteException(errorMessage);
+            try {
+                // first check for MAX timeout then NONE and finally parse the config to int. "max" is now "deprecated"
+                // in the sense that it will no longer be promoted. support for it will be removed at a later date
+                timeout = arguments.get(0).equals(TOKEN_MAX) ? Integer.MAX_VALUE :
+                        arguments.get(0).equals(TOKEN_NONE) ? NO_TIMEOUT : Integer.parseInt(arguments.get(0));
+                if (timeout < NO_TIMEOUT) throw new RemoteException("The value for the timeout cannot be less than " + NO_TIMEOUT);
+                return timeout == NO_TIMEOUT ? "Remote timeout is disabled" : "Set remote timeout to " + timeout + "ms";
+            } catch (Exception ignored) {
+                throw new RemoteException(errorMessage);
+            }
+        } else if (option.equals(TOKEN_ALIAS)) {
+            if (arguments.size() == 1 && arguments.get(0).equals(TOKEN_RESET)) {
+                aliases.clear();
+                return "Aliases cleared";
+            }
+
+            if (arguments.size() == 1 && arguments.get(0).equals(TOKEN_SHOW)) {
+                return aliases;
+            }
+
+            if (arguments.size() % 2 != 0)
+                throw new RemoteException("Arguments to alias must be 'reset' to clear any existing alias settings or key/value alias/binding pairs");
+
+            final Map<String,Object> aliasMap = ElementHelper.asMap(arguments.toArray());
+            aliases.clear();
+            aliasMap.forEach((k,v) -> aliases.put(k, v.toString()));
+            return aliases;
+        }
+
+        return this.toString();
+    }
+
+    @Override
+    public Object submit(final List<String> args) throws RemoteException {
+        final String line = getScript(String.join(" ", args), this.shell);
+
+        try {
+            final List<Result> resultSet = send(line);
+            this.shell.getInterp().getContext().setProperty(RESULT, resultSet);
+            return resultSet.stream().map(result -> result.getObject()).iterator();
+        } catch (SaslException sasl) {
+            throw new RemoteException("Security error - check username/password and related settings", sasl);
+        } catch (Exception ex) {
+            final Optional<ResponseException> inner = findResponseException(ex);
+            if (inner.isPresent()) {
+                final ResponseException responseException = inner.get();
+                if (responseException.getResponseStatusCode() == ResponseStatusCode.SERVER_ERROR_SERIALIZATION)
+                    throw new RemoteException(String.format("Server could not serialize the result requested. Server error - %s. Note that the class must be serializable by the client and server for proper operation.", responseException.getMessage()));
+                else
+                    throw new RemoteException(responseException.getMessage());
+            } else if (ex.getCause() != null) {
+                final Throwable rootCause = ExceptionUtils.getRootCause(ex);
+                if (rootCause instanceof TimeoutException)
+                    throw new RemoteException("Host did not respond in a timely fashion - check the server status and submit again.");
+                else
+                    throw new RemoteException(rootCause.getMessage());
+            } else
+                throw new RemoteException(ex.getMessage());
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        if (this.currentClient != null) this.currentClient.close();
+        if (this.currentCluster != null) this.currentCluster.close();
+    }
+
+    public int getTimeout() {
+        return timeout;
+    }
+
+    private List<Result> send(final String gremlin) throws SaslException {
+        try {
+            final ResultSet rs = this.currentClient.submitAsync(gremlin, aliases, Collections.emptyMap()).get();
+            return timeout > NO_TIMEOUT ? rs.all().get(timeout, TimeUnit.MILLISECONDS) : rs.all().get();
+        } catch(TimeoutException ignored) {
+            throw new IllegalStateException("Request timed out while processing - increase the timeout with the :remote command");
+        } catch (Exception e) {
+            // handle security error as-is and unwrapped
+            final Optional<Throwable> throwable  = Stream.of(ExceptionUtils.getThrowables(e)).filter(t -> t instanceof SaslException).findFirst();
+            if (throwable.isPresent())
+                throw (SaslException) throwable.get();
+
+            throw new IllegalStateException(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public boolean allowRemoteConsole() {
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "Gremlin Server - [" + this.currentCluster + "]" + getSessionStringSegment();
+    }
+
+    private Optional<ResponseException> findResponseException(final Throwable ex) {
+        if (ex instanceof ResponseException)
+            return Optional.of((ResponseException) ex);
+
+        if (null == ex.getCause())
+            return Optional.empty();
+
+        return findResponseException(ex.getCause());
+    }
+
+    private String getSessionStringSegment() {
+        return session.isPresent() ? String.format("-[%s]", session.get()) : "";
+    }
+
+    /**
+     * Retrieve a script as defined in the shell context.  This allows for multi-line scripts to be submitted.
+     */
+    public static String getScript(final String submittedScript, final Groovysh shell) {
+        return submittedScript.startsWith("@") ? shell.getInterp().getContext().getProperty(submittedScript.substring(1)).toString() : submittedScript;
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a91fb80e/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/GephiGremlinPlugin.java
----------------------------------------------------------------------
diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/GephiGremlinPlugin.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/GephiGremlinPlugin.java
new file mode 100644
index 0000000..7698112
--- /dev/null
+++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/GephiGremlinPlugin.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.console.jsr223;
+
+import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin;
+import org.apache.tinkerpop.gremlin.jsr223.console.ConsoleCustomizer;
+import org.codehaus.groovy.tools.shell.Groovysh;
+import org.codehaus.groovy.tools.shell.IO;
+
+import java.util.Map;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class GephiGremlinPlugin extends AbstractGremlinPlugin {
+    private static final String NAME = "tinkerpop.gephi";
+
+    public GephiGremlinPlugin() {
+        super(NAME, new GephiConsoleCustomizer());
+    }
+
+    private static class GephiConsoleCustomizer implements ConsoleCustomizer {
+        @Override
+        public org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor getRemoteAcceptor(final Map<String, Object> environment) {
+            return new GephiRemoteAcceptor((Groovysh) environment.get(ConsoleCustomizer.ENV_CONSOLE_SHELL),
+                    (IO) environment.get(ConsoleCustomizer.ENV_CONSOLE_IO));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a91fb80e/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/UtilitiesGremlinPlugin.java
----------------------------------------------------------------------
diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/UtilitiesGremlinPlugin.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/UtilitiesGremlinPlugin.java
new file mode 100644
index 0000000..57bacda
--- /dev/null
+++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/UtilitiesGremlinPlugin.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.console.jsr223;
+
+import groovyx.gbench.Benchmark;
+import groovyx.gbench.BenchmarkStaticExtension;
+import groovyx.gprof.ProfileStaticExtension;
+import groovyx.gprof.Profiler;
+import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin;
+import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer;
+import org.apache.tinkerpop.gremlin.jsr223.DefaultScriptCustomizer;
+import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer;
+import org.apache.tinkerpop.gremlin.jsr223.ScriptCustomizer;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class UtilitiesGremlinPlugin extends AbstractGremlinPlugin {
+
+    private static final String NAME = "tinkerpop.utilities";
+
+    private static final ImportCustomizer imports;
+
+    private static final ScriptCustomizer scripts;
+
+    static {
+        try {
+            imports = DefaultImportCustomizer.build()
+                    .addClassImports(groovyx.gbench.Benchmark.class,
+                            groovyx.gbench.BenchmarkBuilder.class,
+                            groovyx.gbench.BenchmarkConstants.class,
+                            groovyx.gbench.BenchmarkContext.class,
+                            groovyx.gbench.Benchmarker.class,
+                            groovyx.gbench.BenchmarkList.class,
+                            groovyx.gbench.BenchmarkLogger.class,
+                            groovyx.gbench.BenchmarkMath.class,
+                            groovyx.gbench.BenchmarkMeasure.class,
+                            groovyx.gbench.BenchmarkStaticExtension.class,
+                            groovyx.gbench.BenchmarkSystem.class,
+                            groovyx.gbench.BenchmarkTime.class,
+                            groovyx.gbench.BenchmarkWarmUp.class,
+                            groovyx.gprof.Profiler.class,
+                            groovyx.gprof.ProfileStaticExtension.class,
+                            groovyx.gprof.CallFilter.class,
+                            groovyx.gprof.CallInfo.class,
+                            groovyx.gprof.CallInterceptor.class,
+                            groovyx.gprof.CallMatcher.class,
+                            groovyx.gprof.CallTree.class,
+                            groovyx.gprof.MethodCallFilter.class,
+                            groovyx.gprof.MethodCallInfo.class,
+                            groovyx.gprof.MethodInfo.class,
+                            groovyx.gprof.ProfileMetaClass.class,
+                            groovyx.gprof.ProxyReport.class,
+                            groovyx.gprof.Report.class,
+                            groovyx.gprof.ReportElement.class,
+                            groovyx.gprof.ReportNormalizer.class,
+                            groovyx.gprof.ReportPrinter.class,
+                            groovyx.gprof.ThreadInfo.class,
+                            groovyx.gprof.ThreadRunFilter.class,
+                            groovyx.gprof.Utils.class)
+                    .addMethodImports(
+                            ProfileStaticExtension.class.getMethod("profile", Object.class, Callable.class),
+                            ProfileStaticExtension.class.getMethod("profile", Object.class, Map.class, Callable.class)).create();
+
+            final BufferedReader reader = new BufferedReader(new InputStreamReader(UtilitiesGremlinPlugin.class.getResourceAsStream("UtilitiesGremlinPluginScript.groovy")));
+            final List<String> lines = new ArrayList<>();
+            String line;
+            while ((line = reader.readLine()) != null) {
+                lines.add(line);
+            }
+            reader.close();
+
+            scripts = new DefaultScriptCustomizer(Collections.singletonList(lines));
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    public UtilitiesGremlinPlugin() {
+        super(NAME, imports, scripts);
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a91fb80e/gremlin-console/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin
----------------------------------------------------------------------
diff --git a/gremlin-console/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin b/gremlin-console/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin
new file mode 100644
index 0000000..631c889
--- /dev/null
+++ b/gremlin-console/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin
@@ -0,0 +1,3 @@
+org.apache.tinkerpop.gremlin.console.jsr223.DriverGremlinPlugin
+org.apache.tinkerpop.gremlin.console.jsr223.GephiGremlinPlugin
+org.apache.tinkerpop.gremlin.console.jsr223.UtilitiesGremlinPlugin
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a91fb80e/gremlin-console/src/main/resources/org/apache/tinkerpop/gremlin/console/jsr223/UtilitiesGremlinPluginScript.groovy
----------------------------------------------------------------------
diff --git a/gremlin-console/src/main/resources/org/apache/tinkerpop/gremlin/console/jsr223/UtilitiesGremlinPluginScript.groovy b/gremlin-console/src/main/resources/org/apache/tinkerpop/gremlin/console/jsr223/UtilitiesGremlinPluginScript.groovy
new file mode 100644
index 0000000..5f5e3c6
--- /dev/null
+++ b/gremlin-console/src/main/resources/org/apache/tinkerpop/gremlin/console/jsr223/UtilitiesGremlinPluginScript.groovy
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+/**
+ * @author Daniel Kuppitz (http://thinkaurelius.com)
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+
+describeGraph = { Class<? extends org.apache.tinkerpop.gremlin.structure.Graph> c ->
+    def lf = System.getProperty("line.separator")
+    def optIns = c.getAnnotationsByType(org.apache.tinkerpop.gremlin.structure.Graph.OptIn)
+    def optOuts = c.getAnnotationsByType(org.apache.tinkerpop.gremlin.structure.Graph.OptOut)
+
+    def optInCount = optIns != null ? optIns.size() : 0
+    def optOutCount = optOuts != null ? optOuts.size() : 0
+    def suitesSupported = optIns != null && optIns.size() > 0 ? optIns.collect { "> " + it.value() }.join(lf) : "> none"
+    def testsOptedOut = optOuts != null && optOuts.size() > 0 ? optOuts.collect { "> " + it.test() + "#" + it.method() + "${lf}\t\"" + it.reason() + "\"" }.join(lf) : "> none";
+
+    // not the use of {lf} here rather than triple quoted string is that groovy 2.4.0 seems to have trouble
+    // parsing that into groovysh - note the bug report here: https://jira.codehaus.org/browse/GROOVY-7290
+    return "${lf}" +
+"IMPLEMENTATION - ${c.getCanonicalName()} ${lf}" +
+"TINKERPOP TEST SUITE ${lf}" +
+"- Compliant with ($optInCount of 10 suites) ${lf}" +
+"$suitesSupported ${lf}" +
+"- Opts out of $optOutCount individual tests ${lf}" +
+"$testsOptedOut ${lf}" +
+"- NOTE - ${lf}" +
+"The describeGraph() function shows information about a Graph implementation. ${lf}" +
+"It uses information found in Java Annotations on the implementation itself to ${lf}" +
+"determine this output and does not assess the actual code of the test cases of ${lf}" +
+"the implementation itself.  Compliant implementations will faithfully and ${lf}" +
+"honestly supply these Annotations to provide the most accurate depiction of ${lf}" +
+"their support."
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/a91fb80e/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java
----------------------------------------------------------------------
diff --git a/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java b/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java
index 77422da..dd582b1 100644
--- a/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java
+++ b/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java
@@ -19,10 +19,7 @@
 package org.apache.tinkerpop.gremlin.console.groovy.plugin;
 
 import org.apache.tinkerpop.gremlin.console.plugin.PluggedIn;
-import org.apache.tinkerpop.gremlin.jsr223.BindingsCustomizer;
 import org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin;
-import org.apache.tinkerpop.gremlin.jsr223.ScriptCustomizer;
-import org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin;
 import org.junit.Test;
 
 import java.time.DayOfWeek;
@@ -44,7 +41,7 @@ public class GremlinPluginAdapterTest {
                 .classImports(java.awt.Color.class, java.sql.CallableStatement.class)
                 .enumImports(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY)
                 .methodImports(DayOfWeek.class.getMethod("from", TemporalAccessor.class), DayOfWeek.class.getMethod("values")).create();
-        final PluggedIn.GremlinPluginAdapter adapter = new PluggedIn.GremlinPluginAdapter(plugin);
+        final PluggedIn.GremlinPluginAdapter adapter = new PluggedIn.GremlinPluginAdapter(plugin, null, null);
 
         assertEquals(plugin.getName(), adapter.getName());