You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2016/12/12 12:45:07 UTC
[05/46] tinkerpop git commit: TINKERPOP-1562 Initial effort to
deprecate ScriptEngines.
TINKERPOP-1562 Initial effort to deprecate ScriptEngines.
This opens up some options for how to move plugins up to gremlin-core (or deprecate them completely - not surehow that will flow yet). Anyway, this is a wip commit and a nice stop point.
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/ddafaef9
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/ddafaef9
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/ddafaef9
Branch: refs/heads/tp32
Commit: ddafaef99a7a4dbf7d434b845f43e24e14deea19
Parents: 53e932b
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Thu Nov 17 16:47:31 2016 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Fri Dec 2 06:28:50 2016 -0500
----------------------------------------------------------------------
.../gremlin/jsr223/AbstractGremlinModule.java | 48 +++++
.../gremlin/jsr223/CoreGremlinModule.java | 43 ++++-
.../gremlin/jsr223/ImportCustomizer.java | 4 +
.../gremlin/jsr223/ImportGremlinModule.java | 174 +++++++++++++++++++
.../gremlin/jsr223/ScriptCustomizer.java | 56 ++++++
.../gremlin/jsr223/ScriptFileModule.java | 71 ++++++++
.../gremlin/jsr223/ImportGremlinModuleTest.java | 149 ++++++++++++++++
.../gremlin/groovy/engine/GremlinExecutor.java | 76 +++++++-
.../gremlin/groovy/engine/ScriptEngines.java | 2 +
.../tinkerpop/gremlin/server/Settings.java | 8 +
.../server/util/ServerGremlinExecutor.java | 28 ++-
11 files changed, 648 insertions(+), 11 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ddafaef9/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/AbstractGremlinModule.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/AbstractGremlinModule.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/AbstractGremlinModule.java
new file mode 100644
index 0000000..36104f6
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/AbstractGremlinModule.java
@@ -0,0 +1,48 @@
+/*
+ * 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.Optional;
+import java.util.Set;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public abstract class AbstractGremlinModule implements GremlinModule {
+ protected final String moduleName;
+ protected final Customizer[] customizers;
+ protected final Set<String> appliesTo;
+
+ public AbstractGremlinModule(final String moduleName, final Set<String> appliesTo, final Customizer... customizers) {
+ this.moduleName = moduleName;
+ this.appliesTo = appliesTo;
+ this.customizers = customizers;
+ }
+
+ @Override
+ public String getName() {
+ return moduleName;
+ }
+
+ @Override
+ public Optional<Customizer[]> getCustomizers(final String scriptEngineName) {
+ return null == scriptEngineName || appliesTo.isEmpty() || appliesTo.contains(scriptEngineName) ?
+ Optional.of(customizers) : Optional.empty();
+ }
+}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ddafaef9/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinModule.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinModule.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinModule.java
index dfe9f93..f1fdbe4 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinModule.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinModule.java
@@ -18,6 +18,8 @@
*/
package org.apache.tinkerpop.gremlin.jsr223;
+import org.apache.tinkerpop.gremlin.util.CoreImports;
+
import java.util.Optional;
/**
@@ -25,21 +27,54 @@ import java.util.Optional;
*
* @author Stephen Mallette (http://stephen.genoprime.com)
*/
-public class CoreGremlinModule implements GremlinModule {
+public final class CoreGremlinModule implements GremlinModule {
+
+ private static final String MODULE_NAME = "tinkerpop.core";
+
+ private static final ImportCustomizer gremlinCore = ImportCustomizer.build()
+ .addClassImports(CoreImports.getClassImports())
+ .addEnumImports(CoreImports.getEnumImports())
+ .addMethodImports(CoreImports.getMethodImports()).create();
- private static final Optional<Customizer[]> CUSTOMIZERS = Optional.of(new Customizer[] { ImportCustomizer.GREMLIN_CORE });
+ private static final Customizer[] customizers = new Customizer[] {gremlinCore};
+ private static final Builder builder = new Builder();
+ /**
+ * @deprecated As of 3.2.4, replaced by {@link #instance()} as this field will later become private.
+ */
+ @Deprecated
public static final CoreGremlinModule INSTANCE = new CoreGremlinModule();
private CoreGremlinModule() {}
+ public static CoreGremlinModule instance() {
+ return INSTANCE;
+ }
+
@Override
public Optional<Customizer[]> getCustomizers(final String scriptEngineName) {
- return CUSTOMIZERS;
+ return Optional.of(customizers);
}
@Override
public String getName() {
- return "tinkerpop.core";
+ return MODULE_NAME;
+ }
+
+ /**
+ * {@link GremlinModule} instances all use a builder pattern for instantiation via configuration. This method is
+ * just provided for consistency with that pattern. When instantiating programmatically, use {@link #instance()}.
+ */
+ public static Builder build() {
+ return builder;
+ }
+
+ public final static class Builder {
+
+ private Builder() {}
+
+ public CoreGremlinModule create() {
+ return instance();
+ }
}
}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ddafaef9/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportCustomizer.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportCustomizer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportCustomizer.java
index 6070839..2f0e08c 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportCustomizer.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportCustomizer.java
@@ -34,6 +34,10 @@ import java.util.Set;
*/
public class ImportCustomizer implements Customizer {
+ /**
+ * @deprecated As of release 3.2.4, not replaced.
+ */
+ @Deprecated
public static final ImportCustomizer GREMLIN_CORE = ImportCustomizer.build()
.addClassImports(CoreImports.getClassImports())
.addEnumImports(CoreImports.getEnumImports())
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ddafaef9/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinModule.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinModule.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinModule.java
new file mode 100644
index 0000000..9fcbbce
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinModule.java
@@ -0,0 +1,174 @@
+/*
+ * 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.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * A module that allows custom class, static method and enum imports (i.e. those that are statically defined by a
+ * module within itself). A user might utilize this class to supply their own imports. This module is not specific
+ * to any {@link GremlinScriptEngine} - the imports are supplied to all engines.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class ImportGremlinModule extends AbstractGremlinModule {
+ private static final String MODULE_NAME = "tinkerpop.import";
+
+ private ImportGremlinModule(final Builder builder) {
+ super(MODULE_NAME, builder.appliesTo, ImportCustomizer.build()
+ .addClassImports(builder.classImports)
+ .addEnumImports(builder.enumImports)
+ .addMethodImports(builder.methodImports).create());
+ }
+
+ public static Builder build() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private static final Pattern METHOD_PATTERN = Pattern.compile("(.*)#(.*)\\((.*)\\)");
+ private static final Pattern ENUM_PATTERN = Pattern.compile("(.*)#(.*)");
+ private Set<Class> classImports = new HashSet<>();
+ private Set<Method> methodImports = new HashSet<>();
+ private Set<Enum> enumImports = new HashSet<>();
+ 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 Builder appliesTo(final Collection<String> scriptEngineName) {
+ this.appliesTo.addAll(scriptEngineName);
+ return this;
+ }
+
+ public Builder classImports(final Collection<String> classes) {
+ for (String clazz : classes) {
+ try {
+ classImports.add(Class.forName(clazz));
+ } catch (Exception ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+ return this;
+ }
+
+ public Builder methodImports(final Collection<String> methods) {
+ for (String method : methods) {
+ try {
+ if (method.endsWith("#*")) {
+ final String classString = method.substring(0, method.length() - 2);
+ final Class<?> clazz = Class.forName(classString);
+ methodImports.addAll(allStaticMethods(clazz));
+ } else {
+ final Matcher matcher = METHOD_PATTERN.matcher(method);
+
+ if (!matcher.matches())
+ throw new IllegalArgumentException(String.format("Could not read method descriptor - check format of: %s", method));
+
+ final String classString = matcher.group(1);
+ final String methodString = matcher.group(2);
+ final String argString = matcher.group(3);
+ final Class<?> clazz = Class.forName(classString);
+ methodImports.add(clazz.getMethod(methodString, parse(argString)));
+ }
+ } catch (IllegalArgumentException iae) {
+ throw iae;
+ } catch (Exception ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+ return this;
+ }
+
+ public Builder enumImports(final Collection<String> enums) {
+ for (String enumItem : enums) {
+ try {
+ if (enumItem.endsWith("#*")) {
+ final String classString = enumItem.substring(0, enumItem.length() - 2);
+ final Class<?> clazz = Class.forName(classString);
+ enumImports.addAll(allEnums(clazz));
+ } else {
+ final Matcher matcher = ENUM_PATTERN.matcher(enumItem);
+
+ if (!matcher.matches())
+ throw new IllegalArgumentException(String.format("Could not read enum descriptor - check format of: %s", enumItem));
+
+ final String classString = matcher.group(1);
+ final String enumValString = matcher.group(2);
+ final Class<?> clazz = Class.forName(classString);
+
+ Stream.of(clazz.getEnumConstants())
+ .filter(e -> ((Enum) e).name().equals(enumValString))
+ .findFirst().ifPresent(e -> enumImports.add((Enum) e));
+ }
+ } catch (IllegalArgumentException iae) {
+ throw iae;
+ } catch (Exception ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+ return this;
+ }
+
+ public ImportGremlinModule create() {
+ if (enumImports.isEmpty() && classImports.isEmpty() && methodImports.isEmpty())
+ throw new IllegalStateException("At least one import must be specified");
+
+ return new ImportGremlinModule(this);
+ }
+
+ private static List<Enum> allEnums(final Class<?> clazz) {
+ return Stream.of(clazz.getEnumConstants()).map(e -> (Enum) e).collect(Collectors.toList());
+ }
+
+ private static List<Method> allStaticMethods(final Class<?> clazz) {
+ return Stream.of(clazz.getMethods()).filter(m -> Modifier.isStatic(m.getModifiers())).collect(Collectors.toList());
+ }
+
+ private static Class<?>[] parse(final String argString) {
+ if (null == argString || argString.isEmpty())
+ return new Class<?>[0];
+
+ final List<String> args = Stream.of(argString.split(",")).map(String::trim).collect(Collectors.toList());
+ final Class<?>[] classes = new Class<?>[args.size()];
+ for (int ix = 0; ix < args.size(); ix++) {
+ try {
+ classes[ix] = Class.forName(args.get(ix));
+ } catch (Exception ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ return classes;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ddafaef9/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptCustomizer.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptCustomizer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptCustomizer.java
new file mode 100644
index 0000000..28603df
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptCustomizer.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.jsr223;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class ScriptCustomizer implements Customizer {
+
+ private final Collection<List<String>> scripts;
+
+ public ScriptCustomizer(final Set<File> files) {
+ this(files.stream().map(f -> {
+ try {
+ return Files.lines(f.toPath(), StandardCharsets.UTF_8).collect(Collectors.toList());
+ } catch (IOException ioe) {
+ throw new IllegalStateException(ioe);
+ }
+ }).collect(Collectors.toList()));
+ }
+
+ public ScriptCustomizer(final Collection<List<String>> scripts) {
+ this.scripts = scripts;
+ }
+
+ public Collection<List<String>> scripts() {
+ return scripts;
+ }
+}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ddafaef9/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileModule.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileModule.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileModule.java
new file mode 100644
index 0000000..b5c0d0c
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileModule.java
@@ -0,0 +1,71 @@
+/*
+ * 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.io.File;
+import java.io.FileNotFoundException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class ScriptFileModule extends AbstractGremlinModule {
+ private static final String MODULE_NAME = "tinkerpop.script";
+
+ public ScriptFileModule(final Builder builder) {
+ super(MODULE_NAME, builder.appliesTo, new ScriptCustomizer(builder.files));
+ }
+
+ public static Builder build() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+
+ private final Set<String> appliesTo = new HashSet<>();
+ private Set<File> files = 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 Builder appliesTo(final Collection<String> scriptEngineName) {
+ this.appliesTo.addAll(scriptEngineName);
+ return this;
+ }
+
+ public Builder files(final Set<String> files) {
+ for (String f : files) {
+ final File file = new File(f);
+ if (!file.exists()) throw new IllegalArgumentException(new FileNotFoundException(f));
+ this.files.add(file);
+ }
+
+ return this;
+ }
+
+ public ScriptFileModule create() {
+ return new ScriptFileModule(this);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ddafaef9/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinModuleTest.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinModuleTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinModuleTest.java
new file mode 100644
index 0000000..ce62357
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinModuleTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.T;
+import org.apache.tinkerpop.gremlin.structure.io.IoCore;
+import org.apache.tinkerpop.gremlin.util.Gremlin;
+import org.junit.Test;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsCollectionContaining.hasItems;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class ImportGremlinModuleTest {
+
+ @Test(expected = IllegalStateException.class)
+ public void shouldImportSomething() {
+ ImportGremlinModule.build().create();
+ }
+
+ @Test
+ public void shouldImportClass() {
+ final ImportGremlinModule module = ImportGremlinModule.build()
+ .classImports(Collections.singletonList(Graph.class.getCanonicalName())).create();
+
+ final ImportCustomizer customizer = (ImportCustomizer) module.getCustomizers().get()[0];
+ assertEquals(1, module.getCustomizers().get().length);
+ assertThat(customizer.getClassImports(), hasItems(Graph.class));
+ assertEquals(1, customizer.getClassImports().size());
+ }
+
+ @Test
+ public void shouldImportWildcardMethod() throws Exception {
+ final Method zeroArgs = Gremlin.class.getMethod("version");
+ final ImportGremlinModule module = ImportGremlinModule.build()
+ .methodImports(Collections.singletonList(Gremlin.class.getCanonicalName() + "#*")).create();
+
+ final ImportCustomizer customizer = (ImportCustomizer) module.getCustomizers().get()[0];
+ assertEquals(1, module.getCustomizers().get().length);
+ assertThat(customizer.getMethodImports(), hasItems(zeroArgs));
+
+ // will also have the static main() method
+ assertEquals(2, customizer.getMethodImports().size());
+ }
+
+ @Test
+ public void shouldImportZeroArgMethod() throws Exception {
+ final Method zeroArgs = Gremlin.class.getMethod("version");
+ final ImportGremlinModule module = ImportGremlinModule.build()
+ .methodImports(Collections.singletonList(toMethodDescriptor(zeroArgs))).create();
+
+ final ImportCustomizer customizer = (ImportCustomizer) module.getCustomizers().get()[0];
+ assertEquals(1, module.getCustomizers().get().length);
+ assertThat(customizer.getMethodImports(), hasItems(zeroArgs));
+ assertEquals(1, customizer.getMethodImports().size());
+ }
+
+ @Test
+ public void shouldImportSingleArgMethod() throws Exception {
+ final Method singleArg = IoCore.class.getMethod("createIoBuilder", String.class);
+ final ImportGremlinModule module = ImportGremlinModule.build()
+ .methodImports(Collections.singletonList(toMethodDescriptor(singleArg))).create();
+
+ final ImportCustomizer customizer = (ImportCustomizer) module.getCustomizers().get()[0];
+ assertEquals(1, module.getCustomizers().get().length);
+ assertThat(customizer.getMethodImports(), hasItems(singleArg));
+ assertEquals(1, customizer.getMethodImports().size());
+ }
+
+ @Test
+ public void shouldThrowExceptionIfInvalidMethodDescriptor() throws Exception {
+ final String badDescriptor = "Gremlin*version";
+ try {
+ ImportGremlinModule.build()
+ .methodImports(Collections.singletonList(badDescriptor)).create();
+ fail("Should have failed parsing the method descriptor");
+ } catch (IllegalArgumentException iae) {
+ assertEquals(iae.getMessage(), "Could not read method descriptor - check format of: " + badDescriptor);
+ }
+ }
+
+ @Test
+ public void shouldImportWildcardEnum() throws Exception {
+ final ImportGremlinModule module = ImportGremlinModule.build()
+ .enumImports(Collections.singletonList(T.class.getCanonicalName() + "#*")).create();
+
+ final ImportCustomizer customizer = (ImportCustomizer) module.getCustomizers().get()[0];
+ assertEquals(1, module.getCustomizers().get().length);
+ assertThat(customizer.getEnumImports(), hasItems(T.id, T.key, T.label, T.value));
+ assertEquals(4, customizer.getEnumImports().size());
+ }
+
+ @Test
+ public void shouldImportEnum() throws Exception {
+ final ImportGremlinModule module = ImportGremlinModule.build()
+ .enumImports(Collections.singletonList(T.class.getCanonicalName() + "#" + T.id.name())).create();
+
+ final ImportCustomizer customizer = (ImportCustomizer) module.getCustomizers().get()[0];
+ assertEquals(1, module.getCustomizers().get().length);
+ assertThat(customizer.getEnumImports(), hasItems(T.id));
+ }
+
+ @Test
+ public void shouldThrowExceptionIfInvalidEnumDescriptor() throws Exception {
+ final String badDescriptor = "T*id";
+ try {
+ ImportGremlinModule.build()
+ .enumImports(Collections.singletonList(badDescriptor)).create();
+ fail("Should have failed parsing the enum descriptor");
+ } catch (IllegalArgumentException iae) {
+ assertEquals("Could not read enum descriptor - check format of: " + badDescriptor, iae.getMessage());
+ }
+ }
+
+ private static String toMethodDescriptor(final Method method) {
+ return method.getDeclaringClass().getCanonicalName() +
+ "#" +
+ method.getName() +
+ '(' +
+ String.join(",", Stream.of(method.getParameters()).map(p -> p.getType().getCanonicalName()).collect(Collectors.toList())) +
+ ')';
+ }
+}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ddafaef9/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/GremlinExecutor.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/GremlinExecutor.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/GremlinExecutor.java
index cc30f99..90a3b43 100644
--- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/GremlinExecutor.java
+++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/GremlinExecutor.java
@@ -22,6 +22,9 @@ import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine;
import org.apache.tinkerpop.gremlin.groovy.plugin.GremlinPlugin;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
+import org.apache.tinkerpop.gremlin.jsr223.CachedGremlinScriptEngineManager;
+import org.apache.tinkerpop.gremlin.jsr223.GremlinModule;
+import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineManager;
import org.javatuples.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,6 +36,7 @@ import javax.script.SimpleBindings;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -51,11 +55,11 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Execute Gremlin scripts against a {@code ScriptEngine} instance. It is designed to host any JSR-223 enabled
@@ -79,7 +83,10 @@ public class GremlinExecutor implements AutoCloseable {
*/
private ScriptEngines scriptEngines;
+ private GremlinScriptEngineManager manager;
+
private final Map<String, EngineSettings> settings;
+ private final Map<String, Map<String, Map<String,Object>>> modules;
private final long scriptEvaluationTimeout;
private final Bindings globalBindings;
private final List<List<String>> use;
@@ -92,6 +99,7 @@ public class GremlinExecutor implements AutoCloseable {
private final Set<String> enabledPlugins;
private final boolean suppliedExecutor;
private final boolean suppliedScheduledExecutor;
+ private boolean useGremlinScriptEngineManager;
private GremlinExecutor(final Builder builder, final boolean suppliedExecutor,
final boolean suppliedScheduledExecutor) {
@@ -104,10 +112,22 @@ public class GremlinExecutor implements AutoCloseable {
this.afterFailure = builder.afterFailure;
this.use = builder.use;
this.settings = builder.settings;
+ this.modules = builder.modules;
this.scriptEvaluationTimeout = builder.scriptEvaluationTimeout;
this.globalBindings = builder.globalBindings;
this.enabledPlugins = builder.enabledPlugins;
- this.scriptEngines = createScriptEngines();
+
+ this.manager = new CachedGremlinScriptEngineManager();
+ initializeGremlinScriptEngineManager();
+
+ // this is temporary so that we can have backward compatibilty to the old plugin system and ScriptEngines
+ // approach to configuring Gremlin Server and GremlinExecutor. This code/check should be removed with the
+ // deprecated code around this is removed.
+ if (!useGremlinScriptEngineManager)
+ this.scriptEngines = createScriptEngines();
+ else
+ this.scriptEngines = null;
+
this.suppliedExecutor = suppliedExecutor;
this.suppliedScheduledExecutor = suppliedScheduledExecutor;
}
@@ -284,7 +304,9 @@ public class GremlinExecutor implements AutoCloseable {
logger.debug("Evaluating script - {} - in thread [{}]", script, Thread.currentThread().getName());
- final Object o = scriptEngines.eval(script, bindings, lang);
+ // do this weirdo check until the now deprecated ScriptEngines is gutted
+ final Object o = useGremlinScriptEngineManager ?
+ manager.getEngineByName(lang).eval(script, bindings) : scriptEngines.eval(script, bindings, lang);
// apply a transformation before sending back the result - useful when trying to force serialization
// in the same thread that the eval took place given ThreadLocal nature of graphs as well as some
@@ -396,6 +418,46 @@ public class GremlinExecutor implements AutoCloseable {
return future;
}
+ private void initializeGremlinScriptEngineManager() {
+ this.useGremlinScriptEngineManager = !modules.entrySet().isEmpty();
+
+ for (Map.Entry<String, Map<String, Map<String,Object>>> config : modules.entrySet()) {
+ final String language = config.getKey();
+ final Map<String, Map<String,Object>> moduleConfigs = config.getValue();
+ for (Map.Entry<String, Map<String,Object>> moduleConfig : moduleConfigs.entrySet()) {
+ try {
+ final Class<?> clazz = Class.forName(moduleConfig.getKey());
+ final Method builderMethod = clazz.getMethod("build");
+ Object moduleBuilder = builderMethod.invoke(null);
+
+ final Class<?> builderClazz = moduleBuilder.getClass();
+ final Map<String,Object> customizerConfigs = moduleConfig.getValue();
+ final Method[] methods = builderClazz.getMethods();
+ for (Map.Entry<String,Object> customizerConfig : customizerConfigs.entrySet()) {
+ final Method configMethod = Stream.of(methods).filter(m -> m.getName().equals(customizerConfig.getKey())).findFirst()
+ .orElseThrow(() -> new IllegalStateException("Could not find builder method on " + builderClazz.getCanonicalName()));
+ if (null == customizerConfig.getValue())
+ moduleBuilder = configMethod.invoke(moduleBuilder);
+ else
+ moduleBuilder = configMethod.invoke(moduleBuilder, customizerConfig.getValue());
+ }
+
+ try {
+ final Method appliesTo = builderClazz.getMethod("appliesTo");
+ moduleBuilder = appliesTo.invoke(moduleBuilder, language);
+ } catch (NoSuchMethodException ignored) {
+
+ }
+
+ final Method create = builderClazz.getMethod("create");
+ manager.addModule((GremlinModule) create.invoke(moduleBuilder));
+ } catch (Exception ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+ }
+ }
+
private ScriptEngines createScriptEngines() {
// plugins already on the path - ones static to the classpath
final List<GremlinPlugin> globalPlugins = new ArrayList<>();
@@ -490,6 +552,9 @@ public class GremlinExecutor implements AutoCloseable {
public final static class Builder {
private long scriptEvaluationTimeout = 8000;
private Map<String, EngineSettings> settings = new HashMap<>();
+
+ private Map<String, Map<String, Map<String,Object>>> modules = new HashMap<>();
+
private ExecutorService executorService = null;
private ScheduledExecutorService scheduledExecutorService = null;
private Set<String> enabledPlugins = new HashSet<>();
@@ -528,6 +593,11 @@ public class GremlinExecutor implements AutoCloseable {
return this;
}
+ public Builder addModules(final String engineName, final Map<String, Map<String,Object>> modules) {
+ this.modules.put(engineName, modules);
+ return this;
+ }
+
/**
* Bindings to apply to every script evaluated. Note that the entries of the supplied {@code Bindings} object
* will be copied into a newly created {@link org.apache.tinkerpop.gremlin.jsr223.ConcurrentBindings} object
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ddafaef9/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 3984dbb..6911419 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
@@ -64,7 +64,9 @@ import java.util.stream.Stream;
* Holds a batch of the configured {@code ScriptEngine} objects for the server.
*
* @author Stephen Mallette (http://stephen.genoprime.com)
+ * @deprecated As of release 3.2.4, not directly replaced - see {@link GremlinScriptEngineManager}.
*/
+@Deprecated
public class ScriptEngines implements AutoCloseable {
private static final Logger logger = LoggerFactory.getLogger(ScriptEngines.class);
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ddafaef9/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/Settings.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/Settings.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/Settings.java
index 4bb2089..a8395bb 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/Settings.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/Settings.java
@@ -20,6 +20,8 @@ package org.apache.tinkerpop.gremlin.server;
import io.netty.handler.ssl.SslContext;
import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
+import org.apache.tinkerpop.gremlin.jsr223.GremlinModule;
+import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.server.auth.AllowAllAuthenticator;
@@ -258,6 +260,7 @@ public class Settings {
scriptEngineSettingsDescription.putListPropertyType("staticImports", String.class);
scriptEngineSettingsDescription.putListPropertyType("scripts", String.class);
scriptEngineSettingsDescription.putMapPropertyType("config", String.class, Object.class);
+ scriptEngineSettingsDescription.putMapPropertyType("modules", String.class, Object.class);
constructor.addTypeDescription(scriptEngineSettingsDescription);
final TypeDescription sslSettings = new TypeDescription(SslSettings.class);
@@ -336,6 +339,11 @@ public class Settings {
* {@code ScriptEngine} implementation being used.
*/
public Map<String, Object> config = null;
+
+ /**
+ * A set of configurations for {@link GremlinModule} instances to apply to this {@link GremlinScriptEngine}.
+ */
+ public Map<String,Map<String,Object>> modules = new HashMap<>();
}
/**
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ddafaef9/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/ServerGremlinExecutor.java
----------------------------------------------------------------------
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 c8870ed..eda1d8d 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,7 @@
package org.apache.tinkerpop.gremlin.server.util;
import org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor;
+import org.apache.tinkerpop.gremlin.jsr223.ImportGremlinModule;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
import org.apache.tinkerpop.gremlin.server.Channelizer;
import org.apache.tinkerpop.gremlin.server.GraphManager;
@@ -28,7 +29,9 @@ import org.apache.tinkerpop.gremlin.structure.Graph;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -127,10 +130,27 @@ public class ServerGremlinExecutor<T extends ScheduledExecutorService> {
.scheduledExecutorService(this.scheduledExecutorService);
settings.scriptEngines.forEach((k, v) -> {
- // make sure that server related classes are available at init
- v.imports.add(LifeCycleHook.class.getCanonicalName());
- v.imports.add(LifeCycleHook.Context.class.getCanonicalName());
- gremlinExecutorBuilder.addEngineSettings(k, v.imports, v.staticImports, v.scripts, v.config);
+ // use modules if they are present and the old approach if not
+ if (v.modules.isEmpty()) {
+ // make sure that server related classes are available at init - ultimately this body of code will be
+ // deleted when deprecation is removed
+ v.imports.add(LifeCycleHook.class.getCanonicalName());
+ v.imports.add(LifeCycleHook.Context.class.getCanonicalName());
+ gremlinExecutorBuilder.addEngineSettings(k, v.imports, v.staticImports, v.scripts, v.config);
+ } else {
+ // make sure that server related classes are available at init - ultimately this is the right way to
+ // do things going forward.
+ // TODO: though this Import is kinda sketchy.
+ if (v.modules.containsKey(ImportGremlinModule.class.getName())) {
+ final List<String> listToAddImportsTo = (List<String>) v.modules.get(ImportGremlinModule.class.getName()).get("classImports");
+ listToAddImportsTo.addAll(Arrays.asList(LifeCycleHook.class.getName(), LifeCycleHook.Context.class.getName()));
+ } else {
+ final Map<String,Object> imports = new HashMap<>();
+ imports.put("classImports", Arrays.asList(LifeCycleHook.class.getName(), LifeCycleHook.Context.class.getName()));
+ v.modules.put(ImportGremlinModule.class.getName(), imports);
+ }
+ gremlinExecutorBuilder.addModules(k, v.modules);
+ }
});
gremlinExecutor = gremlinExecutorBuilder.create();