You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by ok...@apache.org on 2016/06/07 13:57:04 UTC

[03/34] incubator-tinkerpop git commit: Added ConfigurationCustomizerCompiler

Added ConfigurationCustomizerCompiler

This CustomizerCompiler implementation makes it possible to set low-level configurations in the GremlinGroovyScriptEngine on the Groovy CompilerConfiguration which ultimately controls script compilation settings used by the classloader.


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

Branch: refs/heads/TINKERPOP-1278
Commit: e32347c13e6c4917ebc691aa70e8531cb6b36e1a
Parents: ddddc5f
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Thu Jun 2 16:39:51 2016 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Thu Jun 2 16:39:51 2016 -0400

----------------------------------------------------------------------
 CHANGELOG.asciidoc                              |  1 +
 .../src/reference/gremlin-applications.asciidoc |  5 +-
 .../gremlin/groovy/engine/ScriptEngines.java    | 28 ++++++-
 .../jsr223/GremlinGroovyScriptEngine.java       |  8 +-
 .../ConfigurationCustomizerProvider.java        | 83 ++++++++++++++++++++
 .../groovy/jsr223/BaseScriptForTesting.java     | 30 +++++++
 .../GremlinGroovyScriptEngineConfigTest.java    | 40 ++++++++++
 .../ConfigurationCustomizerProviderTest.java    | 78 ++++++++++++++++++
 .../gremlin/server/BaseScriptForTesting.java    | 30 +++++++
 .../server/GremlinServerIntegrateTest.java      | 50 +++++++++---
 10 files changed, 338 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e32347c1/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 9590a90..6b53bb2 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -34,6 +34,7 @@ TinkerPop 3.2.1 (NOT OFFICIALLY RELEASED YET)
 * Added `EmptyMemory` for ease of use when no memory exists.
 * Updated `VertexComputing.generateProgram()` API to include `Memory`. (*breaking*)
 * `ImmutablePath.TailPath` is now serializable like `ImmutablePath`.
+* Added `ConfigurationCompilerProvider` which allows fine-grained control of some of the internal `GremlinGroovyScriptEngine` settings at the Groovy compilation level.
 * Intoduced the `application/vnd.gremlin-v1.0+gryo-lite` serialization type to Gremlin Server which users "reference" elements rather than "detached".
 * `GryoMapper` allows overrides of existing serializers on calls to `addCustom` on the builder.
 * Added a traversal style guide to the recipes cookbook.

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e32347c1/docs/src/reference/gremlin-applications.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/reference/gremlin-applications.asciidoc b/docs/src/reference/gremlin-applications.asciidoc
index a8070ba..8c987fe 100644
--- a/docs/src/reference/gremlin-applications.asciidoc
+++ b/docs/src/reference/gremlin-applications.asciidoc
@@ -979,8 +979,8 @@ run Gremlin Server with Ganglia monitoring, download the `org.acplt:oncrpc` jar
 link:http://repo1.maven.org/maven2/org/acplt/oncrpc/1.0.7/[here] and copy it to the Gremlin Server `/lib` directory
 before starting the server.
 
-Security
-^^^^^^^^
+Security and Execution
+^^^^^^^^^^^^^^^^^^^^^^
 
 image:gremlin-server-secure.png[width=175,float=right] Gremlin Server provides for several features that aid in the
 security of the graphs that it exposes.  It has built in SSL support and a pluggable authentication framework using
@@ -1151,6 +1151,7 @@ There are a number of pre-packaged `CustomizerProvider` implementations:
 |=========================================================
 |Customizer |Description
 |`CompileStaticCustomizerProvider` |Applies `CompileStatic` annotations to incoming scripts thus removing dynamic dispatch. More information about static compilation can be found in the link:http://docs.groovy-lang.org/latest/html/documentation/#_static_compilation[Groovy Documentation].  It is possible to configure this `CustomizerProvider` by specifying a comma separated list of link:http://docs.groovy-lang.org/latest/html/documentation/#Typecheckingextensions-Workingwithextensions[type checking extensions] that can have the effect of securing calls to various methods.
+|`ConfigurationCustomizerProvider` |Allows configuration of the the Groovy `CompilerConfiguration` object by taking a `Map` of key/value pairs where the "key" is a property to set on the `CompilerConfiguration`.
 |`ThreadInterruptCustomizerProvider` |Injects checks for thread interruption, thus allowing the thread to potentially respect calls to `Thread.interrupt()`
 |`TimedInterruptCustomizerProvider` |Injects checks into loops to interrupt them if they exceed the configured timeout in milliseconds.
 |`TypeCheckedCustomizerProvider` |Similar to the above mentioned, `CompileStaticCustomizerProvider`, the `TypeCheckedCustomizerProvider` injects `TypeChecked` annotations to incoming scripts.  More information on the nature of this annotation can be found in the link:http://docs.groovy-lang.org/latest/html/documentation/#_the_code_typechecked_code_annotation[Groovy Documentation].  It too takes a comma separated list of link:http://docs.groovy-lang.org/latest/html/documentation/#Typecheckingextensions-Workingwithextensions[type checking extensions].

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e32347c1/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/ScriptEngines.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/ScriptEngines.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/ScriptEngines.java
index 1113eb5..3d54bea 100644
--- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/ScriptEngines.java
+++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/ScriptEngines.java
@@ -23,6 +23,7 @@ import org.apache.tinkerpop.gremlin.groovy.DefaultImportCustomizerProvider;
 import org.apache.tinkerpop.gremlin.groovy.jsr223.DependencyManager;
 import org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine;
 import org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngineFactory;
+import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.ConfigurationCustomizerProvider;
 import org.apache.tinkerpop.gremlin.groovy.plugin.GremlinPlugin;
 import org.apache.tinkerpop.gremlin.groovy.plugin.IllegalEnvironmentException;
 import org.slf4j.Logger;
@@ -382,8 +383,17 @@ public class ScriptEngines implements AutoCloseable {
 
                         final Class<?>[] argClasses = new Class<?>[args.length];
                         Stream.of(args).map(a -> a.getClass()).collect(Collectors.toList()).toArray(argClasses);
-                        final Constructor constructor = providerClass.getConstructor(argClasses);
-                        providers.add((CompilerCustomizerProvider) constructor.newInstance(args));
+
+                        final Optional<Constructor> constructor = Stream.of(providerClass.getConstructors())
+                                .filter(c -> c.getParameterCount() == argClasses.length &&
+                                             allMatch(c.getParameterTypes(), argClasses))
+                                .findFirst();
+
+                        if (constructor.isPresent()) providers.add((CompilerCustomizerProvider)
+                                constructor.get().newInstance(args));
+                        else
+                            throw new IllegalStateException(String.format("Could not configure %s with the supplied options %s",
+                                    ConfigurationCustomizerProvider.class.getName(), Arrays.asList(args)));
                     } else {
                         providers.add((CompilerCustomizerProvider) providerClass.newInstance());
                     }
@@ -400,6 +410,20 @@ public class ScriptEngines implements AutoCloseable {
     }
 
     /**
+     * Determine if the constructor argument types match the arg types that are going to be passed in to that
+     * constructor.
+     */
+    private static boolean allMatch(final Class<?>[] constructorArgTypes, final Class<?>[] argTypes) {
+        for (int ix = 0; ix < constructorArgTypes.length; ix++) {
+            if (!constructorArgTypes[ix].isAssignableFrom(argTypes[ix])) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
      * Takes the bindings from a request for eval and merges them with the {@code ENGINE_SCOPE} bindings.
      */
     private static Bindings mergeBindings(final Bindings bindings, final ScriptEngine engine) {

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e32347c1/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
index 23240cb..0069103 100644
--- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
+++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java
@@ -23,6 +23,7 @@ import org.apache.tinkerpop.gremlin.groovy.DefaultImportCustomizerProvider;
 import org.apache.tinkerpop.gremlin.groovy.EmptyImportCustomizerProvider;
 import org.apache.tinkerpop.gremlin.groovy.ImportCustomizerProvider;
 import org.apache.tinkerpop.gremlin.groovy.NoImportCustomizerProvider;
+import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.ConfigurationCustomizerProvider;
 import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.InterpreterModeCustomizerProvider;
 import org.apache.tinkerpop.gremlin.groovy.loaders.GremlinLoader;
 import org.apache.tinkerpop.gremlin.groovy.plugin.Artifact;
@@ -610,7 +611,12 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl implements
         final CompilerConfiguration conf = new CompilerConfiguration();
         conf.addCompilationCustomizers(this.importCustomizerProvider.create());
 
-        customizerProviders.forEach(p -> conf.addCompilationCustomizers(p.create()));
+        // ConfigurationCustomizerProvider is treated separately
+        customizerProviders.stream().filter(cp -> !(cp instanceof ConfigurationCustomizerProvider))
+                .forEach(p -> conf.addCompilationCustomizers(p.create()));
+
+        customizerProviders.stream().filter(cp -> cp instanceof ConfigurationCustomizerProvider).findFirst()
+                .ifPresent(cp -> ((ConfigurationCustomizerProvider) cp).applyCustomization(conf));
 
         this.loader = new GremlinGroovyClassLoader(getParentLoader(), conf);
     }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e32347c1/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/ConfigurationCustomizerProvider.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/ConfigurationCustomizerProvider.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/ConfigurationCustomizerProvider.java
new file mode 100644
index 0000000..ba9855c
--- /dev/null
+++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/ConfigurationCustomizerProvider.java
@@ -0,0 +1,83 @@
+/*
+ * 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.groovy.jsr223.customizer;
+
+import org.apache.tinkerpop.gremlin.groovy.CompilerCustomizerProvider;
+import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.customizers.CompilationCustomizer;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Allows configurations to be directly supplied to a groovy {@code CompilerConfiguration} when a
+ * {@link org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine} is initialized, providing fine-grained
+ * control over its internals.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class ConfigurationCustomizerProvider implements CompilerCustomizerProvider {
+
+    private final Map<String,Object> properties;
+
+    /**
+     * Creates a new instance using configuration values specified
+     */
+    public ConfigurationCustomizerProvider(final Object... keyValues) {
+        if (null == keyValues || keyValues.length == 0)
+            throw new IllegalArgumentException("ConfigurationCustomizerProvider must have key/values specified");
+
+        if (keyValues.length % 2 != 0)
+            throw new IllegalArgumentException("The keyValues must have an even number of values");
+
+        properties = ElementHelper.asMap(keyValues);
+    }
+
+    /**
+     * Creates a new instance using configuration values specified
+     */
+    public ConfigurationCustomizerProvider(final Map<String,Object> keyValues) {
+        properties = keyValues;
+    }
+
+    public CompilerConfiguration applyCustomization(final CompilerConfiguration compilerConfiguration) {
+        final Class<CompilerConfiguration> clazz = CompilerConfiguration.class;
+        final List<Method> methods = Arrays.asList(clazz.getMethods());
+        for (Map.Entry<String,Object> entry : properties.entrySet()) {
+            final Method method = methods.stream().filter(m -> m.getName().equals("set" + entry.getKey())).findFirst()
+                   .orElseThrow(() -> new IllegalStateException("Invalid setting [" + entry.getKey() + "] for CompilerConfiguration"));
+
+            try {
+                method.invoke(compilerConfiguration, entry.getValue());
+            } catch (Exception ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+
+        return compilerConfiguration;
+    }
+
+    @Override
+    public CompilationCustomizer create() {
+        throw new UnsupportedOperationException("This is a marker implementation that does not create a CompilationCustomizer instance");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e32347c1/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/BaseScriptForTesting.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/BaseScriptForTesting.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/BaseScriptForTesting.java
new file mode 100644
index 0000000..98c8e8c
--- /dev/null
+++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/BaseScriptForTesting.java
@@ -0,0 +1,30 @@
+/*
+ * 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.groovy.jsr223;
+
+import groovy.lang.Script;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public abstract class BaseScriptForTesting extends Script {
+    public String hello(final String name) {
+        return "hello, " + name;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e32347c1/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineConfigTest.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineConfigTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineConfigTest.java
new file mode 100644
index 0000000..d354ffa
--- /dev/null
+++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineConfigTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.groovy.jsr223;
+
+import org.apache.tinkerpop.gremlin.groovy.DefaultImportCustomizerProvider;
+import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.ConfigurationCustomizerProvider;
+import org.junit.Test;
+
+import javax.script.ScriptEngine;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class GremlinGroovyScriptEngineConfigTest {
+    @Test
+    public void shouldAddBaseScriptClass() throws Exception {
+        final ScriptEngine engine = new GremlinGroovyScriptEngine(
+                new ConfigurationCustomizerProvider("ScriptBaseClass", BaseScriptForTesting.class.getName()), new DefaultImportCustomizerProvider());
+
+        assertEquals("hello, stephen", engine.eval("hello('stephen')"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e32347c1/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/ConfigurationCustomizerProviderTest.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/ConfigurationCustomizerProviderTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/ConfigurationCustomizerProviderTest.java
new file mode 100644
index 0000000..54b55b1
--- /dev/null
+++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/ConfigurationCustomizerProviderTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.groovy.jsr223.customizer;
+
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.junit.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class ConfigurationCustomizerProviderTest {
+    @Test(expected = IllegalArgumentException.class)
+    public void shouldThrowExceptionForNoSettings() {
+        new ConfigurationCustomizerProvider();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void shouldThrowExceptionForInvalidSettings() {
+        new ConfigurationCustomizerProvider("only-one-arg");
+    }
+
+    @Test
+    public void shouldThrowExceptionForNotFoundSetting() {
+        final CompilerConfiguration configuration = new CompilerConfiguration();
+        try {
+            final ConfigurationCustomizerProvider provider = new ConfigurationCustomizerProvider(
+                    "Tolerance", 3,
+                    "NotRealSettingThatWouldEverOccur2", new java.util.Date());
+
+            provider.applyCustomization(configuration);
+        } catch (Exception ex) {
+            assertThat(ex, instanceOf(IllegalStateException.class));
+            assertEquals("Invalid setting [NotRealSettingThatWouldEverOccur2] for CompilerConfiguration", ex.getMessage());
+        }
+    }
+
+    @Test
+    public void shouldApplyConfigurationChanges() {
+        final CompilerConfiguration configuration = new CompilerConfiguration();
+
+        assertEquals(10, configuration.getTolerance());
+        assertNull(configuration.getScriptBaseClass());
+        assertEquals(false, configuration.getDebug());
+
+        final ConfigurationCustomizerProvider provider = new ConfigurationCustomizerProvider(
+                "Tolerance", 3,
+                "ScriptBaseClass", "Something",
+                "Debug", true);
+
+        provider.applyCustomization(configuration);
+
+        assertEquals(3, configuration.getTolerance());
+        assertEquals("Something", configuration.getScriptBaseClass());
+        assertEquals(true, configuration.getDebug());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e32347c1/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/BaseScriptForTesting.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/BaseScriptForTesting.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/BaseScriptForTesting.java
new file mode 100644
index 0000000..ae4c713
--- /dev/null
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/BaseScriptForTesting.java
@@ -0,0 +1,30 @@
+/*
+ * 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.server;
+
+import groovy.lang.Script;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public abstract class BaseScriptForTesting extends Script {
+    public String hello(final String name) {
+        return "hello, " + name;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e32347c1/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinServerIntegrateTest.java
----------------------------------------------------------------------
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 16cbdee..a2b415d 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
@@ -41,6 +41,7 @@ import org.apache.tinkerpop.gremlin.driver.simple.SimpleClient;
 import org.apache.tinkerpop.gremlin.driver.simple.WebSocketClient;
 import org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine;
 import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.CompileStaticCustomizerProvider;
+import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.ConfigurationCustomizerProvider;
 import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.InterpreterModeCustomizerProvider;
 import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.SimpleSandboxExtension;
 import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.TimedInterruptCustomizerProvider;
@@ -55,7 +56,6 @@ import org.junit.Before;
 import org.junit.Test;
 
 import java.nio.channels.ClosedChannelException;
-import java.security.cert.CertificateException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -72,12 +72,15 @@ import java.util.stream.IntStream;
 
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.IsInstanceOf.instanceOf;
 import static org.hamcrest.core.IsNot.not;
 import static org.hamcrest.core.StringEndsWith.endsWith;
 import static org.hamcrest.core.StringStartsWith.startsWith;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeThat;
+import static org.junit.Assert.assertEquals;
 
 /**
  * Integration tests for server-side settings and processing.
@@ -161,6 +164,9 @@ public class GremlinServerIntegrateTest extends AbstractGremlinServerIntegration
             case "shouldReceiveFailureTimeOutOnScriptEvalOfOutOfControlLoop":
                 settings.scriptEngines.get("gremlin-groovy").config = getScriptEngineConfForTimedInterrupt();
                 break;
+            case "shouldUseBaseScript":
+                settings.scriptEngines.get("gremlin-groovy").config = getScriptEngineConfForBaseScript();
+                break;
         }
 
         return settings;
@@ -206,6 +212,30 @@ public class GremlinServerIntegrateTest extends AbstractGremlinServerIntegration
         return scriptEngineConf;
     }
 
+    private static Map<String, Object> getScriptEngineConfForBaseScript() {
+        final Map<String,Object> scriptEngineConf = new HashMap<>();
+        final Map<String,Object> compilerCustomizerProviderConf = new HashMap<>();
+        final List<Object> keyValues = new ArrayList<>();
+
+        final Map<String,Object> properties = new HashMap<>();
+        properties.put("ScriptBaseClass", BaseScriptForTesting.class.getName());
+        keyValues.add(properties);
+
+        compilerCustomizerProviderConf.put(ConfigurationCustomizerProvider.class.getName(), keyValues);
+        scriptEngineConf.put("compilerCustomizerProviders", compilerCustomizerProviderConf);
+        return scriptEngineConf;
+    }
+
+    @Test
+    public void shouldUseBaseScript() throws Exception {
+        final Cluster cluster = Cluster.open();
+        final Client client = cluster.connect(name.getMethodName());
+
+        assertEquals("hello, stephen", client.submit("hello('stephen')").all().get().get(0).getString());
+
+        cluster.close();
+    }
+
     @Test
     public void shouldUseInterpreterMode() throws Exception {
         final Cluster cluster = Cluster.open();
@@ -368,12 +398,12 @@ public class GremlinServerIntegrateTest extends AbstractGremlinServerIntegration
                 });
             });
 
-            assertTrue(latch.await(30000, TimeUnit.MILLISECONDS));
+            assertThat(latch.await(30000, TimeUnit.MILLISECONDS), is(true));
             assertEquals(0, latch.getCount());
-            assertFalse(faulty.get());
-            assertTrue(expected.get());
+            assertThat(faulty.get(), is(false));
+            assertThat(expected.get(), is(true));
 
-            assertTrue(recordingAppender.getMessages().stream().anyMatch(m -> m.contains("Pausing response writing as writeBufferHighWaterMark exceeded on")));
+            assertThat(recordingAppender.getMessages().stream().anyMatch(m -> m.contains("Pausing response writing as writeBufferHighWaterMark exceeded on")), is(true));
         } catch (Exception ex) {
             fail("Shouldn't have tossed an exception");
         } finally {
@@ -410,7 +440,7 @@ public class GremlinServerIntegrateTest extends AbstractGremlinServerIntegration
 
             if (!latch.await(3000, TimeUnit.MILLISECONDS))
                 fail("Request should have returned error, but instead timed out");
-            assertTrue(pass.get());
+            assertThat(pass.get(), is(true));
         }
     }
 
@@ -433,7 +463,7 @@ public class GremlinServerIntegrateTest extends AbstractGremlinServerIntegration
 
             if (!latch.await(3000, TimeUnit.MILLISECONDS))
                 fail("Request should have returned error, but instead timed out");
-            assertTrue(pass.get());
+            assertThat(pass.get(), is(true));
         }
     }
 
@@ -456,7 +486,7 @@ public class GremlinServerIntegrateTest extends AbstractGremlinServerIntegration
 
             if (!latch.await(3000, TimeUnit.MILLISECONDS))
                 fail("Request should have returned error, but instead timed out");
-            assertTrue(pass.get());
+            assertThat(pass.get(), is(true));
         }
     }
 
@@ -691,7 +721,7 @@ public class GremlinServerIntegrateTest extends AbstractGremlinServerIntegration
             client.submit("1+1").all().join();
             fail();
         } catch (RuntimeException re) {
-            assertTrue(re.getCause().getCause() instanceof ClosedChannelException);
+            assertThat(re.getCause().getCause() instanceof ClosedChannelException, is(true));
 
             //
             // should recover when the server comes back