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 2017/02/15 00:34:54 UTC

[4/6] tinkerpop git commit: TINKERPOP-1631 Improved consistency of binding application to GremlinScriptEngine

TINKERPOP-1631 Improved consistency of binding application to GremlinScriptEngine

Added a base factory class. Added start of a test framework for GremlinScriptEngineFactory. Pushed all bindings to global - engine is overriden at time of eval() by "parameters".


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

Branch: refs/heads/TINKERPOP-1631
Commit: 4818565467e6b847007dbb4032484a3efb6d091e
Parents: 66ab01b
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Mon Feb 13 08:51:51 2017 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Tue Feb 14 15:05:51 2017 -0500

----------------------------------------------------------------------
 .../AbstractGremlinScriptEngineFactory.java     | 118 +++++++++++++++++++
 .../gremlin/jsr223/BindingsGremlinPlugin.java   |  49 ++------
 .../jsr223/DefaultBindingsCustomizer.java       |  17 +--
 .../DefaultGremlinScriptEngineManager.java      |  43 ++++---
 .../gremlin/jsr223/LazyBindingsCustomizer.java  |  18 +--
 .../GremlinGroovyScriptEngineFactory.java       |  75 ++----------
 .../jsr223/BindingsScriptEngineTest.java        |  65 ++++++----
 .../gremlin/jsr223/MockGremlinScriptEngine.java |  62 ++++++++++
 .../jsr223/MockGremlinScriptEngineFactory.java  |  51 ++++++++
 ...op.gremlin.jsr223.GremlinScriptEngineFactory |   1 +
 .../gremlin/jsr223/bindings-init.groovy         |  22 ++++
 .../hadoop/jsr223/HadoopGremlinPlugin.java      |  11 +-
 .../hadoop/jsr223/HadoopGremlinPluginTest.java  |  51 ++++++++
 13 files changed, 415 insertions(+), 168 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/48185654/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/AbstractGremlinScriptEngineFactory.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/AbstractGremlinScriptEngineFactory.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/AbstractGremlinScriptEngineFactory.java
new file mode 100644
index 0000000..e07e884
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/AbstractGremlinScriptEngineFactory.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.jsr223;
+
+import org.apache.tinkerpop.gremlin.util.Gremlin;
+
+import javax.script.ScriptEngine;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A simple base implementation of the {@link GremlinScriptEngineFactory}.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public abstract class AbstractGremlinScriptEngineFactory implements GremlinScriptEngineFactory {
+
+    private final String engineName;
+    private final String languageName;
+    private final List<String> extensions;
+    private final List<String> mimeTypes;
+
+    protected GremlinScriptEngineManager manager;
+
+    public AbstractGremlinScriptEngineFactory(final String engineName, final String languageName,
+                                              final List<String> extensions, final List<String> mimeTypes) {
+        this.engineName = engineName;
+        this.languageName = languageName;
+        this.extensions = Collections.unmodifiableList(extensions);
+        this.mimeTypes = Collections.unmodifiableList(mimeTypes);
+    }
+
+    @Override
+    public void setCustomizerManager(final GremlinScriptEngineManager manager) {
+        this.manager = manager;
+    }
+
+    @Override
+    public String getEngineName() {
+        return engineName;
+    }
+
+    @Override
+    public String getEngineVersion() {
+        return Gremlin.version();
+    }
+
+    @Override
+    public List<String> getExtensions() {
+        return extensions;
+    }
+
+    @Override
+    public String getLanguageName() {
+        return languageName;
+    }
+
+    @Override
+    public String getLanguageVersion() {
+        return Gremlin.version();
+    }
+
+    @Override
+    public List<String> getMimeTypes() {
+        return mimeTypes;
+    }
+
+    @Override
+    public List<String> getNames() {
+        return Collections.singletonList(languageName);
+    }
+
+    @Override
+    public Object getParameter(final String key) {
+        if (key.equals(ScriptEngine.ENGINE)) {
+            return this.getEngineName();
+        } else if (key.equals(ScriptEngine.ENGINE_VERSION)) {
+            return this.getEngineVersion();
+        } else if (key.equals(ScriptEngine.NAME)) {
+            return engineName;
+        } else if (key.equals(ScriptEngine.LANGUAGE)) {
+            return this.getLanguageName();
+        } else if (key.equals(ScriptEngine.LANGUAGE_VERSION)) {
+            return this.getLanguageVersion();
+        } else
+            return null;
+    }
+
+    /**
+     * Statements are concatenated together by a line feed.
+     */
+    @Override
+    public String getProgram(final String... statements) {
+        String program = "";
+
+        for (String statement : statements) {
+            program = program + statement + System.lineSeparator();
+        }
+
+        return program;
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/48185654/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/BindingsGremlinPlugin.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/BindingsGremlinPlugin.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/BindingsGremlinPlugin.java
index 59304df..003f79d 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/BindingsGremlinPlugin.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/BindingsGremlinPlugin.java
@@ -28,21 +28,13 @@ import java.util.Set;
 import java.util.function.Supplier;
 
 /**
- * A module that allows {@code Bindings} to be applied to a {@link GremlinScriptEngine}. The bindings are controled by
- * their {@code scope} which are determined by the {@code ScriptContext} values where "100" is {@code ENGINE_SCOPE}
- * (bindings apply to the current {@link GremlinScriptEngine}) and "200" is {@code GLOBAL_SCOPE} (bindings apply to the
- * engines created by the current {@link GremlinScriptEngineManager}.
+ * A plugin that allows {@code Bindings} to be applied to a {@link GremlinScriptEngine} at the time of creation.
+ * {@code Bindings} defined with this plugin will always be assigned as {@code ScriptContext.GLOBAL_SCOPE} and as such
+ * will be visible to all {@link GremlinScriptEngine} instances.
  * <p/>
- * Note that bindings are applied in the following order:
- * <ol>
- *   <li>The order in which the {@link GremlinScriptEngine} is requested from the {@link GremlinScriptEngineManager}</li>
- *   <li>The order in which the {@code BindingsGremlinPlugin} instances are added to the {@link GremlinScriptEngineManager}</li>
- * </ol>
- * <p/>
- * Moreover, they will override one another within a scope and among scopes. For instance, {@code ENGINE_SCOPE} bindings
- * will override {@code GLOBAL_SCOPE}. Those bindings that are {@code GLOBAL_SCOPE} and applied to a single
- * {@link GremlinScriptEngine} via an {@link Builder#appliesTo} configuration will still appear present to all other
- * {@link GremlinScriptEngine} created by the {@link GremlinScriptEngineManager} that the plugin was bound to.
+ * Note that bindings are applied in the order in which the {@code BindingsGremlinPlugin} instances are added to the
+ * {@link GremlinScriptEngineManager}. Therefore if there are two plugins added and both include a variable called "x"
+ * then the value of "x" will be the equal to the value provided by the second plugin that overrides the first.
  * <p/>
  * This {@link GremlinPlugin} is not enabled for the {@code ServiceLoader}. It is designed to be instantiated manually.
  *
@@ -52,17 +44,16 @@ public class BindingsGremlinPlugin extends AbstractGremlinPlugin {
     private static final String NAME = "tinkerpop.bindings";
 
     private BindingsGremlinPlugin(final Builder builder) {
-        super(NAME, builder.appliesTo, new DefaultBindingsCustomizer(builder.bindings, builder.scope));
-    }
-
-    public BindingsGremlinPlugin(final Bindings bindings, final int scope) {
-        super(NAME, new DefaultBindingsCustomizer(bindings, scope));
+        super(NAME, new DefaultBindingsCustomizer(builder.bindings));
     }
 
-    public BindingsGremlinPlugin(final Supplier<Bindings> bindingsSupplier, final int scope) {
-        super(NAME, new LazyBindingsCustomizer(bindingsSupplier, scope));
+    BindingsGremlinPlugin(final Supplier<Bindings> bindingsSupplier) {
+        super(NAME, new LazyBindingsCustomizer(bindingsSupplier));
     }
 
+    /**
+     * Builds a set of static bindings.
+     */
     public static BindingsGremlinPlugin.Builder build() {
         return new Builder();
     }
@@ -70,30 +61,14 @@ public class BindingsGremlinPlugin extends AbstractGremlinPlugin {
     public static final class Builder {
 
         private Bindings bindings = new SimpleBindings();
-        private int scope = ScriptContext.ENGINE_SCOPE;
-        private final Set<String> appliesTo = new HashSet<>();
 
         private Builder() {}
 
-        /**
-         * The name of the {@link GremlinScriptEngine} that this module will apply to. Setting no values here will
-         * make the module available to all the engines.
-         */
-        public BindingsGremlinPlugin.Builder appliesTo(final Collection<String> scriptEngineName) {
-            this.appliesTo.addAll(scriptEngineName);
-            return this;
-        }
-
         public Builder bindings(final Map<String, Object> bindings) {
             this.bindings = new SimpleBindings(bindings);
             return this;
         }
 
-        public Builder scope(final int scope) {
-            this.scope = scope;
-            return this;
-        }
-
         public BindingsGremlinPlugin create() {
             return new BindingsGremlinPlugin(this);
         }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/48185654/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultBindingsCustomizer.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultBindingsCustomizer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultBindingsCustomizer.java
index cee5182..59d8230 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultBindingsCustomizer.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultBindingsCustomizer.java
@@ -22,10 +22,7 @@ import javax.script.Bindings;
 import javax.script.ScriptContext;
 
 /**
- * Default implementation of the {@link BindingsCustomizer}. If this customizer is applied directly to a
- * {@link GremlinScriptEngine} it will not apply {@code GLOBAL_SCOPE} bindings. Those can only be applied if the
- * customizer is applied via the {@link GremlinScriptEngineManager} (which would do so through the
- * {@link BindingsGremlinPlugin}.
+ * Default implementation of the {@link BindingsCustomizer} which adds bindings to the {@code ScriptContext.GLOBAL_SCOPE}.
  *
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
@@ -35,13 +32,19 @@ public class DefaultBindingsCustomizer implements BindingsCustomizer {
     private final int scope;
 
     /**
-     * Creates a new object with {@code ScriptContext.ENGINE_SCOPE}.
+     * Creates a new object with {@code ScriptContext.GLOBAL_SCOPE}.
      */
     public DefaultBindingsCustomizer(final Bindings bindings) {
-        this(bindings, ScriptContext.ENGINE_SCOPE);
+        this(bindings, ScriptContext.GLOBAL_SCOPE);
     }
 
-    public DefaultBindingsCustomizer(final Bindings bindings, final int scope) {
+    /**
+     * Creates a new object with a specified scope. There really can't be anything other than a {@code GLOBAL_SCOPE}
+     * specification so this constructor isn't public at the moment. Assigning to {@code ENGINE_SCOPE} is useless
+     * because it is the nature of the {@code ScriptEngine} to override that scope with {@code Bindings} supplied at
+     * the time of execution.
+     */
+    DefaultBindingsCustomizer(final Bindings bindings, final int scope) {
         this.bindings = bindings;
         this.scope = scope;
     }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/48185654/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultGremlinScriptEngineManager.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultGremlinScriptEngineManager.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultGremlinScriptEngineManager.java
index 01b4957..d63147d 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultGremlinScriptEngineManager.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultGremlinScriptEngineManager.java
@@ -18,6 +18,9 @@
  */
 package org.apache.tinkerpop.gremlin.jsr223;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import javax.script.Bindings;
 import javax.script.ScriptContext;
 import java.security.AccessController;
@@ -63,7 +66,7 @@ import java.util.stream.Stream;
  */
 public class DefaultGremlinScriptEngineManager implements GremlinScriptEngineManager {
 
-    private static final boolean DEBUG = false;
+    private static final Logger logger = LoggerFactory.getLogger(DefaultGremlinScriptEngineManager.class);
 
     /**
      * Set of script engine factories discovered.
@@ -222,7 +225,7 @@ public class DefaultGremlinScriptEngineManager implements GremlinScriptEngineMan
             try {
                 return createGremlinScriptEngine(spi);
             } catch (Exception exp) {
-                if (DEBUG) exp.printStackTrace();
+                logger.error(String.format("Could not create GremlinScriptEngine for %s", shortName), exp);
             }
         }
 
@@ -231,7 +234,7 @@ public class DefaultGremlinScriptEngineManager implements GremlinScriptEngineMan
             try {
                 names = spi.getNames();
             } catch (Exception exp) {
-                if (DEBUG) exp.printStackTrace();
+                logger.error("Could not get GremlinScriptEngine names", exp);
             }
 
             if (names != null) {
@@ -240,7 +243,7 @@ public class DefaultGremlinScriptEngineManager implements GremlinScriptEngineMan
                         try {
                             return createGremlinScriptEngine(spi);
                         } catch (Exception exp) {
-                            if (DEBUG) exp.printStackTrace();
+                            logger.error(String.format("Could not create GremlinScriptEngine for %s", shortName), exp);
                         }
                     }
                 }
@@ -269,7 +272,7 @@ public class DefaultGremlinScriptEngineManager implements GremlinScriptEngineMan
             try {
                 return createGremlinScriptEngine(spi);
             } catch (Exception exp) {
-                if (DEBUG) exp.printStackTrace();
+                logger.error(String.format("Could not create GremlinScriptEngine for %s", extension), exp);
             }
         }
 
@@ -278,7 +281,7 @@ public class DefaultGremlinScriptEngineManager implements GremlinScriptEngineMan
             try {
                 exts = spi.getExtensions();
             } catch (Exception exp) {
-                if (DEBUG) exp.printStackTrace();
+                logger.error("Could not get GremlinScriptEngine extensions", exp);
             }
             if (exts == null) continue;
             for (String ext : exts) {
@@ -286,7 +289,7 @@ public class DefaultGremlinScriptEngineManager implements GremlinScriptEngineMan
                     try {
                         return createGremlinScriptEngine(spi);
                     } catch (Exception exp) {
-                        if (DEBUG) exp.printStackTrace();
+                        logger.error(String.format("Could not create GremlinScriptEngine for %s", extension), exp);
                     }
                 }
             }
@@ -314,7 +317,7 @@ public class DefaultGremlinScriptEngineManager implements GremlinScriptEngineMan
             try {
                 return createGremlinScriptEngine(spi);
             } catch (Exception exp) {
-                if (DEBUG) exp.printStackTrace();
+                logger.error(String.format("Could not create GremlinScriptEngine for %s", mimeType), exp);
             }
         }
 
@@ -323,7 +326,7 @@ public class DefaultGremlinScriptEngineManager implements GremlinScriptEngineMan
             try {
                 types = spi.getMimeTypes();
             } catch (Exception exp) {
-                if (DEBUG) exp.printStackTrace();
+                logger.error("Could not get GremlinScriptEngine mimetypes", exp);
             }
             if (types == null) continue;
             for (String type : types) {
@@ -331,7 +334,7 @@ public class DefaultGremlinScriptEngineManager implements GremlinScriptEngineMan
                     try {
                         return createGremlinScriptEngine(spi);
                     } catch (Exception exp) {
-                        if (DEBUG) exp.printStackTrace();
+                        logger.error(String.format("Could not create GremlinScriptEngine for %s", mimeType), exp);
                     }
                 }
             }
@@ -409,9 +412,7 @@ public class DefaultGremlinScriptEngineManager implements GremlinScriptEngineMan
                     (PrivilegedAction<ServiceLoader<GremlinScriptEngineFactory>>) () -> getServiceLoader(loader));
             itty = sl.iterator();
         } catch (ServiceConfigurationError err) {
-            System.err.println("Can't find GremlinScriptEngineFactory providers: " +
-                    err.getMessage());
-            if (DEBUG) err.printStackTrace();
+            logger.error("Can't find GremlinScriptEngineFactory providers: " + err.getMessage(), err);
 
             // do not throw any exception here. user may want to manager their own factories using this manager
             // by explicit registration (by registerXXX) methods.
@@ -425,15 +426,11 @@ public class DefaultGremlinScriptEngineManager implements GremlinScriptEngineMan
                     factory.setCustomizerManager(this);
                     engineSpis.add(factory);
                 } catch (ServiceConfigurationError err) {
-                    System.err.println("GremlinScriptEngineManager providers.next(): "
-                            + err.getMessage());
-                    if (DEBUG) err.printStackTrace();
+                    logger.error("GremlinScriptEngineManager providers.next(): " + err.getMessage(), err);
                 }
             }
         } catch (ServiceConfigurationError err) {
-            System.err.println("GremlinScriptEngineManager providers.hasNext(): "
-                    + err.getMessage());
-            if (DEBUG) err.printStackTrace();
+            logger.error("GremlinScriptEngineManager providers.hasNext(): " + err.getMessage(), err);
             // do not throw any exception here. user may want to manage their own factories using this manager
             // by explicit registration (by registerXXX) methods.
         }
@@ -450,13 +447,14 @@ public class DefaultGremlinScriptEngineManager implements GremlinScriptEngineMan
                 .forEach(bc -> globalScope.putAll(bc.getBindings()));
         engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE);
 
-        // merge in bindings that are marked with engine scope. these get applied to only those GremlinScriptEngine
-        // instances that are of this gremlin language
+        // merge in bindings that are marked with engine scope. there typically won't be any of these but it's just
+        // here for completeness. bindings will typically apply with global scope only as engine scope will generally
+        // be overridden at the time of eval() with the bindings that are supplied to it
         getCustomizers(spi.getEngineName()).stream()
                 .filter(p -> p instanceof BindingsCustomizer)
                 .map(p -> ((BindingsCustomizer) p))
                 .filter(bc -> bc.getScope() == ScriptContext.ENGINE_SCOPE)
-                .forEach(bc -> engine.setBindings(bc.getBindings(), ScriptContext.ENGINE_SCOPE));
+                .forEach(bc -> engine.getBindings(ScriptContext.ENGINE_SCOPE).putAll(bc.getBindings()));
 
         final List<ScriptCustomizer> scriptCustomizers = getCustomizers(spi.getEngineName()).stream()
                 .filter(p -> p instanceof ScriptCustomizer)
@@ -475,6 +473,7 @@ public class DefaultGremlinScriptEngineManager implements GremlinScriptEngineMan
                 throw new IllegalStateException(ex);
             }
         });
+
         return engine;
     }
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/48185654/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/LazyBindingsCustomizer.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/LazyBindingsCustomizer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/LazyBindingsCustomizer.java
index 8feb361..bc4c794 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/LazyBindingsCustomizer.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/LazyBindingsCustomizer.java
@@ -23,10 +23,8 @@ import javax.script.ScriptContext;
 import java.util.function.Supplier;
 
 /**
- * A customizer implementation that provides bindings to a {@link GremlinScriptEngine}. If this customizer is applied
- * directly to a {@link GremlinScriptEngine} it will not apply {@code GLOBAL_SCOPE} bindings. Those can only be applied
- * if the customizer is applied via the {@link GremlinScriptEngineManager} (which would do so through the
- * {@link BindingsGremlinPlugin}.
+ * A customizer implementation that provides bindings to a {@link GremlinScriptEngine} in the
+ * {@code ScriptContext.GLOBAL_SCOPE}.
  *
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
@@ -36,13 +34,19 @@ public class LazyBindingsCustomizer implements BindingsCustomizer {
     private final int scope;
 
     /**
-     * Creates a new object with {@code ScriptContext.ENGINE_SCOPE}.
+     * Creates a new object with {@code ScriptContext.GLOBAL_SCOPE}.
      */
     public LazyBindingsCustomizer(final Supplier<Bindings> bindingsSupplier) {
-        this(bindingsSupplier, ScriptContext.ENGINE_SCOPE);
+        this(bindingsSupplier, ScriptContext.GLOBAL_SCOPE);
     }
 
-    public LazyBindingsCustomizer(final Supplier<Bindings> bindingsSupplier, final int scope) {
+    /**
+     * Creates a new object with a specified scope. There really can't be anything other than a {@code GLOBAL_SCOPE}
+     * specification so this constructor isn't public at the moment. Assigning to {@code ENGINE_SCOPE} is useless
+     * because it is the nature of the {@code ScriptEngine} to override that scope with {@code Bindings} supplied at
+     * the time of execution.
+     */
+    LazyBindingsCustomizer(final Supplier<Bindings> bindingsSupplier, final int scope) {
         this.bindingsSupplier = bindingsSupplier;
         this.scope = scope;
     }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/48185654/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineFactory.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineFactory.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineFactory.java
index 8e5defb..cd7e2fd 100644
--- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineFactory.java
+++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineFactory.java
@@ -18,6 +18,7 @@
  */
 package org.apache.tinkerpop.gremlin.groovy.jsr223;
 
+import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinScriptEngineFactory;
 import org.apache.tinkerpop.gremlin.jsr223.Customizer;
 import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine;
 import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineFactory;
@@ -29,45 +30,20 @@ import java.util.Collections;
 import java.util.List;
 
 /**
+ * A {@link GremlinScriptEngineFactory} implementation that creates {@link GremlinGroovyScriptEngine} instances.
+ *
  * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
  */
-public class GremlinGroovyScriptEngineFactory implements GremlinScriptEngineFactory {
+public class GremlinGroovyScriptEngineFactory extends AbstractGremlinScriptEngineFactory {
 
     private static final String ENGINE_NAME = "gremlin-groovy";
     private static final String LANGUAGE_NAME = "gremlin-groovy";
     private static final String PLAIN = "plain";
     private static final List<String> EXTENSIONS = Collections.singletonList("groovy");
 
-    private GremlinScriptEngineManager manager;
-
-    @Override
-    public void setCustomizerManager(final GremlinScriptEngineManager manager) {
-        this.manager = manager;
-    }
-
-    @Override
-    public String getEngineName() {
-        return ENGINE_NAME;
-    }
-
-    @Override
-    public String getEngineVersion() {
-        return Gremlin.version();
-    }
-
-    @Override
-    public List<String> getExtensions() {
-        return EXTENSIONS;
-    }
-
-    @Override
-    public String getLanguageName() {
-        return LANGUAGE_NAME;
-    }
-
-    @Override
-    public String getLanguageVersion() {
-        return Gremlin.version();
+    public GremlinGroovyScriptEngineFactory() {
+        super(ENGINE_NAME, LANGUAGE_NAME, EXTENSIONS, Collections.singletonList(PLAIN));
     }
 
     @Override
@@ -76,48 +52,11 @@ public class GremlinGroovyScriptEngineFactory implements GremlinScriptEngineFact
     }
 
     @Override
-    public List<String> getMimeTypes() {
-        return Collections.singletonList(PLAIN);
-    }
-
-    @Override
-    public List<String> getNames() {
-        return Collections.singletonList(LANGUAGE_NAME);
-    }
-
-    @Override
     public String getOutputStatement(final String toDisplay) {
         return "println " + toDisplay;
     }
 
     @Override
-    public Object getParameter(final String key) {
-        if (key.equals(ScriptEngine.ENGINE)) {
-            return this.getEngineName();
-        } else if (key.equals(ScriptEngine.ENGINE_VERSION)) {
-            return this.getEngineVersion();
-        } else if (key.equals(ScriptEngine.NAME)) {
-            return ENGINE_NAME;
-        } else if (key.equals(ScriptEngine.LANGUAGE)) {
-            return this.getLanguageName();
-        } else if (key.equals(ScriptEngine.LANGUAGE_VERSION)) {
-            return this.getLanguageVersion();
-        } else
-            return null;
-    }
-
-    @Override
-    public String getProgram(final String... statements) {
-        String program = "";
-
-        for (String statement : statements) {
-            program = program + statement + "\n";
-        }
-
-        return program;
-    }
-
-    @Override
     public GremlinScriptEngine getScriptEngine() {
         final List<Customizer> customizers =  manager.getCustomizers(ENGINE_NAME);
         return (customizers.isEmpty()) ? new GremlinGroovyScriptEngine() :

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/48185654/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/BindingsScriptEngineTest.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/BindingsScriptEngineTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/BindingsScriptEngineTest.java
index e02b834..8d90fec 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/BindingsScriptEngineTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/BindingsScriptEngineTest.java
@@ -18,15 +18,20 @@
  */
 package org.apache.tinkerpop.gremlin.jsr223;
 
+import org.apache.tinkerpop.gremlin.TestHelper;
 import org.junit.Test;
 
 import javax.script.Bindings;
 import javax.script.ScriptContext;
 import javax.script.ScriptException;
 import javax.script.SimpleBindings;
-import java.util.Collections;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
 
 import static org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineSuite.ENGINE_TO_TEST;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
 import static org.junit.Assert.assertEquals;
 
 /**
@@ -42,9 +47,7 @@ public class BindingsScriptEngineTest {
         b.put("y", 2);
 
         manager.addPlugin(BindingsGremlinPlugin.build().
-                bindings(b).
-                scope(ScriptContext.GLOBAL_SCOPE).
-                appliesTo(Collections.singletonList(ENGINE_TO_TEST)).create());
+                bindings(b).create());
 
         final GremlinScriptEngine engine = manager.getEngineByName(ENGINE_TO_TEST);
         assertEquals(1, engine.eval("x"));
@@ -52,44 +55,58 @@ public class BindingsScriptEngineTest {
     }
 
     @Test
-    public void shouldIncludeEngineBindings() throws ScriptException {
-        final GremlinScriptEngineManager manager = new DefaultGremlinScriptEngineManager();
-        final Bindings b = new SimpleBindings();
-        b.put("x", 1);
-        b.put("y", 2);
+    public void shouldRemoveGlobalBindings() throws Exception {
+        final DefaultGremlinScriptEngineManager manager = new DefaultGremlinScriptEngineManager();
+        manager.put("x", 100);
 
-        manager.addPlugin(BindingsGremlinPlugin.build().
-                bindings(b).
-                scope(ScriptContext.ENGINE_SCOPE).
-                appliesTo(Collections.singletonList(ENGINE_TO_TEST)).create());
+        final GremlinScriptEngine engine1 = manager.getEngineByName(ENGINE_TO_TEST);
+        assertThat(engine1.getBindings(ScriptContext.GLOBAL_SCOPE).size(), is(1));
+        assertEquals(101, (int) engine1.eval("x+1"));
+
+        manager.getBindings().remove("x");
+        final GremlinScriptEngine engine2 = manager.getEngineByName(ENGINE_TO_TEST);
+        assertThat(engine2.getBindings(ScriptContext.GLOBAL_SCOPE).size(), is(0));
+    }
 
+    @Test
+    public void shouldAddLazyGlobalBindingsViaPlugin() throws Exception {
+        final Bindings bindings = new SimpleBindings();
+        bindings.put("x", 100);
+        final BindingsGremlinPlugin plugin = new BindingsGremlinPlugin(() -> bindings);
+
+        final DefaultGremlinScriptEngineManager manager = new DefaultGremlinScriptEngineManager();
+        manager.addPlugin(plugin);
         final GremlinScriptEngine engine = manager.getEngineByName(ENGINE_TO_TEST);
-        assertEquals(1, engine.eval("x"));
-        assertEquals(2, engine.eval("y"));
+        assertThat(engine.getBindings(ScriptContext.GLOBAL_SCOPE).size(), is(1));
+        assertEquals(101, (int) engine.eval("x+1"));
     }
 
     @Test
-    public void shouldIncludeEngineBindingsToOverrideGlobalBindings() throws ScriptException {
+    public void shouldExtractGlobalBindingsAfterScriptExecution() throws Exception {
         final GremlinScriptEngineManager manager = new DefaultGremlinScriptEngineManager();
 
         final Bindings b1 = new SimpleBindings();
         b1.put("x", 1);
         b1.put("y", 2);
-        manager.addPlugin(BindingsGremlinPlugin.build().
-                bindings(b1).
-                scope(ScriptContext.GLOBAL_SCOPE).
-                appliesTo(Collections.singletonList(ENGINE_TO_TEST)).create());
+        manager.addPlugin(BindingsGremlinPlugin.build().bindings(b1).create());
 
         final Bindings b2 = new SimpleBindings();
         b2.put("x", 100);
         b2.put("y", 200);
-        manager.addPlugin(BindingsGremlinPlugin.build().
-                bindings(b2).
-                scope(ScriptContext.ENGINE_SCOPE).
-                appliesTo(Collections.singletonList(ENGINE_TO_TEST)).create());
+        manager.addPlugin(BindingsGremlinPlugin.build().bindings(b2).create());
+
+        final File scriptFile = TestHelper.generateTempFileFromResource(BindingsScriptEngineTest.class, "bindings-init.groovy", ".groovy");
+        final List<String> files = new ArrayList<>();
+        files.add(scriptFile.getAbsolutePath());
+        manager.addPlugin(ScriptFileGremlinPlugin.build().files(files).create());
 
         final GremlinScriptEngine engine = manager.getEngineByName(ENGINE_TO_TEST);
         assertEquals(100, engine.eval("x"));
         assertEquals(200, engine.eval("y"));
+        assertEquals(300, engine.eval("z"));
+        assertEquals(600, engine.eval("addItUp(z, addItUp(x,y))"));
+
+        assertEquals(300, engine.getBindings(ScriptContext.GLOBAL_SCOPE).get("z"));
+        assertThat(engine.getBindings(ScriptContext.ENGINE_SCOPE).containsKey("z"), is(false));
     }
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/48185654/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/MockGremlinScriptEngine.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/MockGremlinScriptEngine.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/MockGremlinScriptEngine.java
new file mode 100644
index 0000000..ac0c94f
--- /dev/null
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/MockGremlinScriptEngine.java
@@ -0,0 +1,62 @@
+/*
+ * 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.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+
+import javax.script.AbstractScriptEngine;
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+import javax.script.ScriptException;
+import javax.script.SimpleBindings;
+import java.io.Reader;
+
+/**
+ * This is a "do nothing" implementation of the {@link GremlinScriptEngine} which can be used to help test plugin
+ * implementations which don't have reference to a {@link GremlinScriptEngine} as a dependency.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class MockGremlinScriptEngine extends AbstractScriptEngine implements GremlinScriptEngine {
+    @Override
+    public GremlinScriptEngineFactory getFactory() {
+        return new MockGremlinScriptEngineFactory();
+    }
+
+    @Override
+    public Traversal.Admin eval(final Bytecode bytecode, final Bindings bindings) throws ScriptException {
+        return null;
+    }
+
+    @Override
+    public Object eval(final String script, final ScriptContext context) throws ScriptException {
+        return null;
+    }
+
+    @Override
+    public Object eval(final Reader reader, final ScriptContext context) throws ScriptException {
+        return null;
+    }
+
+    @Override
+    public Bindings createBindings() {
+        return new SimpleBindings();
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/48185654/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/MockGremlinScriptEngineFactory.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/MockGremlinScriptEngineFactory.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/MockGremlinScriptEngineFactory.java
new file mode 100644
index 0000000..b4d0732
--- /dev/null
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/MockGremlinScriptEngineFactory.java
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+/**
+ * This is a "do nothing" implementation of the {@link GremlinScriptEngineFactory} which can be used to help test plugin
+ * implementations which don't have reference to a {@link GremlinScriptEngine} as a dependency.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class MockGremlinScriptEngineFactory extends AbstractGremlinScriptEngineFactory {
+
+    public static final String ENGINE_NAME = "gremlin-mock";
+
+    public MockGremlinScriptEngineFactory() {
+        super(ENGINE_NAME, ENGINE_NAME, Collections.singletonList("script"), Collections.singletonList("plain"));
+    }
+
+    @Override
+    public GremlinScriptEngine getScriptEngine() {
+        return new MockGremlinScriptEngine();
+    }
+
+    @Override
+    public String getMethodCallSyntax(final String obj, final String m, final String... args) {
+        return null;
+    }
+
+    @Override
+    public String getOutputStatement(final String toDisplay) {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/48185654/gremlin-test/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineFactory
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineFactory b/gremlin-test/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineFactory
new file mode 100644
index 0000000..819f04f
--- /dev/null
+++ b/gremlin-test/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineFactory
@@ -0,0 +1 @@
+org.apache.tinkerpop.gremlin.jsr223.MockGremlinScriptEngineFactory
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/48185654/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/jsr223/bindings-init.groovy
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/jsr223/bindings-init.groovy b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/jsr223/bindings-init.groovy
new file mode 100644
index 0000000..e924c13
--- /dev/null
+++ b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/jsr223/bindings-init.groovy
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+def addItUp(x, y) { x + y }
+def globals = [:]
+globals << [z : addItUp(x,y)]

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/48185654/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopGremlinPlugin.java
----------------------------------------------------------------------
diff --git a/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopGremlinPlugin.java b/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopGremlinPlugin.java
index 0c9c287..581920d 100644
--- a/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopGremlinPlugin.java
+++ b/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopGremlinPlugin.java
@@ -77,11 +77,10 @@ public final class HadoopGremlinPlugin extends AbstractGremlinPlugin {
 
     private static final ImportCustomizer imports;
 
-    private static final Set<String> appliesTo = new HashSet<>(Collections.singletonList("gremlin-groovy"));
+    private static final Set<String> appliesTo = Collections.emptySet();
 
     static {
         try {
-            // TODO: most of the imports here were wildcarded, but we dont' allow that anymore - needs review
             imports = DefaultImportCustomizer.build()
                     .addClassImports(
                             Configuration.class,
@@ -131,7 +130,9 @@ public final class HadoopGremlinPlugin extends AbstractGremlinPlugin {
         }
     }
 
-    public HadoopGremlinPlugin() {
+    private static final HadoopGremlinPlugin plugin = new HadoopGremlinPlugin();
+
+    private HadoopGremlinPlugin() {
         super(NAME, appliesTo, imports, bindings, new HadoopConsoleCustomizer());
     }
 
@@ -150,6 +151,10 @@ public final class HadoopGremlinPlugin extends AbstractGremlinPlugin {
         return true;
     }
 
+    public static HadoopGremlinPlugin instance() {
+        return plugin;
+    }
+
     private static class HadoopConsoleCustomizer implements ConsoleCustomizer {
         @Override
         public RemoteAcceptor getRemoteAcceptor(final GremlinShellEnvironment environment) {

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/48185654/hadoop-gremlin/src/test/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopGremlinPluginTest.java
----------------------------------------------------------------------
diff --git a/hadoop-gremlin/src/test/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopGremlinPluginTest.java b/hadoop-gremlin/src/test/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopGremlinPluginTest.java
new file mode 100644
index 0000000..167c834
--- /dev/null
+++ b/hadoop-gremlin/src/test/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopGremlinPluginTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.hadoop.jsr223;
+
+import org.apache.tinkerpop.gremlin.hadoop.structure.io.FileSystemStorage;
+import org.apache.tinkerpop.gremlin.jsr223.DefaultGremlinScriptEngineManager;
+import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine;
+import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineManager;
+import org.apache.tinkerpop.gremlin.jsr223.MockGremlinScriptEngineFactory;
+import org.junit.Test;
+
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class HadoopGremlinPluginTest {
+
+    @Test
+    public void shouldAddBindingsForFileSystem() {
+        final GremlinScriptEngineManager manager = new DefaultGremlinScriptEngineManager();
+        manager.addPlugin(HadoopGremlinPlugin.instance());
+        final GremlinScriptEngine engine = manager.getEngineByName(MockGremlinScriptEngineFactory.ENGINE_NAME);
+        final Bindings b = engine.getBindings(ScriptContext.GLOBAL_SCOPE);
+        assertThat(b.containsKey("hdfs"), is(true));
+        assertThat(b.containsKey("fs"), is(true));
+        assertThat(b.get("hdfs"), instanceOf(FileSystemStorage.class));
+        assertThat(b.get("fs"), instanceOf(FileSystemStorage.class));
+    }
+}