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 2021/10/29 19:28:46 UTC

[tinkerpop] 01/01: TINKERPOP-2583 Added GremlinLangScriptEngine

This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch TINKERPOP-2583
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 26a2ea20fb96a0251919854ce4eb6cede7bb3d2d
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Fri Oct 29 15:26:45 2021 -0400

    TINKERPOP-2583 Added GremlinLangScriptEngine
    
    Uses the grammar to eval the Gremln scripts. Only enabled its usage for the test server yaml files. Not expecting to enable it along 3.5.x but wanted the functionality here in case folks want to play with it a bit.
---
 CHANGELOG.asciidoc                                 |   1 +
 .../gremlin-server-integration-krb5.yaml           |   1 +
 .../gremlin-server-integration-secure.yaml         |   1 +
 .../gremlin-server/gremlin-server-integration.yaml |   1 +
 .../gremlin/jsr223/GremlinLangScriptEngine.java    | 159 +++++++++++++++++++++
 .../jsr223/GremlinLangScriptEngineFactory.java     |  53 +++++++
 .../gremlin/jsr223/GremlinScriptEngine.java        |   9 ++
 ...erpop.gremlin.jsr223.GremlinScriptEngineFactory |   1 +
 .../jsr223/GremlinLangScriptEngineTest.java        |  55 +++++++
 .../apache/tinkerpop/gremlin/driver/Client.java    |   1 +
 .../tinkerpop/gremlin/driver/RequestOptions.java   |  15 ++
 .../gremlin/server/util/ServerGremlinExecutor.java |  15 +-
 .../gremlin/server/GremlinDriverIntegrateTest.java |  61 ++------
 .../server/GremlinServerHttpIntegrateTest.java     |  22 ++-
 .../gremlin/server/GremlinServerIntegrateTest.java |   1 -
 .../gremlin/server/gremlin-server-integration.yaml |   1 +
 16 files changed, 342 insertions(+), 55 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index aad16a2..597e95f 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -48,6 +48,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 * Prevented exception with  `hasValue(null)` and allowed filtering as expected.
 * Refined `DotNetTranslator` to be more explicit with `null` arguments to ensure that the right overloads are called.
 * Created `GremlinParser` to construct `Traversal` objects from `gremlin-language`.
+* Added `GremlinLangScriptEngine` as a `GremlinScriptEngine` implementation that users the grammar and `JavaTranslator` to evalute Gremlin.
 * Added getter method for `bypassTraversal` in `AbstractLambdaTraversal`.
 * Added support for custom GraphBinary types in .NET.
 * Removed some unnecessary exception wrapping around `gremlin-driver` errors now producing a more immediate view of the actual error cause.
diff --git a/docker/gremlin-server/gremlin-server-integration-krb5.yaml b/docker/gremlin-server/gremlin-server-integration-krb5.yaml
index cb7e737..229bb96 100644
--- a/docker/gremlin-server/gremlin-server-integration-krb5.yaml
+++ b/docker/gremlin-server/gremlin-server-integration-krb5.yaml
@@ -26,6 +26,7 @@ graphs: {
   grateful: conf/tinkergraph-empty.properties,
   sink: conf/tinkergraph-empty.properties}
 scriptEngines: {
+  gremlin-lang : {},
   gremlin-groovy: {
     plugins: { org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {},
                org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin: {},
diff --git a/docker/gremlin-server/gremlin-server-integration-secure.yaml b/docker/gremlin-server/gremlin-server-integration-secure.yaml
index e6a999f..4d80841 100644
--- a/docker/gremlin-server/gremlin-server-integration-secure.yaml
+++ b/docker/gremlin-server/gremlin-server-integration-secure.yaml
@@ -26,6 +26,7 @@ graphs: {
   grateful: conf/tinkergraph-empty.properties,
   sink: conf/tinkergraph-empty.properties}
 scriptEngines: {
+  gremlin-lang : {},
   gremlin-groovy: {
     plugins: { org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {},
                org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin: {},
diff --git a/docker/gremlin-server/gremlin-server-integration.yaml b/docker/gremlin-server/gremlin-server-integration.yaml
index a0d5f4a..35c5159 100644
--- a/docker/gremlin-server/gremlin-server-integration.yaml
+++ b/docker/gremlin-server/gremlin-server-integration.yaml
@@ -27,6 +27,7 @@ graphs: {
   sink: conf/tinkergraph-empty.properties
 }
 scriptEngines: {
+  gremlin-lang : {},
   gremlin-groovy: {
     plugins: { org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {},
                org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin: {},
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngine.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngine.java
new file mode 100644
index 0000000..6c771bd
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngine.java
@@ -0,0 +1,159 @@
+/*
+ * 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.jsr223;
+
+import org.apache.tinkerpop.gremlin.language.grammar.GremlinAntlrToJava;
+import org.apache.tinkerpop.gremlin.language.grammar.GremlinQueryParser;
+import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+
+import javax.script.AbstractScriptEngine;
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+import javax.script.ScriptException;
+import javax.script.SimpleBindings;
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * A {@link GremlinScriptEngine} implementation that evaluates Gremlin scripts using {@code gremlin-language}. As it
+ * uses {@code gremlin-language} and thus the ANTLR parser, it is not capable of process arbitrary scripts as the
+ * {@code GremlinGroovyScriptEngine} can and is therefore a more secure Gremlin evaluator. It is obviously restricted
+ * to the capabilities of the ANTLR grammar so therefore syntax that includes things like lambdas are not supported.
+ * For bytecode evaluation it simply uses the {@link JavaTranslator}.
+ * <p/>
+ * As an internal note, technically, this is an incomplete implementation of the {@link GremlinScriptEngine} in the
+ * traditional sense as a drop-in replacement for something like the {@code GremlinGroovyScriptEngine}. As a result,
+ * this {@link GremlinScriptEngine} cannot pass the {@code GremlinScriptEngineSuite} tests in full. On the other hand,
+ * this limitation is precisely what makes this implementation better from a security perspective. Ultimately, this
+ * implementation represents the first step to changes in what it means to have a {@link GremlinScriptEngine}. In some
+ * sense, there is question why a {@link GremlinScriptEngine} approach is necessary at all except for easily plugging
+ * into the existing internals of Gremlin Server or more specifically the {@code GremlinExecutor}.
+ */
+public class GremlinLangScriptEngine extends AbstractScriptEngine implements GremlinScriptEngine {
+    private volatile GremlinScriptEngineFactory factory;
+
+    /**
+     * Creates a new instance using no {@link Customizer}.
+     */
+    public GremlinLangScriptEngine() {
+        this(new Customizer[0]);
+    }
+
+    public GremlinLangScriptEngine(final Customizer... customizers) {
+    }
+
+    @Override
+    public GremlinScriptEngineFactory getFactory() {
+        if (factory == null) {
+            synchronized (this) {
+                if (factory == null) {
+                    factory = new GremlinLangScriptEngineFactory();
+                }
+            }
+        }
+        return this.factory;
+    }
+
+    /**
+     * Bytecode is evaluated by the {@link JavaTranslator}.
+     */
+    @Override
+    public Traversal.Admin eval(final Bytecode bytecode, final Bindings bindings, final String traversalSource) throws ScriptException {
+        if (traversalSource.equals(HIDDEN_G))
+            throw new IllegalArgumentException("The traversalSource cannot have the name " + HIDDEN_G + " - it is reserved");
+
+        if (bindings.containsKey(HIDDEN_G))
+            throw new IllegalArgumentException("Bindings cannot include " + HIDDEN_G + " - it is reserved");
+
+        if (!bindings.containsKey(traversalSource))
+            throw new IllegalArgumentException("The bindings available to the ScriptEngine do not contain a traversalSource named: " + traversalSource);
+
+        final Object b = bindings.get(traversalSource);
+        if (!(b instanceof TraversalSource))
+            throw new IllegalArgumentException(traversalSource + " is of type " + b.getClass().getSimpleName() + " and is not an instance of TraversalSource");
+
+        return JavaTranslator.of((TraversalSource) b).translate(bytecode);
+    }
+
+    /**
+     * Gremlin scripts evaluated by the grammar must be bound to "g" and should evaluate to a "g" in the
+     * {@code ScriptContext} that is of type {@link TraversalSource}
+     */
+    @Override
+    public Object eval(final String script, final ScriptContext context) throws ScriptException {
+        final Object o = context.getAttribute("g");
+        if (!(o instanceof GraphTraversalSource))
+            throw new IllegalArgumentException("g is of type " + o.getClass().getSimpleName() + " and is not an instance of TraversalSource");
+
+        final GremlinAntlrToJava antlr = new GremlinAntlrToJava((GraphTraversalSource) o);
+
+        try {
+            return GremlinQueryParser.parse(script, antlr);
+        } catch (Exception ex) {
+            throw new ScriptException(ex);
+        }
+    }
+
+    @Override
+    public Object eval(final Reader reader, final ScriptContext context) throws ScriptException {
+        return eval(readFully(reader), context);
+    }
+
+    @Override
+    public Bindings createBindings() {
+        return new SimpleBindings();
+    }
+
+    /**
+     * Creates the {@code ScriptContext} using a {@link GremlinScriptContext} which avoids a significant amount of
+     * additional object creation on script evaluation.
+     */
+    @Override
+    protected ScriptContext getScriptContext(final Bindings nn) {
+        final GremlinScriptContext ctxt = new GremlinScriptContext(context.getReader(), context.getWriter(), context.getErrorWriter());
+        final Bindings gs = getBindings(ScriptContext.GLOBAL_SCOPE);
+
+        if (gs != null) ctxt.setBindings(gs, ScriptContext.GLOBAL_SCOPE);
+
+        if (nn != null) {
+            ctxt.setBindings(nn, ScriptContext.ENGINE_SCOPE);
+        } else {
+            throw new NullPointerException("Engine scope Bindings may not be null.");
+        }
+
+        return ctxt;
+    }
+
+    private String readFully(final Reader reader) throws ScriptException {
+        final char arr[] = new char[8192];
+        final StringBuilder buf = new StringBuilder();
+        int numChars;
+        try {
+            while ((numChars = reader.read(arr, 0, arr.length)) > 0) {
+                buf.append(arr, 0, numChars);
+            }
+        } catch (IOException exp) {
+            throw new ScriptException(exp);
+        }
+        return buf.toString();
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngineFactory.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngineFactory.java
new file mode 100644
index 0000000..cc68f08
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngineFactory.java
@@ -0,0 +1,53 @@
+/*
+ * 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.jsr223;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A {@link GremlinScriptEngineFactory} implementation that creates {@link GremlinLangScriptEngine} instances.
+ */
+public class GremlinLangScriptEngineFactory extends AbstractGremlinScriptEngineFactory {
+    private static final String ENGINE_NAME = "gremlin-lang";
+    private static final String LANGUAGE_NAME = "gremlin-lang";
+    private static final String PLAIN = "plain";
+    private static final List<String> EXTENSIONS = Collections.singletonList("gremlin");
+
+    public GremlinLangScriptEngineFactory() {
+        super(ENGINE_NAME, LANGUAGE_NAME, EXTENSIONS, Collections.singletonList(PLAIN));
+    }
+
+    @Override
+    public String getMethodCallSyntax(final String obj, final String m, final String... args) {
+        return null;
+    }
+
+    @Override
+    public String getOutputStatement(final String toDisplay) {
+        return null;
+    }
+
+    @Override
+    public GremlinScriptEngine getScriptEngine() {
+        final List<Customizer> customizers =  manager.getCustomizers(ENGINE_NAME);
+        return (customizers.isEmpty()) ? new GremlinLangScriptEngine() :
+                new GremlinLangScriptEngine(customizers.toArray(new Customizer[customizers.size()]));
+    }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinScriptEngine.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinScriptEngine.java
index 2df511c..270eb78 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinScriptEngine.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinScriptEngine.java
@@ -22,6 +22,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 
 import javax.script.Bindings;
+import javax.script.ScriptContext;
 import javax.script.ScriptEngine;
 import javax.script.ScriptException;
 
@@ -46,6 +47,14 @@ public interface GremlinScriptEngine extends ScriptEngine {
      */
     public default Traversal.Admin eval(final Bytecode bytecode, final String traversalSource) throws ScriptException {
         final Bindings bindings = this.createBindings();
+        final ScriptContext ctx = this.getContext();
+
+        final Bindings gbindings = ctx.getBindings(ScriptContext.GLOBAL_SCOPE);
+        if (gbindings != null) bindings.putAll(gbindings);
+
+        final Bindings ebindings = ctx.getBindings(ScriptContext.ENGINE_SCOPE);
+        if (ebindings != null) bindings.putAll(ebindings);
+
         bindings.putAll(bytecode.getBindings());
         return eval(bytecode, bindings, traversalSource);
     }
diff --git a/gremlin-core/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineFactory b/gremlin-core/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineFactory
new file mode 100644
index 0000000..2e42b9e
--- /dev/null
+++ b/gremlin-core/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineFactory
@@ -0,0 +1 @@
+org.apache.tinkerpop.gremlin.jsr223.GremlinLangScriptEngineFactory
\ No newline at end of file
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngineTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngineTest.java
new file mode 100644
index 0000000..aa4d714
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/GremlinLangScriptEngineTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.jsr223;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import org.junit.Test;
+
+import javax.script.Bindings;
+import javax.script.ScriptException;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.junit.Assert.assertEquals;
+
+public class GremlinLangScriptEngineTest {
+
+    private final static GremlinLangScriptEngine scriptEngine = new GremlinLangScriptEngine();
+    private final static GraphTraversalSource g = EmptyGraph.instance().traversal();
+
+    static {
+        scriptEngine.put("g", g);
+    }
+
+    @Test
+    public void shouldEvalGremlinScript() throws ScriptException {
+        final Object result = scriptEngine.eval("g.V()");
+        assertThat(result, instanceOf(Traversal.Admin.class));
+        assertEquals(g.V().asAdmin().getBytecode(), ((Traversal.Admin) result).getBytecode());
+    }
+
+    @Test
+    public void shouldEvalGremlinBytecode() throws ScriptException {
+        final Object result = scriptEngine.eval(g.V().asAdmin().getBytecode(), "g");
+        assertThat(result, instanceOf(Traversal.Admin.class));
+        assertEquals(g.V().asAdmin().getBytecode(), ((Traversal.Admin) result).getBytecode());
+    }
+}
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
index ef23b7a..b060c5d 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
@@ -368,6 +368,7 @@ public abstract class Client {
         options.getAliases().ifPresent(aliases -> request.addArg(Tokens.ARGS_ALIASES, aliases));
         options.getOverrideRequestId().ifPresent(request::overrideRequestId);
         options.getUserAgent().ifPresent(userAgent -> request.addArg(Tokens.ARGS_USER_AGENT, userAgent));
+        options.getLanguage().ifPresent(lang -> request.addArg(Tokens.ARGS_LANGUAGE, lang));
 
         return submitAsync(request.create());
     }
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/RequestOptions.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/RequestOptions.java
index dcc7cdf..ac923f9 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/RequestOptions.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/RequestOptions.java
@@ -40,6 +40,7 @@ public final class RequestOptions {
     private final Long timeout;
     private final UUID overrideRequestId;
     private final String userAgent;
+    private final String language;
 
     private RequestOptions(final Builder builder) {
         this.aliases = builder.aliases;
@@ -48,6 +49,7 @@ public final class RequestOptions {
         this.timeout = builder.timeout;
         this.overrideRequestId = builder.overrideRequestId;
         this.userAgent = builder.userAgent;
+        this.language = builder.language;
     }
 
     public Optional<UUID> getOverrideRequestId() {
@@ -74,6 +76,10 @@ public final class RequestOptions {
         return Optional.ofNullable(userAgent);
     }
 
+    public Optional<String> getLanguage() {
+        return Optional.ofNullable(language);
+    }
+
     public static Builder build() {
         return new Builder();
     }
@@ -85,6 +91,7 @@ public final class RequestOptions {
         private Long timeout = null;
         private UUID overrideRequestId = null;
         private String userAgent = null;
+        private String language = null;
         private boolean maintainStateAfterException = false;
 
         /**
@@ -144,6 +151,14 @@ public final class RequestOptions {
             return this;
         }
 
+        /**
+         * Sets the language identifier to be sent on the request.
+         */
+        public Builder language(final String language) {
+            this.language = language;
+            return this;
+        }
+
         public RequestOptions create() {
             return new RequestOptions(this);
         }
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/ServerGremlinExecutor.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/ServerGremlinExecutor.java
index 23c9b5b..ae777f2 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/ServerGremlinExecutor.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/ServerGremlinExecutor.java
@@ -19,6 +19,8 @@
 package org.apache.tinkerpop.gremlin.server.util;
 
 import org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor;
+import org.apache.tinkerpop.gremlin.jsr223.GremlinLangScriptEngine;
+import org.apache.tinkerpop.gremlin.jsr223.GremlinLangScriptEngineFactory;
 import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine;
 import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
 import org.apache.tinkerpop.gremlin.server.Channelizer;
@@ -142,10 +144,15 @@ public class ServerGremlinExecutor {
         // runs the init scripts when the GremlinScriptEngine is created.
         settings.scriptEngines.keySet().forEach(engineName -> {
             try {
-                // use no timeout on the engine initialization - perhaps this can be a configuration later
-                final GremlinExecutor.LifeCycle lifeCycle = GremlinExecutor.LifeCycle.build().
-                        evaluationTimeoutOverride(0L).create();
-                gremlinExecutor.eval("1+1", engineName, new SimpleBindings(Collections.emptyMap()), lifeCycle).join();
+                // gremlin-lang does not need to be initialized. not so nice, but gremlin-lang is the only exception
+                // and ultimately, gremlin-lang will likely end up the only choice in gremlin-server.
+                if (!engineName.equals("gremlin-lang")) {
+                    // use no timeout on the engine initialization - perhaps this can be a configuration later
+                    final GremlinExecutor.LifeCycle lifeCycle = GremlinExecutor.LifeCycle.build().
+                            evaluationTimeoutOverride(0L).create();
+                    gremlinExecutor.eval("1+1", engineName, new SimpleBindings(Collections.emptyMap()), lifeCycle).join();
+                }
+
                 registerMetrics(engineName);
                 logger.info("Initialized {} GremlinScriptEngine and registered metrics", engineName);
             } catch (Exception ex) {
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
index ba0dec6..5ab086b 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
@@ -398,49 +398,6 @@ public class GremlinDriverIntegrateTest extends AbstractGremlinServerIntegration
     }
 
     @Test
-    @Ignore("This test had some bad semantics that allowed it to pass even though it was technically failing")
-    public void shouldEventuallySucceedOnSameServerWithDefault() throws Exception {
-        stopServer();
-
-        final Cluster cluster = TestClientFactory.open();
-        final Client client = cluster.connect();
-
-        try {
-            try {
-                client.submit("1+1").all().join().get(0).getInt();
-                fail("Should not have gone through because the server is not running");
-            } catch (Exception i) {
-                final Throwable root = ExceptionUtils.getRootCause(i);
-                assertThat(root, instanceOf(NoHostAvailableException.class));
-            }
-
-            startServer();
-
-            boolean succeedAtLeastOnce = false;
-
-            // default reconnect time is 1 second so wait some extra time to be sure it has time to try to bring it
-            // back to life. usually this passes on the first attempt, but docker is sometimes slow and we get failures
-            // waiting for Gremlin Server to pop back up
-            for (int ix = 3; ix < 13; ix++) {
-                TimeUnit.SECONDS.sleep(ix);
-                try {
-                    final int result = client.submit("1+1").all().join().get(0).getInt();
-                    assertEquals(2, result);
-                    succeedAtLeastOnce = true;
-                    break;
-                } catch (Exception ignored) {
-                    logger.warn("Attempt {} failed on shouldEventuallySucceedOnSameServerWithDefault", ix);
-                }
-            }
-
-            assertThat(succeedAtLeastOnce, is(true));
-
-        } finally {
-            cluster.close();
-        }
-    }
-
-    @Test
     public void shouldEventuallySucceedOnSameServerWithScript() throws Exception {
         stopServer();
 
@@ -1192,6 +1149,19 @@ public class GremlinDriverIntegrateTest extends AbstractGremlinServerIntegration
     }
 
     @Test
+    public void shouldEvalInGremlinLang() {
+        final Cluster cluster = TestClientFactory.open();
+        final Client client = cluster.connect();
+
+        try {
+            final RequestOptions ro = RequestOptions.build().language("gremlin-lang").create();
+            assertEquals(111, client.submit("g.inject(111)", ro).one().getInt());
+        } finally {
+            cluster.close();
+        }
+    }
+
+    @Test
     public void shouldCloseSession() throws Exception {
         final Cluster cluster = TestClientFactory.build().create();
         final Client client = cluster.connect(name.getMethodName());
@@ -1863,10 +1833,9 @@ public class GremlinDriverIntegrateTest extends AbstractGremlinServerIntegration
 
     /**
      * Client created on an initially dead host should fail initially, and recover after the dead host has restarted
-     * @param testClusterClient - boolean flag set to test clustered client if true and sessioned client if false
-     * @throws Exception
+     * @param testClusterClient - boolean flag set to test clustered client if true and sessioned client if false.
      */
-    public void testShouldFailOnInitiallyDeadHost(final boolean testClusterClient) throws Exception {
+    private void testShouldFailOnInitiallyDeadHost(final boolean testClusterClient) throws Exception {
         logger.info("Stopping server.");
         this.stopServer();
 
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerHttpIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerHttpIntegrateTest.java
index 584e5c3..23b2149 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerHttpIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerHttpIntegrateTest.java
@@ -27,7 +27,6 @@ import org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV3d0;
 import org.apache.tinkerpop.gremlin.driver.ser.SerTokens;
 import org.apache.tinkerpop.gremlin.server.auth.SimpleAuthenticator;
 import org.apache.tinkerpop.gremlin.server.channel.HttpChannelizer;
-import org.apache.tinkerpop.gremlin.server.handler.HttpBasicAuthenticationHandler;
 import org.apache.http.Consts;
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpGet;
@@ -49,6 +48,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
 import static org.hamcrest.core.StringContains.containsString;
 import static org.hamcrest.core.StringStartsWith.startsWith;
 import static org.junit.Assert.assertEquals;
@@ -331,6 +331,20 @@ public class GremlinServerHttpIntegrateTest extends AbstractGremlinServerIntegra
     }
 
     @Test
+    public void should200OnGETOverGremlinLangWithGremlinQueryStringArgumentWithIteratorResult() throws Exception {
+        final CloseableHttpClient httpclient = HttpClients.createDefault();
+        final HttpGet httpget = new HttpGet(TestClientFactory.createURLString("?gremlin=g.inject(111)&language=gremlin-lang"));
+
+        try (final CloseableHttpResponse response = httpclient.execute(httpget)) {
+            assertEquals(200, response.getStatusLine().getStatusCode());
+            assertEquals("application/json", response.getEntity().getContentType().getValue());
+            final String json = EntityUtils.toString(response.getEntity());
+            final JsonNode node = mapper.readTree(json);
+            assertEquals(111, node.get("result").get("data").get(GraphSONTokens.VALUEPROP).get(0).get(GraphSONTokens.VALUEPROP).intValue());
+        }
+    }
+
+    @Test
     public void should200OnGETWithGremlinQueryStringArgumentWithIteratorResult() throws Exception {
         final CloseableHttpClient httpclient = HttpClients.createDefault();
         final HttpGet httpget = new HttpGet(TestClientFactory.createURLString("?gremlin=gclassic.V()"));
@@ -635,7 +649,7 @@ public class GremlinServerHttpIntegrateTest extends AbstractGremlinServerIntegra
             assertEquals("application/json", response.getEntity().getContentType().getValue());
             final String json = EntityUtils.toString(response.getEntity());
             final JsonNode node = mapper.readTree(json);
-            assertEquals(true, node.get("result").get("data").get(GraphSONTokens.VALUEPROP).get(0).booleanValue());
+            assertThat(node.get("result").get("data").get(GraphSONTokens.VALUEPROP).get(0).booleanValue(), is(true));
         }
     }
 
@@ -651,7 +665,7 @@ public class GremlinServerHttpIntegrateTest extends AbstractGremlinServerIntegra
             assertEquals("application/json", response.getEntity().getContentType().getValue());
             final String json = EntityUtils.toString(response.getEntity());
             final JsonNode node = mapper.readTree(json);
-            assertEquals(true, node.get("result").get("data").get(GraphSONTokens.VALUEPROP).get(0).isNull());
+            assertThat(node.get("result").get("data").get(GraphSONTokens.VALUEPROP).get(0).isNull(), is(true));
         }
     }
 
@@ -667,7 +681,7 @@ public class GremlinServerHttpIntegrateTest extends AbstractGremlinServerIntegra
             assertEquals("application/json", response.getEntity().getContentType().getValue());
             final String json = EntityUtils.toString(response.getEntity());
             final JsonNode node = mapper.readTree(json);
-            assertEquals(true, node.get("result").get("data").get(GraphSONTokens.VALUEPROP).isArray());
+            assertThat(node.get("result").get("data").get(GraphSONTokens.VALUEPROP).isArray(), is(true));
             assertEquals(1, node.get("result").get("data").get(GraphSONTokens.VALUEPROP).get(0).get(GraphSONTokens.VALUEPROP).intValue());
             assertEquals(2, node.get("result").get("data").get(GraphSONTokens.VALUEPROP).get(1).get(GraphSONTokens.VALUEPROP).intValue());
             assertEquals(3, node.get("result").get("data").get(GraphSONTokens.VALUEPROP).get(2).get(GraphSONTokens.VALUEPROP).intValue());
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
index fcda9b9..71c5401 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
@@ -62,7 +62,6 @@ import org.junit.Test;
 import org.slf4j.LoggerFactory;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
diff --git a/gremlin-server/src/test/resources/org/apache/tinkerpop/gremlin/server/gremlin-server-integration.yaml b/gremlin-server/src/test/resources/org/apache/tinkerpop/gremlin/server/gremlin-server-integration.yaml
index 35a19d7..69f9feb 100644
--- a/gremlin-server/src/test/resources/org/apache/tinkerpop/gremlin/server/gremlin-server-integration.yaml
+++ b/gremlin-server/src/test/resources/org/apache/tinkerpop/gremlin/server/gremlin-server-integration.yaml
@@ -40,6 +40,7 @@ graphs: {
   grateful: conf/tinkergraph-empty.properties,
   sink: conf/tinkergraph-empty.properties}
 scriptEngines: {
+  gremlin-lang : {},
   gremlin-groovy: {
     plugins: { org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {},
                org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin: {},