From ddafaef99a7a4dbf7d434b845f43e24e14deea19 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Thu, 17 Nov 2016 16:47:31 -0500 Subject: [PATCH 01/45] 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. --- .../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 +++++++ .../jsr223/ImportGremlinModuleTest.java | 149 +++++++++++++++ .../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(-) create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/AbstractGremlinModule.java create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinModule.java create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptCustomizer.java create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileModule.java create mode 100644 gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinModuleTest.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 00000000000..36104f6de5d --- /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 appliesTo; + + public AbstractGremlinModule(final String moduleName, final Set appliesTo, final Customizer... customizers) { + this.moduleName = moduleName; + this.appliesTo = appliesTo; + this.customizers = customizers; + } + + @Override + public String getName() { + return moduleName; + } + + @Override + public Optional getCustomizers(final String scriptEngineName) { + return null == scriptEngineName || appliesTo.isEmpty() || appliesTo.contains(scriptEngineName) ? + Optional.of(customizers) : Optional.empty(); + } +} 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 dfe9f933a1f..f1fdbe45693 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 @@ * * @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 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 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(); + } } } 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 6070839a9c1..2f0e08c3a39 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 @@ */ 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()) 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 00000000000..9fcbbce7b79 --- /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 classImports = new HashSet<>(); + private Set methodImports = new HashSet<>(); + private Set enumImports = new HashSet<>(); + private final Set 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 scriptEngineName) { + this.appliesTo.addAll(scriptEngineName); + return this; + } + + public Builder classImports(final Collection 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 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 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 allEnums(final Class clazz) { + return Stream.of(clazz.getEnumConstants()).map(e -> (Enum) e).collect(Collectors.toList()); + } + + private static List 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 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; + } + } +} 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 00000000000..28603df2aaa --- /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> scripts; + + public ScriptCustomizer(final Set 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> scripts) { + this.scripts = scripts; + } + + public Collection> scripts() { + return scripts; + } +} 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 00000000000..b5c0d0c8bab --- /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 appliesTo = new HashSet<>(); + private Set 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 scriptEngineName) { + this.appliesTo.addAll(scriptEngineName); + return this; + } + + public Builder files(final Set 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); + } + } +} 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 00000000000..ce62357971f --- /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())) + + ')'; + } +} 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 cc30f993804..90a3b43b7c6 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.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 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.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 settings; + private final Map>> modules; private final long scriptEvaluationTimeout; private final Bindings globalBindings; private final List> use; @@ -92,6 +99,7 @@ public class GremlinExecutor implements AutoCloseable { private final Set 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 @@ private GremlinExecutor(final Builder builder, final boolean suppliedExecutor, 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 CompletableFuture eval(final String script, final String language 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 CompletableFuture closeAsync() throws Exception { return future; } + private void initializeGremlinScriptEngineManager() { + this.useGremlinScriptEngineManager = !modules.entrySet().isEmpty(); + + for (Map.Entry>> config : modules.entrySet()) { + final String language = config.getKey(); + final Map> moduleConfigs = config.getValue(); + for (Map.Entry> 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 customizerConfigs = moduleConfig.getValue(); + final Method[] methods = builderClazz.getMethods(); + for (Map.Entry 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 globalPlugins = new ArrayList<>(); @@ -490,6 +552,9 @@ public static Builder build(final String engineName, final List imports, public final static class Builder { private long scriptEvaluationTimeout = 8000; private Map settings = new HashMap<>(); + + private Map>> modules = new HashMap<>(); + private ExecutorService executorService = null; private ScheduledExecutorService scheduledExecutorService = null; private Set enabledPlugins = new HashSet<>(); @@ -528,6 +593,11 @@ public Builder addEngineSettings(final String engineName, final List imp return this; } + public Builder addModules(final String engineName, final Map> 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 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 3984dbb85cd..69114198a0d 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 @@ * 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); 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 4bb20892ef9..a8395bb581f 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 @@ 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 static Settings read(final InputStream stream) { 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 static class ScriptEngineSettings { * {@code ScriptEngine} implementation being used. */ public Map config = null; + + /** + * A set of configurations for {@link GremlinModule} instances to apply to this {@link GremlinScriptEngine}. + */ + public Map> modules = new HashMap<>(); } /** 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 c8870ed7d49..eda1d8d08dd 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.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 ServerGremlinExecutor(final Settings settings, final ExecutorService grem .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 listToAddImportsTo = (List) v.modules.get(ImportGremlinModule.class.getName()).get("classImports"); + listToAddImportsTo.addAll(Arrays.asList(LifeCycleHook.class.getName(), LifeCycleHook.Context.class.getName())); + } else { + final Map 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(); From 42194b8de19d74253277396099af8780052ea034 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Fri, 18 Nov 2016 05:34:00 -0500 Subject: [PATCH 02/45] TINKERPOP-1562 More deprecation around methods on building GremlinExecutor that used ScriptEngines. --- .../groovy/engine/GremlinExecutor.java | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) 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 90a3b43b7c6..5f7ab0981ba 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 @@ -83,7 +83,7 @@ public class GremlinExecutor implements AutoCloseable { */ private ScriptEngines scriptEngines; - private GremlinScriptEngineManager manager; + private GremlinScriptEngineManager gremlinScriptEngineManager; private final Map settings; private final Map>> modules; @@ -117,7 +117,7 @@ private GremlinExecutor(final Builder builder, final boolean suppliedExecutor, this.globalBindings = builder.globalBindings; this.enabledPlugins = builder.enabledPlugins; - this.manager = new CachedGremlinScriptEngineManager(); + this.gremlinScriptEngineManager = new CachedGremlinScriptEngineManager(); initializeGremlinScriptEngineManager(); // this is temporary so that we can have backward compatibilty to the old plugin system and ScriptEngines @@ -306,7 +306,7 @@ public CompletableFuture eval(final String script, final String language // 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); + gremlinScriptEngineManager.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 @@ -349,10 +349,18 @@ public CompletableFuture eval(final String script, final String language return evaluationFuture; } + /** + * @deprecated As of release 3.2.4, replaced by {@link #getScriptEngineManager()}. + */ + @Deprecated public ScriptEngines getScriptEngines() { return this.scriptEngines; } + public GremlinScriptEngineManager getScriptEngineManager() { + return this.gremlinScriptEngineManager; + } + public ExecutorService getExecutorService() { return executorService; } @@ -450,7 +458,7 @@ private void initializeGremlinScriptEngineManager() { } final Method create = builderClazz.getMethod("create"); - manager.addModule((GremlinModule) create.invoke(moduleBuilder)); + gremlinScriptEngineManager.addModule((GremlinModule) create.invoke(moduleBuilder)); } catch (Exception ex) { throw new IllegalStateException(ex); } @@ -542,6 +550,8 @@ public static Builder build() { /** * Create a {@code Builder} and specify the first ScriptEngine to be included. + * + * @deprecated As of release 3.2.4, replaced by {@link #build()}. */ public static Builder build(final String engineName, final List imports, final List staticImports, final List scripts, @@ -580,7 +590,10 @@ private Builder() { * @param staticImports A list of static imports for the engine. * @param scripts A list of scripts to execute in the engine to initialize it. * @param config Custom configuration map for the ScriptEngine + * + * @deprecated As of release 3.2.4, replaced by {@link #addModules(String, Map)}. */ + @Deprecated public Builder addEngineSettings(final String engineName, final List imports, final List staticImports, final List scripts, final Map config) { @@ -593,6 +606,12 @@ public Builder addEngineSettings(final String engineName, final List imp return this; } + /** + * Add a configuration for a {@link GremlinModule} to the executor. The key is the fully qualified class name + * of the {@link GremlinModule} instance and the value is a map of configuration values. In that map, the key + * is the name of a builder method on the {@link GremlinModule} and the value is some argument to pass to that + * method. + */ public Builder addModules(final String engineName, final Map> modules) { this.modules.put(engineName, modules); return this; @@ -623,7 +642,10 @@ public Builder scriptEvaluationTimeout(final long scriptEvaluationTimeout) { /** * Replaces any settings provided. + * + * @deprecated As of release 3.2.4, replaced by {@link #addModules(String, Map)}. */ + @Deprecated public Builder engineSettings(final Map settings) { this.settings = settings; return this; @@ -681,7 +703,10 @@ public Builder afterFailure(final BiConsumer afterFailure) /** * A set of maven coordinates for dependencies to be applied for the script engine instances. + * + * @deprecated As of release 3.2.4, not replaced. */ + @Deprecated public Builder use(final List> use) { this.use = use; return this; @@ -689,7 +714,11 @@ public Builder use(final List> use) { /** * Set of the names of plugins that should be enabled for the engine. + * + * @deprecated As of release 3.2.4, replaced by {@link #addModules(String, Map)} though behavior is not quite + * the same. */ + @Deprecated public Builder enabledPlugins(final Set enabledPlugins) { this.enabledPlugins = enabledPlugins; return this; From 9f65e6aa3d32eab2ef76bb7e3ae1f60b8332268f Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Mon, 21 Nov 2016 07:36:40 -0500 Subject: [PATCH 03/45] TINKERPOP-1562 Add GreminServerGremlinModule for Gremlin Server specific imports Allow use of instance() as a way for GremlinModules to be instantiated. --- .../gremlin/jsr223/AbstractGremlinModule.java | 10 ++++ .../gremlin/jsr223/CoreGremlinModule.java | 18 ------- .../groovy/engine/GremlinExecutor.java | 49 +++++++++++-------- .../util/GremlinServerGremlinModule.java | 41 ++++++++++++++++ .../server/util/ServerGremlinExecutor.java | 13 +---- 5 files changed, 81 insertions(+), 50 deletions(-) create mode 100644 gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/GremlinServerGremlinModule.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 index 36104f6de5d..4b9cd2c6ac7 100644 --- 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 @@ -18,6 +18,7 @@ */ package org.apache.tinkerpop.gremlin.jsr223; +import java.util.Collections; import java.util.Optional; import java.util.Set; @@ -29,6 +30,15 @@ public abstract class AbstractGremlinModule implements GremlinModule { protected final Customizer[] customizers; protected final Set appliesTo; + /** + * Creates a base {@link GremlinModule} that will apply to all {@link GremlinScriptEngine} instances. + */ + public AbstractGremlinModule(final String moduleName, final Customizer... customizers) { + this(moduleName, Collections.emptySet(), customizers); + } + /** + * Creates a base {@link GremlinModule} that will apply to specific {@link GremlinScriptEngine} instances. + */ public AbstractGremlinModule(final String moduleName, final Set appliesTo, final Customizer... customizers) { this.moduleName = moduleName; this.appliesTo = appliesTo; 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 f1fdbe45693..ff477672dbf 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 @@ -37,7 +37,6 @@ public final class CoreGremlinModule implements GremlinModule { .addMethodImports(CoreImports.getMethodImports()).create(); 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. @@ -60,21 +59,4 @@ public Optional getCustomizers(final String scriptEngineName) { public String getName() { 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(); - } - } } 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 5f7ab0981ba..1ad41c76714 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 @@ -435,30 +435,37 @@ private void initializeGremlinScriptEngineManager() { for (Map.Entry> 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 customizerConfigs = moduleConfig.getValue(); - final Method[] methods = builderClazz.getMethods(); - for (Map.Entry 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()); - } + // first try instance() and if that fails try to use build() try { - final Method appliesTo = builderClazz.getMethod("appliesTo"); - moduleBuilder = appliesTo.invoke(moduleBuilder, language); - } catch (NoSuchMethodException ignored) { - + final Method instanceMethod = clazz.getMethod("instance"); + gremlinScriptEngineManager.addModule((GremlinModule) instanceMethod.invoke(null)); + } catch (Exception ex) { + final Method builderMethod = clazz.getMethod("build"); + Object moduleBuilder = builderMethod.invoke(null); + + final Class builderClazz = moduleBuilder.getClass(); + final Map customizerConfigs = moduleConfig.getValue(); + final Method[] methods = builderClazz.getMethods(); + for (Map.Entry 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"); + gremlinScriptEngineManager.addModule((GremlinModule) create.invoke(moduleBuilder)); } - - final Method create = builderClazz.getMethod("create"); - gremlinScriptEngineManager.addModule((GremlinModule) create.invoke(moduleBuilder)); } catch (Exception ex) { throw new IllegalStateException(ex); } diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/GremlinServerGremlinModule.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/GremlinServerGremlinModule.java new file mode 100644 index 00000000000..863c640533e --- /dev/null +++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/GremlinServerGremlinModule.java @@ -0,0 +1,41 @@ +/* + * 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.util; + +import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinModule; +import org.apache.tinkerpop.gremlin.jsr223.GremlinModule; +import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; + +/** + * A {@link GremlinModule} implementation that adds Gremlin Server specific classes to the imports. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public final class GremlinServerGremlinModule extends AbstractGremlinModule { + private static final String MODULE_NAME = "tinkerpop.server"; + private static final GremlinServerGremlinModule instance = new GremlinServerGremlinModule(); + + public GremlinServerGremlinModule() { + super(MODULE_NAME, ImportCustomizer.build().addClassImports(LifeCycleHook.class, LifeCycleHook.Context.class).create()); + } + + public static GremlinServerGremlinModule instance() { + return instance; + } +} 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 eda1d8d08dd..bb6c9cebffe 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 @@ -138,17 +138,8 @@ public ServerGremlinExecutor(final Settings settings, final ExecutorService grem 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 listToAddImportsTo = (List) v.modules.get(ImportGremlinModule.class.getName()).get("classImports"); - listToAddImportsTo.addAll(Arrays.asList(LifeCycleHook.class.getName(), LifeCycleHook.Context.class.getName())); - } else { - final Map imports = new HashMap<>(); - imports.put("classImports", Arrays.asList(LifeCycleHook.class.getName(), LifeCycleHook.Context.class.getName())); - v.modules.put(ImportGremlinModule.class.getName(), imports); - } + // make sure that server related classes are available at init - new approach. the LifeCycleHook stuff + // will be added explicitly via configuration using GremlinServerGremlinModule in the yaml gremlinExecutorBuilder.addModules(k, v.modules); } }); From 299ad1c778657e4b80f36218f70ce6c998164070 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Mon, 21 Nov 2016 07:54:35 -0500 Subject: [PATCH 04/45] TINKERPOP-1562 Add GremlinModule for Giraph. --- .../giraph/jsr223/GiraphGremlinModule.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/jsr223/GiraphGremlinModule.java diff --git a/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/jsr223/GiraphGremlinModule.java b/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/jsr223/GiraphGremlinModule.java new file mode 100644 index 00000000000..69f3586146a --- /dev/null +++ b/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/jsr223/GiraphGremlinModule.java @@ -0,0 +1,58 @@ +/* + * 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.giraph.jsr223; + +import org.apache.tinkerpop.gremlin.giraph.process.computer.EmptyOutEdges; +import org.apache.tinkerpop.gremlin.giraph.process.computer.GiraphComputation; +import org.apache.tinkerpop.gremlin.giraph.process.computer.GiraphGraphComputer; +import org.apache.tinkerpop.gremlin.giraph.process.computer.GiraphMemory; +import org.apache.tinkerpop.gremlin.giraph.process.computer.GiraphMessageCombiner; +import org.apache.tinkerpop.gremlin.giraph.process.computer.GiraphMessenger; +import org.apache.tinkerpop.gremlin.giraph.process.computer.GiraphVertex; +import org.apache.tinkerpop.gremlin.giraph.process.computer.GiraphWorkerContext; +import org.apache.tinkerpop.gremlin.giraph.process.computer.MemoryAggregator; +import org.apache.tinkerpop.gremlin.giraph.process.computer.PassThroughMemory; +import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinModule; +import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public final class GiraphGremlinModule extends AbstractGremlinModule { + private static final String MODULE_NAME = "tinkerpop.giraph"; + private static final GiraphGremlinModule instance = new GiraphGremlinModule(); + + private GiraphGremlinModule() { + super(MODULE_NAME, ImportCustomizer.build().addClassImports( + EmptyOutEdges.class, + GiraphComputation.class, + GiraphGraphComputer.class, + GiraphMemory.class, + GiraphMessageCombiner.class, + GiraphMessenger.class, + GiraphVertex.class, + GiraphWorkerContext.class, + MemoryAggregator.class, + PassThroughMemory.class).create()); + } + + public static GiraphGremlinModule instance() { + return instance; + } +} \ No newline at end of file From a596da3490f271ca6787d3c10444b6b2f099bb4d Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Mon, 21 Nov 2016 07:55:27 -0500 Subject: [PATCH 05/45] TINKERPOP-1562 Moved GremlinServerGremlinModule to standard directory structure --- .../server/{util => jsr223}/GremlinServerGremlinModule.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/{util => jsr223}/GremlinServerGremlinModule.java (91%) diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/GremlinServerGremlinModule.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/jsr223/GremlinServerGremlinModule.java similarity index 91% rename from gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/GremlinServerGremlinModule.java rename to gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/jsr223/GremlinServerGremlinModule.java index 863c640533e..b161f699ded 100644 --- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/GremlinServerGremlinModule.java +++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/jsr223/GremlinServerGremlinModule.java @@ -16,11 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.tinkerpop.gremlin.server.util; +package org.apache.tinkerpop.gremlin.server.jsr223; import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinModule; import org.apache.tinkerpop.gremlin.jsr223.GremlinModule; import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; +import org.apache.tinkerpop.gremlin.server.util.LifeCycleHook; /** * A {@link GremlinModule} implementation that adds Gremlin Server specific classes to the imports. @@ -31,7 +32,7 @@ public final class GremlinServerGremlinModule extends AbstractGremlinModule { private static final String MODULE_NAME = "tinkerpop.server"; private static final GremlinServerGremlinModule instance = new GremlinServerGremlinModule(); - public GremlinServerGremlinModule() { + private GremlinServerGremlinModule() { super(MODULE_NAME, ImportCustomizer.build().addClassImports(LifeCycleHook.class, LifeCycleHook.Context.class).create()); } From 8ee22256e9d833d00624666998376b7b6ba14a94 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Mon, 21 Nov 2016 10:56:21 -0500 Subject: [PATCH 06/45] TINKERPOP-1562 Deprecated GiraphGremlinPlugin. --- .../gremlin/giraph/groovy/plugin/GiraphGremlinPlugin.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/groovy/plugin/GiraphGremlinPlugin.java b/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/groovy/plugin/GiraphGremlinPlugin.java index ba882f5ea44..29f2455d9dd 100644 --- a/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/groovy/plugin/GiraphGremlinPlugin.java +++ b/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/groovy/plugin/GiraphGremlinPlugin.java @@ -19,6 +19,7 @@ package org.apache.tinkerpop.gremlin.giraph.groovy.plugin; +import org.apache.tinkerpop.gremlin.giraph.jsr223.GiraphGremlinModule; import org.apache.tinkerpop.gremlin.giraph.process.computer.GiraphGraphComputer; import org.apache.tinkerpop.gremlin.groovy.plugin.AbstractGremlinPlugin; import org.apache.tinkerpop.gremlin.groovy.plugin.IllegalEnvironmentException; @@ -32,7 +33,9 @@ /** * @author Marko A. Rodriguez (http://markorodriguez.com) + * @deprecated As of release 3.2.4, replaced by {@link GiraphGremlinModule}. */ +@Deprecated public final class GiraphGremlinPlugin extends AbstractGremlinPlugin { protected static String NAME = "tinkerpop.giraph"; From d7d6d5b2b8788fa420223d8986c27c535255494c Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Mon, 21 Nov 2016 13:13:30 -0500 Subject: [PATCH 07/45] TINKERPOP-1562 Move RemoteAcceptor related stuff to gremlin-core. --- .../console/jsr223/GephiRemoteAcceptor.groovy | 372 ++++++++++++++++++ .../console/plugin/GephiRemoteAcceptor.groovy | 1 + .../gremlin/jsr223/GremlinModule.java | 9 + .../gremlin/jsr223/ImportGremlinModule.java | 1 - .../gremlin/jsr223/RemoteAcceptor.java | 81 ++++ .../gremlin/jsr223/RemoteException.java | 40 ++ .../gremlin/groovy/plugin/RemoteAcceptor.java | 2 + .../groovy/plugin/RemoteException.java | 2 + 8 files changed, 507 insertions(+), 1 deletion(-) create mode 100644 gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/RemoteAcceptor.java create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/RemoteException.java diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy new file mode 100644 index 00000000000..11e1e5c7a86 --- /dev/null +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy @@ -0,0 +1,372 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.console.jsr223 + +import groovy.json.JsonOutput +import groovy.json.JsonSlurper +import groovy.transform.CompileStatic +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.client.methods.HttpUriRequest +import org.apache.http.client.methods.RequestBuilder +import org.apache.http.entity.StringEntity +import org.apache.http.impl.client.CloseableHttpClient +import org.apache.http.impl.client.HttpClients +import org.apache.http.util.EntityUtils +import org.apache.tinkerpop.gremlin.console.plugin.GephiTraversalVisualizationStrategy +import org.apache.tinkerpop.gremlin.jsr223.RemoteAcceptor +import org.apache.tinkerpop.gremlin.jsr223.RemoteException +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource +import org.apache.tinkerpop.gremlin.structure.Edge +import org.apache.tinkerpop.gremlin.structure.Graph +import org.apache.tinkerpop.gremlin.structure.Vertex +import org.codehaus.groovy.tools.shell.Groovysh +import org.codehaus.groovy.tools.shell.IO + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + * @author Randall Barnhart (randompi@gmail.com) + */ +class GephiRemoteAcceptor implements RemoteAcceptor { + + private String host = "localhost" + private int port = 8080 + private String workspace = "workspace1" + + private final Groovysh shell + private final IO io + + private final Random rand = new Random(); + boolean traversalSubmittedForViz = false + long vizStepDelay + private float[] vizStartRGBColor + private float[] vizDefaultRGBColor + private char vizColorToFade + private float vizColorFadeRate + private float vizStartSize + private float vizSizeDecrementRate + private Map vertexAttributes = [:] + + private CloseableHttpClient httpclient = HttpClients.createDefault(); + + public GephiRemoteAcceptor(final Groovysh shell, final IO io) { + this.shell = shell + this.io = io + + // traversal visualization defaults + vizStepDelay = 1000; // 1 second pause between viz of steps + vizStartRGBColor = [0.0f, 1.0f, 0.5f] // light aqua green + vizDefaultRGBColor = [0.6f, 0.6f, 0.6f] // light grey + vizColorToFade = 'g' // will fade so blue is strongest + vizColorFadeRate = 0.7 // the multiplicative rate to fade visited vertices + vizStartSize = 10 + vizSizeDecrementRate = 0.33f + } + + @Override + connect(final List args) throws RemoteException { + if (args.size() >= 1) + workspace = args[0] + + if (args.size() >= 2) + host = args[1] + + if (args.size() >= 3) { + try { + port = Integer.parseInt(args[2]) + } catch (Exception ex) { + throw new RemoteException("Port must be an integer value") + } + } + + def vizConfig = " with stepDelay:$vizStepDelay, startRGBColor:$vizStartRGBColor, " + + "colorToFade:$vizColorToFade, colorFadeRate:$vizColorFadeRate, startSize:$vizStartSize," + + "sizeDecrementRate:$vizSizeDecrementRate" + + return "Connection to Gephi - http://$host:$port/$workspace" + vizConfig + } + + @Override + Object configure(final List args) throws RemoteException { + if (args.size() < 2) + throw new RemoteException("Invalid config arguments - check syntax") + + if (args[0] == "host") + host = args[1] + else if (args[0] == "port") { + try { + port = Integer.parseInt(args[1]) + } catch (Exception ignored) { + throw new RemoteException("Port must be an integer value") + } + } else if (args[0] == "workspace") + workspace = args[1] + else if (args[0] == "stepDelay") + parseVizStepDelay(args[1]) + else if (args[0] == "startRGBColor") + parseVizStartRGBColor(args[1]) + else if (args[0] == "colorToFade") + parseVizColorToFade(args[1]) + else if (args[0] == "colorFadeRate") + parseVizColorFadeRate(args[1]) + else if (args[0] == "sizeDecrementRate") + parseVizSizeDecrementRate(args[1]) + else if (args[0] == "startSize") + parseVizStartSize(args[1]) + else if (args[0] == "visualTraversal") { + def graphVar = shell.interp.context.getVariable(args[1]) + if (!(graphVar instanceof Graph)) + throw new RemoteException("Invalid argument to 'visualTraversal' - first parameter must be a Graph instance") + + def gVar = args.size() == 3 ? args[2] : "vg" + def theG = GraphTraversalSource.build().with(new GephiTraversalVisualizationStrategy(this)).create(graphVar) + shell.interp.context.setVariable(gVar, theG) + } else + throw new RemoteException("Invalid config arguments - check syntax") + + return "Connection to Gephi - http://$host:$port/$workspace" + + " with stepDelay:$vizStepDelay, startRGBColor:$vizStartRGBColor, " + + "colorToFade:$vizColorToFade, colorFadeRate:$vizColorFadeRate, startSize:$vizStartSize," + + "sizeDecrementRate:$vizSizeDecrementRate" + } + + @Override + @CompileStatic + Object submit(final List args) throws RemoteException { + final String line = String.join(" ", args) + if (line.trim() == "clear") { + clearGraph() + io.out.println("Gephi workspace cleared") + return + } + + // need to clear the vertex attributes + vertexAttributes.clear() + + // this tells the GraphTraversalVisualizationStrategy that if the line eval's to a traversal it should + // try to visualize it + traversalSubmittedForViz = true + + // get the colors/sizes back to basics before trying visualize + resetColorsSizes() + + final Object o = shell.execute(line) + if (o instanceof Graph) { + clearGraph() + def graph = (Graph) o + def g = graph.traversal() + g.V().sideEffect { addVertexToGephi(g, it.get()) }.iterate() + } + + traversalSubmittedForViz = false + } + + @Override + void close() throws IOException { + httpclient.close() + } + + /** + * Visits the last set of vertices traversed and degrades their color and size. + */ + def updateVisitedVertices(def List except = []) { + vertexAttributes.keySet().findAll{ vertexId -> !except.contains(vertexId) }.each { String vertexId -> + def attrs = vertexAttributes[vertexId] + float currentColor = attrs.color + currentColor *= vizColorFadeRate + + int currentSize = attrs["size"] + currentSize = Math.max(vizStartSize, currentSize - (currentSize * vizSizeDecrementRate)) + + vertexAttributes.get(vertexId).color = currentColor + vertexAttributes.get(vertexId).size = currentSize + + changeVertexAttributes(vertexId) + } + } + + def changeVertexAttributes(def String vertexId) { + def props = [:] + props.put(vizColorToFade.toString(), vertexAttributes[vertexId].color) + props.put("size", vertexAttributes[vertexId].size) + updateGephiGraph([cn: [(vertexId): props]]) + } + + /** + * Visit a vertex traversed and initialize its color and size. + */ + def visitVertexInGephi(def Vertex v) { + def props = [:] + props.put('r', vizStartRGBColor[0]) + props.put('g', vizStartRGBColor[1]) + props.put('b', vizStartRGBColor[2]) + props.put('size', vizStartSize * 2.5) + props.put('visited', 1) + + updateGephiGraph([cn: [(v.id().toString()): props]]) + + vertexAttributes[v.id().toString()] = [color: vizStartRGBColor[fadeColorIndex()], size: vizStartSize * 2.5, touch: 1] + } + + def fadeColorIndex() { + if (vizColorToFade == 'r') + return 0 + else if (vizColorToFade == 'g') + return 1 + else if (vizColorToFade == 'b') + return 2 + } + + def addVertexToGephi(def GraphTraversalSource g, def Vertex v, def boolean ignoreEdges = false) { + // grab the first property value from the strategies of values + def props = g.V(v).valueMap().next().collectEntries { kv -> [(kv.key): kv.value[0]] } + props << [label: v.label()] + props.put('r', vizDefaultRGBColor[0]) + props.put('g', vizDefaultRGBColor[1]) + props.put('b', vizDefaultRGBColor[2]) + props.put('x', rand.nextFloat()) + props.put('y', rand.nextFloat()) + props.put('size', 10) + props.put('visited', 0) + + // only add if it does not exist in graph already + if (!getFromGephiGraph([operation: "getNode", id: v.id().toString()]).isPresent()) + updateGephiGraph([an: [(v.id().toString()): props]]) + + if (!ignoreEdges) { + g.V(v).outE().sideEffect { + addEdgeToGephi(g, it.get()) + }.iterate() + } + } + + @CompileStatic + def addEdgeToGephi(def GraphTraversalSource g, def Edge e) { + def props = g.E(e).valueMap().next() + props.put('label', e.label()) + props.put('source', e.outVertex().id().toString()) + props.put('target', e.inVertex().id().toString()) + props.put('directed', true) + props.put('visited', 0) + + // make sure the in vertex is there but don't add its edges - that will happen later as we are looping + // all vertices in the graph + addVertexToGephi(g, e.inVertex(), true) + + // both vertices are definitely there now, so add the edge + updateGephiGraph([ae: [(e.id().toString()): props]]) + } + + def clearGraph() { + updateGephiGraph([dn: [filter: "ALL"]]) + } + + def resetColorsSizes() { + updateGephiGraph([cn: [filter: [nodeAttribute: [attribute: "visited", value: 1]], + attributes: [size: 1, r: vizDefaultRGBColor[0], g: vizDefaultRGBColor[1], b: vizDefaultRGBColor[2]]]]) + updateGephiGraph([ce: [filter: [edgeAttribute: [attribute: "visited", value: 1]], + attributes: [size: 1, r: vizDefaultRGBColor[0], g: vizDefaultRGBColor[1], b: vizDefaultRGBColor[2]]]]) + } + + def getFromGephiGraph(def Map queryArgs) { + def requestBuilder = RequestBuilder.get("http://$host:$port/$workspace") + queryArgs.each { requestBuilder = requestBuilder.addParameter(it.key, it.value) } + + def httpResp = makeRequest(requestBuilder.build()) + def resp = EntityUtils.toString(httpResp.entity) + + // gephi streaming plugin does not set the content type or respect the Accept header - treat as text + if (resp.isEmpty()) + return Optional.empty() + else + return Optional.of(new JsonSlurper().parseText(resp)) + } + + def updateGephiGraph(def Map postBody) { + def requestBuilder = RequestBuilder.post("http://$host:$port/$workspace") + .addParameter("format", "JSON") + .addParameter("operation", "updateGraph") + .setEntity(new StringEntity(JsonOutput.toJson(postBody))) + EntityUtils.consume(makeRequest(requestBuilder.build()).entity) + } + + private CloseableHttpResponse makeRequest(HttpUriRequest request) { + def httpResp = httpclient.execute(request) + if (httpResp.getStatusLine().getStatusCode() == 200) { + return httpResp + } else { + def resp = EntityUtils.toString(httpResp.entity) + throw new RuntimeException("Unsuccessful request to Gephi - [${httpResp.getStatusLine().getStatusCode()}] ${httpResp.getStatusLine().getReasonPhrase()} - $resp") + } + } + + @Override + public String toString() { + return "Gephi - [$workspace]" + } + + private void parseVizStepDelay(String arg) { + try { + vizStepDelay = Long.parseLong(arg) + } catch (Exception ignored) { + throw new RemoteException("The stepDelay must be a long value") + } + } + + private void parseVizStartRGBColor(String arg) { + try { + vizStartRGBColor = arg[1..-2].tokenize(',')*.toFloat() + assert (vizStartRGBColor.length == 3) + } catch (Exception ignored) { + throw new RemoteException("The vizStartRGBColor must be an array of 3 float values, e.g. [0.0,1.0,0.5]") + } + } + + private void parseVizColorToFade(String arg) { + try { + vizColorToFade = arg.charAt(0).toLowerCase(); + assert (vizColorToFade == 'r' || vizColorToFade == 'g' || vizColorToFade == 'b') + } catch (Exception ignored) { + throw new RemoteException("The vizColorToFade must be one character value among: r, g, b, R, G, B") + } + } + + private void parseVizColorFadeRate(String arg) { + try { + vizColorFadeRate = Float.parseFloat(arg) + } catch (Exception ignored) { + throw new RemoteException("The colorFadeRate must be a float value") + } + } + + private void parseVizSizeDecrementRate(String arg) { + try { + vizSizeDecrementRate = Float.parseFloat(arg) + } catch (Exception ignored) { + throw new RemoteException("The sizeDecrementRate must be a float value") + } + } + + private void parseVizStartSize(String arg) { + try { + vizStartSize = Float.parseFloat(arg) + } catch (Exception ignored) { + throw new RemoteException("The startSize must be a float value") + } + } +} diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/GephiRemoteAcceptor.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/GephiRemoteAcceptor.groovy index 41984440da3..30527a4326e 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/GephiRemoteAcceptor.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/GephiRemoteAcceptor.groovy @@ -40,6 +40,7 @@ import org.codehaus.groovy.tools.shell.IO /** * @author Stephen Mallette (http://stephen.genoprime.com) * @author Randall Barnhart (randompi@gmail.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.console.jsr223.GephiRemoteAcceptor} */ class GephiRemoteAcceptor implements RemoteAcceptor { diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinModule.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinModule.java index 20e9ff86591..1077cf3ace8 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinModule.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinModule.java @@ -58,4 +58,13 @@ public default Optional getCustomizers(){ * @param scriptEngineName The name of the {@code ScriptEngine} or null to get all the available {@code Customizers} */ public Optional getCustomizers(final String scriptEngineName); + + /** + * Allows a plugin to utilize features of the {@code :remote} and {@code :submit} commands of the Gremlin Console. + * This method does not need to be implemented if the plugin is not meant for the Console for some reason or + * if it does not intend to take advantage of those commands. + */ + public default Optional remoteAcceptor() { + return Optional.empty(); + } } 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 index 9fcbbce7b79..e9ca47738ee 100644 --- 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 @@ -23,7 +23,6 @@ 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; diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/RemoteAcceptor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/RemoteAcceptor.java new file mode 100644 index 00000000000..f917c092fb5 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/RemoteAcceptor.java @@ -0,0 +1,81 @@ +/* + * 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.Closeable; +import java.util.List; + +/** + * The Gremlin Console supports the {@code :remote} and {@code :submit} commands which provide standardized ways + * for plugins to provide "remote connections" to resources and a way to "submit" a command to those resources. + * A "remote connection" does not necessarily have to be a remote server. It simply refers to a resource that is + * external to the console. + *

+ * By implementing this interface and returning an instance of it through {@link GremlinModule#remoteAcceptor()} a + * plugin can hook into those commands and provide remoting features. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public interface RemoteAcceptor extends Closeable { + + public static final String RESULT = "result"; + + /** + * Gets called when {@code :remote} is used in conjunction with the "connect" option. It is up to the + * implementation to decide how additional arguments on the line should be treated after "connect". + * + * @return an object to display as output to the user + * @throws RemoteException if there is a problem with connecting + */ + public Object connect(final List args) throws RemoteException; + + /** + * Gets called when {@code :remote} is used in conjunction with the {@code config} option. It is up to the + * implementation to decide how additional arguments on the line should be treated after {@code config}. + * + * @return an object to display as output to the user + * @throws RemoteException if there is a problem with configuration + */ + public Object configure(final List args) throws RemoteException; + + /** + * Gets called when {@code :submit} is executed. It is up to the implementation to decide how additional + * arguments on the line should be treated after {@code :submit}. + * + * @return an object to display as output to the user + * @throws RemoteException if there is a problem with submission + */ + public Object submit(final List args) throws RemoteException; + + /** + * If the {@code RemoteAcceptor} is used in the Gremlin Console, then this method might be called to determine + * if it can be used in a fashion that supports the {@code :remote console} command. By default, this value is + * set to {@code false}. + *

+ * A {@code RemoteAcceptor} should only return {@code true} for this method if it expects that all activities it + * supports are executed through the {@code :sumbit} command. If the users interaction with the remote requires + * working with both local and remote evaluation at the same time, it is likely best to keep this method return + * {@code false}. A good example of this type of plugin would be the Gephi Plugin which uses {@code :remote config} + * to configure a local {@code TraversalSource} to be used and expects calls to {@code :submit} for the same body + * of analysis. + */ + public default boolean allowRemoteConsole() { + return false; + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/RemoteException.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/RemoteException.java new file mode 100644 index 00000000000..a75e6d6e578 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/RemoteException.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.jsr223; + +/** + * A mapper {@code Exception} to be thrown when there are problems with processing a command given to a + * {@link RemoteAcceptor}. The message provided to the exception will + * be displayed to the user in the Console. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class RemoteException extends Exception { + public RemoteException(final String message) { + super(message); + } + + public RemoteException(final String message, final Throwable cause) { + super(message, cause); + } + + public RemoteException(final Throwable cause) { + super(cause); + } +} diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/RemoteAcceptor.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/RemoteAcceptor.java index 0cf8bac6d31..b3d03fe8c36 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/RemoteAcceptor.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/RemoteAcceptor.java @@ -33,7 +33,9 @@ * plugin can hook into those commands and provide remoting features. * * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.jsr223.RemoteAcceptor}; */ +@Deprecated public interface RemoteAcceptor extends Closeable { public static final String RESULT = "result"; diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/RemoteException.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/RemoteException.java index 62efda5a5a9..f41d12358d9 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/RemoteException.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/RemoteException.java @@ -24,7 +24,9 @@ * be displayed to the user in the Console. * * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.jsr223.RemoteException}; */ +@Deprecated public class RemoteException extends Exception { public RemoteException(final String message) { super(message); From 68487c2b89fd589256925af2750907ef8da3c07a Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Mon, 21 Nov 2016 13:15:02 -0500 Subject: [PATCH 08/45] TINKERPOP-1562 Renamed GremlinModule to GremlinPlugin in gremlin-core --- .../groovy/plugin/GiraphGremlinPlugin.java | 3 +-- ...inModule.java => GiraphGremlinPlugin.java} | 10 +++++----- ...Module.java => AbstractGremlinPlugin.java} | 10 +++++----- ...mlinModule.java => CoreGremlinPlugin.java} | 8 ++++---- .../DefaultGremlinScriptEngineManager.java | 6 +++--- ...{GremlinModule.java => GremlinPlugin.java} | 2 +- .../jsr223/GremlinScriptEngineManager.java | 4 ++-- ...inModule.java => ImportGremlinPlugin.java} | 8 ++++---- .../gremlin/jsr223/RemoteAcceptor.java | 2 +- ...dule.java => ScriptFileGremlinPlugin.java} | 8 ++++---- ...Test.java => ImportGremlinPluginTest.java} | 20 +++++++++---------- .../groovy/engine/GremlinExecutor.java | 19 +++++++++--------- .../tinkerpop/gremlin/server/Settings.java | 4 ++-- ...e.java => GremlinServerGremlinPlugin.java} | 14 ++++++------- .../server/util/ServerGremlinExecutor.java | 3 --- 15 files changed, 58 insertions(+), 63 deletions(-) rename giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/jsr223/{GiraphGremlinModule.java => GiraphGremlinPlugin.java} (88%) rename gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/{AbstractGremlinModule.java => AbstractGremlinPlugin.java} (85%) rename gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/{CoreGremlinModule.java => CoreGremlinPlugin.java} (89%) rename gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/{GremlinModule.java => GremlinPlugin.java} (98%) rename gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/{ImportGremlinModule.java => ImportGremlinPlugin.java} (97%) rename gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/{ScriptFileModule.java => ScriptFileGremlinPlugin.java} (90%) rename gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/{ImportGremlinModuleTest.java => ImportGremlinPluginTest.java} (91%) rename gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/jsr223/{GremlinServerGremlinModule.java => GremlinServerGremlinPlugin.java} (74%) diff --git a/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/groovy/plugin/GiraphGremlinPlugin.java b/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/groovy/plugin/GiraphGremlinPlugin.java index 29f2455d9dd..80d98f99f28 100644 --- a/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/groovy/plugin/GiraphGremlinPlugin.java +++ b/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/groovy/plugin/GiraphGremlinPlugin.java @@ -19,7 +19,6 @@ package org.apache.tinkerpop.gremlin.giraph.groovy.plugin; -import org.apache.tinkerpop.gremlin.giraph.jsr223.GiraphGremlinModule; import org.apache.tinkerpop.gremlin.giraph.process.computer.GiraphGraphComputer; import org.apache.tinkerpop.gremlin.groovy.plugin.AbstractGremlinPlugin; import org.apache.tinkerpop.gremlin.groovy.plugin.IllegalEnvironmentException; @@ -33,7 +32,7 @@ /** * @author Marko A. Rodriguez (http://markorodriguez.com) - * @deprecated As of release 3.2.4, replaced by {@link GiraphGremlinModule}. + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.giraph.jsr223.GiraphGremlinPlugin}. */ @Deprecated public final class GiraphGremlinPlugin extends AbstractGremlinPlugin { diff --git a/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/jsr223/GiraphGremlinModule.java b/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/jsr223/GiraphGremlinPlugin.java similarity index 88% rename from giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/jsr223/GiraphGremlinModule.java rename to giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/jsr223/GiraphGremlinPlugin.java index 69f3586146a..a96ac86d52c 100644 --- a/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/jsr223/GiraphGremlinModule.java +++ b/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/jsr223/GiraphGremlinPlugin.java @@ -28,17 +28,17 @@ import org.apache.tinkerpop.gremlin.giraph.process.computer.GiraphWorkerContext; import org.apache.tinkerpop.gremlin.giraph.process.computer.MemoryAggregator; import org.apache.tinkerpop.gremlin.giraph.process.computer.PassThroughMemory; -import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinModule; +import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; /** * @author Stephen Mallette (http://stephen.genoprime.com) */ -public final class GiraphGremlinModule extends AbstractGremlinModule { +public final class GiraphGremlinPlugin extends AbstractGremlinPlugin { private static final String MODULE_NAME = "tinkerpop.giraph"; - private static final GiraphGremlinModule instance = new GiraphGremlinModule(); + private static final GiraphGremlinPlugin instance = new GiraphGremlinPlugin(); - private GiraphGremlinModule() { + private GiraphGremlinPlugin() { super(MODULE_NAME, ImportCustomizer.build().addClassImports( EmptyOutEdges.class, GiraphComputation.class, @@ -52,7 +52,7 @@ private GiraphGremlinModule() { PassThroughMemory.class).create()); } - public static GiraphGremlinModule instance() { + public static GiraphGremlinPlugin instance() { return instance; } } \ No newline at end of file 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/AbstractGremlinPlugin.java similarity index 85% rename from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/AbstractGremlinModule.java rename to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/AbstractGremlinPlugin.java index 4b9cd2c6ac7..f25c611404f 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/AbstractGremlinModule.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/AbstractGremlinPlugin.java @@ -25,21 +25,21 @@ /** * @author Stephen Mallette (http://stephen.genoprime.com) */ -public abstract class AbstractGremlinModule implements GremlinModule { +public abstract class AbstractGremlinPlugin implements GremlinPlugin { protected final String moduleName; protected final Customizer[] customizers; protected final Set appliesTo; /** - * Creates a base {@link GremlinModule} that will apply to all {@link GremlinScriptEngine} instances. + * Creates a base {@link GremlinPlugin} that will apply to all {@link GremlinScriptEngine} instances. */ - public AbstractGremlinModule(final String moduleName, final Customizer... customizers) { + public AbstractGremlinPlugin(final String moduleName, final Customizer... customizers) { this(moduleName, Collections.emptySet(), customizers); } /** - * Creates a base {@link GremlinModule} that will apply to specific {@link GremlinScriptEngine} instances. + * Creates a base {@link GremlinPlugin} that will apply to specific {@link GremlinScriptEngine} instances. */ - public AbstractGremlinModule(final String moduleName, final Set appliesTo, final Customizer... customizers) { + public AbstractGremlinPlugin(final String moduleName, final Set appliesTo, final Customizer... customizers) { this.moduleName = moduleName; this.appliesTo = appliesTo; this.customizers = customizers; 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/CoreGremlinPlugin.java similarity index 89% rename from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinModule.java rename to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinPlugin.java index ff477672dbf..3f5ac2c25b8 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/CoreGremlinPlugin.java @@ -27,7 +27,7 @@ * * @author Stephen Mallette (http://stephen.genoprime.com) */ -public final class CoreGremlinModule implements GremlinModule { +public final class CoreGremlinPlugin implements GremlinPlugin { private static final String MODULE_NAME = "tinkerpop.core"; @@ -42,11 +42,11 @@ public final class CoreGremlinModule implements GremlinModule { * @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(); + public static final CoreGremlinPlugin INSTANCE = new CoreGremlinPlugin(); - private CoreGremlinModule() {} + private CoreGremlinPlugin() {} - public static CoreGremlinModule instance() { + public static CoreGremlinPlugin instance() { return INSTANCE; } 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 25a061270b1..10bdfa31f2f 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 @@ -95,7 +95,7 @@ public class DefaultGremlinScriptEngineManager implements GremlinScriptEngineMan * List of extensions for the {@link GremlinScriptEngineManager} which will be used to supply * {@link Customizer} instances to {@link GremlinScriptEngineFactory} that are instantiated. */ - private List modules = new ArrayList<>(); + private List modules = new ArrayList<>(); /** * The effect of calling this constructor is the same as calling @@ -125,7 +125,7 @@ public List getCustomizers(final String scriptEngineName) { } @Override - public void addModule(final GremlinModule module) { + public void addModule(final GremlinPlugin module) { // TODO: should modules be a set based on "name" to ensure uniqueness? not sure what bad stuff can happen with dupes if (module != null) modules.add(module); } @@ -378,7 +378,7 @@ private ServiceLoader getServiceLoader(final ClassLo private void initEngines(final ClassLoader loader) { // always need this module for a scriptengine to be "Gremlin-enabled" - modules.add(CoreGremlinModule.INSTANCE); + modules.add(CoreGremlinPlugin.INSTANCE); Iterator itty; try { diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinModule.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinPlugin.java similarity index 98% rename from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinModule.java rename to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinPlugin.java index 1077cf3ace8..390c0278c1b 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinModule.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinPlugin.java @@ -23,7 +23,7 @@ /** * @author Stephen Mallette (http://stephen.genoprime.com) */ -public interface GremlinModule { +public interface GremlinPlugin { /** * The name of the module. This name should be unique (use a namespaced approach) as naming clashes will * prevent proper module operations. Modules developed by TinkerPop will be prefixed with "tinkerpop." diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinScriptEngineManager.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinScriptEngineManager.java index 270d136e507..b2966c5a46b 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinScriptEngineManager.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinScriptEngineManager.java @@ -118,9 +118,9 @@ public interface GremlinScriptEngineManager { public List getEngineFactories(); /** - * Add {@link GremlinModule} instances to customize newly created {@link GremlinScriptEngine} instances. + * Add {@link GremlinPlugin} instances to customize newly created {@link GremlinScriptEngine} instances. */ - public void addModule(final GremlinModule module); + public void addModule(final GremlinPlugin module); /** * Registers a {@link GremlinScriptEngineFactory} to handle a language name. Overrides any such association found 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/ImportGremlinPlugin.java similarity index 97% rename from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinModule.java rename to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPlugin.java index e9ca47738ee..f99ee68d6c2 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinModule.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPlugin.java @@ -36,10 +36,10 @@ * * @author Stephen Mallette (http://stephen.genoprime.com) */ -public final class ImportGremlinModule extends AbstractGremlinModule { +public final class ImportGremlinPlugin extends AbstractGremlinPlugin { private static final String MODULE_NAME = "tinkerpop.import"; - private ImportGremlinModule(final Builder builder) { + private ImportGremlinPlugin(final Builder builder) { super(MODULE_NAME, builder.appliesTo, ImportCustomizer.build() .addClassImports(builder.classImports) .addEnumImports(builder.enumImports) @@ -138,11 +138,11 @@ public Builder enumImports(final Collection enums) { return this; } - public ImportGremlinModule create() { + public ImportGremlinPlugin create() { if (enumImports.isEmpty() && classImports.isEmpty() && methodImports.isEmpty()) throw new IllegalStateException("At least one import must be specified"); - return new ImportGremlinModule(this); + return new ImportGremlinPlugin(this); } private static List allEnums(final Class clazz) { diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/RemoteAcceptor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/RemoteAcceptor.java index f917c092fb5..9c96892fcaf 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/RemoteAcceptor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/RemoteAcceptor.java @@ -27,7 +27,7 @@ * A "remote connection" does not necessarily have to be a remote server. It simply refers to a resource that is * external to the console. *

- * By implementing this interface and returning an instance of it through {@link GremlinModule#remoteAcceptor()} a + * By implementing this interface and returning an instance of it through {@link GremlinPlugin#remoteAcceptor()} a * plugin can hook into those commands and provide remoting features. * * @author Stephen Mallette (http://stephen.genoprime.com) 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/ScriptFileGremlinPlugin.java similarity index 90% rename from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileModule.java rename to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java index b5c0d0c8bab..30e66ed7260 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileModule.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java @@ -27,10 +27,10 @@ /** * @author Stephen Mallette (http://stephen.genoprime.com) */ -public final class ScriptFileModule extends AbstractGremlinModule { +public final class ScriptFileGremlinPlugin extends AbstractGremlinPlugin { private static final String MODULE_NAME = "tinkerpop.script"; - public ScriptFileModule(final Builder builder) { + public ScriptFileGremlinPlugin(final Builder builder) { super(MODULE_NAME, builder.appliesTo, new ScriptCustomizer(builder.files)); } @@ -64,8 +64,8 @@ public Builder files(final Set files) { return this; } - public ScriptFileModule create() { - return new ScriptFileModule(this); + public ScriptFileGremlinPlugin create() { + return new ScriptFileGremlinPlugin(this); } } } 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/ImportGremlinPluginTest.java similarity index 91% rename from gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinModuleTest.java rename to gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPluginTest.java index ce62357971f..428fc57817c 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinModuleTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPluginTest.java @@ -37,16 +37,16 @@ /** * @author Stephen Mallette (http://stephen.genoprime.com) */ -public class ImportGremlinModuleTest { +public class ImportGremlinPluginTest { @Test(expected = IllegalStateException.class) public void shouldImportSomething() { - ImportGremlinModule.build().create(); + ImportGremlinPlugin.build().create(); } @Test public void shouldImportClass() { - final ImportGremlinModule module = ImportGremlinModule.build() + final ImportGremlinPlugin module = ImportGremlinPlugin.build() .classImports(Collections.singletonList(Graph.class.getCanonicalName())).create(); final ImportCustomizer customizer = (ImportCustomizer) module.getCustomizers().get()[0]; @@ -58,7 +58,7 @@ public void shouldImportClass() { @Test public void shouldImportWildcardMethod() throws Exception { final Method zeroArgs = Gremlin.class.getMethod("version"); - final ImportGremlinModule module = ImportGremlinModule.build() + final ImportGremlinPlugin module = ImportGremlinPlugin.build() .methodImports(Collections.singletonList(Gremlin.class.getCanonicalName() + "#*")).create(); final ImportCustomizer customizer = (ImportCustomizer) module.getCustomizers().get()[0]; @@ -72,7 +72,7 @@ public void shouldImportWildcardMethod() throws Exception { @Test public void shouldImportZeroArgMethod() throws Exception { final Method zeroArgs = Gremlin.class.getMethod("version"); - final ImportGremlinModule module = ImportGremlinModule.build() + final ImportGremlinPlugin module = ImportGremlinPlugin.build() .methodImports(Collections.singletonList(toMethodDescriptor(zeroArgs))).create(); final ImportCustomizer customizer = (ImportCustomizer) module.getCustomizers().get()[0]; @@ -84,7 +84,7 @@ public void shouldImportZeroArgMethod() throws Exception { @Test public void shouldImportSingleArgMethod() throws Exception { final Method singleArg = IoCore.class.getMethod("createIoBuilder", String.class); - final ImportGremlinModule module = ImportGremlinModule.build() + final ImportGremlinPlugin module = ImportGremlinPlugin.build() .methodImports(Collections.singletonList(toMethodDescriptor(singleArg))).create(); final ImportCustomizer customizer = (ImportCustomizer) module.getCustomizers().get()[0]; @@ -97,7 +97,7 @@ public void shouldImportSingleArgMethod() throws Exception { public void shouldThrowExceptionIfInvalidMethodDescriptor() throws Exception { final String badDescriptor = "Gremlin*version"; try { - ImportGremlinModule.build() + ImportGremlinPlugin.build() .methodImports(Collections.singletonList(badDescriptor)).create(); fail("Should have failed parsing the method descriptor"); } catch (IllegalArgumentException iae) { @@ -107,7 +107,7 @@ public void shouldThrowExceptionIfInvalidMethodDescriptor() throws Exception { @Test public void shouldImportWildcardEnum() throws Exception { - final ImportGremlinModule module = ImportGremlinModule.build() + final ImportGremlinPlugin module = ImportGremlinPlugin.build() .enumImports(Collections.singletonList(T.class.getCanonicalName() + "#*")).create(); final ImportCustomizer customizer = (ImportCustomizer) module.getCustomizers().get()[0]; @@ -118,7 +118,7 @@ public void shouldImportWildcardEnum() throws Exception { @Test public void shouldImportEnum() throws Exception { - final ImportGremlinModule module = ImportGremlinModule.build() + final ImportGremlinPlugin module = ImportGremlinPlugin.build() .enumImports(Collections.singletonList(T.class.getCanonicalName() + "#" + T.id.name())).create(); final ImportCustomizer customizer = (ImportCustomizer) module.getCustomizers().get()[0]; @@ -130,7 +130,7 @@ public void shouldImportEnum() throws Exception { public void shouldThrowExceptionIfInvalidEnumDescriptor() throws Exception { final String badDescriptor = "T*id"; try { - ImportGremlinModule.build() + ImportGremlinPlugin.build() .enumImports(Collections.singletonList(badDescriptor)).create(); fail("Should have failed parsing the enum descriptor"); } catch (IllegalArgumentException iae) { 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 1ad41c76714..8659e24bce4 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 @@ -20,10 +20,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.GremlinPlugin; import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineManager; import org.javatuples.Pair; import org.slf4j.Logger; @@ -439,7 +438,7 @@ private void initializeGremlinScriptEngineManager() { // first try instance() and if that fails try to use build() try { final Method instanceMethod = clazz.getMethod("instance"); - gremlinScriptEngineManager.addModule((GremlinModule) instanceMethod.invoke(null)); + gremlinScriptEngineManager.addModule((GremlinPlugin) instanceMethod.invoke(null)); } catch (Exception ex) { final Method builderMethod = clazz.getMethod("build"); Object moduleBuilder = builderMethod.invoke(null); @@ -464,7 +463,7 @@ private void initializeGremlinScriptEngineManager() { } final Method create = builderClazz.getMethod("create"); - gremlinScriptEngineManager.addModule((GremlinModule) create.invoke(moduleBuilder)); + gremlinScriptEngineManager.addModule((GremlinPlugin) create.invoke(moduleBuilder)); } } catch (Exception ex) { throw new IllegalStateException(ex); @@ -475,8 +474,8 @@ private void initializeGremlinScriptEngineManager() { private ScriptEngines createScriptEngines() { // plugins already on the path - ones static to the classpath - final List globalPlugins = new ArrayList<>(); - ServiceLoader.load(GremlinPlugin.class).forEach(globalPlugins::add); + final List globalPlugins = new ArrayList<>(); + ServiceLoader.load(org.apache.tinkerpop.gremlin.groovy.plugin.GremlinPlugin.class).forEach(globalPlugins::add); return new ScriptEngines(se -> { // this first part initializes the scriptengines Map @@ -487,7 +486,7 @@ private ScriptEngines createScriptEngines() { } // use grabs dependencies and returns plugins to load - final List pluginsToLoad = new ArrayList<>(globalPlugins); + final List pluginsToLoad = new ArrayList<>(globalPlugins); use.forEach(u -> { if (u.size() != 3) logger.warn("Could not resolve dependencies for [{}]. Each entry for the 'use' configuration must include [groupId, artifactId, version]", u); @@ -614,9 +613,9 @@ public Builder addEngineSettings(final String engineName, final List imp } /** - * Add a configuration for a {@link GremlinModule} to the executor. The key is the fully qualified class name - * of the {@link GremlinModule} instance and the value is a map of configuration values. In that map, the key - * is the name of a builder method on the {@link GremlinModule} and the value is some argument to pass to that + * Add a configuration for a {@link GremlinPlugin} to the executor. The key is the fully qualified class name + * of the {@link GremlinPlugin} instance and the value is a map of configuration values. In that map, the key + * is the name of a builder method on the {@link GremlinPlugin} and the value is some argument to pass to that * method. */ public Builder addModules(final String engineName, final Map> modules) { 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 a8395bb581f..75cff3b0b9d 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,7 +20,7 @@ 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.GremlinPlugin; import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine; import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy; @@ -341,7 +341,7 @@ public static class ScriptEngineSettings { public Map config = null; /** - * A set of configurations for {@link GremlinModule} instances to apply to this {@link GremlinScriptEngine}. + * A set of configurations for {@link GremlinPlugin} instances to apply to this {@link GremlinScriptEngine}. */ public Map> modules = new HashMap<>(); } diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/jsr223/GremlinServerGremlinModule.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/jsr223/GremlinServerGremlinPlugin.java similarity index 74% rename from gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/jsr223/GremlinServerGremlinModule.java rename to gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/jsr223/GremlinServerGremlinPlugin.java index b161f699ded..e8826ae7161 100644 --- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/jsr223/GremlinServerGremlinModule.java +++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/jsr223/GremlinServerGremlinPlugin.java @@ -18,25 +18,25 @@ */ package org.apache.tinkerpop.gremlin.server.jsr223; -import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinModule; -import org.apache.tinkerpop.gremlin.jsr223.GremlinModule; +import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; +import org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin; import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; import org.apache.tinkerpop.gremlin.server.util.LifeCycleHook; /** - * A {@link GremlinModule} implementation that adds Gremlin Server specific classes to the imports. + * A {@link GremlinPlugin} implementation that adds Gremlin Server specific classes to the imports. * * @author Stephen Mallette (http://stephen.genoprime.com) */ -public final class GremlinServerGremlinModule extends AbstractGremlinModule { +public final class GremlinServerGremlinPlugin extends AbstractGremlinPlugin { private static final String MODULE_NAME = "tinkerpop.server"; - private static final GremlinServerGremlinModule instance = new GremlinServerGremlinModule(); + private static final GremlinServerGremlinPlugin instance = new GremlinServerGremlinPlugin(); - private GremlinServerGremlinModule() { + private GremlinServerGremlinPlugin() { super(MODULE_NAME, ImportCustomizer.build().addClassImports(LifeCycleHook.class, LifeCycleHook.Context.class).create()); } - public static GremlinServerGremlinModule instance() { + public static GremlinServerGremlinPlugin instance() { return instance; } } 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 bb6c9cebffe..1204bee4e86 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,7 +19,6 @@ 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; @@ -29,9 +28,7 @@ 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; From c348a0722cfbcddd708ec8d640bd56244f5455db Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Mon, 21 Nov 2016 13:34:27 -0500 Subject: [PATCH 09/45] TINKERPOP-1562 Bring back GremlinModule and deprecate it. GremlinModule was introduced in 3.2.3. Wasn't sure if GremlinModule was equal to a GremlinPlugin so I had named it differently. It was never promoted as a replacement for GremlinPlugin and it was never used in core infrastructure like Gremlin Console or Gremlin Server, but I figured it better to avoid breaking change and simply deprecate it for removal later. --- .../gremlin/jsr223/CoreGremlinModule.java | 64 +++++++++++++++++ .../DefaultGremlinScriptEngineManager.java | 39 ++++++++-- .../gremlin/jsr223/GremlinModule.java | 72 +++++++++++++++++++ .../jsr223/GremlinScriptEngineManager.java | 10 ++- .../groovy/engine/GremlinExecutor.java | 4 +- .../GremlinEnabledScriptEngineTest.java | 28 +++++++- 6 files changed, 207 insertions(+), 10 deletions(-) create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinModule.java create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinModule.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 new file mode 100644 index 00000000000..686906499b3 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinModule.java @@ -0,0 +1,64 @@ +/* + * 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.CoreImports; + +import java.util.Optional; + +/** + * This module is required for a {@code ScriptEngine} to be Gremlin-enabled. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, replaced by {@link CoreGremlinPlugin}. + */ +@Deprecated +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 Customizer[] customizers = new Customizer[] {gremlinCore}; + + /** + * @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 getCustomizers(final String scriptEngineName) { + return Optional.of(customizers); + } + + @Override + public String getName() { + return MODULE_NAME; + } +} 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 10bdfa31f2f..1484f90c1e6 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 @@ -95,7 +95,15 @@ public class DefaultGremlinScriptEngineManager implements GremlinScriptEngineMan * List of extensions for the {@link GremlinScriptEngineManager} which will be used to supply * {@link Customizer} instances to {@link GremlinScriptEngineFactory} that are instantiated. */ - private List modules = new ArrayList<>(); + private List plugins = new ArrayList<>(); + + /** + * List of extensions for the {@link GremlinScriptEngineManager} which will be used to supply + * {@link Customizer} instances to {@link GremlinScriptEngineFactory} that are instantiated. + * + * @deprecated As of release 3.2.4, replaced by {@link #plugins}. + */ + private List modules = new ArrayList<>(); /** * The effect of calling this constructor is the same as calling @@ -118,18 +126,37 @@ public DefaultGremlinScriptEngineManager(final ClassLoader loader) { @Override public List getCustomizers(final String scriptEngineName) { - return modules.stream().flatMap(module -> { - final Optional moduleCustomizers = module.getCustomizers(scriptEngineName); - return Stream.of(moduleCustomizers.orElse(new Customizer[0])); + final List pluginCustomizers = plugins.stream().flatMap(plugin -> { + final Optional customizers = plugin.getCustomizers(scriptEngineName); + return Stream.of(customizers.orElse(new Customizer[0])); }).collect(Collectors.toList()); + + // modules are deprecated in favor of GremlinPlugin - this line will eventually be removed + pluginCustomizers.addAll(modules.stream().flatMap(plugin -> { + final Optional customizers = plugin.getCustomizers(scriptEngineName); + return Stream.of(customizers.orElse(new Customizer[0])); + }).collect(Collectors.toList())); + + + return pluginCustomizers; } + /** + * @deprecated As of release 3.2.4, replaced by {@link #addPlugin(GremlinPlugin)}. + */ @Override - public void addModule(final GremlinPlugin module) { + @Deprecated + public void addModule(final GremlinModule module) { // TODO: should modules be a set based on "name" to ensure uniqueness? not sure what bad stuff can happen with dupes if (module != null) modules.add(module); } + @Override + public void addPlugin(final GremlinPlugin plugin) { + // TODO: should modules be a set based on "name" to ensure uniqueness? not sure what bad stuff can happen with dupes + if (plugin != null) plugins.add(plugin); + } + /** * Stores the specified {@code Bindings} as a global for all {@link GremlinScriptEngine} objects created by it. * If the bindings are to be updated by multiple threads it is recommended that a {@link ConcurrentBindings} @@ -378,7 +405,7 @@ private ServiceLoader getServiceLoader(final ClassLo private void initEngines(final ClassLoader loader) { // always need this module for a scriptengine to be "Gremlin-enabled" - modules.add(CoreGremlinPlugin.INSTANCE); + plugins.add(CoreGremlinPlugin.instance()); Iterator itty; try { diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinModule.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinModule.java new file mode 100644 index 00000000000..1345841c58d --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinModule.java @@ -0,0 +1,72 @@ +/* + * 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; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, replaced by {@link GremlinPlugin}. + */ +@Deprecated +public interface GremlinModule { + /** + * The name of the module. This name should be unique (use a namespaced approach) as naming clashes will + * prevent proper module operations. Modules developed by TinkerPop will be prefixed with "tinkerpop." + * For example, TinkerPop's implementation of Giraph would be named "tinkerpop.giraph". If Facebook were + * to do their own implementation the implementation might be called "facebook.giraph". + */ + public String getName(); + + /** + * Some modules may require a restart of the plugin host for the classloader to pick up the features. This is + * typically true of modules that rely on {@code Class.forName()} to dynamically instantiate classes from the + * root classloader (e.g. JDBC drivers that instantiate via @{code DriverManager}). + */ + public default boolean requireRestart() { + return false; + } + + /** + * Gets the list of all {@link Customizer} implementations to assign to a new {@link GremlinScriptEngine}. This is + * the same as doing {@code getCustomizers(null)}. + */ + public default Optional getCustomizers(){ + return getCustomizers(null); + } + + /** + * Gets the list of {@link Customizer} implementations to assign to a new {@link GremlinScriptEngine}. The + * implementation should filter the returned {@code Customizers} according to the supplied name of the + * Gremlin-enabled {@code ScriptEngine}. By providing a filter, {@code GremlinModule} developers can have the + * ability to target specific {@code ScriptEngines}. + * + * @param scriptEngineName The name of the {@code ScriptEngine} or null to get all the available {@code Customizers} + */ + public Optional getCustomizers(final String scriptEngineName); + + /** + * Allows a plugin to utilize features of the {@code :remote} and {@code :submit} commands of the Gremlin Console. + * This method does not need to be implemented if the plugin is not meant for the Console for some reason or + * if it does not intend to take advantage of those commands. + */ + public default Optional remoteAcceptor() { + return Optional.empty(); + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinScriptEngineManager.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinScriptEngineManager.java index b2966c5a46b..a48d7618287 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinScriptEngineManager.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinScriptEngineManager.java @@ -117,10 +117,18 @@ public interface GremlinScriptEngineManager { */ public List getEngineFactories(); + /** + * Add {@link GremlinModule} instances to customize newly created {@link GremlinScriptEngine} instances. + * + * @deprecated As of release 3.2.4, replaced by {@link #addPlugin(GremlinPlugin)}. + */ + @Deprecated + public void addModule(final GremlinModule module); + /** * Add {@link GremlinPlugin} instances to customize newly created {@link GremlinScriptEngine} instances. */ - public void addModule(final GremlinPlugin module); + public void addPlugin(final GremlinPlugin plugin); /** * Registers a {@link GremlinScriptEngineFactory} to handle a language name. Overrides any such association found 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 8659e24bce4..4449e1b798a 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 @@ -438,7 +438,7 @@ private void initializeGremlinScriptEngineManager() { // first try instance() and if that fails try to use build() try { final Method instanceMethod = clazz.getMethod("instance"); - gremlinScriptEngineManager.addModule((GremlinPlugin) instanceMethod.invoke(null)); + gremlinScriptEngineManager.addPlugin((GremlinPlugin) instanceMethod.invoke(null)); } catch (Exception ex) { final Method builderMethod = clazz.getMethod("build"); Object moduleBuilder = builderMethod.invoke(null); @@ -463,7 +463,7 @@ private void initializeGremlinScriptEngineManager() { } final Method create = builderClazz.getMethod("create"); - gremlinScriptEngineManager.addModule((GremlinPlugin) create.invoke(moduleBuilder)); + gremlinScriptEngineManager.addPlugin((GremlinPlugin) create.invoke(moduleBuilder)); } } catch (Exception ex) { throw new IllegalStateException(ex); diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java index 76e8f55d675..34a37ae0903 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java @@ -25,7 +25,9 @@ import org.junit.Test; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Optional; import static org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineSuite.ENGINE_TO_TEST; import static org.junit.Assert.assertEquals; @@ -47,11 +49,35 @@ public void shouldGetEngineByName() throws Exception { @Test public void shouldHaveCoreImportsInPlace() throws Exception { - // TODO: delete this test - other tests will give us confidence on such things as we get further along final GremlinScriptEngine scriptEngine = manager.getEngineByName(ENGINE_TO_TEST); final List classesToCheck = Arrays.asList(Vertex.class, Edge.class, Graph.class, VertexProperty.class); for (Class clazz : classesToCheck) { assertEquals(clazz, scriptEngine.eval(clazz.getSimpleName())); } } + + @Test + public void shouldSupportDeprecatedGremlinModules() throws Exception { + final GremlinScriptEngineManager mgr = new DefaultGremlinScriptEngineManager(); + mgr.addModule(new GremlinModule() { + @Override + public String getName() { + return "test.junk"; + } + + @Override + public Optional getCustomizers(final String scriptEngineName) { + return Optional.of(new Customizer[] {ImportCustomizer.build() + .addClassImports(java.awt.Color.class) + .addClassImports(java.sql.CallableStatement.class) + .create() }); + } + }); + + final GremlinScriptEngine scriptEngine = mgr.getEngineByName(ENGINE_TO_TEST); + final List classesToCheck = Arrays.asList(java.awt.Color.class, java.sql.CallableStatement.class); + for (Class clazz : classesToCheck) { + assertEquals(clazz, scriptEngine.eval(clazz.getSimpleName())); + } + } } From 1a7a3b37f895d31f9f4f441088353e0bfeb3d8d1 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Mon, 21 Nov 2016 17:15:53 -0500 Subject: [PATCH 10/45] TINKERPOP-1562 Start getting Console working with new plugin stuff Built adapters to get new RemoteAcceptor to behave as the old RemoteAcceptor and to get new GremlinPlugin to work as the old GremlinPlugin. Deprecated some classes and undeprecated others - still a pretty big WIP commit at this point but nothing appears to be broken. --- .../tinkerpop/gremlin/console/Console.groovy | 14 +- .../tinkerpop/gremlin/console/Mediator.groovy | 4 +- .../console/jsr223/GephiRemoteAcceptor.groovy | 372 ------------------ .../plugin/ConsolePluginAcceptor.groovy | 2 +- .../console/plugin/GephiRemoteAcceptor.groovy | 1 - .../gremlin/console/plugin/PluggedIn.groovy | 79 ++++ .../tinkerpop/gremlin/jsr223/Customizer.java | 4 +- .../gremlin/jsr223/GremlinModule.java | 2 + .../gremlin/jsr223/GremlinPlugin.java | 9 - .../jsr223/GremlinPluginException.java | 41 ++ .../jsr223/console/ConsoleCustomizer.java | 33 ++ .../jsr223/console/PluginAcceptor.java | 62 +++ .../jsr223/{ => console}/RemoteAcceptor.java | 6 +- .../jsr223/{ => console}/RemoteException.java | 2 +- .../gremlin/groovy/plugin/GremlinPlugin.java | 1 + .../gremlin/groovy/plugin/PluginAcceptor.java | 2 + .../gremlin/groovy/plugin/RemoteAcceptor.java | 2 +- .../GremlinEnabledScriptEngineTest.java | 1 + 18 files changed, 244 insertions(+), 393 deletions(-) delete mode 100644 gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinPluginException.java create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/ConsoleCustomizer.java create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/PluginAcceptor.java rename gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/{ => console}/RemoteAcceptor.java (95%) rename gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/{ => console}/RemoteException.java (96%) diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy index 69d6ba3c4ba..cb6e90f0742 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy @@ -103,6 +103,8 @@ class Console { // hide output temporarily while imports execute showShellEvaluationOutput(false) + // TODO: register CoreGremlinPlugin if using v3d3 + // add the default imports new ConsoleImportCustomizerProvider().getCombinedImports().stream() .collect { IMPORT_SPACE + it }.each { groovy.execute(it) } @@ -123,9 +125,17 @@ class Console { // check for available plugins. if they are in the "active" plugins strategies then "activate" them def activePlugins = Mediator.readPluginState() - ServiceLoader.load(GremlinPlugin.class, groovy.getInterp().getClassLoader()).each { plugin -> + def pluginClass = mediator.useV3d3 ? org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin : GremlinPlugin + ServiceLoader.load(pluginClass, groovy.getInterp().getClassLoader()).each { plugin -> if (!mediator.availablePlugins.containsKey(plugin.class.name)) { - def pluggedIn = new PluggedIn(plugin, groovy, io, false) + def pluggedIn + + if (Mediator.useV3d3) { + pluggedIn = new PluggedIn(new PluggedIn.GremlinPluginAdapter(plugin), groovy, io, false) + } else { + pluggedIn = new PluggedIn(plugin, groovy, io, false) + } + mediator.availablePlugins.put(plugin.class.name, pluggedIn) if (activePlugins.contains(plugin.class.name)) { diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Mediator.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Mediator.groovy index 047e3d7cbb0..396c563f0e3 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Mediator.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Mediator.groovy @@ -21,8 +21,6 @@ package org.apache.tinkerpop.gremlin.console import org.apache.tinkerpop.gremlin.console.plugin.PluggedIn import org.apache.tinkerpop.gremlin.groovy.plugin.RemoteAcceptor -import java.util.concurrent.CompletableFuture - /** * @author Stephen Mallette (http://stephen.genoprime.com) */ @@ -36,6 +34,8 @@ class Mediator { private static String LINE_SEP = System.getProperty("line.separator") + public static final boolean useV3d3 = System.getProperty("plugins", "v3d3") == "v3d3" + public Mediator(final Console console) { this.console = console } diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy deleted file mode 100644 index 11e1e5c7a86..00000000000 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.tinkerpop.gremlin.console.jsr223 - -import groovy.json.JsonOutput -import groovy.json.JsonSlurper -import groovy.transform.CompileStatic -import org.apache.http.client.methods.CloseableHttpResponse -import org.apache.http.client.methods.HttpUriRequest -import org.apache.http.client.methods.RequestBuilder -import org.apache.http.entity.StringEntity -import org.apache.http.impl.client.CloseableHttpClient -import org.apache.http.impl.client.HttpClients -import org.apache.http.util.EntityUtils -import org.apache.tinkerpop.gremlin.console.plugin.GephiTraversalVisualizationStrategy -import org.apache.tinkerpop.gremlin.jsr223.RemoteAcceptor -import org.apache.tinkerpop.gremlin.jsr223.RemoteException -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource -import org.apache.tinkerpop.gremlin.structure.Edge -import org.apache.tinkerpop.gremlin.structure.Graph -import org.apache.tinkerpop.gremlin.structure.Vertex -import org.codehaus.groovy.tools.shell.Groovysh -import org.codehaus.groovy.tools.shell.IO - -/** - * @author Stephen Mallette (http://stephen.genoprime.com) - * @author Randall Barnhart (randompi@gmail.com) - */ -class GephiRemoteAcceptor implements RemoteAcceptor { - - private String host = "localhost" - private int port = 8080 - private String workspace = "workspace1" - - private final Groovysh shell - private final IO io - - private final Random rand = new Random(); - boolean traversalSubmittedForViz = false - long vizStepDelay - private float[] vizStartRGBColor - private float[] vizDefaultRGBColor - private char vizColorToFade - private float vizColorFadeRate - private float vizStartSize - private float vizSizeDecrementRate - private Map vertexAttributes = [:] - - private CloseableHttpClient httpclient = HttpClients.createDefault(); - - public GephiRemoteAcceptor(final Groovysh shell, final IO io) { - this.shell = shell - this.io = io - - // traversal visualization defaults - vizStepDelay = 1000; // 1 second pause between viz of steps - vizStartRGBColor = [0.0f, 1.0f, 0.5f] // light aqua green - vizDefaultRGBColor = [0.6f, 0.6f, 0.6f] // light grey - vizColorToFade = 'g' // will fade so blue is strongest - vizColorFadeRate = 0.7 // the multiplicative rate to fade visited vertices - vizStartSize = 10 - vizSizeDecrementRate = 0.33f - } - - @Override - connect(final List args) throws RemoteException { - if (args.size() >= 1) - workspace = args[0] - - if (args.size() >= 2) - host = args[1] - - if (args.size() >= 3) { - try { - port = Integer.parseInt(args[2]) - } catch (Exception ex) { - throw new RemoteException("Port must be an integer value") - } - } - - def vizConfig = " with stepDelay:$vizStepDelay, startRGBColor:$vizStartRGBColor, " + - "colorToFade:$vizColorToFade, colorFadeRate:$vizColorFadeRate, startSize:$vizStartSize," + - "sizeDecrementRate:$vizSizeDecrementRate" - - return "Connection to Gephi - http://$host:$port/$workspace" + vizConfig - } - - @Override - Object configure(final List args) throws RemoteException { - if (args.size() < 2) - throw new RemoteException("Invalid config arguments - check syntax") - - if (args[0] == "host") - host = args[1] - else if (args[0] == "port") { - try { - port = Integer.parseInt(args[1]) - } catch (Exception ignored) { - throw new RemoteException("Port must be an integer value") - } - } else if (args[0] == "workspace") - workspace = args[1] - else if (args[0] == "stepDelay") - parseVizStepDelay(args[1]) - else if (args[0] == "startRGBColor") - parseVizStartRGBColor(args[1]) - else if (args[0] == "colorToFade") - parseVizColorToFade(args[1]) - else if (args[0] == "colorFadeRate") - parseVizColorFadeRate(args[1]) - else if (args[0] == "sizeDecrementRate") - parseVizSizeDecrementRate(args[1]) - else if (args[0] == "startSize") - parseVizStartSize(args[1]) - else if (args[0] == "visualTraversal") { - def graphVar = shell.interp.context.getVariable(args[1]) - if (!(graphVar instanceof Graph)) - throw new RemoteException("Invalid argument to 'visualTraversal' - first parameter must be a Graph instance") - - def gVar = args.size() == 3 ? args[2] : "vg" - def theG = GraphTraversalSource.build().with(new GephiTraversalVisualizationStrategy(this)).create(graphVar) - shell.interp.context.setVariable(gVar, theG) - } else - throw new RemoteException("Invalid config arguments - check syntax") - - return "Connection to Gephi - http://$host:$port/$workspace" + - " with stepDelay:$vizStepDelay, startRGBColor:$vizStartRGBColor, " + - "colorToFade:$vizColorToFade, colorFadeRate:$vizColorFadeRate, startSize:$vizStartSize," + - "sizeDecrementRate:$vizSizeDecrementRate" - } - - @Override - @CompileStatic - Object submit(final List args) throws RemoteException { - final String line = String.join(" ", args) - if (line.trim() == "clear") { - clearGraph() - io.out.println("Gephi workspace cleared") - return - } - - // need to clear the vertex attributes - vertexAttributes.clear() - - // this tells the GraphTraversalVisualizationStrategy that if the line eval's to a traversal it should - // try to visualize it - traversalSubmittedForViz = true - - // get the colors/sizes back to basics before trying visualize - resetColorsSizes() - - final Object o = shell.execute(line) - if (o instanceof Graph) { - clearGraph() - def graph = (Graph) o - def g = graph.traversal() - g.V().sideEffect { addVertexToGephi(g, it.get()) }.iterate() - } - - traversalSubmittedForViz = false - } - - @Override - void close() throws IOException { - httpclient.close() - } - - /** - * Visits the last set of vertices traversed and degrades their color and size. - */ - def updateVisitedVertices(def List except = []) { - vertexAttributes.keySet().findAll{ vertexId -> !except.contains(vertexId) }.each { String vertexId -> - def attrs = vertexAttributes[vertexId] - float currentColor = attrs.color - currentColor *= vizColorFadeRate - - int currentSize = attrs["size"] - currentSize = Math.max(vizStartSize, currentSize - (currentSize * vizSizeDecrementRate)) - - vertexAttributes.get(vertexId).color = currentColor - vertexAttributes.get(vertexId).size = currentSize - - changeVertexAttributes(vertexId) - } - } - - def changeVertexAttributes(def String vertexId) { - def props = [:] - props.put(vizColorToFade.toString(), vertexAttributes[vertexId].color) - props.put("size", vertexAttributes[vertexId].size) - updateGephiGraph([cn: [(vertexId): props]]) - } - - /** - * Visit a vertex traversed and initialize its color and size. - */ - def visitVertexInGephi(def Vertex v) { - def props = [:] - props.put('r', vizStartRGBColor[0]) - props.put('g', vizStartRGBColor[1]) - props.put('b', vizStartRGBColor[2]) - props.put('size', vizStartSize * 2.5) - props.put('visited', 1) - - updateGephiGraph([cn: [(v.id().toString()): props]]) - - vertexAttributes[v.id().toString()] = [color: vizStartRGBColor[fadeColorIndex()], size: vizStartSize * 2.5, touch: 1] - } - - def fadeColorIndex() { - if (vizColorToFade == 'r') - return 0 - else if (vizColorToFade == 'g') - return 1 - else if (vizColorToFade == 'b') - return 2 - } - - def addVertexToGephi(def GraphTraversalSource g, def Vertex v, def boolean ignoreEdges = false) { - // grab the first property value from the strategies of values - def props = g.V(v).valueMap().next().collectEntries { kv -> [(kv.key): kv.value[0]] } - props << [label: v.label()] - props.put('r', vizDefaultRGBColor[0]) - props.put('g', vizDefaultRGBColor[1]) - props.put('b', vizDefaultRGBColor[2]) - props.put('x', rand.nextFloat()) - props.put('y', rand.nextFloat()) - props.put('size', 10) - props.put('visited', 0) - - // only add if it does not exist in graph already - if (!getFromGephiGraph([operation: "getNode", id: v.id().toString()]).isPresent()) - updateGephiGraph([an: [(v.id().toString()): props]]) - - if (!ignoreEdges) { - g.V(v).outE().sideEffect { - addEdgeToGephi(g, it.get()) - }.iterate() - } - } - - @CompileStatic - def addEdgeToGephi(def GraphTraversalSource g, def Edge e) { - def props = g.E(e).valueMap().next() - props.put('label', e.label()) - props.put('source', e.outVertex().id().toString()) - props.put('target', e.inVertex().id().toString()) - props.put('directed', true) - props.put('visited', 0) - - // make sure the in vertex is there but don't add its edges - that will happen later as we are looping - // all vertices in the graph - addVertexToGephi(g, e.inVertex(), true) - - // both vertices are definitely there now, so add the edge - updateGephiGraph([ae: [(e.id().toString()): props]]) - } - - def clearGraph() { - updateGephiGraph([dn: [filter: "ALL"]]) - } - - def resetColorsSizes() { - updateGephiGraph([cn: [filter: [nodeAttribute: [attribute: "visited", value: 1]], - attributes: [size: 1, r: vizDefaultRGBColor[0], g: vizDefaultRGBColor[1], b: vizDefaultRGBColor[2]]]]) - updateGephiGraph([ce: [filter: [edgeAttribute: [attribute: "visited", value: 1]], - attributes: [size: 1, r: vizDefaultRGBColor[0], g: vizDefaultRGBColor[1], b: vizDefaultRGBColor[2]]]]) - } - - def getFromGephiGraph(def Map queryArgs) { - def requestBuilder = RequestBuilder.get("http://$host:$port/$workspace") - queryArgs.each { requestBuilder = requestBuilder.addParameter(it.key, it.value) } - - def httpResp = makeRequest(requestBuilder.build()) - def resp = EntityUtils.toString(httpResp.entity) - - // gephi streaming plugin does not set the content type or respect the Accept header - treat as text - if (resp.isEmpty()) - return Optional.empty() - else - return Optional.of(new JsonSlurper().parseText(resp)) - } - - def updateGephiGraph(def Map postBody) { - def requestBuilder = RequestBuilder.post("http://$host:$port/$workspace") - .addParameter("format", "JSON") - .addParameter("operation", "updateGraph") - .setEntity(new StringEntity(JsonOutput.toJson(postBody))) - EntityUtils.consume(makeRequest(requestBuilder.build()).entity) - } - - private CloseableHttpResponse makeRequest(HttpUriRequest request) { - def httpResp = httpclient.execute(request) - if (httpResp.getStatusLine().getStatusCode() == 200) { - return httpResp - } else { - def resp = EntityUtils.toString(httpResp.entity) - throw new RuntimeException("Unsuccessful request to Gephi - [${httpResp.getStatusLine().getStatusCode()}] ${httpResp.getStatusLine().getReasonPhrase()} - $resp") - } - } - - @Override - public String toString() { - return "Gephi - [$workspace]" - } - - private void parseVizStepDelay(String arg) { - try { - vizStepDelay = Long.parseLong(arg) - } catch (Exception ignored) { - throw new RemoteException("The stepDelay must be a long value") - } - } - - private void parseVizStartRGBColor(String arg) { - try { - vizStartRGBColor = arg[1..-2].tokenize(',')*.toFloat() - assert (vizStartRGBColor.length == 3) - } catch (Exception ignored) { - throw new RemoteException("The vizStartRGBColor must be an array of 3 float values, e.g. [0.0,1.0,0.5]") - } - } - - private void parseVizColorToFade(String arg) { - try { - vizColorToFade = arg.charAt(0).toLowerCase(); - assert (vizColorToFade == 'r' || vizColorToFade == 'g' || vizColorToFade == 'b') - } catch (Exception ignored) { - throw new RemoteException("The vizColorToFade must be one character value among: r, g, b, R, G, B") - } - } - - private void parseVizColorFadeRate(String arg) { - try { - vizColorFadeRate = Float.parseFloat(arg) - } catch (Exception ignored) { - throw new RemoteException("The colorFadeRate must be a float value") - } - } - - private void parseVizSizeDecrementRate(String arg) { - try { - vizSizeDecrementRate = Float.parseFloat(arg) - } catch (Exception ignored) { - throw new RemoteException("The sizeDecrementRate must be a float value") - } - } - - private void parseVizStartSize(String arg) { - try { - vizStartSize = Float.parseFloat(arg) - } catch (Exception ignored) { - throw new RemoteException("The startSize must be a float value") - } - } -} diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/ConsolePluginAcceptor.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/ConsolePluginAcceptor.groovy index 54bd119f0bb..b7ff7d44a7c 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/ConsolePluginAcceptor.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/ConsolePluginAcceptor.groovy @@ -26,7 +26,7 @@ import org.codehaus.groovy.tools.shell.IO /** * @author Stephen Mallette (http://stephen.genoprime.com) */ -class ConsolePluginAcceptor implements PluginAcceptor { +class ConsolePluginAcceptor implements PluginAcceptor, org.apache.tinkerpop.gremlin.jsr223.console.PluginAcceptor { public static final String ENVIRONMENT_NAME = "console"; public static final String ENVIRONMENT_SHELL = "ConsolePluginAcceptor.shell" diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/GephiRemoteAcceptor.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/GephiRemoteAcceptor.groovy index 30527a4326e..41984440da3 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/GephiRemoteAcceptor.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/GephiRemoteAcceptor.groovy @@ -40,7 +40,6 @@ import org.codehaus.groovy.tools.shell.IO /** * @author Stephen Mallette (http://stephen.genoprime.com) * @author Randall Barnhart (randompi@gmail.com) - * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.console.jsr223.GephiRemoteAcceptor} */ class GephiRemoteAcceptor implements RemoteAcceptor { diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy index f35d1ed424a..def49ed1e7a 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy @@ -19,6 +19,14 @@ package org.apache.tinkerpop.gremlin.console.plugin import org.apache.tinkerpop.gremlin.groovy.plugin.GremlinPlugin +import org.apache.tinkerpop.gremlin.groovy.plugin.IllegalEnvironmentException +import org.apache.tinkerpop.gremlin.groovy.plugin.PluginAcceptor +import org.apache.tinkerpop.gremlin.groovy.plugin.PluginInitializationException +import org.apache.tinkerpop.gremlin.groovy.plugin.RemoteAcceptor +import org.apache.tinkerpop.gremlin.groovy.plugin.RemoteException +import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer +import org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin +import org.apache.tinkerpop.gremlin.jsr223.console.ConsoleCustomizer import org.codehaus.groovy.tools.shell.Groovysh import org.codehaus.groovy.tools.shell.IO @@ -55,4 +63,75 @@ class PluggedIn { void deactivate() { this.activated = false } + + public static class GremlinPluginAdapter implements GremlinPlugin { + org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin corePlugin + + public GremlinPluginAdapter(final org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin corePlugin) { + this.corePlugin = corePlugin + } + + @Override + String getName() { + return corePlugin.getName() + } + + @Override + void pluginTo(final PluginAcceptor pluginAcceptor) throws IllegalEnvironmentException, PluginInitializationException { + // TODO: handle script customizer + corePlugin.getCustomizers("gremlin-groovy").each { + if (it instanceof ImportCustomizer) { + def imports = [] as Set + it.classImports.each { imports.add("import " + it.canonicalName )} + it.methodImports.each { imports.add("import static " + it.declaringClass.canonicalName + "." + it.name) } + it.enumImports.each { imports.add("import static " + it.declaringClass.canonicalName + "." + it.name()) } + pluginAcceptor.addImports(imports) + } + } + } + + @Override + boolean requireRestart() { + return corePlugin.requireRestart() + } + + @Override + Optional remoteAcceptor() { + // find a consoleCustomizer if available + if (!corePlugin.getCustomizers("gremlin-groovy").any{ it instanceof ConsoleCustomizer }) + Optional.empty() + + ConsoleCustomizer customizer = (ConsoleCustomizer) corePlugin.getCustomizers("gremlin-groovy").find{ it instanceof ConsoleCustomizer } + return Optional.of(new RemoteAcceptorAdapter(customizer.remoteAcceptor)) + } + } + + public static class RemoteAcceptorAdapter implements RemoteAcceptor { + + private org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor remoteAcceptor + + public RemoteAcceptorAdapter(org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor remoteAcceptor) { + this.remoteAcceptor = remoteAcceptor + } + + @Override + Object connect(final List args) throws RemoteException { + return remoteAcceptor.connect(args) + } + + @Override + Object configure(final List args) throws RemoteException { + return remoteAcceptor.configure(args) + } + + @Override + Object submit(final List args) throws RemoteException { + return remoteAcceptor.submit(args) + } + + @Override + void close() throws IOException { + remoteAcceptor.close() + } + } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/Customizer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/Customizer.java index 60dc810a5dc..ee3548c0af6 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/Customizer.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/Customizer.java @@ -19,7 +19,9 @@ package org.apache.tinkerpop.gremlin.jsr223; /** - * The {@code Customizer} provides a general way to provide configurations to a {@link GremlinScriptEngine}. + * The {@code Customizer} provides a general way to provide configurations to a {@link GremlinScriptEngine}. This is an + * "internal" interface that acts as a marker and should not be implemented directly. Those wishing to write a + * {@code Customizer} should use one of its sub-interfaces, like {@link ImportCustomizer} * * @author Stephen Mallette (http://stephen.genoprime.com) */ diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinModule.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinModule.java index 1345841c58d..f05b51c79dd 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinModule.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinModule.java @@ -18,6 +18,8 @@ */ package org.apache.tinkerpop.gremlin.jsr223; +import org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor; + import java.util.Optional; /** diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinPlugin.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinPlugin.java index 390c0278c1b..163e364f0b0 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinPlugin.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinPlugin.java @@ -58,13 +58,4 @@ public default Optional getCustomizers(){ * @param scriptEngineName The name of the {@code ScriptEngine} or null to get all the available {@code Customizers} */ public Optional getCustomizers(final String scriptEngineName); - - /** - * Allows a plugin to utilize features of the {@code :remote} and {@code :submit} commands of the Gremlin Console. - * This method does not need to be implemented if the plugin is not meant for the Console for some reason or - * if it does not intend to take advantage of those commands. - */ - public default Optional remoteAcceptor() { - return Optional.empty(); - } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinPluginException.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinPluginException.java new file mode 100644 index 00000000000..ce4ab2ffecc --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinPluginException.java @@ -0,0 +1,41 @@ +/* + * 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; + +/** + * Base exception for {@link GremlinPlugin}. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public abstract class GremlinPluginException extends Exception { + public GremlinPluginException() { + } + + public GremlinPluginException(final String message) { + super(message); + } + + public GremlinPluginException(final String message, final Throwable cause) { + super(message, cause); + } + + public GremlinPluginException(final Throwable cause) { + super(cause); + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/ConsoleCustomizer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/ConsoleCustomizer.java new file mode 100644 index 00000000000..47771bcce0a --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/ConsoleCustomizer.java @@ -0,0 +1,33 @@ +/* + * 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.console; + +import org.apache.tinkerpop.gremlin.jsr223.Customizer; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public interface ConsoleCustomizer extends Customizer { + /** + * Allows a plugin to utilize features of the {@code :remote} and {@code :submit} commands of the Gremlin Console. + * This method does not need to be implemented if the plugin is not meant for the Console for some reason or + * if it does not intend to take advantage of those commands. + */ + public RemoteAcceptor getRemoteAcceptor(); +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/PluginAcceptor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/PluginAcceptor.java new file mode 100644 index 00000000000..ce8b9138823 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/PluginAcceptor.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.console; + +import org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin; + +import javax.script.ScriptException; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/** + * A {@link GremlinPlugin} can be used in multiple environments (e.g. ScriptEngine implementation). Any environment + * wishing to allow plugins should implement the {@code PluginAcceptor}. It acts as an adapter to those environments + * and provides the abstractions required for a plugin to work regardless of the environmental implementations. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public interface PluginAcceptor { + + public void addImports(final Set importStatements); + + /** + * Add a variable binding for the plugin host. + */ + public void addBinding(final String key, final Object val); + + /** + * Gets the list of bindings from the plugin host. These bindings will represent the "global" binding list. + */ + public Map getBindings(); + + /** + * Evaluate a script in the {@code PluginAcceptor}. + */ + public Object eval(final String script) throws ScriptException; + + /** + * Returns a map of implementation specific variables that can be referenced by the plugin. Those writing + * plugins should examine the details of the various {@code PluginAcceptor} implementations for the variables + * that they pass, as they may provide important information useful to the plugin itself. + */ + public default Map environment() { + return Collections.emptyMap(); + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/RemoteAcceptor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/RemoteAcceptor.java similarity index 95% rename from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/RemoteAcceptor.java rename to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/RemoteAcceptor.java index 9c96892fcaf..aee77ae388d 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/RemoteAcceptor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/RemoteAcceptor.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.tinkerpop.gremlin.jsr223; +package org.apache.tinkerpop.gremlin.jsr223.console; import java.io.Closeable; import java.util.List; @@ -27,8 +27,8 @@ * A "remote connection" does not necessarily have to be a remote server. It simply refers to a resource that is * external to the console. *

- * By implementing this interface and returning an instance of it through {@link GremlinPlugin#remoteAcceptor()} a - * plugin can hook into those commands and provide remoting features. + * By implementing this interface and returning an instance of it through {@link ConsoleCustomizer#getRemoteAcceptor()} + * a plugin can hook into those commands and provide remoting features. * * @author Stephen Mallette (http://stephen.genoprime.com) */ diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/RemoteException.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/RemoteException.java similarity index 96% rename from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/RemoteException.java rename to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/RemoteException.java index a75e6d6e578..7f4850b78a5 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/RemoteException.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/RemoteException.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.tinkerpop.gremlin.jsr223; +package org.apache.tinkerpop.gremlin.jsr223.console; /** * A mapper {@code Exception} to be thrown when there are problems with processing a command given to a diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/GremlinPlugin.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/GremlinPlugin.java index 9e2b94e39a1..86fb0d8d0ad 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/GremlinPlugin.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/GremlinPlugin.java @@ -28,6 +28,7 @@ * of this interface to install. * * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As for 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin} */ public interface GremlinPlugin { public static final String ENVIRONMENT = "GremlinPlugin.env"; diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/PluginAcceptor.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/PluginAcceptor.java index 292ba3501de..957138116ee 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/PluginAcceptor.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/PluginAcceptor.java @@ -31,7 +31,9 @@ * and provides the abstractions required for a plugin to work regardless of the environmental implementations. * * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.console.PluginAcceptor}. */ +@Deprecated public interface PluginAcceptor { /** * If the {@code PluginAcceptor} implements the {@link DependencyManager} interface it will try to import the diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/RemoteAcceptor.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/RemoteAcceptor.java index b3d03fe8c36..2bb8663d0a1 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/RemoteAcceptor.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/RemoteAcceptor.java @@ -33,7 +33,7 @@ * plugin can hook into those commands and provide remoting features. * * @author Stephen Mallette (http://stephen.genoprime.com) - * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.jsr223.RemoteAcceptor}; + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor}; */ @Deprecated public interface RemoteAcceptor extends Closeable { diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java index 34a37ae0903..9910a4c856e 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java @@ -56,6 +56,7 @@ public void shouldHaveCoreImportsInPlace() throws Exception { } } + @org.junit.Ignore("TEMPORARY - until GremlinJythonScriptEngine supports this stuff") @Test public void shouldSupportDeprecatedGremlinModules() throws Exception { final GremlinScriptEngineManager mgr = new DefaultGremlinScriptEngineManager(); From 2537d5bbd686f77449bb9d64148fecf2ad9d0d3a Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Tue, 22 Nov 2016 06:33:22 -0500 Subject: [PATCH 11/45] TINKERPOP-1562 ImportCustomizer has become an interface This is a breaking change unfortunately, but this was not a feature that was published or used in 3.2.3 so I don't expect wide usage. --- .../giraph/jsr223/GiraphGremlinPlugin.java | 4 +- .../gremlin/console/plugin/PluggedIn.groovy | 3 +- .../gremlin/jsr223/CoreGremlinModule.java | 2 +- .../gremlin/jsr223/CoreGremlinPlugin.java | 2 +- .../jsr223/DefaultImportCustomizer.java | 112 ++++++++++++++++++ .../gremlin/jsr223/ImportCustomizer.java | 85 +------------ .../gremlin/jsr223/ImportGremlinPlugin.java | 2 +- .../jsr223/ImportGremlinPluginTest.java | 12 +- ...tDefaultImportCustomizerProviderTest.java} | 2 +- .../jsr223/GremlinServerGremlinPlugin.java | 4 +- .../GremlinEnabledScriptEngineTest.java | 3 +- 11 files changed, 132 insertions(+), 99 deletions(-) create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultImportCustomizer.java rename gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/{DefaultImportCustomizerProviderTest.java => DefaultDefaultImportCustomizerProviderTest.java} (98%) diff --git a/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/jsr223/GiraphGremlinPlugin.java b/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/jsr223/GiraphGremlinPlugin.java index a96ac86d52c..ee49ed5e79e 100644 --- a/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/jsr223/GiraphGremlinPlugin.java +++ b/giraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/giraph/jsr223/GiraphGremlinPlugin.java @@ -29,7 +29,7 @@ import org.apache.tinkerpop.gremlin.giraph.process.computer.MemoryAggregator; import org.apache.tinkerpop.gremlin.giraph.process.computer.PassThroughMemory; import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; -import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; /** * @author Stephen Mallette (http://stephen.genoprime.com) @@ -39,7 +39,7 @@ public final class GiraphGremlinPlugin extends AbstractGremlinPlugin { private static final GiraphGremlinPlugin instance = new GiraphGremlinPlugin(); private GiraphGremlinPlugin() { - super(MODULE_NAME, ImportCustomizer.build().addClassImports( + super(MODULE_NAME, DefaultImportCustomizer.build().addClassImports( EmptyOutEdges.class, GiraphComputation.class, GiraphGraphComputer.class, diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy index def49ed1e7a..053a072d863 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy @@ -24,8 +24,7 @@ import org.apache.tinkerpop.gremlin.groovy.plugin.PluginAcceptor import org.apache.tinkerpop.gremlin.groovy.plugin.PluginInitializationException import org.apache.tinkerpop.gremlin.groovy.plugin.RemoteAcceptor import org.apache.tinkerpop.gremlin.groovy.plugin.RemoteException -import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer -import org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin +import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer import org.apache.tinkerpop.gremlin.jsr223.console.ConsoleCustomizer import org.codehaus.groovy.tools.shell.Groovysh import org.codehaus.groovy.tools.shell.IO 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 686906499b3..d398f89872f 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 @@ -33,7 +33,7 @@ public final class CoreGremlinModule implements GremlinModule { private static final String MODULE_NAME = "tinkerpop.core"; - private static final ImportCustomizer gremlinCore = ImportCustomizer.build() + private static final ImportCustomizer gremlinCore = DefaultImportCustomizer.build() .addClassImports(CoreImports.getClassImports()) .addEnumImports(CoreImports.getEnumImports()) .addMethodImports(CoreImports.getMethodImports()).create(); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinPlugin.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinPlugin.java index 3f5ac2c25b8..410b222ab0b 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinPlugin.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinPlugin.java @@ -31,7 +31,7 @@ public final class CoreGremlinPlugin implements GremlinPlugin { private static final String MODULE_NAME = "tinkerpop.core"; - private static final ImportCustomizer gremlinCore = ImportCustomizer.build() + private static final ImportCustomizer gremlinCore = DefaultImportCustomizer.build() .addClassImports(CoreImports.getClassImports()) .addEnumImports(CoreImports.getEnumImports()) .addMethodImports(CoreImports.getMethodImports()).create(); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultImportCustomizer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultImportCustomizer.java new file mode 100644 index 00000000000..85d6531e852 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultImportCustomizer.java @@ -0,0 +1,112 @@ +/* + * 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.CoreImports; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * Simple implementation of the {@link ImportCustomizer} which allows direct setting of all the different import types. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class DefaultImportCustomizer implements ImportCustomizer { + /** + * @deprecated As of release 3.2.4, not replaced. + */ + @Deprecated + public static final ImportCustomizer GREMLIN_CORE = DefaultImportCustomizer.build() + .addClassImports(CoreImports.getClassImports()) + .addEnumImports(CoreImports.getEnumImports()) + .addMethodImports(CoreImports.getMethodImports()).create(); + + private final Set classImports; + private final Set methodImports; + private final Set enumImports; + + private DefaultImportCustomizer(final Builder builder) { + classImports = builder.classImports; + methodImports = builder.methodImports; + enumImports = builder.enumImports; + } + + public Set getClassImports() { + return Collections.unmodifiableSet(classImports); + } + + public Set getMethodImports() { + return Collections.unmodifiableSet(methodImports); + } + + public Set getEnumImports() { + return Collections.unmodifiableSet(enumImports); + } + + public static Builder build() { + return new Builder(); + } + + public static class Builder { + private Set classImports = new HashSet<>(); + private Set methodImports = new HashSet<>(); + private Set enumImports = new HashSet<>(); + + private Builder() {} + + public Builder addClassImports(final Class... clazz) { + classImports.addAll(Arrays.asList(clazz)); + return this; + } + + public Builder addClassImports(final Collection classes) { + classImports.addAll(classes); + return this; + } + + public Builder addMethodImports(final Method... method) { + methodImports.addAll(Arrays.asList(method)); + return this; + } + + public Builder addMethodImports(final Collection methods) { + methodImports.addAll(methods); + return this; + } + + public Builder addEnumImports(final Enum... e) { + enumImports.addAll(Arrays.asList(e)); + return this; + } + + public Builder addEnumImports(final Collection enums) { + enumImports.addAll(enums); + return this; + } + + public DefaultImportCustomizer create() { + return new DefaultImportCustomizer(this); + } + } +} 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 2f0e08c3a39..7eced82d0ac 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 @@ -18,13 +18,7 @@ */ package org.apache.tinkerpop.gremlin.jsr223; -import org.apache.tinkerpop.gremlin.util.CoreImports; - import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; import java.util.Set; /** @@ -32,82 +26,11 @@ * * @author Stephen Mallette (http://stephen.genoprime.com) */ -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()) - .addMethodImports(CoreImports.getMethodImports()).create(); - - private final Set classImports; - private final Set methodImports; - private final Set enumImports; - - private ImportCustomizer(final Builder builder) { - classImports = builder.classImports; - methodImports = builder.methodImports; - enumImports = builder.enumImports; - } - - public Set getClassImports() { - return Collections.unmodifiableSet(classImports); - } - - public Set getMethodImports() { - return Collections.unmodifiableSet(methodImports); - } - - public Set getEnumImports() { - return Collections.unmodifiableSet(enumImports); - } - - public static Builder build() { - return new Builder(); - } - - public static class Builder { - private Set classImports = new HashSet<>(); - private Set methodImports = new HashSet<>(); - private Set enumImports = new HashSet<>(); - - private Builder() {} - - public Builder addClassImports(final Class... clazz) { - classImports.addAll(Arrays.asList(clazz)); - return this; - } - - public Builder addClassImports(final Collection classes) { - classImports.addAll(classes); - return this; - } - - public Builder addMethodImports(final Method... method) { - methodImports.addAll(Arrays.asList(method)); - return this; - } - - public Builder addMethodImports(final Collection methods) { - methodImports.addAll(methods); - return this; - } +public interface ImportCustomizer extends Customizer { - public Builder addEnumImports(final Enum... e) { - enumImports.addAll(Arrays.asList(e)); - return this; - } + public Set getClassImports(); - public Builder addEnumImports(final Collection enums) { - enumImports.addAll(enums); - return this; - } + public Set getMethodImports(); - public ImportCustomizer create() { - return new ImportCustomizer(this); - } - } + public Set getEnumImports(); } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPlugin.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPlugin.java index f99ee68d6c2..a5ac27854a4 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPlugin.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPlugin.java @@ -40,7 +40,7 @@ public final class ImportGremlinPlugin extends AbstractGremlinPlugin { private static final String MODULE_NAME = "tinkerpop.import"; private ImportGremlinPlugin(final Builder builder) { - super(MODULE_NAME, builder.appliesTo, ImportCustomizer.build() + super(MODULE_NAME, builder.appliesTo, DefaultImportCustomizer.build() .addClassImports(builder.classImports) .addEnumImports(builder.enumImports) .addMethodImports(builder.methodImports).create()); diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPluginTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPluginTest.java index 428fc57817c..59c64dfed14 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPluginTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPluginTest.java @@ -49,7 +49,7 @@ public void shouldImportClass() { final ImportGremlinPlugin module = ImportGremlinPlugin.build() .classImports(Collections.singletonList(Graph.class.getCanonicalName())).create(); - final ImportCustomizer customizer = (ImportCustomizer) module.getCustomizers().get()[0]; + final DefaultImportCustomizer customizer = (DefaultImportCustomizer) module.getCustomizers().get()[0]; assertEquals(1, module.getCustomizers().get().length); assertThat(customizer.getClassImports(), hasItems(Graph.class)); assertEquals(1, customizer.getClassImports().size()); @@ -61,7 +61,7 @@ public void shouldImportWildcardMethod() throws Exception { final ImportGremlinPlugin module = ImportGremlinPlugin.build() .methodImports(Collections.singletonList(Gremlin.class.getCanonicalName() + "#*")).create(); - final ImportCustomizer customizer = (ImportCustomizer) module.getCustomizers().get()[0]; + final DefaultImportCustomizer customizer = (DefaultImportCustomizer) module.getCustomizers().get()[0]; assertEquals(1, module.getCustomizers().get().length); assertThat(customizer.getMethodImports(), hasItems(zeroArgs)); @@ -75,7 +75,7 @@ public void shouldImportZeroArgMethod() throws Exception { final ImportGremlinPlugin module = ImportGremlinPlugin.build() .methodImports(Collections.singletonList(toMethodDescriptor(zeroArgs))).create(); - final ImportCustomizer customizer = (ImportCustomizer) module.getCustomizers().get()[0]; + final DefaultImportCustomizer customizer = (DefaultImportCustomizer) module.getCustomizers().get()[0]; assertEquals(1, module.getCustomizers().get().length); assertThat(customizer.getMethodImports(), hasItems(zeroArgs)); assertEquals(1, customizer.getMethodImports().size()); @@ -87,7 +87,7 @@ public void shouldImportSingleArgMethod() throws Exception { final ImportGremlinPlugin module = ImportGremlinPlugin.build() .methodImports(Collections.singletonList(toMethodDescriptor(singleArg))).create(); - final ImportCustomizer customizer = (ImportCustomizer) module.getCustomizers().get()[0]; + final DefaultImportCustomizer customizer = (DefaultImportCustomizer) module.getCustomizers().get()[0]; assertEquals(1, module.getCustomizers().get().length); assertThat(customizer.getMethodImports(), hasItems(singleArg)); assertEquals(1, customizer.getMethodImports().size()); @@ -110,7 +110,7 @@ public void shouldImportWildcardEnum() throws Exception { final ImportGremlinPlugin module = ImportGremlinPlugin.build() .enumImports(Collections.singletonList(T.class.getCanonicalName() + "#*")).create(); - final ImportCustomizer customizer = (ImportCustomizer) module.getCustomizers().get()[0]; + final DefaultImportCustomizer customizer = (DefaultImportCustomizer) 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()); @@ -121,7 +121,7 @@ public void shouldImportEnum() throws Exception { final ImportGremlinPlugin module = ImportGremlinPlugin.build() .enumImports(Collections.singletonList(T.class.getCanonicalName() + "#" + T.id.name())).create(); - final ImportCustomizer customizer = (ImportCustomizer) module.getCustomizers().get()[0]; + final DefaultImportCustomizer customizer = (DefaultImportCustomizer) module.getCustomizers().get()[0]; assertEquals(1, module.getCustomizers().get().length); assertThat(customizer.getEnumImports(), hasItems(T.id)); } diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/DefaultImportCustomizerProviderTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/DefaultDefaultImportCustomizerProviderTest.java similarity index 98% rename from gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/DefaultImportCustomizerProviderTest.java rename to gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/DefaultDefaultImportCustomizerProviderTest.java index 74df889f141..9a3785f1c90 100644 --- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/DefaultImportCustomizerProviderTest.java +++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/DefaultDefaultImportCustomizerProviderTest.java @@ -30,7 +30,7 @@ /** * @author Stephen Mallette (http://stephen.genoprime.com) */ -public class DefaultImportCustomizerProviderTest { +public class DefaultDefaultImportCustomizerProviderTest { static { SugarLoader.load(); } diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/jsr223/GremlinServerGremlinPlugin.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/jsr223/GremlinServerGremlinPlugin.java index e8826ae7161..a08aa76c943 100644 --- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/jsr223/GremlinServerGremlinPlugin.java +++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/jsr223/GremlinServerGremlinPlugin.java @@ -20,7 +20,7 @@ import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; import org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin; -import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; import org.apache.tinkerpop.gremlin.server.util.LifeCycleHook; /** @@ -33,7 +33,7 @@ public final class GremlinServerGremlinPlugin extends AbstractGremlinPlugin { private static final GremlinServerGremlinPlugin instance = new GremlinServerGremlinPlugin(); private GremlinServerGremlinPlugin() { - super(MODULE_NAME, ImportCustomizer.build().addClassImports(LifeCycleHook.class, LifeCycleHook.Context.class).create()); + super(MODULE_NAME, DefaultImportCustomizer.build().addClassImports(LifeCycleHook.class, LifeCycleHook.Context.class).create()); } public static GremlinServerGremlinPlugin instance() { diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java index 9910a4c856e..723d8984820 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java @@ -25,7 +25,6 @@ import org.junit.Test; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Optional; @@ -68,7 +67,7 @@ public String getName() { @Override public Optional getCustomizers(final String scriptEngineName) { - return Optional.of(new Customizer[] {ImportCustomizer.build() + return Optional.of(new Customizer[] {DefaultImportCustomizer.build() .addClassImports(java.awt.Color.class) .addClassImports(java.sql.CallableStatement.class) .create() }); From e9e5cfec66bca5f8beb671625f7fe2fff0038907 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Tue, 22 Nov 2016 06:49:14 -0500 Subject: [PATCH 12/45] TINKERPOP-1562 Change ScriptCustomizer to an interface. This class had been added as part of this branch so recasting it as an interface is non-breaking. Get PluggedIn to process the ScriptCustomizer. --- .../gremlin/console/plugin/PluggedIn.groovy | 7 ++- .../jsr223/DefaultScriptCustomizer.java | 57 +++++++++++++++++++ .../gremlin/jsr223/ScriptCustomizer.java | 36 +++--------- .../jsr223/ScriptFileGremlinPlugin.java | 2 +- 4 files changed, 70 insertions(+), 32 deletions(-) create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultScriptCustomizer.java diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy index 053a072d863..54659fa9c42 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy @@ -24,7 +24,8 @@ import org.apache.tinkerpop.gremlin.groovy.plugin.PluginAcceptor import org.apache.tinkerpop.gremlin.groovy.plugin.PluginInitializationException import org.apache.tinkerpop.gremlin.groovy.plugin.RemoteAcceptor import org.apache.tinkerpop.gremlin.groovy.plugin.RemoteException -import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer +import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer +import org.apache.tinkerpop.gremlin.jsr223.ScriptCustomizer import org.apache.tinkerpop.gremlin.jsr223.console.ConsoleCustomizer import org.codehaus.groovy.tools.shell.Groovysh import org.codehaus.groovy.tools.shell.IO @@ -33,6 +34,7 @@ import org.codehaus.groovy.tools.shell.IO * @author Stephen Mallette (http://stephen.genoprime.com) */ class PluggedIn { + private static final String LINE_SEPARATOR = System.getProperty("line.separator") private final GremlinPlugin plugin private boolean activated = false @@ -77,7 +79,6 @@ class PluggedIn { @Override void pluginTo(final PluginAcceptor pluginAcceptor) throws IllegalEnvironmentException, PluginInitializationException { - // TODO: handle script customizer corePlugin.getCustomizers("gremlin-groovy").each { if (it instanceof ImportCustomizer) { def imports = [] as Set @@ -85,6 +86,8 @@ class PluggedIn { it.methodImports.each { imports.add("import static " + it.declaringClass.canonicalName + "." + it.name) } it.enumImports.each { imports.add("import static " + it.declaringClass.canonicalName + "." + it.name()) } pluginAcceptor.addImports(imports) + } else if (it instanceof ScriptCustomizer) { + it.getScripts().collect { it.join(LINE_SEPARATOR) }.each { pluginAcceptor.eval(it) } } } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultScriptCustomizer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultScriptCustomizer.java new file mode 100644 index 00000000000..9640f28ae24 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultScriptCustomizer.java @@ -0,0 +1,57 @@ +/* + * 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.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Default implementation of the {@link ScriptCustomizer} that can create the script list from a list of files or + * from lines of script. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class DefaultScriptCustomizer implements ScriptCustomizer { + + private final Collection> scripts; + + public DefaultScriptCustomizer(final Set 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 DefaultScriptCustomizer(final Collection> scripts) { + this.scripts = scripts; + } + + public Collection> getScripts() { + return scripts; + } +} 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 index 28603df2aaa..eb2f8bc3b9e 100644 --- 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 @@ -18,39 +18,17 @@ */ 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; /** + * A {@link Customizer} that executes scripts in a {@link GremlinScriptEngine} instance for purpose of initialization. + * * @author Stephen Mallette (http://stephen.genoprime.com) */ -public class ScriptCustomizer implements Customizer { - - private final Collection> scripts; - - public ScriptCustomizer(final Set 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> scripts) { - this.scripts = scripts; - } - - public Collection> scripts() { - return scripts; - } +public interface ScriptCustomizer extends Customizer { + /** + * Gets a collection of scripts where each is represented as a list of script lines. + */ + public Collection> getScripts(); } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java index 30e66ed7260..3fd811a5593 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java @@ -31,7 +31,7 @@ public final class ScriptFileGremlinPlugin extends AbstractGremlinPlugin { private static final String MODULE_NAME = "tinkerpop.script"; public ScriptFileGremlinPlugin(final Builder builder) { - super(MODULE_NAME, builder.appliesTo, new ScriptCustomizer(builder.files)); + super(MODULE_NAME, builder.appliesTo, new DefaultScriptCustomizer(builder.files)); } public static Builder build() { From aee85b12f55c2a6b6426e6386677d6bec46513a8 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Tue, 22 Nov 2016 06:59:49 -0500 Subject: [PATCH 13/45] TINKERPOP-1562 Rename "modules" as "plugin" --- .../groovy/engine/GremlinExecutor.java | 40 +++++++++---------- .../tinkerpop/gremlin/server/Settings.java | 4 +- .../server/util/ServerGremlinExecutor.java | 6 +-- 3 files changed, 25 insertions(+), 25 deletions(-) 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 4449e1b798a..84f199220aa 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 @@ -85,7 +85,7 @@ public class GremlinExecutor implements AutoCloseable { private GremlinScriptEngineManager gremlinScriptEngineManager; private final Map settings; - private final Map>> modules; + private final Map>> plugins; private final long scriptEvaluationTimeout; private final Bindings globalBindings; private final List> use; @@ -111,7 +111,7 @@ private GremlinExecutor(final Builder builder, final boolean suppliedExecutor, this.afterFailure = builder.afterFailure; this.use = builder.use; this.settings = builder.settings; - this.modules = builder.modules; + this.plugins = builder.plugins; this.scriptEvaluationTimeout = builder.scriptEvaluationTimeout; this.globalBindings = builder.globalBindings; this.enabledPlugins = builder.enabledPlugins; @@ -426,14 +426,14 @@ public CompletableFuture closeAsync() throws Exception { } private void initializeGremlinScriptEngineManager() { - this.useGremlinScriptEngineManager = !modules.entrySet().isEmpty(); + this.useGremlinScriptEngineManager = !plugins.entrySet().isEmpty(); - for (Map.Entry>> config : modules.entrySet()) { + for (Map.Entry>> config : plugins.entrySet()) { final String language = config.getKey(); - final Map> moduleConfigs = config.getValue(); - for (Map.Entry> moduleConfig : moduleConfigs.entrySet()) { + final Map> pluginConfigs = config.getValue(); + for (Map.Entry> pluginConfig : pluginConfigs.entrySet()) { try { - final Class clazz = Class.forName(moduleConfig.getKey()); + final Class clazz = Class.forName(pluginConfig.getKey()); // first try instance() and if that fails try to use build() try { @@ -441,29 +441,29 @@ private void initializeGremlinScriptEngineManager() { gremlinScriptEngineManager.addPlugin((GremlinPlugin) instanceMethod.invoke(null)); } catch (Exception ex) { final Method builderMethod = clazz.getMethod("build"); - Object moduleBuilder = builderMethod.invoke(null); + Object pluginBuilder = builderMethod.invoke(null); - final Class builderClazz = moduleBuilder.getClass(); - final Map customizerConfigs = moduleConfig.getValue(); + final Class builderClazz = pluginBuilder.getClass(); + final Map customizerConfigs = pluginConfig.getValue(); final Method[] methods = builderClazz.getMethods(); for (Map.Entry 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); + pluginBuilder = configMethod.invoke(pluginBuilder); else - moduleBuilder = configMethod.invoke(moduleBuilder, customizerConfig.getValue()); + pluginBuilder = configMethod.invoke(pluginBuilder, customizerConfig.getValue()); } try { final Method appliesTo = builderClazz.getMethod("appliesTo"); - moduleBuilder = appliesTo.invoke(moduleBuilder, language); + pluginBuilder = appliesTo.invoke(pluginBuilder, language); } catch (NoSuchMethodException ignored) { } final Method create = builderClazz.getMethod("create"); - gremlinScriptEngineManager.addPlugin((GremlinPlugin) create.invoke(moduleBuilder)); + gremlinScriptEngineManager.addPlugin((GremlinPlugin) create.invoke(pluginBuilder)); } } catch (Exception ex) { throw new IllegalStateException(ex); @@ -569,7 +569,7 @@ public final static class Builder { private long scriptEvaluationTimeout = 8000; private Map settings = new HashMap<>(); - private Map>> modules = new HashMap<>(); + private Map>> plugins = new HashMap<>(); private ExecutorService executorService = null; private ScheduledExecutorService scheduledExecutorService = null; @@ -597,7 +597,7 @@ private Builder() { * @param scripts A list of scripts to execute in the engine to initialize it. * @param config Custom configuration map for the ScriptEngine * - * @deprecated As of release 3.2.4, replaced by {@link #addModules(String, Map)}. + * @deprecated As of release 3.2.4, replaced by {@link #addPlugins(String, Map)}. */ @Deprecated public Builder addEngineSettings(final String engineName, final List imports, @@ -618,8 +618,8 @@ public Builder addEngineSettings(final String engineName, final List imp * is the name of a builder method on the {@link GremlinPlugin} and the value is some argument to pass to that * method. */ - public Builder addModules(final String engineName, final Map> modules) { - this.modules.put(engineName, modules); + public Builder addPlugins(final String engineName, final Map> plugins) { + this.plugins.put(engineName, plugins); return this; } @@ -649,7 +649,7 @@ public Builder scriptEvaluationTimeout(final long scriptEvaluationTimeout) { /** * Replaces any settings provided. * - * @deprecated As of release 3.2.4, replaced by {@link #addModules(String, Map)}. + * @deprecated As of release 3.2.4, replaced by {@link #addPlugins(String, Map)}. */ @Deprecated public Builder engineSettings(final Map settings) { @@ -721,7 +721,7 @@ public Builder use(final List> use) { /** * Set of the names of plugins that should be enabled for the engine. * - * @deprecated As of release 3.2.4, replaced by {@link #addModules(String, Map)} though behavior is not quite + * @deprecated As of release 3.2.4, replaced by {@link #addPlugins(String, Map)} though behavior is not quite * the same. */ @Deprecated 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 75cff3b0b9d..97e28751dd0 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 @@ -260,7 +260,7 @@ public static Settings read(final InputStream stream) { scriptEngineSettingsDescription.putListPropertyType("staticImports", String.class); scriptEngineSettingsDescription.putListPropertyType("scripts", String.class); scriptEngineSettingsDescription.putMapPropertyType("config", String.class, Object.class); - scriptEngineSettingsDescription.putMapPropertyType("modules", String.class, Object.class); + scriptEngineSettingsDescription.putMapPropertyType("plugins", String.class, Object.class); constructor.addTypeDescription(scriptEngineSettingsDescription); final TypeDescription sslSettings = new TypeDescription(SslSettings.class); @@ -343,7 +343,7 @@ public static class ScriptEngineSettings { /** * A set of configurations for {@link GremlinPlugin} instances to apply to this {@link GremlinScriptEngine}. */ - public Map> modules = new HashMap<>(); + public Map> plugins = new HashMap<>(); } /** 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 1204bee4e86..2b2f1f1ce4b 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 @@ -127,8 +127,8 @@ public ServerGremlinExecutor(final Settings settings, final ExecutorService grem .scheduledExecutorService(this.scheduledExecutorService); settings.scriptEngines.forEach((k, v) -> { - // use modules if they are present and the old approach if not - if (v.modules.isEmpty()) { + // use plugins if they are present and the old approach if not + if (v.plugins.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()); @@ -137,7 +137,7 @@ public ServerGremlinExecutor(final Settings settings, final ExecutorService grem } else { // make sure that server related classes are available at init - new approach. the LifeCycleHook stuff // will be added explicitly via configuration using GremlinServerGremlinModule in the yaml - gremlinExecutorBuilder.addModules(k, v.modules); + gremlinExecutorBuilder.addPlugins(k, v.plugins); } }); From a4fa9da27b4334141c3001f628883adbcad6ca19 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Tue, 22 Nov 2016 09:39:37 -0500 Subject: [PATCH 14/45] TINKERPOP-1562 Fixed problems related to loading new/old plugins Tested in Gremlin Console and it looks like the flag that will tell it to load one plugin version or the other is working properly. --- .../tinkerpop/gremlin/console/Mediator.groovy | 2 +- .../gremlin/console/plugin/PluggedIn.groovy | 2 +- .../jsr223/TinkerGraphGremlinPlugin.java | 77 +++++++++++++++++++ ...che.tinkerpop.gremlin.jsr223.GremlinPlugin | 1 + 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/jsr223/TinkerGraphGremlinPlugin.java create mode 100644 tinkergraph-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Mediator.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Mediator.groovy index 396c563f0e3..18e8d58468a 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Mediator.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Mediator.groovy @@ -34,7 +34,7 @@ class Mediator { private static String LINE_SEP = System.getProperty("line.separator") - public static final boolean useV3d3 = System.getProperty("plugins", "v3d3") == "v3d3" + public static final boolean useV3d3 = System.getProperty("plugins", "v3d2") == "v3d3" public Mediator(final Console console) { this.console = console diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy index 54659fa9c42..d298cd79932 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy @@ -79,7 +79,7 @@ class PluggedIn { @Override void pluginTo(final PluginAcceptor pluginAcceptor) throws IllegalEnvironmentException, PluginInitializationException { - corePlugin.getCustomizers("gremlin-groovy").each { + corePlugin.getCustomizers("gremlin-groovy").get().each { if (it instanceof ImportCustomizer) { def imports = [] as Set it.classImports.each { imports.add("import " + it.canonicalName )} diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/jsr223/TinkerGraphGremlinPlugin.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/jsr223/TinkerGraphGremlinPlugin.java new file mode 100644 index 00000000000..16a6cb5f83b --- /dev/null +++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/jsr223/TinkerGraphGremlinPlugin.java @@ -0,0 +1,77 @@ +/* + * 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.tinkergraph.jsr223; + +import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; +import org.apache.tinkerpop.gremlin.jsr223.Customizer; +import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; +import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComputer; +import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComputerView; +import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerMapEmitter; +import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerMemory; +import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerMessenger; +import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerReduceEmitter; +import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerWorkerPool; +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerEdge; +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerElement; +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory; +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph; +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraphVariables; +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerHelper; +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistry; +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV2d0; +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerProperty; +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerVertex; +import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerVertexProperty; + +import java.util.Optional; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public final class TinkerGraphGremlinPlugin extends AbstractGremlinPlugin { + private static final String MODULE_NAME = "tinkerpop.tinkergraph"; + + public TinkerGraphGremlinPlugin() { + super(MODULE_NAME, DefaultImportCustomizer.build().addClassImports( + TinkerEdge.class, + TinkerElement.class, + TinkerFactory.class, + TinkerGraph.class, + TinkerGraphVariables.class, + TinkerHelper.class, + TinkerIoRegistry.class, + TinkerIoRegistryV2d0.class, + TinkerProperty.class, + TinkerVertex.class, + TinkerVertexProperty.class, + TinkerGraphComputer.class, + TinkerGraphComputerView.class, + TinkerMapEmitter.class, + TinkerMemory.class, + TinkerMessenger.class, + TinkerReduceEmitter.class, + TinkerWorkerPool.class).create()); + } + + @Override + public Optional getCustomizers() { + return null; + } +} diff --git a/tinkergraph-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin b/tinkergraph-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin new file mode 100644 index 00000000000..f63b96fa630 --- /dev/null +++ b/tinkergraph-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin @@ -0,0 +1 @@ +org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin \ No newline at end of file From f5a1ebb54f698b4957435e05297befcc2ab97ef9 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Tue, 22 Nov 2016 09:42:12 -0500 Subject: [PATCH 15/45] TINKERPOP-1562 Deprecated TinkerGraphGremlinPlugin. --- .../tinkergraph/groovy/plugin/TinkerGraphGremlinPlugin.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/groovy/plugin/TinkerGraphGremlinPlugin.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/groovy/plugin/TinkerGraphGremlinPlugin.java index df353143c71..5c8231afe8b 100644 --- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/groovy/plugin/TinkerGraphGremlinPlugin.java +++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/groovy/plugin/TinkerGraphGremlinPlugin.java @@ -30,10 +30,11 @@ /** * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin}. */ +@Deprecated public final class TinkerGraphGremlinPlugin extends AbstractGremlinPlugin { - private static final Set IMPORTS = new HashSet() {{ add(IMPORT_SPACE + TinkerGraph.class.getPackage().getName() + DOT_STAR); add(IMPORT_SPACE + TinkerGraphComputer.class.getPackage().getName() + DOT_STAR); From c41250ce0a6c87242a974333dd97078a71be06b9 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Tue, 22 Nov 2016 10:57:32 -0500 Subject: [PATCH 16/45] TINKERPOP-1562 Added SparkGremlinPlugin SparkGremlinPlugin required binding injections so a BindingCustomizer was added along with two implementations - one for direct assignment and one for lazy assignment. --- .../gremlin/console/plugin/PluggedIn.groovy | 3 + .../plugin/GremlinPluginAdapterTest.java | 63 +++++++++++++ .../gremlin/jsr223/BindingsCustomizer.java | 33 +++++++ .../jsr223/DefaultBindingsCustomizer.java | 40 ++++++++ .../gremlin/jsr223/ImportGremlinPlugin.java | 16 ++++ .../console/LazyBindingsCustomizer.java | 41 +++++++++ .../groovy/engine/GremlinExecutor.java | 6 +- .../groovy/plugin/SparkGremlinPlugin.java | 2 + .../spark/jsr223/SparkGremlinPlugin.java | 92 +++++++++++++++++++ .../jsr223/TinkerGraphGremlinPlugin.java | 46 +++++----- 10 files changed, 317 insertions(+), 25 deletions(-) create mode 100644 gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/BindingsCustomizer.java create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultBindingsCustomizer.java create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/LazyBindingsCustomizer.java create mode 100644 spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/jsr223/SparkGremlinPlugin.java diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy index d298cd79932..b707226b66c 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy @@ -24,6 +24,7 @@ import org.apache.tinkerpop.gremlin.groovy.plugin.PluginAcceptor import org.apache.tinkerpop.gremlin.groovy.plugin.PluginInitializationException import org.apache.tinkerpop.gremlin.groovy.plugin.RemoteAcceptor import org.apache.tinkerpop.gremlin.groovy.plugin.RemoteException +import org.apache.tinkerpop.gremlin.jsr223.BindingsCustomizer import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer import org.apache.tinkerpop.gremlin.jsr223.ScriptCustomizer import org.apache.tinkerpop.gremlin.jsr223.console.ConsoleCustomizer @@ -88,6 +89,8 @@ class PluggedIn { pluginAcceptor.addImports(imports) } else if (it instanceof ScriptCustomizer) { it.getScripts().collect { it.join(LINE_SEPARATOR) }.each { pluginAcceptor.eval(it) } + } else if (it instanceof BindingsCustomizer) { + it.bindings.entrySet().each { k, v -> pluginAcceptor.addBinding(k,v) } } } } diff --git a/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java b/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java new file mode 100644 index 00000000000..77422da68d2 --- /dev/null +++ b/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.console.groovy.plugin; + +import org.apache.tinkerpop.gremlin.console.plugin.PluggedIn; +import org.apache.tinkerpop.gremlin.jsr223.BindingsCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin; +import org.apache.tinkerpop.gremlin.jsr223.ScriptCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin; +import org.junit.Test; + +import java.time.DayOfWeek; +import java.time.temporal.TemporalAccessor; +import java.util.Set; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsCollectionContaining.hasItems; +import static org.junit.Assert.assertEquals; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class GremlinPluginAdapterTest { + + @Test + public void shouldAdaptForImportCustomizer() throws Exception { + final ImportGremlinPlugin plugin = ImportGremlinPlugin.build() + .classImports(java.awt.Color.class, java.sql.CallableStatement.class) + .enumImports(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY) + .methodImports(DayOfWeek.class.getMethod("from", TemporalAccessor.class), DayOfWeek.class.getMethod("values")).create(); + final PluggedIn.GremlinPluginAdapter adapter = new PluggedIn.GremlinPluginAdapter(plugin); + + assertEquals(plugin.getName(), adapter.getName()); + + final SpyPluginAcceptor spy = new SpyPluginAcceptor(); + adapter.pluginTo(spy); + + final Set imports = spy.getImports(); + assertEquals(6, imports.size()); + assertThat(imports, hasItems("import " + java.awt.Color.class.getCanonicalName())); + assertThat(imports, hasItems("import " + java.sql.CallableStatement.class.getCanonicalName())); + assertThat(imports, hasItems("import static " + DayOfWeek.class.getCanonicalName() + "." + DayOfWeek.SATURDAY.name())); + assertThat(imports, hasItems("import static " + DayOfWeek.class.getCanonicalName() + "." + DayOfWeek.SUNDAY.name())); + assertThat(imports, hasItems("import static " + DayOfWeek.class.getCanonicalName() + ".from")); + assertThat(imports, hasItems("import static " + DayOfWeek.class.getCanonicalName() + ".values")); + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/BindingsCustomizer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/BindingsCustomizer.java new file mode 100644 index 00000000000..02c129e210a --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/BindingsCustomizer.java @@ -0,0 +1,33 @@ +/* + * 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 javax.script.Bindings; + +/** + * Provides a way to alter the bindings on a {@link GremlinScriptEngine}. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public interface BindingsCustomizer extends Customizer { + /** + * Gets the bindings to add to a {@link GremlinScriptEngine}. + */ + public Bindings getBindings(); +} 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 new file mode 100644 index 00000000000..0073d392d56 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultBindingsCustomizer.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.jsr223; + +import javax.script.Bindings; + +/** + * Default implementation of the {@link BindingsCustomizer}. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class DefaultBindingsCustomizer implements BindingsCustomizer { + + private final Bindings bindings; + + public DefaultBindingsCustomizer(final Bindings bindings) { + this.bindings = bindings; + } + + @Override + public Bindings getBindings() { + return bindings; + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPlugin.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPlugin.java index a5ac27854a4..26290d322d0 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPlugin.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPlugin.java @@ -20,6 +20,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; @@ -69,6 +70,11 @@ public Builder appliesTo(final Collection scriptEngineName) { return this; } + public Builder classImports(final Class... classes) { + classImports.addAll(Arrays.asList(classes)); + return this; + } + public Builder classImports(final Collection classes) { for (String clazz : classes) { try { @@ -108,6 +114,11 @@ public Builder methodImports(final Collection methods) { return this; } + public Builder methodImports(final Method... methods) { + methodImports.addAll(Arrays.asList(methods)); + return this; + } + public Builder enumImports(final Collection enums) { for (String enumItem : enums) { try { @@ -138,6 +149,11 @@ public Builder enumImports(final Collection enums) { return this; } + public Builder enumImports(final Enum... enums) { + enumImports.addAll(Arrays.asList(enums)); + return this; + } + public ImportGremlinPlugin create() { if (enumImports.isEmpty() && classImports.isEmpty() && methodImports.isEmpty()) throw new IllegalStateException("At least one import must be specified"); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/LazyBindingsCustomizer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/LazyBindingsCustomizer.java new file mode 100644 index 00000000000..f21a2ab4a3b --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/LazyBindingsCustomizer.java @@ -0,0 +1,41 @@ +/* + * 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.console; + +import org.apache.tinkerpop.gremlin.jsr223.BindingsCustomizer; + +import javax.script.Bindings; +import java.util.function.Supplier; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class LazyBindingsCustomizer implements BindingsCustomizer { + + private final Supplier bindingsSupplier; + + public LazyBindingsCustomizer(final Supplier bindingsSupplier) { + this.bindingsSupplier = bindingsSupplier; + } + + @Override + public Bindings getBindings() { + return bindingsSupplier.get(); + } +} 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 84f199220aa..87d5728987c 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 @@ -447,7 +447,11 @@ private void initializeGremlinScriptEngineManager() { final Map customizerConfigs = pluginConfig.getValue(); final Method[] methods = builderClazz.getMethods(); for (Map.Entry customizerConfig : customizerConfigs.entrySet()) { - final Method configMethod = Stream.of(methods).filter(m -> m.getName().equals(customizerConfig.getKey())).findFirst() + final Method configMethod = Stream.of(methods).filter(m -> { + final Class type = customizerConfig.getValue().getClass(); + return m.getName().equals(customizerConfig.getKey()) && m.getParameters().length <= 1 + && m.getParameters()[0].getType().isAssignableFrom(type); + }).findFirst() .orElseThrow(() -> new IllegalStateException("Could not find builder method on " + builderClazz.getCanonicalName())); if (null == customizerConfig.getValue()) pluginBuilder = configMethod.invoke(pluginBuilder); diff --git a/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/groovy/plugin/SparkGremlinPlugin.java b/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/groovy/plugin/SparkGremlinPlugin.java index 1fe23e3ba9d..c6eb6822fb2 100644 --- a/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/groovy/plugin/SparkGremlinPlugin.java +++ b/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/groovy/plugin/SparkGremlinPlugin.java @@ -34,7 +34,9 @@ /** * @author Marko A. Rodriguez (http://markorodriguez.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.spark.jsr223.SparkGremlinPlugin}. */ +@Deprecated public final class SparkGremlinPlugin extends AbstractGremlinPlugin { protected static String NAME = "tinkerpop.spark"; diff --git a/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/jsr223/SparkGremlinPlugin.java b/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/jsr223/SparkGremlinPlugin.java new file mode 100644 index 00000000000..840f59302d4 --- /dev/null +++ b/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/jsr223/SparkGremlinPlugin.java @@ -0,0 +1,92 @@ +/* + * 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.spark.jsr223; + +import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; +import org.apache.tinkerpop.gremlin.jsr223.BindingsCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.console.LazyBindingsCustomizer; +import org.apache.tinkerpop.gremlin.spark.process.computer.CombineIterator; +import org.apache.tinkerpop.gremlin.spark.process.computer.MapIterator; +import org.apache.tinkerpop.gremlin.spark.process.computer.MemoryAccumulator; +import org.apache.tinkerpop.gremlin.spark.process.computer.ReduceIterator; +import org.apache.tinkerpop.gremlin.spark.process.computer.SparkExecutor; +import org.apache.tinkerpop.gremlin.spark.process.computer.SparkGraphComputer; +import org.apache.tinkerpop.gremlin.spark.process.computer.SparkMemory; +import org.apache.tinkerpop.gremlin.spark.process.computer.SparkMessenger; +import org.apache.tinkerpop.gremlin.spark.structure.Spark; +import org.apache.tinkerpop.gremlin.spark.structure.io.InputFormatRDD; +import org.apache.tinkerpop.gremlin.spark.structure.io.InputOutputHelper; +import org.apache.tinkerpop.gremlin.spark.structure.io.InputRDD; +import org.apache.tinkerpop.gremlin.spark.structure.io.InputRDDFormat; +import org.apache.tinkerpop.gremlin.spark.structure.io.OutputFormatRDD; +import org.apache.tinkerpop.gremlin.spark.structure.io.OutputRDD; +import org.apache.tinkerpop.gremlin.spark.structure.io.PersistedInputRDD; +import org.apache.tinkerpop.gremlin.spark.structure.io.PersistedOutputRDD; +import org.apache.tinkerpop.gremlin.spark.structure.io.SparkContextStorage; + +import javax.script.Bindings; +import javax.script.SimpleBindings; + +/** + * @author Marko A. Rodriguez (http://markorodriguez.com) + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public final class SparkGremlinPlugin extends AbstractGremlinPlugin { + + protected static String NAME = "tinkerpop.spark"; + + private static final ImportCustomizer imports = DefaultImportCustomizer.build().addClassImports( + SparkGraphComputer.class, + CombineIterator.class, + MapIterator.class, + MemoryAccumulator.class, + ReduceIterator.class, + SparkExecutor.class, + SparkGraphComputer.class, + SparkMemory.class, + SparkMessenger.class, + Spark.class, + InputFormatRDD.class, + InputOutputHelper.class, + InputRDD.class, + InputRDDFormat.class, + OutputFormatRDD.class, + OutputRDD.class, + PersistedInputRDD.class, + PersistedOutputRDD.class, + SparkContextStorage.class).create(); + + private static final BindingsCustomizer bindings = new LazyBindingsCustomizer(() -> { + final Bindings bindings = new SimpleBindings(); + bindings.put("spark", SparkContextStorage.open()); + return bindings; + }); + + public SparkGremlinPlugin() { + super(NAME, imports, bindings); + } + + @Override + public boolean requireRestart() { + return true; + } +} \ No newline at end of file diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/jsr223/TinkerGraphGremlinPlugin.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/jsr223/TinkerGraphGremlinPlugin.java index 16a6cb5f83b..68e649c141a 100644 --- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/jsr223/TinkerGraphGremlinPlugin.java +++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/jsr223/TinkerGraphGremlinPlugin.java @@ -21,6 +21,7 @@ import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; import org.apache.tinkerpop.gremlin.jsr223.Customizer; import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComputer; import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComputerView; import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerMapEmitter; @@ -48,30 +49,27 @@ public final class TinkerGraphGremlinPlugin extends AbstractGremlinPlugin { private static final String MODULE_NAME = "tinkerpop.tinkergraph"; - public TinkerGraphGremlinPlugin() { - super(MODULE_NAME, DefaultImportCustomizer.build().addClassImports( - TinkerEdge.class, - TinkerElement.class, - TinkerFactory.class, - TinkerGraph.class, - TinkerGraphVariables.class, - TinkerHelper.class, - TinkerIoRegistry.class, - TinkerIoRegistryV2d0.class, - TinkerProperty.class, - TinkerVertex.class, - TinkerVertexProperty.class, - TinkerGraphComputer.class, - TinkerGraphComputerView.class, - TinkerMapEmitter.class, - TinkerMemory.class, - TinkerMessenger.class, - TinkerReduceEmitter.class, - TinkerWorkerPool.class).create()); - } + private static final ImportCustomizer imports = DefaultImportCustomizer.build() + .addClassImports(TinkerEdge.class, + TinkerElement.class, + TinkerFactory.class, + TinkerGraph.class, + TinkerGraphVariables.class, + TinkerHelper.class, + TinkerIoRegistry.class, + TinkerIoRegistryV2d0.class, + TinkerProperty.class, + TinkerVertex.class, + TinkerVertexProperty.class, + TinkerGraphComputer.class, + TinkerGraphComputerView.class, + TinkerMapEmitter.class, + TinkerMemory.class, + TinkerMessenger.class, + TinkerReduceEmitter.class, + TinkerWorkerPool.class).create(); - @Override - public Optional getCustomizers() { - return null; + public TinkerGraphGremlinPlugin() { + super(MODULE_NAME, imports); } } From 2e65a113e2a9e0b56bcbdba4c124cb1590759cf3 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Tue, 22 Nov 2016 11:15:57 -0500 Subject: [PATCH 17/45] TINKERPOP-1562 Add new Neo4j GremlinPlugin and deprecated old one --- .../groovy/plugin/AbstractGremlinPlugin.java | 2 + .../groovy/plugin/Neo4jGremlinPlugin.java | 1 + .../neo4j/jsr223/Neo4jGremlinPlugin.java | 71 +++++++++++++++++++ ...che.tinkerpop.gremlin.jsr223.GremlinPlugin | 1 + ...che.tinkerpop.gremlin.jsr223.GremlinPlugin | 1 + .../jsr223/TinkerGraphGremlinPlugin.java | 7 +- 6 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/jsr223/Neo4jGremlinPlugin.java create mode 100644 neo4j-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin create mode 100644 spark-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/AbstractGremlinPlugin.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/AbstractGremlinPlugin.java index fcc8d05f1c1..090da6e3333 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/AbstractGremlinPlugin.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/AbstractGremlinPlugin.java @@ -30,7 +30,9 @@ * shell and io objects. * * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin}. */ +@Deprecated public abstract class AbstractGremlinPlugin implements GremlinPlugin { public static final String ENV_CONSOLE_IO = "ConsolePluginAcceptor.io"; public static final String ENV_CONSOLE_SHELL = "ConsolePluginAcceptor.shell"; diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/groovy/plugin/Neo4jGremlinPlugin.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/groovy/plugin/Neo4jGremlinPlugin.java index f3fe37c3501..9053cf37d5b 100644 --- a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/groovy/plugin/Neo4jGremlinPlugin.java +++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/groovy/plugin/Neo4jGremlinPlugin.java @@ -30,6 +30,7 @@ /** * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.neo4j.jsr223.Neo4jGremlinPlugin}. */ public final class Neo4jGremlinPlugin extends AbstractGremlinPlugin { diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/jsr223/Neo4jGremlinPlugin.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/jsr223/Neo4jGremlinPlugin.java new file mode 100644 index 00000000000..82a8d183037 --- /dev/null +++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/jsr223/Neo4jGremlinPlugin.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.neo4j.jsr223; + +import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; +import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; +import org.apache.tinkerpop.gremlin.neo4j.process.traversal.LabelP; +import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jEdge; +import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jElement; +import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraph; +import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jGraphVariables; +import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jHelper; +import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jProperty; +import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jVertex; +import org.apache.tinkerpop.gremlin.neo4j.structure.Neo4jVertexProperty; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public final class Neo4jGremlinPlugin extends AbstractGremlinPlugin { + + private static final String NAME = "tinkerpop.neo4j"; + + private static final ImportCustomizer imports; + + static { + try { + imports = DefaultImportCustomizer.build() + .addClassImports(Neo4jEdge.class, + Neo4jElement.class, + Neo4jGraph.class, + Neo4jGraphVariables.class, + Neo4jHelper.class, + Neo4jProperty.class, + Neo4jVertex.class, + Neo4jVertexProperty.class) + .addMethodImports(LabelP.class.getMethod("of", String.class)).create(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public Neo4jGremlinPlugin() { + super(NAME, imports); + } + + @Override + public boolean requireRestart() { + return true; + } +} \ No newline at end of file diff --git a/neo4j-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin b/neo4j-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin new file mode 100644 index 00000000000..cb86e575535 --- /dev/null +++ b/neo4j-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin @@ -0,0 +1 @@ +org.apache.tinkerpop.gremlin.neo4j.jsr223.Neo4jGremlinPlugin \ No newline at end of file diff --git a/spark-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin b/spark-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin new file mode 100644 index 00000000000..939b6ff123e --- /dev/null +++ b/spark-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin @@ -0,0 +1 @@ +org.apache.tinkerpop.gremlin.spark.jsr223.SparkGremlinPlugin \ No newline at end of file diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/jsr223/TinkerGraphGremlinPlugin.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/jsr223/TinkerGraphGremlinPlugin.java index 68e649c141a..19188d218e6 100644 --- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/jsr223/TinkerGraphGremlinPlugin.java +++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/jsr223/TinkerGraphGremlinPlugin.java @@ -19,7 +19,6 @@ package org.apache.tinkerpop.gremlin.tinkergraph.jsr223; import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; -import org.apache.tinkerpop.gremlin.jsr223.Customizer; import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComputer; @@ -41,13 +40,11 @@ import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerVertex; import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerVertexProperty; -import java.util.Optional; - /** * @author Stephen Mallette (http://stephen.genoprime.com) */ public final class TinkerGraphGremlinPlugin extends AbstractGremlinPlugin { - private static final String MODULE_NAME = "tinkerpop.tinkergraph"; + private static final String NAME = "tinkerpop.tinkergraph"; private static final ImportCustomizer imports = DefaultImportCustomizer.build() .addClassImports(TinkerEdge.class, @@ -70,6 +67,6 @@ public final class TinkerGraphGremlinPlugin extends AbstractGremlinPlugin { TinkerWorkerPool.class).create(); public TinkerGraphGremlinPlugin() { - super(MODULE_NAME, imports); + super(NAME, imports); } } From d0c941e86ccd561576552cdda9c58424462def9d Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Tue, 22 Nov 2016 13:41:58 -0500 Subject: [PATCH 18/45] TINKERPOP-1562 Add new HadoopGremlinPlugin and deprecated the old. Had to add some capabilities to plugin system to allow for passing of console environment bits (like groovysh for example). --- .../gremlin/console/plugin/PluggedIn.groovy | 2 +- .../{console => }/LazyBindingsCustomizer.java | 2 +- .../jsr223/console/ConsoleCustomizer.java | 7 +- .../jsr223/console/RemoteAcceptor.java | 3 +- .../gremlin/groovy/plugin/PluginAcceptor.java | 2 +- .../groovy/plugin/HadoopGremlinPlugin.java | 2 + .../groovy/plugin/HadoopRemoteAcceptor.java | 2 + .../hadoop/jsr223/HadoopGremlinPlugin.java | 154 ++++++++++++++++++ .../hadoop/jsr223/HadoopRemoteAcceptor.java | 127 +++++++++++++++ ...che.tinkerpop.gremlin.jsr223.GremlinPlugin | 1 + .../groovy/plugin/Neo4jGremlinPlugin.java | 1 + .../spark/jsr223/SparkGremlinPlugin.java | 2 +- 12 files changed, 299 insertions(+), 6 deletions(-) rename gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/{console => }/LazyBindingsCustomizer.java (96%) create mode 100644 hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopGremlinPlugin.java create mode 100644 hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopRemoteAcceptor.java create mode 100644 hadoop-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy index b707226b66c..dc63a2fc1b0 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy @@ -90,7 +90,7 @@ class PluggedIn { } else if (it instanceof ScriptCustomizer) { it.getScripts().collect { it.join(LINE_SEPARATOR) }.each { pluginAcceptor.eval(it) } } else if (it instanceof BindingsCustomizer) { - it.bindings.entrySet().each { k, v -> pluginAcceptor.addBinding(k,v) } + it.bindings.entrySet().each { kv -> pluginAcceptor.addBinding(kv.key, kv.value) } } } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/LazyBindingsCustomizer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/LazyBindingsCustomizer.java similarity index 96% rename from gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/LazyBindingsCustomizer.java rename to gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/LazyBindingsCustomizer.java index f21a2ab4a3b..4117ae5fccd 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/LazyBindingsCustomizer.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/LazyBindingsCustomizer.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.tinkerpop.gremlin.jsr223.console; +package org.apache.tinkerpop.gremlin.jsr223; import org.apache.tinkerpop.gremlin.jsr223.BindingsCustomizer; diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/ConsoleCustomizer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/ConsoleCustomizer.java index 47771bcce0a..7b3d78800f6 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/ConsoleCustomizer.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/ConsoleCustomizer.java @@ -20,14 +20,19 @@ import org.apache.tinkerpop.gremlin.jsr223.Customizer; +import java.util.Map; + /** * @author Stephen Mallette (http://stephen.genoprime.com) */ public interface ConsoleCustomizer extends Customizer { + public static final String ENV_CONSOLE_IO = "ConsolePluginAcceptor.io"; + public static final String ENV_CONSOLE_SHELL = "ConsolePluginAcceptor.shell"; + /** * Allows a plugin to utilize features of the {@code :remote} and {@code :submit} commands of the Gremlin Console. * This method does not need to be implemented if the plugin is not meant for the Console for some reason or * if it does not intend to take advantage of those commands. */ - public RemoteAcceptor getRemoteAcceptor(); + public RemoteAcceptor getRemoteAcceptor(final Map environment); } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/RemoteAcceptor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/RemoteAcceptor.java index aee77ae388d..ab4c81d98c9 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/RemoteAcceptor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/RemoteAcceptor.java @@ -20,6 +20,7 @@ import java.io.Closeable; import java.util.List; +import java.util.Map; /** * The Gremlin Console supports the {@code :remote} and {@code :submit} commands which provide standardized ways @@ -27,7 +28,7 @@ * A "remote connection" does not necessarily have to be a remote server. It simply refers to a resource that is * external to the console. *

- * By implementing this interface and returning an instance of it through {@link ConsoleCustomizer#getRemoteAcceptor()} + * By implementing this interface and returning an instance of it through {@link ConsoleCustomizer#getRemoteAcceptor(Map)} * a plugin can hook into those commands and provide remoting features. * * @author Stephen Mallette (http://stephen.genoprime.com) diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/PluginAcceptor.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/PluginAcceptor.java index 957138116ee..91652377680 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/PluginAcceptor.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/PluginAcceptor.java @@ -31,7 +31,7 @@ * and provides the abstractions required for a plugin to work regardless of the environmental implementations. * * @author Stephen Mallette (http://stephen.genoprime.com) - * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.console.PluginAcceptor}. + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.jsr223.console.PluginAcceptor}. */ @Deprecated public interface PluginAcceptor { diff --git a/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/groovy/plugin/HadoopGremlinPlugin.java b/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/groovy/plugin/HadoopGremlinPlugin.java index 021f9d34b23..ca446efa82d 100644 --- a/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/groovy/plugin/HadoopGremlinPlugin.java +++ b/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/groovy/plugin/HadoopGremlinPlugin.java @@ -43,7 +43,9 @@ /** * @author Marko A. Rodriguez (http://markorodriguez.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.hadoop.jsr223.HadoopGremlinPlugin}. */ +@Deprecated public final class HadoopGremlinPlugin extends AbstractGremlinPlugin { protected static String NAME = "tinkerpop.hadoop"; diff --git a/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/groovy/plugin/HadoopRemoteAcceptor.java b/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/groovy/plugin/HadoopRemoteAcceptor.java index 558376e18ca..acae442a8c1 100644 --- a/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/groovy/plugin/HadoopRemoteAcceptor.java +++ b/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/groovy/plugin/HadoopRemoteAcceptor.java @@ -39,7 +39,9 @@ /** * @author Marko A. Rodriguez (http://markorodriguez.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.hadoop.jsr223.HadoopRemoteAcceptor}. */ +@Deprecated public final class HadoopRemoteAcceptor implements RemoteAcceptor { private static final String USE_SUGAR = "useSugar"; 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 new file mode 100644 index 00000000000..5e210273bfe --- /dev/null +++ b/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopGremlinPlugin.java @@ -0,0 +1,154 @@ +/* + * 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.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.io.IOUtils; +import org.apache.hadoop.io.compress.CodecPool; +import org.apache.hadoop.mapreduce.lib.input.SequenceFileInputFormat; +import org.apache.hadoop.mapreduce.lib.output.SequenceFileOutputFormat; +import org.apache.hadoop.util.ToolRunner; +import org.apache.tinkerpop.gremlin.hadoop.Constants; +import org.apache.tinkerpop.gremlin.hadoop.process.computer.mapreduce.MapReduceGraphComputer; +import org.apache.tinkerpop.gremlin.hadoop.structure.HadoopConfiguration; +import org.apache.tinkerpop.gremlin.hadoop.structure.HadoopEdge; +import org.apache.tinkerpop.gremlin.hadoop.structure.HadoopElement; +import org.apache.tinkerpop.gremlin.hadoop.structure.HadoopGraph; +import org.apache.tinkerpop.gremlin.hadoop.structure.HadoopProperty; +import org.apache.tinkerpop.gremlin.hadoop.structure.HadoopVertex; +import org.apache.tinkerpop.gremlin.hadoop.structure.HadoopVertexProperty; +import org.apache.tinkerpop.gremlin.hadoop.structure.io.FileSystemStorage; +import org.apache.tinkerpop.gremlin.hadoop.structure.io.VertexWritable; +import org.apache.tinkerpop.gremlin.hadoop.structure.io.graphson.GraphSONInputFormat; +import org.apache.tinkerpop.gremlin.hadoop.structure.io.graphson.GraphSONOutputFormat; +import org.apache.tinkerpop.gremlin.hadoop.structure.io.graphson.GraphSONRecordReader; +import org.apache.tinkerpop.gremlin.hadoop.structure.io.graphson.GraphSONRecordWriter; +import org.apache.tinkerpop.gremlin.hadoop.structure.io.gryo.GryoInputFormat; +import org.apache.tinkerpop.gremlin.hadoop.structure.io.gryo.GryoOutputFormat; +import org.apache.tinkerpop.gremlin.hadoop.structure.io.gryo.GryoRecordReader; +import org.apache.tinkerpop.gremlin.hadoop.structure.io.gryo.GryoRecordWriter; +import org.apache.tinkerpop.gremlin.hadoop.structure.io.script.ScriptInputFormat; +import org.apache.tinkerpop.gremlin.hadoop.structure.util.ConfUtil; +import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; +import org.apache.tinkerpop.gremlin.jsr223.BindingsCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.Customizer; +import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.LazyBindingsCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.console.ConsoleCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor; +import org.codehaus.groovy.tools.shell.Groovysh; + +import javax.script.Bindings; +import javax.script.SimpleBindings; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +/** + * @author Marko A. Rodriguez (http://markorodriguez.com) + */ +public final class HadoopGremlinPlugin extends AbstractGremlinPlugin { + + protected static String NAME = "tinkerpop.hadoop"; + + private static final BindingsCustomizer bindings; + + private static final ImportCustomizer imports; + + private static final Set appliesTo = new HashSet<>(Collections.singletonList("gremlin-groovy")); + + static { + try { + // TODO: most of the imports here were wildcarded, but we dont' allow that anymore - needs review + imports = DefaultImportCustomizer.build() + .addClassImports( + Configuration.class, + org.apache.hadoop.hdfs.DFSClient.class, + FileSystem.class, + ToolRunner.class, + IOUtils.class, + CodecPool.class, + SequenceFileInputFormat.class, + SequenceFileOutputFormat.class, + Constants.class, + HadoopConfiguration.class, + HadoopEdge.class, + HadoopElement.class, + HadoopGraph.class, + HadoopProperty.class, + HadoopVertex.class, + HadoopVertexProperty.class, + ConfUtil.class, + VertexWritable.class, + GraphSONInputFormat.class, + GraphSONOutputFormat.class, + GraphSONRecordReader.class, + GraphSONRecordWriter.class, + GryoInputFormat.class, + GryoOutputFormat.class, + GryoRecordReader.class, + GryoRecordWriter.class, + ScriptInputFormat.class, + MapReduceGraphComputer.class).create(); + + bindings = new LazyBindingsCustomizer(() -> { + try { + final Bindings bindings = new SimpleBindings(); + bindings.put("hdfs", FileSystemStorage.open(FileSystem.get(new Configuration()))); + bindings.put("fs", FileSystemStorage.open(FileSystem.getLocal(new Configuration()))); + return bindings; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + }); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public HadoopGremlinPlugin() { + super(NAME, appliesTo, imports, bindings, new HadoopConsoleCustomizer()); + } + + @Override + public Optional getCustomizers(final String scriptEngineName) { + if (null == System.getenv(Constants.HADOOP_GREMLIN_LIBS)) + HadoopGraph.LOGGER.warn("Be sure to set the environmental variable: " + Constants.HADOOP_GREMLIN_LIBS); + else + HadoopGraph.LOGGER.info(Constants.HADOOP_GREMLIN_LIBS + " is set to: " + System.getenv(Constants.HADOOP_GREMLIN_LIBS)); + + return super.getCustomizers(scriptEngineName); + } + + @Override + public boolean requireRestart() { + return true; + } + + private static class HadoopConsoleCustomizer implements ConsoleCustomizer { + @Override + public RemoteAcceptor getRemoteAcceptor(final Map environment) { + return new HadoopRemoteAcceptor((Groovysh) environment.get(ConsoleCustomizer.ENV_CONSOLE_SHELL)); + } + } +} \ No newline at end of file diff --git a/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopRemoteAcceptor.java b/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopRemoteAcceptor.java new file mode 100644 index 00000000000..f2367bd1cc8 --- /dev/null +++ b/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopRemoteAcceptor.java @@ -0,0 +1,127 @@ +/* + * 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.groovy.loaders.SugarLoader; +import org.apache.tinkerpop.gremlin.hadoop.structure.HadoopGraph; +import org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor; +import org.apache.tinkerpop.gremlin.jsr223.console.RemoteException; +import org.apache.tinkerpop.gremlin.process.computer.ComputerResult; +import org.apache.tinkerpop.gremlin.process.computer.traversal.TraversalVertexProgram; +import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.ComputerResultStep; +import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource; +import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep; +import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversal; +import org.codehaus.groovy.tools.shell.Groovysh; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +/** + * @author Marko A. Rodriguez (http://markorodriguez.com) + */ +public final class HadoopRemoteAcceptor implements RemoteAcceptor { + + private static final String USE_SUGAR = "useSugar"; + private static final String USE_TRAVERSAL_SOURCE = "useTraversalSource"; + private static final String SPACE = " "; + + private HadoopGraph hadoopGraph; + private Groovysh shell; + private boolean useSugar = false; + private TraversalSource traversalSource; + + public HadoopRemoteAcceptor(final Groovysh shell) { + this.shell = shell; + } + + @Override + public Object connect(final List args) throws RemoteException { + if (args.size() != 1 && args.size() != 2) { + throw new IllegalArgumentException("Usage: :remote connect " + HadoopGremlinPlugin.NAME + " "); + } + this.hadoopGraph = (HadoopGraph) this.shell.getInterp().getContext().getVariable(args.get(0)); + if (args.size() == 2) + this.traversalSource = ((TraversalSource) this.shell.getInterp().getContext().getVariable(args.get(1))); + else + this.traversalSource = this.hadoopGraph.traversal(); + /// + final HashMap configuration = new HashMap<>(); + configuration.put(USE_SUGAR, this.useSugar); + configuration.put(USE_TRAVERSAL_SOURCE, this.traversalSource); + return Collections.unmodifiableMap(configuration); + } + + @Override + public Object configure(final List args) throws RemoteException { + for (int i = 0; i < args.size(); i = i + 2) { + if (args.get(i).equals(USE_SUGAR)) + this.useSugar = Boolean.valueOf(args.get(i + 1)); + else if (args.get(i).equals(USE_TRAVERSAL_SOURCE)) { + this.traversalSource = ((TraversalSource) this.shell.getInterp().getContext().getVariable(args.get(i + 1))); + } else + throw new IllegalArgumentException("The provided configuration is unknown: " + args.get(i) + ":" + args.get(i + 1)); + } + /// + final HashMap configuration = new HashMap<>(); + configuration.put(USE_SUGAR, this.useSugar); + configuration.put(USE_TRAVERSAL_SOURCE, this.traversalSource); + return Collections.unmodifiableMap(configuration); + } + + @Override + public Object submit(final List args) throws RemoteException { + try { + String script = getScript(String.join(SPACE, args), this.shell); + if (this.useSugar) + script = SugarLoader.class.getCanonicalName() + ".load()\n" + script; + final TraversalVertexProgram program = TraversalVertexProgram.build().traversal(this.traversalSource, "gremlin-groovy", script).create(this.hadoopGraph); + final ComputerResult computerResult = VertexProgramStrategy.getComputer(this.traversalSource.getStrategies()).get().apply(this.hadoopGraph).program(program).submit().get(); + this.shell.getInterp().getContext().setVariable(RESULT, computerResult); + /// + final Traversal.Admin traversal = new DefaultTraversal<>(computerResult.graph()); + traversal.addStep(new ComputerResultStep<>(traversal)); + traversal.addStart(traversal.getTraverserGenerator().generate(computerResult, EmptyStep.instance(), 1l)); + return traversal; + } catch (final Exception e) { + throw new RemoteException(e); + } + } + + @Override + public boolean allowRemoteConsole() { + return true; + } + + @Override + public void close() throws IOException { + this.hadoopGraph.close(); + } + + /** + * Retrieve a script as defined in the shell context. This allows for multi-line scripts to be submitted. + */ + public static String getScript(final String submittedScript, final Groovysh shell) { + return submittedScript.startsWith("@") ? shell.getInterp().getContext().getProperty(submittedScript.substring(1)).toString() : submittedScript; + } +} diff --git a/hadoop-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin b/hadoop-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin new file mode 100644 index 00000000000..07223dd698e --- /dev/null +++ b/hadoop-gremlin/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin @@ -0,0 +1 @@ +org.apache.tinkerpop.gremlin.hadoop.jsr223.HadoopGremlinPlugin \ No newline at end of file diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/groovy/plugin/Neo4jGremlinPlugin.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/groovy/plugin/Neo4jGremlinPlugin.java index 9053cf37d5b..68c00165de3 100644 --- a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/groovy/plugin/Neo4jGremlinPlugin.java +++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/groovy/plugin/Neo4jGremlinPlugin.java @@ -32,6 +32,7 @@ * @author Stephen Mallette (http://stephen.genoprime.com) * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.neo4j.jsr223.Neo4jGremlinPlugin}. */ +@Deprecated public final class Neo4jGremlinPlugin extends AbstractGremlinPlugin { private static final Set IMPORTS = new HashSet() {{ diff --git a/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/jsr223/SparkGremlinPlugin.java b/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/jsr223/SparkGremlinPlugin.java index 840f59302d4..9403fa40afa 100644 --- a/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/jsr223/SparkGremlinPlugin.java +++ b/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/jsr223/SparkGremlinPlugin.java @@ -23,7 +23,7 @@ import org.apache.tinkerpop.gremlin.jsr223.BindingsCustomizer; import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; -import org.apache.tinkerpop.gremlin.jsr223.console.LazyBindingsCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.LazyBindingsCustomizer; import org.apache.tinkerpop.gremlin.spark.process.computer.CombineIterator; import org.apache.tinkerpop.gremlin.spark.process.computer.MapIterator; import org.apache.tinkerpop.gremlin.spark.process.computer.MemoryAccumulator; From bb5b47dd98aa936673b1588202dbcc378a83b718 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Tue, 22 Nov 2016 14:20:11 -0500 Subject: [PATCH 19/45] TINKERPOP-1562 Added new plugins for gremlin-groovy to replace deprecated ones. Specifically did SugarGremlinPlugin and CredentialsGrpahGremlinPlugin. --- .../jsr223/ScriptEnginePluginAcceptor.java | 2 + .../groovy/jsr223/SugarGremlinPlugin.java | 45 +++++++++++++++ .../groovy/plugin/SugarGremlinPlugin.java | 2 + .../CredentialGraphGremlinPlugin.java | 2 + .../jsr223/CredentialGraphGremlinPlugin.java | 55 +++++++++++++++++++ ...che.tinkerpop.gremlin.jsr223.GremlinPlugin | 2 + 6 files changed, 108 insertions(+) create mode 100644 gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/SugarGremlinPlugin.java create mode 100644 gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/jsr223/CredentialGraphGremlinPlugin.java create mode 100644 gremlin-groovy/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ScriptEnginePluginAcceptor.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ScriptEnginePluginAcceptor.java index 0bd51c2b9dd..5832e0b074f 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ScriptEnginePluginAcceptor.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ScriptEnginePluginAcceptor.java @@ -34,7 +34,9 @@ * interact with them on initialization. * * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, not replaced. */ +@Deprecated public class ScriptEnginePluginAcceptor implements PluginAcceptor { private final ScriptEngine scriptEngine; diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/SugarGremlinPlugin.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/SugarGremlinPlugin.java new file mode 100644 index 00000000000..95c610f128d --- /dev/null +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/SugarGremlinPlugin.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.groovy.jsr223; + +import org.apache.tinkerpop.gremlin.groovy.loaders.SugarLoader; +import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; +import org.apache.tinkerpop.gremlin.jsr223.DefaultScriptCustomizer; + +import java.util.Collections; + +/** + * A plugin implementation which allows for the usage of Gremlin Groovy's syntactic sugar. + * + * @author Marko A. Rodriguez (http://markorodriguez.com) + */ +public class SugarGremlinPlugin extends AbstractGremlinPlugin { + + private static final String NAME = "tinkerpop.sugar"; + + public SugarGremlinPlugin() { + super(NAME, new DefaultScriptCustomizer(Collections.singletonList( + Collections.singletonList(SugarLoader.class.getPackage().getName() + "." + SugarLoader.class.getSimpleName() + ".load()")))); + } + + @Override + public String getName() { + return "tinkerpop.sugar"; + } +} diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/SugarGremlinPlugin.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/SugarGremlinPlugin.java index 845ffc64c8b..0666d71417a 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/SugarGremlinPlugin.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/SugarGremlinPlugin.java @@ -24,7 +24,9 @@ * A plugin implementation which allows for the usage of Gremlin Groovy's syntactic sugar. * * @author Marko A. Rodriguez (http://markorodriguez.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.groovy.jsr223.SugarGremlinPlugin}. */ +@Deprecated public class SugarGremlinPlugin extends AbstractGremlinPlugin { @Override diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/CredentialGraphGremlinPlugin.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/CredentialGraphGremlinPlugin.java index 8550615103e..72ca1d58d5b 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/CredentialGraphGremlinPlugin.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/CredentialGraphGremlinPlugin.java @@ -30,7 +30,9 @@ * Plugin for the "credentials graph". This plugin imports the {@link CredentialGraph} to its environment. * * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.groovy.plugin.dsl.credential.CredentialGraphGremlinPlugin}. */ +@Deprecated public class CredentialGraphGremlinPlugin extends AbstractGremlinPlugin { private static final Set IMPORTS = new HashSet() {{ diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/jsr223/CredentialGraphGremlinPlugin.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/jsr223/CredentialGraphGremlinPlugin.java new file mode 100644 index 00000000000..761567bdec3 --- /dev/null +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/jsr223/CredentialGraphGremlinPlugin.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.groovy.plugin.dsl.credential.jsr223; + +import org.apache.tinkerpop.gremlin.groovy.plugin.dsl.credential.CredentialGraph; +import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; +import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; +import org.apache.tinkerpop.gremlin.structure.Graph; + +import java.util.HashSet; +import java.util.Set; + +/** + * Plugin for the "credentials graph". This plugin imports the {@link CredentialGraph} to its environment. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class CredentialGraphGremlinPlugin extends AbstractGremlinPlugin { + + private static final String NAME = "tinkerpop.credentials"; + + private static final ImportCustomizer imports; + + static { + try { + imports = DefaultImportCustomizer.build() + .addClassImports(CredentialGraph.class) + .addMethodImports(CredentialGraph.class.getMethod("credentials", Graph.class)) + .create(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public CredentialGraphGremlinPlugin() { + super(NAME, imports); + } +} \ No newline at end of file diff --git a/gremlin-groovy/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin b/gremlin-groovy/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin new file mode 100644 index 00000000000..0004a807980 --- /dev/null +++ b/gremlin-groovy/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin @@ -0,0 +1,2 @@ +org.apache.tinkerpop.gremlin.groovy.jsr223.SugarGremlinPlugin +org.apache.tinkerpop.gremlin.groovy.plugin.dsl.credential.jsr223.CredentialGraphGremlinPlugin \ No newline at end of file From a91fb80e64ceb2e5c97b5f8c2ef20205d075ec18 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Tue, 22 Nov 2016 16:35:16 -0500 Subject: [PATCH 20/45] TINKERPOP-1562 Added gremlin-console plugins under the new model. Had to rework the PluggedIn adapters a bit so that they could better handle Console environment variable.s --- .../tinkerpop/gremlin/console/Console.groovy | 4 +- .../console/jsr223/GephiRemoteAcceptor.groovy | 372 ++++++++++++++++++ .../gremlin/console/plugin/PluggedIn.groovy | 14 +- .../groovy/plugin/DriverGremlinPlugin.java | 2 + .../groovy/plugin/DriverRemoteAcceptor.java | 2 + .../groovy/plugin/GephiGremlinPlugin.java | 1 + .../groovy/plugin/UtilitiesGremlinPlugin.java | 1 + .../console/jsr223/DriverGremlinPlugin.java | 106 +++++ .../console/jsr223/DriverRemoteAcceptor.java | 238 +++++++++++ .../console/jsr223/GephiGremlinPlugin.java | 45 +++ .../jsr223/UtilitiesGremlinPlugin.java | 106 +++++ ...che.tinkerpop.gremlin.jsr223.GremlinPlugin | 3 + .../UtilitiesGremlinPluginScript.groovy | 52 +++ .../plugin/GremlinPluginAdapterTest.java | 5 +- 14 files changed, 940 insertions(+), 11 deletions(-) create mode 100644 gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy create mode 100644 gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverGremlinPlugin.java create mode 100644 gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverRemoteAcceptor.java create mode 100644 gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/GephiGremlinPlugin.java create mode 100644 gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/UtilitiesGremlinPlugin.java create mode 100644 gremlin-console/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin create mode 100644 gremlin-console/src/main/resources/org/apache/tinkerpop/gremlin/console/jsr223/UtilitiesGremlinPluginScript.groovy diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy index cb6e90f0742..d39e085afe9 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy @@ -131,9 +131,9 @@ class Console { def pluggedIn if (Mediator.useV3d3) { - pluggedIn = new PluggedIn(new PluggedIn.GremlinPluginAdapter(plugin), groovy, io, false) + pluggedIn = new PluggedIn(new PluggedIn.GremlinPluginAdapter((org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin) plugin, groovy, io), groovy, io, false) } else { - pluggedIn = new PluggedIn(plugin, groovy, io, false) + pluggedIn = new PluggedIn((GremlinPlugin) plugin, groovy, io, false) } mediator.availablePlugins.put(plugin.class.name, pluggedIn) diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy new file mode 100644 index 00000000000..dbc11568d3c --- /dev/null +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy @@ -0,0 +1,372 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.console.jsr223 + +import groovy.json.JsonOutput +import groovy.json.JsonSlurper +import groovy.transform.CompileStatic +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.client.methods.HttpUriRequest +import org.apache.http.client.methods.RequestBuilder +import org.apache.http.entity.StringEntity +import org.apache.http.impl.client.CloseableHttpClient +import org.apache.http.impl.client.HttpClients +import org.apache.http.util.EntityUtils +import org.apache.tinkerpop.gremlin.console.plugin.GephiTraversalVisualizationStrategy +import org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor +import org.apache.tinkerpop.gremlin.jsr223.console.RemoteException +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource +import org.apache.tinkerpop.gremlin.structure.Edge +import org.apache.tinkerpop.gremlin.structure.Graph +import org.apache.tinkerpop.gremlin.structure.Vertex +import org.codehaus.groovy.tools.shell.Groovysh +import org.codehaus.groovy.tools.shell.IO + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + * @author Randall Barnhart (randompi@gmail.com) + */ +class GephiRemoteAcceptor implements RemoteAcceptor { + + private String host = "localhost" + private int port = 8080 + private String workspace = "workspace1" + + private final Groovysh shell + private final IO io + + private final Random rand = new Random(); + boolean traversalSubmittedForViz = false + long vizStepDelay + private float[] vizStartRGBColor + private float[] vizDefaultRGBColor + private char vizColorToFade + private float vizColorFadeRate + private float vizStartSize + private float vizSizeDecrementRate + private Map vertexAttributes = [:] + + private CloseableHttpClient httpclient = HttpClients.createDefault(); + + public GephiRemoteAcceptor(final Groovysh shell, final IO io) { + this.shell = shell + this.io = io + + // traversal visualization defaults + vizStepDelay = 1000; // 1 second pause between viz of steps + vizStartRGBColor = [0.0f, 1.0f, 0.5f] // light aqua green + vizDefaultRGBColor = [0.6f, 0.6f, 0.6f] // light grey + vizColorToFade = 'g' // will fade so blue is strongest + vizColorFadeRate = 0.7 // the multiplicative rate to fade visited vertices + vizStartSize = 10 + vizSizeDecrementRate = 0.33f + } + + @Override + connect(final List args) throws RemoteException { + if (args.size() >= 1) + workspace = args[0] + + if (args.size() >= 2) + host = args[1] + + if (args.size() >= 3) { + try { + port = Integer.parseInt(args[2]) + } catch (Exception ex) { + throw new RemoteException("Port must be an integer value") + } + } + + def vizConfig = " with stepDelay:$vizStepDelay, startRGBColor:$vizStartRGBColor, " + + "colorToFade:$vizColorToFade, colorFadeRate:$vizColorFadeRate, startSize:$vizStartSize," + + "sizeDecrementRate:$vizSizeDecrementRate" + + return "Connection to Gephi - http://$host:$port/$workspace" + vizConfig + } + + @Override + Object configure(final List args) throws RemoteException { + if (args.size() < 2) + throw new RemoteException("Invalid config arguments - check syntax") + + if (args[0] == "host") + host = args[1] + else if (args[0] == "port") { + try { + port = Integer.parseInt(args[1]) + } catch (Exception ignored) { + throw new RemoteException("Port must be an integer value") + } + } else if (args[0] == "workspace") + workspace = args[1] + else if (args[0] == "stepDelay") + parseVizStepDelay(args[1]) + else if (args[0] == "startRGBColor") + parseVizStartRGBColor(args[1]) + else if (args[0] == "colorToFade") + parseVizColorToFade(args[1]) + else if (args[0] == "colorFadeRate") + parseVizColorFadeRate(args[1]) + else if (args[0] == "sizeDecrementRate") + parseVizSizeDecrementRate(args[1]) + else if (args[0] == "startSize") + parseVizStartSize(args[1]) + else if (args[0] == "visualTraversal") { + def graphVar = shell.interp.context.getVariable(args[1]) + if (!(graphVar instanceof Graph)) + throw new RemoteException("Invalid argument to 'visualTraversal' - first parameter must be a Graph instance") + + def gVar = args.size() == 3 ? args[2] : "vg" + def theG = GraphTraversalSource.build().with(new GephiTraversalVisualizationStrategy(this)).create(graphVar) + shell.interp.context.setVariable(gVar, theG) + } else + throw new RemoteException("Invalid config arguments - check syntax") + + return "Connection to Gephi - http://$host:$port/$workspace" + + " with stepDelay:$vizStepDelay, startRGBColor:$vizStartRGBColor, " + + "colorToFade:$vizColorToFade, colorFadeRate:$vizColorFadeRate, startSize:$vizStartSize," + + "sizeDecrementRate:$vizSizeDecrementRate" + } + + @Override + @CompileStatic + Object submit(final List args) throws RemoteException { + final String line = String.join(" ", args) + if (line.trim() == "clear") { + clearGraph() + io.out.println("Gephi workspace cleared") + return + } + + // need to clear the vertex attributes + vertexAttributes.clear() + + // this tells the GraphTraversalVisualizationStrategy that if the line eval's to a traversal it should + // try to visualize it + traversalSubmittedForViz = true + + // get the colors/sizes back to basics before trying visualize + resetColorsSizes() + + final Object o = shell.execute(line) + if (o instanceof Graph) { + clearGraph() + def graph = (Graph) o + def g = graph.traversal() + g.V().sideEffect { addVertexToGephi(g, it.get()) }.iterate() + } + + traversalSubmittedForViz = false + } + + @Override + void close() throws IOException { + httpclient.close() + } + + /** + * Visits the last set of vertices traversed and degrades their color and size. + */ + def updateVisitedVertices(def List except = []) { + vertexAttributes.keySet().findAll{ vertexId -> !except.contains(vertexId) }.each { String vertexId -> + def attrs = vertexAttributes[vertexId] + float currentColor = attrs.color + currentColor *= vizColorFadeRate + + int currentSize = attrs["size"] + currentSize = Math.max(vizStartSize, currentSize - (currentSize * vizSizeDecrementRate)) + + vertexAttributes.get(vertexId).color = currentColor + vertexAttributes.get(vertexId).size = currentSize + + changeVertexAttributes(vertexId) + } + } + + def changeVertexAttributes(def String vertexId) { + def props = [:] + props.put(vizColorToFade.toString(), vertexAttributes[vertexId].color) + props.put("size", vertexAttributes[vertexId].size) + updateGephiGraph([cn: [(vertexId): props]]) + } + + /** + * Visit a vertex traversed and initialize its color and size. + */ + def visitVertexInGephi(def Vertex v) { + def props = [:] + props.put('r', vizStartRGBColor[0]) + props.put('g', vizStartRGBColor[1]) + props.put('b', vizStartRGBColor[2]) + props.put('size', vizStartSize * 2.5) + props.put('visited', 1) + + updateGephiGraph([cn: [(v.id().toString()): props]]) + + vertexAttributes[v.id().toString()] = [color: vizStartRGBColor[fadeColorIndex()], size: vizStartSize * 2.5, touch: 1] + } + + def fadeColorIndex() { + if (vizColorToFade == 'r') + return 0 + else if (vizColorToFade == 'g') + return 1 + else if (vizColorToFade == 'b') + return 2 + } + + def addVertexToGephi(def GraphTraversalSource g, def Vertex v, def boolean ignoreEdges = false) { + // grab the first property value from the strategies of values + def props = g.V(v).valueMap().next().collectEntries { kv -> [(kv.key): kv.value[0]] } + props << [label: v.label()] + props.put('r', vizDefaultRGBColor[0]) + props.put('g', vizDefaultRGBColor[1]) + props.put('b', vizDefaultRGBColor[2]) + props.put('x', rand.nextFloat()) + props.put('y', rand.nextFloat()) + props.put('size', 10) + props.put('visited', 0) + + // only add if it does not exist in graph already + if (!getFromGephiGraph([operation: "getNode", id: v.id().toString()]).isPresent()) + updateGephiGraph([an: [(v.id().toString()): props]]) + + if (!ignoreEdges) { + g.V(v).outE().sideEffect { + addEdgeToGephi(g, it.get()) + }.iterate() + } + } + + @CompileStatic + def addEdgeToGephi(def GraphTraversalSource g, def Edge e) { + def props = g.E(e).valueMap().next() + props.put('label', e.label()) + props.put('source', e.outVertex().id().toString()) + props.put('target', e.inVertex().id().toString()) + props.put('directed', true) + props.put('visited', 0) + + // make sure the in vertex is there but don't add its edges - that will happen later as we are looping + // all vertices in the graph + addVertexToGephi(g, e.inVertex(), true) + + // both vertices are definitely there now, so add the edge + updateGephiGraph([ae: [(e.id().toString()): props]]) + } + + def clearGraph() { + updateGephiGraph([dn: [filter: "ALL"]]) + } + + def resetColorsSizes() { + updateGephiGraph([cn: [filter: [nodeAttribute: [attribute: "visited", value: 1]], + attributes: [size: 1, r: vizDefaultRGBColor[0], g: vizDefaultRGBColor[1], b: vizDefaultRGBColor[2]]]]) + updateGephiGraph([ce: [filter: [edgeAttribute: [attribute: "visited", value: 1]], + attributes: [size: 1, r: vizDefaultRGBColor[0], g: vizDefaultRGBColor[1], b: vizDefaultRGBColor[2]]]]) + } + + def getFromGephiGraph(def Map queryArgs) { + def requestBuilder = RequestBuilder.get("http://$host:$port/$workspace") + queryArgs.each { requestBuilder = requestBuilder.addParameter(it.key, it.value) } + + def httpResp = makeRequest(requestBuilder.build()) + def resp = EntityUtils.toString(httpResp.entity) + + // gephi streaming plugin does not set the content type or respect the Accept header - treat as text + if (resp.isEmpty()) + return Optional.empty() + else + return Optional.of(new JsonSlurper().parseText(resp)) + } + + def updateGephiGraph(def Map postBody) { + def requestBuilder = RequestBuilder.post("http://$host:$port/$workspace") + .addParameter("format", "JSON") + .addParameter("operation", "updateGraph") + .setEntity(new StringEntity(JsonOutput.toJson(postBody))) + EntityUtils.consume(makeRequest(requestBuilder.build()).entity) + } + + private CloseableHttpResponse makeRequest(HttpUriRequest request) { + def httpResp = httpclient.execute(request) + if (httpResp.getStatusLine().getStatusCode() == 200) { + return httpResp + } else { + def resp = EntityUtils.toString(httpResp.entity) + throw new RuntimeException("Unsuccessful request to Gephi - [${httpResp.getStatusLine().getStatusCode()}] ${httpResp.getStatusLine().getReasonPhrase()} - $resp") + } + } + + @Override + public String toString() { + return "Gephi - [$workspace]" + } + + private void parseVizStepDelay(String arg) { + try { + vizStepDelay = Long.parseLong(arg) + } catch (Exception ignored) { + throw new RemoteException("The stepDelay must be a long value") + } + } + + private void parseVizStartRGBColor(String arg) { + try { + vizStartRGBColor = arg[1..-2].tokenize(',')*.toFloat() + assert (vizStartRGBColor.length == 3) + } catch (Exception ignored) { + throw new RemoteException("The vizStartRGBColor must be an array of 3 float values, e.g. [0.0,1.0,0.5]") + } + } + + private void parseVizColorToFade(String arg) { + try { + vizColorToFade = arg.charAt(0).toLowerCase(); + assert (vizColorToFade == 'r' || vizColorToFade == 'g' || vizColorToFade == 'b') + } catch (Exception ignored) { + throw new RemoteException("The vizColorToFade must be one character value among: r, g, b, R, G, B") + } + } + + private void parseVizColorFadeRate(String arg) { + try { + vizColorFadeRate = Float.parseFloat(arg) + } catch (Exception ignored) { + throw new RemoteException("The colorFadeRate must be a float value") + } + } + + private void parseVizSizeDecrementRate(String arg) { + try { + vizSizeDecrementRate = Float.parseFloat(arg) + } catch (Exception ignored) { + throw new RemoteException("The sizeDecrementRate must be a float value") + } + } + + private void parseVizStartSize(String arg) { + try { + vizStartSize = Float.parseFloat(arg) + } catch (Exception ignored) { + throw new RemoteException("The startSize must be a float value") + } + } +} diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy index dc63a2fc1b0..7a08a9de10a 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy @@ -68,9 +68,13 @@ class PluggedIn { public static class GremlinPluginAdapter implements GremlinPlugin { org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin corePlugin + private final Groovysh shell + private final IO io - public GremlinPluginAdapter(final org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin corePlugin) { + public GremlinPluginAdapter(final org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin corePlugin, final Groovysh shell, final IO io) { this.corePlugin = corePlugin + this.shell = shell + this.io = io } @Override @@ -103,11 +107,11 @@ class PluggedIn { @Override Optional remoteAcceptor() { // find a consoleCustomizer if available - if (!corePlugin.getCustomizers("gremlin-groovy").any{ it instanceof ConsoleCustomizer }) - Optional.empty() + if (!corePlugin.getCustomizers("gremlin-groovy").isPresent() || !corePlugin.getCustomizers("gremlin-groovy").get().any{ it instanceof ConsoleCustomizer }) + return Optional.empty() - ConsoleCustomizer customizer = (ConsoleCustomizer) corePlugin.getCustomizers("gremlin-groovy").find{ it instanceof ConsoleCustomizer } - return Optional.of(new RemoteAcceptorAdapter(customizer.remoteAcceptor)) + ConsoleCustomizer customizer = (ConsoleCustomizer) corePlugin.getCustomizers("gremlin-groovy").get().find{ it instanceof ConsoleCustomizer } + return Optional.of(new RemoteAcceptorAdapter(customizer.getRemoteAcceptor([(ConsoleCustomizer.ENV_CONSOLE_SHELL): shell, (ConsoleCustomizer.ENV_CONSOLE_IO): io]))) } } diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverGremlinPlugin.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverGremlinPlugin.java index 2c52bd5617c..2f8fc755869 100644 --- a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverGremlinPlugin.java +++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverGremlinPlugin.java @@ -35,7 +35,9 @@ /** * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.console.jsr223.DriverGremlinPlugin}. */ +@Deprecated public class DriverGremlinPlugin extends AbstractGremlinPlugin { private static final Set IMPORTS = new HashSet() {{ diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverRemoteAcceptor.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverRemoteAcceptor.java index c346540f185..80e8194b979 100644 --- a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverRemoteAcceptor.java +++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverRemoteAcceptor.java @@ -50,7 +50,9 @@ * * @author Stephen Mallette (http://stephen.genoprime.com) * @author Marko A. Rodriguez (http://markorodriguez.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.console.jsr223.DriverRemoteAcceptor}. */ +@Deprecated public class DriverRemoteAcceptor implements RemoteAcceptor { public static final int NO_TIMEOUT = 0; diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GephiGremlinPlugin.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GephiGremlinPlugin.java index 6977aa83d1f..df0541f4fea 100644 --- a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GephiGremlinPlugin.java +++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GephiGremlinPlugin.java @@ -29,6 +29,7 @@ /** * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.console.jsr223.GephiGremlinPlugin}. */ public class GephiGremlinPlugin extends AbstractGremlinPlugin { diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/UtilitiesGremlinPlugin.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/UtilitiesGremlinPlugin.java index 59c2b1a1004..d1c853d49e3 100644 --- a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/UtilitiesGremlinPlugin.java +++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/UtilitiesGremlinPlugin.java @@ -34,6 +34,7 @@ /** * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.console.jsr223.UtilitiesGremlinPlugin}. */ public class UtilitiesGremlinPlugin extends AbstractGremlinPlugin { diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverGremlinPlugin.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverGremlinPlugin.java new file mode 100644 index 00000000000..89cec10988c --- /dev/null +++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverGremlinPlugin.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.console.jsr223; + +import org.apache.tinkerpop.gremlin.driver.Client; +import org.apache.tinkerpop.gremlin.driver.Cluster; +import org.apache.tinkerpop.gremlin.driver.Host; +import org.apache.tinkerpop.gremlin.driver.LoadBalancingStrategy; +import org.apache.tinkerpop.gremlin.driver.MessageSerializer; +import org.apache.tinkerpop.gremlin.driver.Result; +import org.apache.tinkerpop.gremlin.driver.ResultSet; +import org.apache.tinkerpop.gremlin.driver.Tokens; +import org.apache.tinkerpop.gremlin.driver.exception.ConnectionException; +import org.apache.tinkerpop.gremlin.driver.exception.ResponseException; +import org.apache.tinkerpop.gremlin.driver.message.RequestMessage; +import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage; +import org.apache.tinkerpop.gremlin.driver.message.ResponseResult; +import org.apache.tinkerpop.gremlin.driver.message.ResponseStatus; +import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode; +import org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection; +import org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteTraversal; +import org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteTraversalSideEffects; +import org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerGremlinV1d0; +import org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerGremlinV2d0; +import org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV1d0; +import org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV2d0; +import org.apache.tinkerpop.gremlin.driver.ser.GryoLiteMessageSerializerV1d0; +import org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0; +import org.apache.tinkerpop.gremlin.driver.ser.JsonBuilderGryoSerializer; +import org.apache.tinkerpop.gremlin.driver.ser.MessageTextSerializer; +import org.apache.tinkerpop.gremlin.driver.ser.SerTokens; +import org.apache.tinkerpop.gremlin.driver.ser.SerializationException; +import org.apache.tinkerpop.gremlin.driver.ser.Serializers; +import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; +import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.console.ConsoleCustomizer; +import org.codehaus.groovy.tools.shell.Groovysh; + +import java.util.Map; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class DriverGremlinPlugin extends AbstractGremlinPlugin { + + private static final String NAME = "tinkerpop.server"; + + private static final ImportCustomizer imports = DefaultImportCustomizer.build() + .addClassImports(Cluster.class, + Client.class, + Host.class, + LoadBalancingStrategy.class, + MessageSerializer.class, + Result.class, + ResultSet.class, + Tokens.class, + ConnectionException.class, + ResponseException.class, + RequestMessage.class, + ResponseMessage.class, + ResponseResult.class, + ResponseStatus.class, + ResponseStatusCode.class, + GraphSONMessageSerializerGremlinV1d0.class, + GraphSONMessageSerializerGremlinV2d0.class, + GraphSONMessageSerializerV1d0.class, + GraphSONMessageSerializerV2d0.class, + GryoLiteMessageSerializerV1d0.class, + GryoMessageSerializerV1d0.class, + JsonBuilderGryoSerializer.class, + MessageTextSerializer.class, + SerializationException.class, + Serializers.class, + SerTokens.class, + DriverRemoteConnection.class, + DriverRemoteTraversal.class, + DriverRemoteTraversalSideEffects.class).create(); + + public DriverGremlinPlugin() { + super(NAME, imports, new DriverConsoleCustomizer()); + } + + private static class DriverConsoleCustomizer implements ConsoleCustomizer { + @Override + public org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor getRemoteAcceptor(final Map environment) { + return new DriverRemoteAcceptor((Groovysh) environment.get(ConsoleCustomizer.ENV_CONSOLE_SHELL)); + } + } +} diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverRemoteAcceptor.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverRemoteAcceptor.java new file mode 100644 index 00000000000..93ac18422ad --- /dev/null +++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverRemoteAcceptor.java @@ -0,0 +1,238 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.console.jsr223; + +import org.apache.commons.lang.exception.ExceptionUtils; +import org.apache.tinkerpop.gremlin.driver.Client; +import org.apache.tinkerpop.gremlin.driver.Cluster; +import org.apache.tinkerpop.gremlin.driver.Result; +import org.apache.tinkerpop.gremlin.driver.ResultSet; +import org.apache.tinkerpop.gremlin.driver.exception.ResponseException; +import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode; +import org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor; +import org.apache.tinkerpop.gremlin.jsr223.console.RemoteException; +import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; +import org.codehaus.groovy.tools.shell.Groovysh; + +import javax.security.sasl.SaslException; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Stream; + +/** + * A {@link RemoteAcceptor} that takes input from the console and sends it to Gremlin Server over the standard + * Java driver. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + * @author Marko A. Rodriguez (http://markorodriguez.com) + */ +public class DriverRemoteAcceptor implements RemoteAcceptor { + public static final int NO_TIMEOUT = 0; + + private Cluster currentCluster; + private Client currentClient; + private int timeout = NO_TIMEOUT; + private Map aliases = new HashMap<>(); + private Optional session = Optional.empty(); + + private static final String TOKEN_RESET = "reset"; + private static final String TOKEN_SHOW = "show"; + + /** + * @deprecated As of 3.1.3, replaced by "none" option + */ + @Deprecated + private static final String TOKEN_MAX = "max"; + private static final String TOKEN_NONE = "none"; + private static final String TOKEN_TIMEOUT = "timeout"; + private static final String TOKEN_ALIAS = "alias"; + private static final String TOKEN_SESSION = "session"; + private static final String TOKEN_SESSION_MANAGED = "session-managed"; + private static final List POSSIBLE_TOKENS = Arrays.asList(TOKEN_TIMEOUT, TOKEN_ALIAS); + + private final Groovysh shell; + + public DriverRemoteAcceptor(final Groovysh shell) { + this.shell = shell; + } + + @Override + public Object connect(final List args) throws RemoteException { + if (args.size() < 1) throw new RemoteException("Expects the location of a configuration file as an argument"); + + try { + this.currentCluster = Cluster.open(args.get(0)); + final boolean useSession = args.size() >= 2 && (args.get(1).equals(TOKEN_SESSION) || args.get(1).equals(TOKEN_SESSION_MANAGED)); + if (useSession) { + final String sessionName = args.size() == 3 ? args.get(2) : UUID.randomUUID().toString(); + session = Optional.of(sessionName); + + final boolean managed = args.get(1).equals(TOKEN_SESSION_MANAGED); + + this.currentClient = this.currentCluster.connect(sessionName, managed); + } else { + this.currentClient = this.currentCluster.connect(); + } + this.currentClient.init(); + return String.format("Configured %s", this.currentCluster) + getSessionStringSegment(); + } catch (final FileNotFoundException ignored) { + throw new RemoteException("The 'connect' option must be accompanied by a valid configuration file"); + } catch (final Exception ex) { + throw new RemoteException("Error during 'connect' - " + ex.getMessage(), ex); + } + } + + @Override + public Object configure(final List args) throws RemoteException { + final String option = args.size() == 0 ? "" : args.get(0); + if (!POSSIBLE_TOKENS.contains(option)) + throw new RemoteException(String.format("The 'config' option expects one of ['%s'] as an argument", String.join(",", POSSIBLE_TOKENS))); + + final List arguments = args.subList(1, args.size()); + + if (option.equals(TOKEN_TIMEOUT)) { + final String errorMessage = "The timeout option expects a positive integer representing milliseconds or 'none' as an argument"; + if (arguments.size() != 1) throw new RemoteException(errorMessage); + try { + // first check for MAX timeout then NONE and finally parse the config to int. "max" is now "deprecated" + // in the sense that it will no longer be promoted. support for it will be removed at a later date + timeout = arguments.get(0).equals(TOKEN_MAX) ? Integer.MAX_VALUE : + arguments.get(0).equals(TOKEN_NONE) ? NO_TIMEOUT : Integer.parseInt(arguments.get(0)); + if (timeout < NO_TIMEOUT) throw new RemoteException("The value for the timeout cannot be less than " + NO_TIMEOUT); + return timeout == NO_TIMEOUT ? "Remote timeout is disabled" : "Set remote timeout to " + timeout + "ms"; + } catch (Exception ignored) { + throw new RemoteException(errorMessage); + } + } else if (option.equals(TOKEN_ALIAS)) { + if (arguments.size() == 1 && arguments.get(0).equals(TOKEN_RESET)) { + aliases.clear(); + return "Aliases cleared"; + } + + if (arguments.size() == 1 && arguments.get(0).equals(TOKEN_SHOW)) { + return aliases; + } + + if (arguments.size() % 2 != 0) + throw new RemoteException("Arguments to alias must be 'reset' to clear any existing alias settings or key/value alias/binding pairs"); + + final Map aliasMap = ElementHelper.asMap(arguments.toArray()); + aliases.clear(); + aliasMap.forEach((k,v) -> aliases.put(k, v.toString())); + return aliases; + } + + return this.toString(); + } + + @Override + public Object submit(final List args) throws RemoteException { + final String line = getScript(String.join(" ", args), this.shell); + + try { + final List resultSet = send(line); + this.shell.getInterp().getContext().setProperty(RESULT, resultSet); + return resultSet.stream().map(result -> result.getObject()).iterator(); + } catch (SaslException sasl) { + throw new RemoteException("Security error - check username/password and related settings", sasl); + } catch (Exception ex) { + final Optional inner = findResponseException(ex); + if (inner.isPresent()) { + final ResponseException responseException = inner.get(); + if (responseException.getResponseStatusCode() == ResponseStatusCode.SERVER_ERROR_SERIALIZATION) + throw new RemoteException(String.format("Server could not serialize the result requested. Server error - %s. Note that the class must be serializable by the client and server for proper operation.", responseException.getMessage())); + else + throw new RemoteException(responseException.getMessage()); + } else if (ex.getCause() != null) { + final Throwable rootCause = ExceptionUtils.getRootCause(ex); + if (rootCause instanceof TimeoutException) + throw new RemoteException("Host did not respond in a timely fashion - check the server status and submit again."); + else + throw new RemoteException(rootCause.getMessage()); + } else + throw new RemoteException(ex.getMessage()); + } + } + + @Override + public void close() throws IOException { + if (this.currentClient != null) this.currentClient.close(); + if (this.currentCluster != null) this.currentCluster.close(); + } + + public int getTimeout() { + return timeout; + } + + private List send(final String gremlin) throws SaslException { + try { + final ResultSet rs = this.currentClient.submitAsync(gremlin, aliases, Collections.emptyMap()).get(); + return timeout > NO_TIMEOUT ? rs.all().get(timeout, TimeUnit.MILLISECONDS) : rs.all().get(); + } catch(TimeoutException ignored) { + throw new IllegalStateException("Request timed out while processing - increase the timeout with the :remote command"); + } catch (Exception e) { + // handle security error as-is and unwrapped + final Optional throwable = Stream.of(ExceptionUtils.getThrowables(e)).filter(t -> t instanceof SaslException).findFirst(); + if (throwable.isPresent()) + throw (SaslException) throwable.get(); + + throw new IllegalStateException(e.getMessage(), e); + } + } + + @Override + public boolean allowRemoteConsole() { + return true; + } + + @Override + public String toString() { + return "Gremlin Server - [" + this.currentCluster + "]" + getSessionStringSegment(); + } + + private Optional findResponseException(final Throwable ex) { + if (ex instanceof ResponseException) + return Optional.of((ResponseException) ex); + + if (null == ex.getCause()) + return Optional.empty(); + + return findResponseException(ex.getCause()); + } + + private String getSessionStringSegment() { + return session.isPresent() ? String.format("-[%s]", session.get()) : ""; + } + + /** + * Retrieve a script as defined in the shell context. This allows for multi-line scripts to be submitted. + */ + public static String getScript(final String submittedScript, final Groovysh shell) { + return submittedScript.startsWith("@") ? shell.getInterp().getContext().getProperty(submittedScript.substring(1)).toString() : submittedScript; + } +} diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/GephiGremlinPlugin.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/GephiGremlinPlugin.java new file mode 100644 index 00000000000..7698112af73 --- /dev/null +++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/GephiGremlinPlugin.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.console.jsr223; + +import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; +import org.apache.tinkerpop.gremlin.jsr223.console.ConsoleCustomizer; +import org.codehaus.groovy.tools.shell.Groovysh; +import org.codehaus.groovy.tools.shell.IO; + +import java.util.Map; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class GephiGremlinPlugin extends AbstractGremlinPlugin { + private static final String NAME = "tinkerpop.gephi"; + + public GephiGremlinPlugin() { + super(NAME, new GephiConsoleCustomizer()); + } + + private static class GephiConsoleCustomizer implements ConsoleCustomizer { + @Override + public org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor getRemoteAcceptor(final Map environment) { + return new GephiRemoteAcceptor((Groovysh) environment.get(ConsoleCustomizer.ENV_CONSOLE_SHELL), + (IO) environment.get(ConsoleCustomizer.ENV_CONSOLE_IO)); + } + } +} diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/UtilitiesGremlinPlugin.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/UtilitiesGremlinPlugin.java new file mode 100644 index 00000000000..57bacda2f97 --- /dev/null +++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/UtilitiesGremlinPlugin.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.console.jsr223; + +import groovyx.gbench.Benchmark; +import groovyx.gbench.BenchmarkStaticExtension; +import groovyx.gprof.ProfileStaticExtension; +import groovyx.gprof.Profiler; +import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; +import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.DefaultScriptCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.ScriptCustomizer; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class UtilitiesGremlinPlugin extends AbstractGremlinPlugin { + + private static final String NAME = "tinkerpop.utilities"; + + private static final ImportCustomizer imports; + + private static final ScriptCustomizer scripts; + + static { + try { + imports = DefaultImportCustomizer.build() + .addClassImports(groovyx.gbench.Benchmark.class, + groovyx.gbench.BenchmarkBuilder.class, + groovyx.gbench.BenchmarkConstants.class, + groovyx.gbench.BenchmarkContext.class, + groovyx.gbench.Benchmarker.class, + groovyx.gbench.BenchmarkList.class, + groovyx.gbench.BenchmarkLogger.class, + groovyx.gbench.BenchmarkMath.class, + groovyx.gbench.BenchmarkMeasure.class, + groovyx.gbench.BenchmarkStaticExtension.class, + groovyx.gbench.BenchmarkSystem.class, + groovyx.gbench.BenchmarkTime.class, + groovyx.gbench.BenchmarkWarmUp.class, + groovyx.gprof.Profiler.class, + groovyx.gprof.ProfileStaticExtension.class, + groovyx.gprof.CallFilter.class, + groovyx.gprof.CallInfo.class, + groovyx.gprof.CallInterceptor.class, + groovyx.gprof.CallMatcher.class, + groovyx.gprof.CallTree.class, + groovyx.gprof.MethodCallFilter.class, + groovyx.gprof.MethodCallInfo.class, + groovyx.gprof.MethodInfo.class, + groovyx.gprof.ProfileMetaClass.class, + groovyx.gprof.ProxyReport.class, + groovyx.gprof.Report.class, + groovyx.gprof.ReportElement.class, + groovyx.gprof.ReportNormalizer.class, + groovyx.gprof.ReportPrinter.class, + groovyx.gprof.ThreadInfo.class, + groovyx.gprof.ThreadRunFilter.class, + groovyx.gprof.Utils.class) + .addMethodImports( + ProfileStaticExtension.class.getMethod("profile", Object.class, Callable.class), + ProfileStaticExtension.class.getMethod("profile", Object.class, Map.class, Callable.class)).create(); + + final BufferedReader reader = new BufferedReader(new InputStreamReader(UtilitiesGremlinPlugin.class.getResourceAsStream("UtilitiesGremlinPluginScript.groovy"))); + final List lines = new ArrayList<>(); + String line; + while ((line = reader.readLine()) != null) { + lines.add(line); + } + reader.close(); + + scripts = new DefaultScriptCustomizer(Collections.singletonList(lines)); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public UtilitiesGremlinPlugin() { + super(NAME, imports, scripts); + } +} diff --git a/gremlin-console/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin b/gremlin-console/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin new file mode 100644 index 00000000000..631c889eeca --- /dev/null +++ b/gremlin-console/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin @@ -0,0 +1,3 @@ +org.apache.tinkerpop.gremlin.console.jsr223.DriverGremlinPlugin +org.apache.tinkerpop.gremlin.console.jsr223.GephiGremlinPlugin +org.apache.tinkerpop.gremlin.console.jsr223.UtilitiesGremlinPlugin \ No newline at end of file diff --git a/gremlin-console/src/main/resources/org/apache/tinkerpop/gremlin/console/jsr223/UtilitiesGremlinPluginScript.groovy b/gremlin-console/src/main/resources/org/apache/tinkerpop/gremlin/console/jsr223/UtilitiesGremlinPluginScript.groovy new file mode 100644 index 00000000000..5f5e3c613f8 --- /dev/null +++ b/gremlin-console/src/main/resources/org/apache/tinkerpop/gremlin/console/jsr223/UtilitiesGremlinPluginScript.groovy @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * @author Daniel Kuppitz (http://thinkaurelius.com) + * @author Marko A. Rodriguez (http://markorodriguez.com) + * @author Stephen Mallette (http://stephen.genoprime.com) + */ + +describeGraph = { Class c -> + def lf = System.getProperty("line.separator") + def optIns = c.getAnnotationsByType(org.apache.tinkerpop.gremlin.structure.Graph.OptIn) + def optOuts = c.getAnnotationsByType(org.apache.tinkerpop.gremlin.structure.Graph.OptOut) + + def optInCount = optIns != null ? optIns.size() : 0 + def optOutCount = optOuts != null ? optOuts.size() : 0 + def suitesSupported = optIns != null && optIns.size() > 0 ? optIns.collect { "> " + it.value() }.join(lf) : "> none" + def testsOptedOut = optOuts != null && optOuts.size() > 0 ? optOuts.collect { "> " + it.test() + "#" + it.method() + "${lf}\t\"" + it.reason() + "\"" }.join(lf) : "> none"; + + // not the use of {lf} here rather than triple quoted string is that groovy 2.4.0 seems to have trouble + // parsing that into groovysh - note the bug report here: https://jira.codehaus.org/browse/GROOVY-7290 + return "${lf}" + +"IMPLEMENTATION - ${c.getCanonicalName()} ${lf}" + +"TINKERPOP TEST SUITE ${lf}" + +"- Compliant with ($optInCount of 10 suites) ${lf}" + +"$suitesSupported ${lf}" + +"- Opts out of $optOutCount individual tests ${lf}" + +"$testsOptedOut ${lf}" + +"- NOTE - ${lf}" + +"The describeGraph() function shows information about a Graph implementation. ${lf}" + +"It uses information found in Java Annotations on the implementation itself to ${lf}" + +"determine this output and does not assess the actual code of the test cases of ${lf}" + +"the implementation itself. Compliant implementations will faithfully and ${lf}" + +"honestly supply these Annotations to provide the most accurate depiction of ${lf}" + +"their support." +} \ No newline at end of file diff --git a/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java b/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java index 77422da68d2..dd582b1efe0 100644 --- a/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java +++ b/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java @@ -19,10 +19,7 @@ package org.apache.tinkerpop.gremlin.console.groovy.plugin; import org.apache.tinkerpop.gremlin.console.plugin.PluggedIn; -import org.apache.tinkerpop.gremlin.jsr223.BindingsCustomizer; import org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin; -import org.apache.tinkerpop.gremlin.jsr223.ScriptCustomizer; -import org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin; import org.junit.Test; import java.time.DayOfWeek; @@ -44,7 +41,7 @@ public void shouldAdaptForImportCustomizer() throws Exception { .classImports(java.awt.Color.class, java.sql.CallableStatement.class) .enumImports(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY) .methodImports(DayOfWeek.class.getMethod("from", TemporalAccessor.class), DayOfWeek.class.getMethod("values")).create(); - final PluggedIn.GremlinPluginAdapter adapter = new PluggedIn.GremlinPluginAdapter(plugin); + final PluggedIn.GremlinPluginAdapter adapter = new PluggedIn.GremlinPluginAdapter(plugin, null, null); assertEquals(plugin.getName(), adapter.getName()); From 03e931d151684e2186ae9b8b1f7887b93817157f Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Wed, 23 Nov 2016 11:31:06 -0500 Subject: [PATCH 21/45] TINKERPOP-1562 Minor changes to javadocs. Also, moved a public static field back to ImportCustomizer (after it became a interface). --- .../gremlin/jsr223/CoreGremlinPlugin.java | 7 ++++--- .../gremlin/jsr223/DefaultImportCustomizer.java | 8 -------- .../gremlin/jsr223/ImportCustomizer.java | 10 ++++++++++ .../gremlin/jsr223/ImportGremlinPlugin.java | 7 ++++--- .../gremlin/jsr223/ScriptFileGremlinPlugin.java | 16 ++++++++++------ 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinPlugin.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinPlugin.java index 410b222ab0b..d5796913341 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinPlugin.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinPlugin.java @@ -23,13 +23,14 @@ import java.util.Optional; /** - * This module is required for a {@code ScriptEngine} to be Gremlin-enabled. + * This module is required for a {@code ScriptEngine} to be Gremlin-enabled. This {@link GremlinPlugin} is not enabled + * for the {@code ServiceLoader}. It is designed to be instantiated manually. * * @author Stephen Mallette (http://stephen.genoprime.com) */ public final class CoreGremlinPlugin implements GremlinPlugin { - private static final String MODULE_NAME = "tinkerpop.core"; + private static final String NAME = "tinkerpop.core"; private static final ImportCustomizer gremlinCore = DefaultImportCustomizer.build() .addClassImports(CoreImports.getClassImports()) @@ -57,6 +58,6 @@ public Optional getCustomizers(final String scriptEngineName) { @Override public String getName() { - return MODULE_NAME; + return NAME; } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultImportCustomizer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultImportCustomizer.java index 85d6531e852..fa0965d107c 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultImportCustomizer.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultImportCustomizer.java @@ -33,14 +33,6 @@ * @author Stephen Mallette (http://stephen.genoprime.com) */ public class DefaultImportCustomizer implements ImportCustomizer { - /** - * @deprecated As of release 3.2.4, not replaced. - */ - @Deprecated - public static final ImportCustomizer GREMLIN_CORE = DefaultImportCustomizer.build() - .addClassImports(CoreImports.getClassImports()) - .addEnumImports(CoreImports.getEnumImports()) - .addMethodImports(CoreImports.getMethodImports()).create(); private final Set classImports; private final Set methodImports; 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 7eced82d0ac..7b056ffcc9f 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 @@ -18,6 +18,8 @@ */ package org.apache.tinkerpop.gremlin.jsr223; +import org.apache.tinkerpop.gremlin.util.CoreImports; + import java.lang.reflect.Method; import java.util.Set; @@ -27,6 +29,14 @@ * @author Stephen Mallette (http://stephen.genoprime.com) */ public interface ImportCustomizer extends Customizer { + /** + * @deprecated As of release 3.2.4, not replaced. + */ + @Deprecated + public static final ImportCustomizer GREMLIN_CORE = DefaultImportCustomizer.build() + .addClassImports(CoreImports.getClassImports()) + .addEnumImports(CoreImports.getEnumImports()) + .addMethodImports(CoreImports.getMethodImports()).create(); public Set getClassImports(); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPlugin.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPlugin.java index 26290d322d0..0d446c06289 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPlugin.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ImportGremlinPlugin.java @@ -33,15 +33,16 @@ /** * 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. + * to any {@link GremlinScriptEngine} - the imports are supplied to all engines. This {@link GremlinPlugin} is not + * enabled for the {@code ServiceLoader}. It is designed to be instantiated manually. * * @author Stephen Mallette (http://stephen.genoprime.com) */ public final class ImportGremlinPlugin extends AbstractGremlinPlugin { - private static final String MODULE_NAME = "tinkerpop.import"; + private static final String NAME = "tinkerpop.import"; private ImportGremlinPlugin(final Builder builder) { - super(MODULE_NAME, builder.appliesTo, DefaultImportCustomizer.build() + super(NAME, builder.appliesTo, DefaultImportCustomizer.build() .addClassImports(builder.classImports) .addEnumImports(builder.enumImports) .addMethodImports(builder.methodImports).create()); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java index 3fd811a5593..757001cffb4 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java @@ -25,13 +25,16 @@ import java.util.Set; /** + * Loads scripts from one or more files into the {@link GremlinScriptEngine} at startup. This {@link GremlinPlugin} is + * not enabled for the {@code ServiceLoader}. It is designed to be instantiated manually. + * * @author Stephen Mallette (http://stephen.genoprime.com) */ public final class ScriptFileGremlinPlugin extends AbstractGremlinPlugin { - private static final String MODULE_NAME = "tinkerpop.script"; + private static final String NAME = "tinkerpop.script"; - public ScriptFileGremlinPlugin(final Builder builder) { - super(MODULE_NAME, builder.appliesTo, new DefaultScriptCustomizer(builder.files)); + private ScriptFileGremlinPlugin(final Builder builder) { + super(NAME, builder.appliesTo, new DefaultScriptCustomizer(builder.files)); } public static Builder build() { @@ -47,10 +50,11 @@ 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. + * make the module available to all the engines. Typically, this value should be set as a script's syntax will + * be bound to the {@link GremlinScriptEngine} language. */ - public Builder appliesTo(final Collection scriptEngineName) { - this.appliesTo.addAll(scriptEngineName); + public Builder appliesTo(final Collection scriptEngineNames) { + this.appliesTo.addAll(scriptEngineNames); return this; } From 33460fc159249a503a3ffa9439d0d8c3c72ef0cf Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Wed, 23 Nov 2016 14:37:26 -0500 Subject: [PATCH 22/45] TINKERPOP-1562 Added more tests for plugins/customizers --- .../CachedGremlinScriptEngineManager.java | 1 + .../jsr223/DefaultImportCustomizer.java | 2 - .../jsr223/LazyBindingsCustomizer.java | 2 - .../SingleGremlinScriptEngineManager.java | 2 +- .../jsr223/DefaultImportCustomizerTest.java | 76 +++++++++++++++++++ .../jsr223/DefaultScriptCustomizerTest.java | 56 ++++++++++++++ .../jsr223/ScriptFileGremlinPluginTest.java | 61 +++++++++++++++ .../jsr223/SingleScriptEngineManagerTest.java | 45 +++++++++++ .../gremlin/jsr223/script-customizer-1.groovy | 3 + .../gremlin/jsr223/script-customizer-2.groovy | 2 + pom.xml | 3 +- 11 files changed, 247 insertions(+), 6 deletions(-) create mode 100644 gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/DefaultImportCustomizerTest.java create mode 100644 gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/DefaultScriptCustomizerTest.java create mode 100644 gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPluginTest.java create mode 100644 gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/SingleScriptEngineManagerTest.java create mode 100644 gremlin-core/src/test/resources/org/apache/tinkerpop/gremlin/jsr223/script-customizer-1.groovy create mode 100644 gremlin-core/src/test/resources/org/apache/tinkerpop/gremlin/jsr223/script-customizer-2.groovy diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CachedGremlinScriptEngineManager.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CachedGremlinScriptEngineManager.java index 9839b1b3aa8..5798e1c8c96 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CachedGremlinScriptEngineManager.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CachedGremlinScriptEngineManager.java @@ -92,6 +92,7 @@ public GremlinScriptEngine getEngineByMimeType(final String mimeType) { } private void registerLookUpInfo(final GremlinScriptEngine engine, final String shortName) { + if (null == engine) throw new IllegalArgumentException(String.format("%s is not an available GremlinScriptEngine", shortName)); cache.putIfAbsent(shortName, engine); engine.getFactory().getExtensions().forEach(ext -> extensionToName.putIfAbsent(ext, shortName)); engine.getFactory().getMimeTypes().forEach(mime -> mimeToName.putIfAbsent(mime, shortName)); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultImportCustomizer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultImportCustomizer.java index fa0965d107c..3642f9711c4 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultImportCustomizer.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultImportCustomizer.java @@ -18,8 +18,6 @@ */ package org.apache.tinkerpop.gremlin.jsr223; -import org.apache.tinkerpop.gremlin.util.CoreImports; - import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; 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 4117ae5fccd..01ae662b3d6 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 @@ -18,8 +18,6 @@ */ package org.apache.tinkerpop.gremlin.jsr223; -import org.apache.tinkerpop.gremlin.jsr223.BindingsCustomizer; - import javax.script.Bindings; import java.util.function.Supplier; diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/SingleGremlinScriptEngineManager.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/SingleGremlinScriptEngineManager.java index 94743684852..f9022dc1780 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/SingleGremlinScriptEngineManager.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/SingleGremlinScriptEngineManager.java @@ -23,7 +23,7 @@ * * @author Stephen Mallette (http://stephen.genoprime.com) */ -public class SingleGremlinScriptEngineManager { +public final class SingleGremlinScriptEngineManager { private static final GremlinScriptEngineManager cached = new CachedGremlinScriptEngineManager(); private SingleGremlinScriptEngineManager() {} diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/DefaultImportCustomizerTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/DefaultImportCustomizerTest.java new file mode 100644 index 00000000000..c010aa56167 --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/DefaultImportCustomizerTest.java @@ -0,0 +1,76 @@ +/* + * 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.T; +import org.junit.Test; + +import java.lang.reflect.Method; +import java.time.DayOfWeek; +import java.util.Arrays; +import java.util.Collections; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsCollectionContaining.hasItems; +import static org.junit.Assert.assertEquals; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class DefaultImportCustomizerTest { + @Test + public void shouldReturnAssignedImports() throws Exception { + final Method abs = Math.class.getMethod("abs", double.class); + final Enum dayOfWeekEnum = DayOfWeek.SATURDAY; + final Enum tEnum = T.id; + final ImportCustomizer imports = DefaultImportCustomizer.build() + .addClassImports(java.awt.Color.class, java.awt.AlphaComposite.class) + .addMethodImports(abs) + .addEnumImports(dayOfWeekEnum, tEnum).create(); + + assertEquals(2, imports.getClassImports().size()); + assertThat(imports.getClassImports(), hasItems(java.awt.Color.class, java.awt.AlphaComposite.class)); + + assertEquals(1, imports.getMethodImports().size()); + assertThat(imports.getMethodImports(), hasItems(abs)); + + assertEquals(2, imports.getEnumImports().size()); + assertThat(imports.getEnumImports(), hasItems(dayOfWeekEnum, tEnum)); + } + + @Test + public void shouldReturnAssignedImportsWhenBuiltViaCollections() throws Exception { + final Method abs = Math.class.getMethod("abs", double.class); + final Enum dayOfWeekEnum = DayOfWeek.SATURDAY; + final Enum tEnum = T.id; + final ImportCustomizer imports = DefaultImportCustomizer.build() + .addClassImports(Arrays.asList(java.awt.Color.class, java.awt.AlphaComposite.class)) + .addMethodImports(Collections.singletonList(abs)) + .addEnumImports(Arrays.asList(dayOfWeekEnum, tEnum)).create(); + + assertEquals(2, imports.getClassImports().size()); + assertThat(imports.getClassImports(), hasItems(java.awt.Color.class, java.awt.AlphaComposite.class)); + + assertEquals(1, imports.getMethodImports().size()); + assertThat(imports.getMethodImports(), hasItems(abs)); + + assertEquals(2, imports.getEnumImports().size()); + assertThat(imports.getEnumImports(), hasItems(dayOfWeekEnum, tEnum)); + } +} diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/DefaultScriptCustomizerTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/DefaultScriptCustomizerTest.java new file mode 100644 index 00000000000..3e4da135fee --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/DefaultScriptCustomizerTest.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 org.apache.tinkerpop.gremlin.TestHelper; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class DefaultScriptCustomizerTest { + + @Test + public void shouldOpenViaPropertiesFileConfig() throws IOException { + final File scriptFile1 = TestHelper.generateTempFileFromResource(DefaultScriptCustomizerTest.class, "script-customizer-1.groovy", ".groovy"); + final File scriptFile2 = TestHelper.generateTempFileFromResource(DefaultScriptCustomizerTest.class, "script-customizer-2.groovy", ".groovy"); + final Set files = new HashSet<>(); + files.add(scriptFile1); + files.add(scriptFile2); + final ScriptCustomizer scripts = new DefaultScriptCustomizer(files); + + final Collection> linesInFiles = scripts.getScripts(); + final String scriptCombined = linesInFiles.stream().flatMap(Collection::stream).map(s -> s + System.lineSeparator()).reduce("", String::concat); + assertEquals("x = 1 + 1" + System.lineSeparator() + + "y = 10 * x" + System.lineSeparator() + + "z = 1 + x + y" + System.lineSeparator() + + "l = g.V(z).out()" + System.lineSeparator() + + " .group().by('name')" + System.lineSeparator(), scriptCombined); + + } +} diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPluginTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPluginTest.java new file mode 100644 index 00000000000..81cf9e60a95 --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPluginTest.java @@ -0,0 +1,61 @@ +/* + * 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.TestHelper; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsInstanceOf.instanceOf; +import static org.junit.Assert.assertEquals; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class ScriptFileGremlinPluginTest { + @Test + public void shouldOpenViaPropertiesFileConfig() throws IOException { + final File scriptFile1 = TestHelper.generateTempFileFromResource(DefaultScriptCustomizerTest.class, "script-customizer-1.groovy", ".groovy"); + final File scriptFile2 = TestHelper.generateTempFileFromResource(DefaultScriptCustomizerTest.class, "script-customizer-2.groovy", ".groovy"); + final Set files = new HashSet<>(); + files.add(scriptFile1.getAbsolutePath()); + files.add(scriptFile2.getAbsolutePath()); + final GremlinPlugin plugin = ScriptFileGremlinPlugin.build().files(files).create(); + + assertThat(plugin.getCustomizers().isPresent(), is(true)); + assertThat(plugin.getCustomizers().get()[0], instanceOf(ScriptCustomizer.class)); + final ScriptCustomizer customizer = (ScriptCustomizer) plugin.getCustomizers().get()[0]; + final Collection> linesInFiles = customizer.getScripts(); + final String scriptCombined = linesInFiles.stream().flatMap(Collection::stream).map(s -> s + System.lineSeparator()).reduce("", String::concat); + assertEquals("x = 1 + 1" + System.lineSeparator() + + "y = 10 * x" + System.lineSeparator() + + "z = 1 + x + y" + System.lineSeparator() + + "l = g.V(z).out()" + System.lineSeparator() + + " .group().by('name')" + System.lineSeparator(), scriptCombined); + + } +} diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/SingleScriptEngineManagerTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/SingleScriptEngineManagerTest.java new file mode 100644 index 00000000000..6765c94b139 --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/SingleScriptEngineManagerTest.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.jsr223; + +import org.junit.Test; + +import static org.junit.Assert.assertSame; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class SingleScriptEngineManagerTest { + private static final GremlinScriptEngineManager mgr = SingleGremlinScriptEngineManager.instance(); + + @Test + public void shouldGetSameInstance() { + assertSame(mgr, SingleGremlinScriptEngineManager.instance()); + assertSame(mgr, SingleGremlinScriptEngineManager.instance()); + assertSame(mgr, SingleGremlinScriptEngineManager.instance()); + assertSame(mgr, SingleGremlinScriptEngineManager.instance()); + assertSame(mgr, SingleGremlinScriptEngineManager.instance()); + assertSame(mgr, SingleGremlinScriptEngineManager.getInstance()); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldNotGetGremlinScriptEngineAsItIsNotRegistered() { + mgr.getEngineByName("gremlin-groovy"); + } +} diff --git a/gremlin-core/src/test/resources/org/apache/tinkerpop/gremlin/jsr223/script-customizer-1.groovy b/gremlin-core/src/test/resources/org/apache/tinkerpop/gremlin/jsr223/script-customizer-1.groovy new file mode 100644 index 00000000000..c2cc784523b --- /dev/null +++ b/gremlin-core/src/test/resources/org/apache/tinkerpop/gremlin/jsr223/script-customizer-1.groovy @@ -0,0 +1,3 @@ +x = 1 + 1 +y = 10 * x +z = 1 + x + y \ No newline at end of file diff --git a/gremlin-core/src/test/resources/org/apache/tinkerpop/gremlin/jsr223/script-customizer-2.groovy b/gremlin-core/src/test/resources/org/apache/tinkerpop/gremlin/jsr223/script-customizer-2.groovy new file mode 100644 index 00000000000..1a4f9a9200f --- /dev/null +++ b/gremlin-core/src/test/resources/org/apache/tinkerpop/gremlin/jsr223/script-customizer-2.groovy @@ -0,0 +1,2 @@ +l = g.V(z).out() + .group().by('name') \ No newline at end of file diff --git a/pom.xml b/pom.xml index e4d1ba7f467..0bff1e7fd98 100644 --- a/pom.xml +++ b/pom.xml @@ -285,9 +285,10 @@ limitations under the License. **/*.xml **/*.ldjson **/goal.txt - **/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/script/*.txt **/src/main/resources/META-INF/services/** **/src/test/resources/META-INF/services/** + **/src/test/resources/org/apache/tinkerpop/gremlin/jsr223/script-customizer-*.groovy + **/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/script/*.txt **/src/main/ext/** **/src/main/static/** **/_bsp/** From 35c8fcd651cf0f4788eb4e0435ed7fa966304d5c Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Wed, 23 Nov 2016 15:36:13 -0500 Subject: [PATCH 23/45] TINKERPOP-1562 Added more tests for the GremlnPluginAdapter --- .../plugin/GremlinPluginAdapterTest.java | 70 +++++++++++++++++++ .../groovy/plugin/script-customizer-1.groovy | 3 + .../groovy/plugin/script-customizer-2.groovy | 2 + pom.xml | 1 + 4 files changed, 76 insertions(+) create mode 100644 gremlin-console/src/test/resources/org/apache/tinkerpop/gremlin/console/groovy/plugin/script-customizer-1.groovy create mode 100644 gremlin-console/src/test/resources/org/apache/tinkerpop/gremlin/console/groovy/plugin/script-customizer-2.groovy diff --git a/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java b/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java index dd582b1efe0..c3ade0492a2 100644 --- a/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java +++ b/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java @@ -18,12 +18,28 @@ */ package org.apache.tinkerpop.gremlin.console.groovy.plugin; +import org.apache.tinkerpop.gremlin.TestHelper; import org.apache.tinkerpop.gremlin.console.plugin.PluggedIn; +import org.apache.tinkerpop.gremlin.jsr223.BindingsCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.Customizer; +import org.apache.tinkerpop.gremlin.jsr223.DefaultBindingsCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.DefaultScriptCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin; import org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin; +import org.apache.tinkerpop.gremlin.jsr223.ScriptCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin; import org.junit.Test; +import javax.script.Bindings; +import javax.script.SimpleBindings; +import java.io.File; import java.time.DayOfWeek; import java.time.temporal.TemporalAccessor; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.Set; import static org.hamcrest.MatcherAssert.assertThat; @@ -57,4 +73,58 @@ public void shouldAdaptForImportCustomizer() throws Exception { assertThat(imports, hasItems("import static " + DayOfWeek.class.getCanonicalName() + ".from")); assertThat(imports, hasItems("import static " + DayOfWeek.class.getCanonicalName() + ".values")); } + + @Test + public void shouldAdaptForScriptCustomizer() throws Exception { + final File scriptFile1 = TestHelper.generateTempFileFromResource(GremlinPluginAdapterTest.class, "script-customizer-1.groovy", ".groovy"); + final File scriptFile2 = TestHelper.generateTempFileFromResource(GremlinPluginAdapterTest.class, "script-customizer-2.groovy", ".groovy"); + final Set files = new HashSet<>(); + files.add(scriptFile1.getAbsolutePath()); + files.add(scriptFile2.getAbsolutePath()); + final ScriptFileGremlinPlugin plugin = ScriptFileGremlinPlugin.build().files(files).create(); + final PluggedIn.GremlinPluginAdapter adapter = new PluggedIn.GremlinPluginAdapter(plugin, null, null); + + assertEquals(plugin.getName(), adapter.getName()); + + final List evals = new ArrayList<>(); + final SpyPluginAcceptor spy = new SpyPluginAcceptor(evals::add); + adapter.pluginTo(spy); + + assertEquals("x = 1 + 1\n" + + "y = 10 * x\n" + + "z = 1 + x + y", evals.get(0)); + assertEquals("l = g.V(z).out()\n" + + " .group().by('name')", evals.get(1)); + } + + @Test + public void shouldAdaptForBindingsCustomizer() throws Exception { + final Bindings bindings = new SimpleBindings(); + bindings.put("x", 1); + bindings.put("y", "yes"); + bindings.put("z", true); + final BindingsCustomizer bindingsCustomizer = new DefaultBindingsCustomizer(bindings); + final GremlinPlugin plugin = new GremlinPlugin() { + @Override + public String getName() { + return "anon-bindings"; + } + + @Override + public Optional getCustomizers(final String scriptEngineName) { + return Optional.of(new Customizer[]{bindingsCustomizer}); + } + }; + final PluggedIn.GremlinPluginAdapter adapter = new PluggedIn.GremlinPluginAdapter(plugin, null, null); + + assertEquals(plugin.getName(), adapter.getName()); + + final SpyPluginAcceptor spy = new SpyPluginAcceptor(); + adapter.pluginTo(spy); + + final Map bindingsFromSpy = spy.getBindings(); + assertEquals(1, bindingsFromSpy.get("x")); + assertEquals("yes", bindingsFromSpy.get("y")); + assertEquals(true, bindingsFromSpy.get("z")); + } } diff --git a/gremlin-console/src/test/resources/org/apache/tinkerpop/gremlin/console/groovy/plugin/script-customizer-1.groovy b/gremlin-console/src/test/resources/org/apache/tinkerpop/gremlin/console/groovy/plugin/script-customizer-1.groovy new file mode 100644 index 00000000000..c2cc784523b --- /dev/null +++ b/gremlin-console/src/test/resources/org/apache/tinkerpop/gremlin/console/groovy/plugin/script-customizer-1.groovy @@ -0,0 +1,3 @@ +x = 1 + 1 +y = 10 * x +z = 1 + x + y \ No newline at end of file diff --git a/gremlin-console/src/test/resources/org/apache/tinkerpop/gremlin/console/groovy/plugin/script-customizer-2.groovy b/gremlin-console/src/test/resources/org/apache/tinkerpop/gremlin/console/groovy/plugin/script-customizer-2.groovy new file mode 100644 index 00000000000..1a4f9a9200f --- /dev/null +++ b/gremlin-console/src/test/resources/org/apache/tinkerpop/gremlin/console/groovy/plugin/script-customizer-2.groovy @@ -0,0 +1,2 @@ +l = g.V(z).out() + .group().by('name') \ No newline at end of file diff --git a/pom.xml b/pom.xml index 0bff1e7fd98..37c7c7e4486 100644 --- a/pom.xml +++ b/pom.xml @@ -287,6 +287,7 @@ limitations under the License. **/goal.txt **/src/main/resources/META-INF/services/** **/src/test/resources/META-INF/services/** + **/src/test/resources/org/apache/tinkerpop/gremlin/console/groovy/plugin/script-customizer-*.groovy **/src/test/resources/org/apache/tinkerpop/gremlin/jsr223/script-customizer-*.groovy **/src/main/resources/org/apache/tinkerpop/gremlin/structure/io/script/*.txt **/src/main/ext/** From 2efa2e4b75ea8032355677165097b492f2979aa8 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Wed, 23 Nov 2016 17:10:13 -0500 Subject: [PATCH 24/45] TINKERPOP-1562 Abstracted groovysh/io to GremlinShellEnvironment The GremlinShellEnvironment provides a way to abstract groovysh and io classes (i.e. groovy specific classes) so that plugins don't need to depend on gremlin-groovy at all. --- .../console/jsr223/GephiRemoteAcceptor.groovy | 15 +++----- .../console/plugin/GephiRemoteAcceptor.groovy | 2 + .../gremlin/console/plugin/PluggedIn.groovy | 26 ++++++++++++- .../console/jsr223/DriverGremlinPlugin.java | 8 ++-- .../console/jsr223/DriverRemoteAcceptor.java | 16 ++++---- .../console/jsr223/GephiGremlinPlugin.java | 10 ++--- .../jsr223/console/ConsoleCustomizer.java | 7 +--- .../console/GremlinShellEnvironment.java | 37 +++++++++++++++++++ .../hadoop/jsr223/HadoopGremlinPlugin.java | 7 ++-- .../hadoop/jsr223/HadoopRemoteAcceptor.java | 22 +++++------ 10 files changed, 99 insertions(+), 51 deletions(-) create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/GremlinShellEnvironment.java diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy index dbc11568d3c..4e41fa4885c 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/jsr223/GephiRemoteAcceptor.groovy @@ -29,14 +29,13 @@ import org.apache.http.impl.client.CloseableHttpClient import org.apache.http.impl.client.HttpClients import org.apache.http.util.EntityUtils import org.apache.tinkerpop.gremlin.console.plugin.GephiTraversalVisualizationStrategy +import org.apache.tinkerpop.gremlin.jsr223.console.GremlinShellEnvironment import org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor import org.apache.tinkerpop.gremlin.jsr223.console.RemoteException import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource import org.apache.tinkerpop.gremlin.structure.Edge import org.apache.tinkerpop.gremlin.structure.Graph import org.apache.tinkerpop.gremlin.structure.Vertex -import org.codehaus.groovy.tools.shell.Groovysh -import org.codehaus.groovy.tools.shell.IO /** * @author Stephen Mallette (http://stephen.genoprime.com) @@ -48,8 +47,7 @@ class GephiRemoteAcceptor implements RemoteAcceptor { private int port = 8080 private String workspace = "workspace1" - private final Groovysh shell - private final IO io + private final GremlinShellEnvironment shell private final Random rand = new Random(); boolean traversalSubmittedForViz = false @@ -64,9 +62,8 @@ class GephiRemoteAcceptor implements RemoteAcceptor { private CloseableHttpClient httpclient = HttpClients.createDefault(); - public GephiRemoteAcceptor(final Groovysh shell, final IO io) { + public GephiRemoteAcceptor(final GremlinShellEnvironment shell) { this.shell = shell - this.io = io // traversal visualization defaults vizStepDelay = 1000; // 1 second pause between viz of steps @@ -129,13 +126,13 @@ class GephiRemoteAcceptor implements RemoteAcceptor { else if (args[0] == "startSize") parseVizStartSize(args[1]) else if (args[0] == "visualTraversal") { - def graphVar = shell.interp.context.getVariable(args[1]) + def graphVar = shell.getVariable(args[1]) if (!(graphVar instanceof Graph)) throw new RemoteException("Invalid argument to 'visualTraversal' - first parameter must be a Graph instance") def gVar = args.size() == 3 ? args[2] : "vg" def theG = GraphTraversalSource.build().with(new GephiTraversalVisualizationStrategy(this)).create(graphVar) - shell.interp.context.setVariable(gVar, theG) + shell.setVariable(gVar, theG) } else throw new RemoteException("Invalid config arguments - check syntax") @@ -151,7 +148,7 @@ class GephiRemoteAcceptor implements RemoteAcceptor { final String line = String.join(" ", args) if (line.trim() == "clear") { clearGraph() - io.out.println("Gephi workspace cleared") + shell.println("Gephi workspace cleared") return } diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/GephiRemoteAcceptor.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/GephiRemoteAcceptor.groovy index 41984440da3..902a479e176 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/GephiRemoteAcceptor.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/GephiRemoteAcceptor.groovy @@ -40,7 +40,9 @@ import org.codehaus.groovy.tools.shell.IO /** * @author Stephen Mallette (http://stephen.genoprime.com) * @author Randall Barnhart (randompi@gmail.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.console.jsr223.GephiRemoteAcceptor} */ +@Deprecated class GephiRemoteAcceptor implements RemoteAcceptor { private String host = "localhost" diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy index 7a08a9de10a..364e6efbb31 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/plugin/PluggedIn.groovy @@ -28,6 +28,7 @@ import org.apache.tinkerpop.gremlin.jsr223.BindingsCustomizer import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer import org.apache.tinkerpop.gremlin.jsr223.ScriptCustomizer import org.apache.tinkerpop.gremlin.jsr223.console.ConsoleCustomizer +import org.apache.tinkerpop.gremlin.jsr223.console.GremlinShellEnvironment import org.codehaus.groovy.tools.shell.Groovysh import org.codehaus.groovy.tools.shell.IO @@ -111,7 +112,30 @@ class PluggedIn { return Optional.empty() ConsoleCustomizer customizer = (ConsoleCustomizer) corePlugin.getCustomizers("gremlin-groovy").get().find{ it instanceof ConsoleCustomizer } - return Optional.of(new RemoteAcceptorAdapter(customizer.getRemoteAcceptor([(ConsoleCustomizer.ENV_CONSOLE_SHELL): shell, (ConsoleCustomizer.ENV_CONSOLE_IO): io]))) + return Optional.of(new RemoteAcceptorAdapter(customizer.getRemoteAcceptor(new GroovyGremlinShellEnvironment()))) + } + + public class GroovyGremlinShellEnvironment implements GremlinShellEnvironment { + + @Override + def T getVariable(final String variableName) { + return (T) shell.interp.context.getVariable(variableName) + } + + @Override + def void setVariable(final String variableName, final T variableValue) { + shell.interp.context.setVariable(variableName, variableValue) + } + + @Override + void println(final String line) { + io.println(line) + } + + @Override + def T execute(final String line) { + return (T) shell.execute(line) + } } } diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverGremlinPlugin.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverGremlinPlugin.java index 89cec10988c..fb78ee92eeb 100644 --- a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverGremlinPlugin.java +++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverGremlinPlugin.java @@ -51,9 +51,7 @@ import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; import org.apache.tinkerpop.gremlin.jsr223.console.ConsoleCustomizer; -import org.codehaus.groovy.tools.shell.Groovysh; - -import java.util.Map; +import org.apache.tinkerpop.gremlin.jsr223.console.GremlinShellEnvironment; /** * @author Stephen Mallette (http://stephen.genoprime.com) @@ -99,8 +97,8 @@ public DriverGremlinPlugin() { private static class DriverConsoleCustomizer implements ConsoleCustomizer { @Override - public org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor getRemoteAcceptor(final Map environment) { - return new DriverRemoteAcceptor((Groovysh) environment.get(ConsoleCustomizer.ENV_CONSOLE_SHELL)); + public org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor getRemoteAcceptor(final GremlinShellEnvironment environment) { + return new DriverRemoteAcceptor(environment); } } } diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverRemoteAcceptor.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverRemoteAcceptor.java index 93ac18422ad..aa02606a653 100644 --- a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverRemoteAcceptor.java +++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/DriverRemoteAcceptor.java @@ -25,10 +25,10 @@ import org.apache.tinkerpop.gremlin.driver.ResultSet; import org.apache.tinkerpop.gremlin.driver.exception.ResponseException; import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode; +import org.apache.tinkerpop.gremlin.jsr223.console.GremlinShellEnvironment; import org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor; import org.apache.tinkerpop.gremlin.jsr223.console.RemoteException; import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; -import org.codehaus.groovy.tools.shell.Groovysh; import javax.security.sasl.SaslException; import java.io.FileNotFoundException; @@ -75,10 +75,10 @@ public class DriverRemoteAcceptor implements RemoteAcceptor { private static final String TOKEN_SESSION_MANAGED = "session-managed"; private static final List POSSIBLE_TOKENS = Arrays.asList(TOKEN_TIMEOUT, TOKEN_ALIAS); - private final Groovysh shell; + private final GremlinShellEnvironment shellEnvironment; - public DriverRemoteAcceptor(final Groovysh shell) { - this.shell = shell; + public DriverRemoteAcceptor(final GremlinShellEnvironment shellEnvironment) { + this.shellEnvironment = shellEnvironment; } @Override @@ -152,11 +152,11 @@ public Object configure(final List args) throws RemoteException { @Override public Object submit(final List args) throws RemoteException { - final String line = getScript(String.join(" ", args), this.shell); + final String line = getScript(String.join(" ", args), this.shellEnvironment); try { final List resultSet = send(line); - this.shell.getInterp().getContext().setProperty(RESULT, resultSet); + this.shellEnvironment.setVariable(RESULT, resultSet); return resultSet.stream().map(result -> result.getObject()).iterator(); } catch (SaslException sasl) { throw new RemoteException("Security error - check username/password and related settings", sasl); @@ -232,7 +232,7 @@ private String getSessionStringSegment() { /** * Retrieve a script as defined in the shell context. This allows for multi-line scripts to be submitted. */ - public static String getScript(final String submittedScript, final Groovysh shell) { - return submittedScript.startsWith("@") ? shell.getInterp().getContext().getProperty(submittedScript.substring(1)).toString() : submittedScript; + public static String getScript(final String submittedScript, final GremlinShellEnvironment shellEnvironment) { + return submittedScript.startsWith("@") ? shellEnvironment.getVariable(submittedScript.substring(1)).toString() : submittedScript; } } diff --git a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/GephiGremlinPlugin.java b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/GephiGremlinPlugin.java index 7698112af73..c30f8641b61 100644 --- a/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/GephiGremlinPlugin.java +++ b/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/jsr223/GephiGremlinPlugin.java @@ -20,10 +20,7 @@ import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; import org.apache.tinkerpop.gremlin.jsr223.console.ConsoleCustomizer; -import org.codehaus.groovy.tools.shell.Groovysh; -import org.codehaus.groovy.tools.shell.IO; - -import java.util.Map; +import org.apache.tinkerpop.gremlin.jsr223.console.GremlinShellEnvironment; /** * @author Stephen Mallette (http://stephen.genoprime.com) @@ -37,9 +34,8 @@ public GephiGremlinPlugin() { private static class GephiConsoleCustomizer implements ConsoleCustomizer { @Override - public org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor getRemoteAcceptor(final Map environment) { - return new GephiRemoteAcceptor((Groovysh) environment.get(ConsoleCustomizer.ENV_CONSOLE_SHELL), - (IO) environment.get(ConsoleCustomizer.ENV_CONSOLE_IO)); + public org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor getRemoteAcceptor(final GremlinShellEnvironment gremlinShellEnvironment) { + return new GephiRemoteAcceptor(gremlinShellEnvironment); } } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/ConsoleCustomizer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/ConsoleCustomizer.java index 7b3d78800f6..92044884213 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/ConsoleCustomizer.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/ConsoleCustomizer.java @@ -20,19 +20,14 @@ import org.apache.tinkerpop.gremlin.jsr223.Customizer; -import java.util.Map; - /** * @author Stephen Mallette (http://stephen.genoprime.com) */ public interface ConsoleCustomizer extends Customizer { - public static final String ENV_CONSOLE_IO = "ConsolePluginAcceptor.io"; - public static final String ENV_CONSOLE_SHELL = "ConsolePluginAcceptor.shell"; - /** * Allows a plugin to utilize features of the {@code :remote} and {@code :submit} commands of the Gremlin Console. * This method does not need to be implemented if the plugin is not meant for the Console for some reason or * if it does not intend to take advantage of those commands. */ - public RemoteAcceptor getRemoteAcceptor(final Map environment); + public RemoteAcceptor getRemoteAcceptor(final GremlinShellEnvironment environment); } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/GremlinShellEnvironment.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/GremlinShellEnvironment.java new file mode 100644 index 00000000000..6fc1363fa43 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/GremlinShellEnvironment.java @@ -0,0 +1,37 @@ +/* + * 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.console; + +/** + * Provides an abstraction over a "Gremlin Shell" (i.e. the core of a console), enabling the plugin to not have to + * be hardcoded specifically to any particular shell, like the Gremlin Groovy Console, and thus allowing it to + * not have to depend on the gremlin-groovy module itself. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public interface GremlinShellEnvironment { + + public T getVariable(final String variableName); + + public void setVariable(final String variableName, final T variableValue); + + public void println(final String line); + + public T execute(final String line); +} 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 5e210273bfe..b7403b6e253 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 @@ -53,14 +53,13 @@ import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; import org.apache.tinkerpop.gremlin.jsr223.LazyBindingsCustomizer; import org.apache.tinkerpop.gremlin.jsr223.console.ConsoleCustomizer; +import org.apache.tinkerpop.gremlin.jsr223.console.GremlinShellEnvironment; import org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor; -import org.codehaus.groovy.tools.shell.Groovysh; import javax.script.Bindings; import javax.script.SimpleBindings; import java.util.Collections; import java.util.HashSet; -import java.util.Map; import java.util.Optional; import java.util.Set; @@ -147,8 +146,8 @@ public boolean requireRestart() { private static class HadoopConsoleCustomizer implements ConsoleCustomizer { @Override - public RemoteAcceptor getRemoteAcceptor(final Map environment) { - return new HadoopRemoteAcceptor((Groovysh) environment.get(ConsoleCustomizer.ENV_CONSOLE_SHELL)); + public RemoteAcceptor getRemoteAcceptor(final GremlinShellEnvironment environment) { + return new HadoopRemoteAcceptor(environment); } } } \ No newline at end of file diff --git a/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopRemoteAcceptor.java b/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopRemoteAcceptor.java index f2367bd1cc8..1fcdab11da9 100644 --- a/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopRemoteAcceptor.java +++ b/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/jsr223/HadoopRemoteAcceptor.java @@ -20,6 +20,7 @@ import org.apache.tinkerpop.gremlin.groovy.loaders.SugarLoader; import org.apache.tinkerpop.gremlin.hadoop.structure.HadoopGraph; +import org.apache.tinkerpop.gremlin.jsr223.console.GremlinShellEnvironment; import org.apache.tinkerpop.gremlin.jsr223.console.RemoteAcceptor; import org.apache.tinkerpop.gremlin.jsr223.console.RemoteException; import org.apache.tinkerpop.gremlin.process.computer.ComputerResult; @@ -30,7 +31,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep; import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversal; -import org.codehaus.groovy.tools.shell.Groovysh; import java.io.IOException; import java.util.Collections; @@ -47,12 +47,12 @@ public final class HadoopRemoteAcceptor implements RemoteAcceptor { private static final String SPACE = " "; private HadoopGraph hadoopGraph; - private Groovysh shell; + private GremlinShellEnvironment shellEnvironment; private boolean useSugar = false; private TraversalSource traversalSource; - public HadoopRemoteAcceptor(final Groovysh shell) { - this.shell = shell; + public HadoopRemoteAcceptor(final GremlinShellEnvironment shellEnvironment) { + this.shellEnvironment = shellEnvironment; } @Override @@ -60,9 +60,9 @@ public Object connect(final List args) throws RemoteException { if (args.size() != 1 && args.size() != 2) { throw new IllegalArgumentException("Usage: :remote connect " + HadoopGremlinPlugin.NAME + " "); } - this.hadoopGraph = (HadoopGraph) this.shell.getInterp().getContext().getVariable(args.get(0)); + this.hadoopGraph = this.shellEnvironment.getVariable(args.get(0)); if (args.size() == 2) - this.traversalSource = ((TraversalSource) this.shell.getInterp().getContext().getVariable(args.get(1))); + this.traversalSource = this.shellEnvironment.getVariable(args.get(1)); else this.traversalSource = this.hadoopGraph.traversal(); /// @@ -78,7 +78,7 @@ public Object configure(final List args) throws RemoteException { if (args.get(i).equals(USE_SUGAR)) this.useSugar = Boolean.valueOf(args.get(i + 1)); else if (args.get(i).equals(USE_TRAVERSAL_SOURCE)) { - this.traversalSource = ((TraversalSource) this.shell.getInterp().getContext().getVariable(args.get(i + 1))); + this.traversalSource = this.shellEnvironment.getVariable(args.get(i + 1)); } else throw new IllegalArgumentException("The provided configuration is unknown: " + args.get(i) + ":" + args.get(i + 1)); } @@ -92,12 +92,12 @@ else if (args.get(i).equals(USE_TRAVERSAL_SOURCE)) { @Override public Object submit(final List args) throws RemoteException { try { - String script = getScript(String.join(SPACE, args), this.shell); + String script = getScript(String.join(SPACE, args), this.shellEnvironment); if (this.useSugar) script = SugarLoader.class.getCanonicalName() + ".load()\n" + script; final TraversalVertexProgram program = TraversalVertexProgram.build().traversal(this.traversalSource, "gremlin-groovy", script).create(this.hadoopGraph); final ComputerResult computerResult = VertexProgramStrategy.getComputer(this.traversalSource.getStrategies()).get().apply(this.hadoopGraph).program(program).submit().get(); - this.shell.getInterp().getContext().setVariable(RESULT, computerResult); + this.shellEnvironment.setVariable(RESULT, computerResult); /// final Traversal.Admin traversal = new DefaultTraversal<>(computerResult.graph()); traversal.addStep(new ComputerResultStep<>(traversal)); @@ -121,7 +121,7 @@ public void close() throws IOException { /** * Retrieve a script as defined in the shell context. This allows for multi-line scripts to be submitted. */ - public static String getScript(final String submittedScript, final Groovysh shell) { - return submittedScript.startsWith("@") ? shell.getInterp().getContext().getProperty(submittedScript.substring(1)).toString() : submittedScript; + public static String getScript(final String submittedScript, final GremlinShellEnvironment shell) { + return submittedScript.startsWith("@") ? shell.getVariable(submittedScript.substring(1)).toString() : submittedScript; } } From a2ac1f29613576b3bf409c0c766d3eae66d45780 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Wed, 23 Nov 2016 17:22:58 -0500 Subject: [PATCH 25/45] TINKERPOP-1562 Added JSR-223 packages in gremin-core to core javadoc --- pom.xml | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 37c7c7e4486..3c5f8ed01c3 100644 --- a/pom.xml +++ b/pom.xml @@ -1060,7 +1060,7 @@ limitations under the License. ${basedir}/docs/javadoc/overview.html true - giraph-gremlin/src/main/java:gremlin-core/src/main/java:gremlin-driver/src/main/java:gremlin-groovy/src/main/java:gremlin-groovy-test/src/main/java:gremlin-server/src/main/java:gremlin-test/src/main/java:hadoop-gremlin/src/main/java:neo4j-gremlin/src/main/java:spark-gremlin/src/main/java:tinkergraph-gremlin/src/main/java + giraph-gremlin/src/main/java:gremlin-core/src/main/java:gremlin-driver/src/main/java:gremlin-groovy/src/main/java:gremlin-groovy-test/src/main/java:gremlin-python/src/main/java:gremlin-server/src/main/java:gremlin-test/src/main/java:hadoop-gremlin/src/main/java:neo4j-gremlin/src/main/java:spark-gremlin/src/main/java:tinkergraph-gremlin/src/main/java @@ -1080,11 +1080,20 @@ limitations under the License. gremlin-core/src/main/java:gremlin-driver/src/main/java + + + org/apache/tinkerpop/gremlin/jsr223/*.java + + + org/apache/tinkerpop/gremlin/jsr223/console/*.java + - org/apache/tinkerpop/gremlin/structure/*.java + + org/apache/tinkerpop/gremlin/structure/*.java - org/apache/tinkerpop/gremlin/structure/io/*.java + + org/apache/tinkerpop/gremlin/structure/io/*.java org/apache/tinkerpop/gremlin/structure/io/graphml/GraphMLReader.java @@ -1105,7 +1114,8 @@ limitations under the License. org/apache/tinkerpop/gremlin/structure/io/gryo/GryoWriter.java - org/apache/tinkerpop/gremlin/process/*.java + + org/apache/tinkerpop/gremlin/process/*.java org/apache/tinkerpop/gremlin/process/computer/*.java @@ -1120,7 +1130,8 @@ limitations under the License. org/apache/tinkerpop/gremlin/process/computer/traversal/TraversalVertexProgram.java - org/apache/tinkerpop/gremlin/process/traversal/*.java + + org/apache/tinkerpop/gremlin/process/traversal/*.java org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java From 05ff0c0fe957a9fe697c5b1a176c682837ee0c64 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Thu, 24 Nov 2016 08:11:35 -0500 Subject: [PATCH 26/45] TINKERPOP-1562 Hooked up GremlinJythonScriptEngine to Customizers GremlnJythonScriptEngine is now initialized the same way that GremlinGroovyScriptEngine is. --- .../jsr223/GremlinJythonScriptEngine.java | 167 ++++++++++++------ .../GremlinJythonScriptEngineFactory.java | 5 +- .../jsr223/GremlinJythonScriptEngineTest.java | 12 +- .../GremlinEnabledScriptEngineTest.java | 1 - 4 files changed, 120 insertions(+), 65 deletions(-) diff --git a/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngine.java b/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngine.java index 554d80a9695..1b95a0288fb 100644 --- a/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngine.java +++ b/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngine.java @@ -19,8 +19,10 @@ package org.apache.tinkerpop.gremlin.python.jsr223; +import org.apache.tinkerpop.gremlin.jsr223.Customizer; import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine; import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineFactory; +import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; import org.apache.tinkerpop.gremlin.process.traversal.Bytecode; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource; @@ -35,7 +37,13 @@ import javax.script.ScriptException; import java.io.Reader; import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; /** * @author Marko A. Rodriguez (http://markorodriguez.com) @@ -44,6 +52,10 @@ public class GremlinJythonScriptEngine implements GremlinScriptEngine { private final PyScriptEngine pyScriptEngine; + /** + * @deprecated As of release 3.2.4, replaced by {@link #GremlinJythonScriptEngine(Customizer...)}. + */ + @Deprecated public GremlinJythonScriptEngine() { this.pyScriptEngine = (PyScriptEngine) new PyScriptEngineFactory().getScriptEngine(); try { @@ -61,70 +73,48 @@ public GremlinJythonScriptEngine() { this.pyScriptEngine.eval(SymbolHelper.toPython(x.name()) + " = " + x.getDeclaringClass().getSimpleName() + "." + x.name()); } - // add sugar methods - this.pyScriptEngine.eval("def getitem_bypass(self, index):\n" + - " if isinstance(index,int):\n return self.range(index,index+1)\n" + - " elif isinstance(index,slice):\n return self.range(index.start,index.stop)\n" + - " else:\n return TypeError('Index must be int or slice')"); - this.pyScriptEngine.eval(GraphTraversal.class.getSimpleName() + ".__getitem__ = getitem_bypass"); - this.pyScriptEngine.eval(GraphTraversal.class.getSimpleName() + ".__getattr__ = lambda self, key: self.values(key)\n"); - this.pyScriptEngine.eval("\n" + - "from java.lang import Long\n" + - "import org.apache.tinkerpop.gremlin.util.function.Lambda\n" + // todo: remove or remove imported subclass names? (choose) - "from org.apache.tinkerpop.gremlin.util.function.Lambda import AbstractLambda\n" + - "from org.apache.tinkerpop.gremlin.util.function.Lambda import UnknownArgLambda\n" + - "from org.apache.tinkerpop.gremlin.util.function.Lambda import ZeroArgLambda\n" + - "from org.apache.tinkerpop.gremlin.util.function.Lambda import OneArgLambda\n" + - "from org.apache.tinkerpop.gremlin.util.function.Lambda import TwoArgLambda\n\n" + - - "class JythonUnknownArgLambda(UnknownArgLambda):\n" + - " def __init__(self,func,script='none',lang='gremlin-jython'):\n" + - " UnknownArgLambda.__init__(self, script, lang, -1)\n" + - " self.func = func\n" + - " def __repr__(self):\n" + - " return self.getLambdaScript()\n\n" + - - "class JythonZeroArgLambda(ZeroArgLambda):\n" + - " def __init__(self,func,script='none',lang='gremlin-jython'):\n" + - " ZeroArgLambda.__init__(self, script, lang)\n" + - " self.func = func\n" + - " def __repr__(self):\n" + - " return self.getLambdaScript()\n" + - " def get(self):\n" + - " return self.func()\n\n" + - - "class JythonOneArgLambda(OneArgLambda):\n" + - " def __init__(self,func,script='none',lang='gremlin-jython'):\n" + - " OneArgLambda.__init__(self, script, lang)\n" + - " self.func = func\n" + - " def __repr__(self):\n" + - " return self.getLambdaScript()\n" + - " def test(self,a):\n" + - " return self.func(a)\n" + - " def apply(self,a):\n" + - " return self.func(a)\n" + - " def accept(self,a):\n" + - " self.func(a)\n" + - " def compare(self,a,b):\n" + - " return self.func(a,b)\n\n" + - - "class JythonTwoArgLambda(TwoArgLambda):\n" + - " def __init__(self,func,script='none',lang='gremlin-jython'):\n" + - " TwoArgLambda.__init__(self, script, lang)\n" + - " self.func = func\n" + - " def __repr__(self):\n" + - " return self.getLambdaScript()\n" + - " def apply(self,a,b):\n" + - " return self.func(a,b)\n" + - " def compare(self,a,b):\n" + - " return self.func(a,b)\n" - ); + loadSugar(); } catch (final ScriptException e) { throw new IllegalStateException(e.getMessage(), e); } } + public GremlinJythonScriptEngine(final Customizer... customizers) { + this.pyScriptEngine = (PyScriptEngine) new PyScriptEngineFactory().getScriptEngine(); + final List listOfCustomizers = Arrays.asList(customizers); + + final List importCustomizers = listOfCustomizers.stream() + .filter(p -> p instanceof ImportCustomizer) + .map(p -> (ImportCustomizer) p) + .collect(Collectors.toList()); + + try { + for (ImportCustomizer ic : importCustomizers) { + for (Class c : ic.getClassImports()) { + if (null == c.getDeclaringClass()) + this.pyScriptEngine.eval("from " + c.getPackage().getName() + " import " + c.getSimpleName()); + else + this.pyScriptEngine.eval("from " + c.getPackage().getName() + "." + c.getDeclaringClass().getSimpleName() + " import " + c.getSimpleName()); + } + + for (Method m : ic.getMethodImports()) { + this.pyScriptEngine.eval(SymbolHelper.toPython(m.getName()) + " = " + m.getDeclaringClass().getSimpleName() + "." + m.getName()); + } + + // enums need to import after methods for some reason or else label comes in as a PyReflectedFunction + for (Enum e : ic.getEnumImports()) { + this.pyScriptEngine.eval(SymbolHelper.toPython(e.name()) + " = " + e.getDeclaringClass().getSimpleName() + "." + e.name()); + } + } + + loadSugar(); + + } catch (Exception ex) { + throw new IllegalStateException(ex); + } + } + @Override public Traversal.Admin eval(final Bytecode bytecode, final Bindings bindings) throws ScriptException { bindings.putAll(bytecode.getBindings()); @@ -209,4 +199,65 @@ public void setContext(final ScriptContext context) { public GremlinScriptEngineFactory getFactory() { return new GremlinJythonScriptEngineFactory(); } + + private void loadSugar() throws ScriptException { + // add sugar methods + this.pyScriptEngine.eval("def getitem_bypass(self, index):\n" + + " if isinstance(index,int):\n return self.range(index,index+1)\n" + + " elif isinstance(index,slice):\n return self.range(index.start,index.stop)\n" + + " else:\n return TypeError('Index must be int or slice')"); + this.pyScriptEngine.eval(GraphTraversal.class.getSimpleName() + ".__getitem__ = getitem_bypass"); + this.pyScriptEngine.eval(GraphTraversal.class.getSimpleName() + ".__getattr__ = lambda self, key: self.values(key)\n"); + this.pyScriptEngine.eval("\n" + + "from java.lang import Long\n" + + "import org.apache.tinkerpop.gremlin.util.function.Lambda\n" + // todo: remove or remove imported subclass names? (choose) + "from org.apache.tinkerpop.gremlin.util.function.Lambda import AbstractLambda\n" + + "from org.apache.tinkerpop.gremlin.util.function.Lambda import UnknownArgLambda\n" + + "from org.apache.tinkerpop.gremlin.util.function.Lambda import ZeroArgLambda\n" + + "from org.apache.tinkerpop.gremlin.util.function.Lambda import OneArgLambda\n" + + "from org.apache.tinkerpop.gremlin.util.function.Lambda import TwoArgLambda\n\n" + + + "class JythonUnknownArgLambda(UnknownArgLambda):\n" + + " def __init__(self,func,script='none',lang='gremlin-jython'):\n" + + " UnknownArgLambda.__init__(self, script, lang, -1)\n" + + " self.func = func\n" + + " def __repr__(self):\n" + + " return self.getLambdaScript()\n\n" + + + "class JythonZeroArgLambda(ZeroArgLambda):\n" + + " def __init__(self,func,script='none',lang='gremlin-jython'):\n" + + " ZeroArgLambda.__init__(self, script, lang)\n" + + " self.func = func\n" + + " def __repr__(self):\n" + + " return self.getLambdaScript()\n" + + " def get(self):\n" + + " return self.func()\n\n" + + + "class JythonOneArgLambda(OneArgLambda):\n" + + " def __init__(self,func,script='none',lang='gremlin-jython'):\n" + + " OneArgLambda.__init__(self, script, lang)\n" + + " self.func = func\n" + + " def __repr__(self):\n" + + " return self.getLambdaScript()\n" + + " def test(self,a):\n" + + " return self.func(a)\n" + + " def apply(self,a):\n" + + " return self.func(a)\n" + + " def accept(self,a):\n" + + " self.func(a)\n" + + " def compare(self,a,b):\n" + + " return self.func(a,b)\n\n" + + + "class JythonTwoArgLambda(TwoArgLambda):\n" + + " def __init__(self,func,script='none',lang='gremlin-jython'):\n" + + " TwoArgLambda.__init__(self, script, lang)\n" + + " self.func = func\n" + + " def __repr__(self):\n" + + " return self.getLambdaScript()\n" + + " def apply(self,a,b):\n" + + " return self.func(a,b)\n" + + " def compare(self,a,b):\n" + + " return self.func(a,b)\n" + ); + } } diff --git a/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngineFactory.java b/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngineFactory.java index c94e94fc805..696c2ead40f 100644 --- a/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngineFactory.java +++ b/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngineFactory.java @@ -19,6 +19,7 @@ package org.apache.tinkerpop.gremlin.python.jsr223; +import org.apache.tinkerpop.gremlin.jsr223.Customizer; import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine; import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineFactory; import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineManager; @@ -100,6 +101,8 @@ public Object getParameter(final String key) { @Override public GremlinScriptEngine getScriptEngine() { - return new GremlinJythonScriptEngine(); + final List customizers = manager.getCustomizers(GREMLIN_JYTHON); + return (customizers.isEmpty()) ? new GremlinJythonScriptEngine() : + new GremlinJythonScriptEngine(customizers.toArray(new Customizer[customizers.size()])); } } \ No newline at end of file diff --git a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngineTest.java b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngineTest.java index 8bc60a58deb..8ec1cf4c23b 100644 --- a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngineTest.java +++ b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngineTest.java @@ -36,6 +36,8 @@ import java.util.Arrays; import java.util.HashSet; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -56,11 +58,11 @@ public void shouldGetEngineByName() throws Exception { @Test public void shouldHaveCoreImports() throws Exception { final ScriptEngine engine = new DefaultGremlinScriptEngineManager().getEngineByName("gremlin-jython"); - assertTrue(engine.eval("Graph") instanceof Class); - assertTrue(engine.eval("__") instanceof Class); - assertTrue(engine.eval("T") instanceof Class); - assertTrue(engine.eval("label") instanceof T); - assertTrue(engine.eval("T.label") instanceof T); + assertThat(engine.eval("Graph"), instanceOf(Class.class)); + assertThat(engine.eval("__"), instanceOf(Class.class)); + assertThat(engine.eval("T"), instanceOf(Class.class)); + assertThat(engine.eval("label"), instanceOf(T.class)); + assertThat(engine.eval("T.label"), instanceOf(T.class)); assertEquals(SackFunctions.Barrier.class, engine.eval("Barrier")); assertEquals(SackFunctions.Barrier.normSack, engine.eval("Barrier.normSack")); assertEquals(Column.class, engine.eval("Column")); diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java index 723d8984820..f6bbcb80aa8 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java @@ -55,7 +55,6 @@ public void shouldHaveCoreImportsInPlace() throws Exception { } } - @org.junit.Ignore("TEMPORARY - until GremlinJythonScriptEngine supports this stuff") @Test public void shouldSupportDeprecatedGremlinModules() throws Exception { final GremlinScriptEngineManager mgr = new DefaultGremlinScriptEngineManager(); From 9f33bee4d75ee359c6687d4b777bb687a459925f Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Sat, 26 Nov 2016 07:37:28 -0500 Subject: [PATCH 27/45] TINKERPOP-1562 Get customizers for both python and jython. The customizers are combined to a set and therefore should only apply once. Presumably there wouldn't be a different set of customizers for one versus the other. If there were and you wanted them separate I guess you'd have to build two separate script engine instances. --- .../jsr223/GremlinJythonScriptEngineFactory.java | 6 +++++- .../jsr223/GremlinEnabledScriptEngineTest.java | 11 +++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngineFactory.java b/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngineFactory.java index 696c2ead40f..34978a7c12a 100644 --- a/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngineFactory.java +++ b/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngineFactory.java @@ -29,7 +29,9 @@ import javax.script.ScriptEngine; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * @author Marko A. Rodriguez (http://markorodriguez.com) @@ -101,7 +103,9 @@ public Object getParameter(final String key) { @Override public GremlinScriptEngine getScriptEngine() { - final List customizers = manager.getCustomizers(GREMLIN_JYTHON); + final Set customizers = new HashSet<>(manager.getCustomizers(GREMLIN_JYTHON)); + customizers.addAll(manager.getCustomizers(GREMLIN_PYTHON)); + return (customizers.isEmpty()) ? new GremlinJythonScriptEngine() : new GremlinJythonScriptEngine(customizers.toArray(new Customizer[customizers.size()])); } diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java index f6bbcb80aa8..8fa70b09f08 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java @@ -25,6 +25,7 @@ import org.junit.Test; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -79,4 +80,14 @@ public Optional getCustomizers(final String scriptEngineName) { assertEquals(clazz, scriptEngine.eval(clazz.getSimpleName())); } } + + @Test + public void shouldReturnOneCustomizers() { + // just returns the core plugin as the other assigned plugin doesn't match the tested engine + final GremlinScriptEngineManager mgr = new DefaultGremlinScriptEngineManager(); + mgr.addPlugin(ImportGremlinPlugin.build() + .classImports(java.awt.Color.class) + .appliesTo(Collections.singletonList("fake-script-engine")).create()); + assertEquals(1, mgr.getCustomizers(ENGINE_TO_TEST).size()); + } } From 80f79bcc4d296662faf0e5296607f274ac9e3ab5 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Mon, 28 Nov 2016 16:51:12 -0500 Subject: [PATCH 28/45] TINKERPOP=1562 Loaded "gremlin" imports in the scriptengines Rather than load them in the DefaultGremlinScriptEngineManager it seemed better to get them loaded in the engines themselves that way if someone opted to construct the GremlinScriptEngine directly the imports would be present. --- .../tinkerpop/gremlin/jsr223/CoreGremlinPlugin.java | 3 ++- .../jsr223/DefaultGremlinScriptEngineManager.java | 3 --- .../groovy/jsr223/GremlinGroovyScriptEngine.java | 8 ++++++-- .../python/jsr223/GremlinJythonScriptEngine.java | 11 ++++++----- .../jsr223/GremlinEnabledScriptEngineTest.java | 5 ++--- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinPlugin.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinPlugin.java index d5796913341..a3063cf7904 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinPlugin.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinPlugin.java @@ -24,7 +24,8 @@ /** * This module is required for a {@code ScriptEngine} to be Gremlin-enabled. This {@link GremlinPlugin} is not enabled - * for the {@code ServiceLoader}. It is designed to be instantiated manually. + * for the {@code ServiceLoader}. It is designed to be instantiated manually and compliant {@link GremlinScriptEngine} + * instances will automatically install it by default when created. * * @author Stephen Mallette (http://stephen.genoprime.com) */ 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 1484f90c1e6..34ef9951c4b 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 @@ -404,9 +404,6 @@ private ServiceLoader getServiceLoader(final ClassLo } private void initEngines(final ClassLoader loader) { - // always need this module for a scriptengine to be "Gremlin-enabled" - plugins.add(CoreGremlinPlugin.instance()); - Iterator itty; try { final ServiceLoader sl = AccessController.doPrivileged( 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 3ce400e3f34..264587a9808 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 @@ -38,6 +38,7 @@ import org.apache.tinkerpop.gremlin.groovy.plugin.Artifact; import org.apache.tinkerpop.gremlin.groovy.plugin.GremlinPlugin; import org.apache.tinkerpop.gremlin.groovy.plugin.GremlinPluginException; +import org.apache.tinkerpop.gremlin.jsr223.CoreGremlinPlugin; import org.apache.tinkerpop.gremlin.jsr223.Customizer; import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine; import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineFactory; @@ -184,7 +185,7 @@ protected Map initialValue() { * Creates a new instance using the {@link DefaultImportCustomizerProvider}. */ public GremlinGroovyScriptEngine() { - this((CompilerCustomizerProvider) new DefaultImportCustomizerProvider()); + this(new Customizer[0]); } /** @@ -196,7 +197,10 @@ public GremlinGroovyScriptEngine(final ImportCustomizerProvider importCustomizer } public GremlinGroovyScriptEngine(final Customizer... customizers) { - final List listOfCustomizers = Arrays.asList(customizers); + final List listOfCustomizers = new ArrayList<>(Arrays.asList(customizers)); + + // always need this plugin for a scriptengine to be "Gremlin-enabled" + CoreGremlinPlugin.instance().getCustomizers("gremlin-groovy").ifPresent(c -> listOfCustomizers.addAll(Arrays.asList(c))); GremlinLoader.load(); diff --git a/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngine.java b/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngine.java index 1b95a0288fb..1a4b57a9cde 100644 --- a/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngine.java +++ b/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngine.java @@ -19,6 +19,7 @@ package org.apache.tinkerpop.gremlin.python.jsr223; +import org.apache.tinkerpop.gremlin.jsr223.CoreGremlinPlugin; import org.apache.tinkerpop.gremlin.jsr223.Customizer; import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine; import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineFactory; @@ -27,7 +28,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.util.CoreImports; import org.python.jsr223.PyScriptEngine; import org.python.jsr223.PyScriptEngineFactory; @@ -37,12 +37,10 @@ import javax.script.ScriptException; import java.io.Reader; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.stream.Collectors; /** @@ -82,7 +80,10 @@ public GremlinJythonScriptEngine() { public GremlinJythonScriptEngine(final Customizer... customizers) { this.pyScriptEngine = (PyScriptEngine) new PyScriptEngineFactory().getScriptEngine(); - final List listOfCustomizers = Arrays.asList(customizers); + final List listOfCustomizers = new ArrayList<>(Arrays.asList(customizers)); + + // always need this plugin for a scriptengine to be "Gremlin-enabled" + CoreGremlinPlugin.instance().getCustomizers("gremlin-groovy").ifPresent(c -> listOfCustomizers.addAll(Arrays.asList(c))); final List importCustomizers = listOfCustomizers.stream() .filter(p -> p instanceof ImportCustomizer) diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java index 8fa70b09f08..5a880f8fed1 100644 --- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java +++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinEnabledScriptEngineTest.java @@ -82,12 +82,11 @@ public Optional getCustomizers(final String scriptEngineName) { } @Test - public void shouldReturnOneCustomizers() { - // just returns the core plugin as the other assigned plugin doesn't match the tested engine + public void shouldReturnNoCustomizers() { final GremlinScriptEngineManager mgr = new DefaultGremlinScriptEngineManager(); mgr.addPlugin(ImportGremlinPlugin.build() .classImports(java.awt.Color.class) .appliesTo(Collections.singletonList("fake-script-engine")).create()); - assertEquals(1, mgr.getCustomizers(ENGINE_TO_TEST).size()); + assertEquals(0, mgr.getCustomizers(ENGINE_TO_TEST).size()); } } From a2a23596322acf8201a67ac7d3baf8ef7dbb135d Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Tue, 29 Nov 2016 09:21:49 -0500 Subject: [PATCH 29/45] TINKERPOP-1562 Deprecated plugin exceptions. These are needed anymore under the revised plugin model in gremlin-core. --- .../jsr223/GremlinPluginException.java | 41 ------------------- .../groovy/plugin/GremlinPluginException.java | 2 + .../plugin/IllegalEnvironmentException.java | 2 + .../plugin/PluginInitializationException.java | 2 + 4 files changed, 6 insertions(+), 41 deletions(-) delete mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinPluginException.java diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinPluginException.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinPluginException.java deleted file mode 100644 index ce4ab2ffecc..00000000000 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/GremlinPluginException.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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; - -/** - * Base exception for {@link GremlinPlugin}. - * - * @author Stephen Mallette (http://stephen.genoprime.com) - */ -public abstract class GremlinPluginException extends Exception { - public GremlinPluginException() { - } - - public GremlinPluginException(final String message) { - super(message); - } - - public GremlinPluginException(final String message, final Throwable cause) { - super(message, cause); - } - - public GremlinPluginException(final Throwable cause) { - super(cause); - } -} diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/GremlinPluginException.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/GremlinPluginException.java index d85c7439a4c..c8664c759db 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/GremlinPluginException.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/GremlinPluginException.java @@ -22,7 +22,9 @@ * Base exception for {@link GremlinPlugin}. * * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, not replaced. */ +@Deprecated public abstract class GremlinPluginException extends Exception { public GremlinPluginException() { } diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/IllegalEnvironmentException.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/IllegalEnvironmentException.java index 86dbcbc0bf4..8d6fbe222e3 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/IllegalEnvironmentException.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/IllegalEnvironmentException.java @@ -23,7 +23,9 @@ * the needs of the {@link GremlinPlugin}. * * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, not replaced. */ +@Deprecated public class IllegalEnvironmentException extends GremlinPluginException { public IllegalEnvironmentException(final GremlinPlugin plugin, final String... expectedKeys) { super(String.format("The %s plugin may not be compatible with this environment - requires the follow ScriptEngine environment keys [%s]", plugin.getName(), String.join(",", expectedKeys))); diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/PluginInitializationException.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/PluginInitializationException.java index 7d872158736..0dd6fc1fd4d 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/PluginInitializationException.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/PluginInitializationException.java @@ -23,7 +23,9 @@ * {@code ScriptEngine}. * * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, not replaced. */ +@Deprecated public class PluginInitializationException extends GremlinPluginException { public PluginInitializationException(final String message) { super(message); From 39e39652a0d3e6a4f9ef480d16b08372774f2b30 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Tue, 29 Nov 2016 09:39:10 -0500 Subject: [PATCH 30/45] TINKERPOP-1562 Deprecated Artifact Moved it to a new home as the hole plugin package is going to go away in 3.3.0 --- .../groovy/util/DependencyGrabber.groovy | 15 ++-- .../gremlin/groovy/plugin/Artifact.java | 2 + .../gremlin/groovy/util/Artifact.java | 86 +++++++++++++++++++ .../gremlin/groovy/plugin/ArtifactTest.java | 2 - .../gremlin/groovy/util/ArtifactTest.java | 85 ++++++++++++++++++ 5 files changed, 182 insertions(+), 8 deletions(-) create mode 100644 gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/util/Artifact.java create mode 100644 gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/util/ArtifactTest.java diff --git a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/util/DependencyGrabber.groovy b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/util/DependencyGrabber.groovy index e7ab55b4ed7..f2bfe5c8bbe 100644 --- a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/util/DependencyGrabber.groovy +++ b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/util/DependencyGrabber.groovy @@ -20,11 +20,14 @@ package org.apache.tinkerpop.gremlin.groovy.util import groovy.grape.Grape import org.apache.commons.lang3.SystemUtils -import org.apache.tinkerpop.gremlin.groovy.plugin.Artifact import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.nio.file.* +import java.nio.file.DirectoryStream +import java.nio.file.FileSystems +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.StandardCopyOption import java.util.jar.JarFile import java.util.jar.Manifest @@ -118,9 +121,9 @@ class DependencyGrabber { .findAll {!filesAlreadyInPath.collect { it.getFileName().toString() }.contains(it.fileName.toFile().name)} .each(copyTo(targetPluginPath)) getAdditionalDependencies(targetPluginPath, artifact).collect(convertUriToPath(fs)) - .findAll { !(it.fileName.toFile().name ==~ /(slf4j|logback\-classic)-.*\.jar/) } - .findAll { !filesAlreadyInPath.collect { it.getFileName().toString() }.contains(it.fileName.toFile().name)} - .each(copyTo(targetPluginPath)) + .findAll { !(it.fileName.toFile().name ==~ /(slf4j|logback\-classic)-.*\.jar/) } + .findAll { !filesAlreadyInPath.collect { it.getFileName().toString() }.contains(it.fileName.toFile().name)} + .each(copyTo(targetPluginPath)) // get dependencies for the lib path. the lib path should not filter out any jars - used for reference dependencyLocations.collect(convertUriToPath(fs)).each(copyTo(targetLibPath)) @@ -162,7 +165,7 @@ class DependencyGrabber { * Windows places a starting forward slash in the URI that needs to be stripped off or else the * {@code FileSystem} won't properly resolve it. */ - private static Closure convertUriToPath(final FileSystem fs) { + private static Closure convertUriToPath(def fs) { return { URI uri -> def p = SystemUtils.IS_OS_WINDOWS ? uri.path.substring(1) : uri.path return fs.getPath(p) diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/Artifact.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/Artifact.java index 29ce7d68067..7fb0bd319b0 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/Artifact.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/Artifact.java @@ -22,7 +22,9 @@ * A software artifact identified by its maven coordinates. * * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.groovy.util.Artifact} */ +@Deprecated public class Artifact { private final String group; private final String artifact; diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/util/Artifact.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/util/Artifact.java new file mode 100644 index 00000000000..2e2554b3dec --- /dev/null +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/util/Artifact.java @@ -0,0 +1,86 @@ +/* + * 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.util; + +/** + * A software artifact identified by its maven coordinates. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class Artifact { + private final String group; + private final String artifact; + private final String version; + + /** + * Create a new instance. + * + * @param group the {@code groupId} + * @param artifact the {@code artifactId} + * @param version the {@code version} + */ + public Artifact(final String group, final String artifact, final String version) { + if (group == null || group.isEmpty()) + throw new IllegalArgumentException("group cannot be null or empty"); + + if (artifact == null || artifact.isEmpty()) + throw new IllegalArgumentException("artifact cannot be null or empty"); + + if (version == null || version.isEmpty()) + throw new IllegalArgumentException("version cannot be null or empty"); + + this.group = group; + this.artifact = artifact; + this.version = version; + } + + public String getGroup() { + return group; + } + + public String getArtifact() { + return artifact; + } + + public String getVersion() { + return version; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final Artifact a = (Artifact) o; + + if (group != null ? !group.equals(a.group) : a.group != null) return false; + if (artifact != null ? !artifact.equals(a.artifact) : a.artifact != null) return false; + if (version != null ? !version.equals(a.version) : a.version != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = group != null ? group.hashCode() : 0; + result = 31 * result + (artifact != null ? artifact.hashCode() : 0); + result = 31 * result + (version != null ? version.hashCode() : 0); + return result; + } +} \ No newline at end of file diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/plugin/ArtifactTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/plugin/ArtifactTest.java index a876df8e368..f3c96f9e050 100644 --- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/plugin/ArtifactTest.java +++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/plugin/ArtifactTest.java @@ -18,13 +18,11 @@ */ package org.apache.tinkerpop.gremlin.groovy.plugin; - import org.junit.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; - /** * @author Nghia Tran (https://github.com/n-tran) */ diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/util/ArtifactTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/util/ArtifactTest.java new file mode 100644 index 00000000000..0e0c2835f2d --- /dev/null +++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/util/ArtifactTest.java @@ -0,0 +1,85 @@ +/* + * 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.util; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +/** + * @author Nghia Tran (https://github.com/n-tran) + */ +public class ArtifactTest { + @Test + public void shouldBeEqualIfSame() { + final Artifact a = new Artifact("org.apache.tinkerpop","tinkergraph-gremlin","3.0.0"); + final Artifact b = a; + + assertThat(a.equals(b), is(true)); + } + + @Test + public void shouldNotBeEqualIfArgumentIsNull() { + final Artifact a = new Artifact("org.apache.tinkerpop","tinkergraph-gremlin","3.0.0"); + final Artifact b = null; + + assertThat(a.equals(b), is(false)); + } + + @Test + public void shouldNotBeEqualIfArgumentIsNotAnArtifact() { + final Artifact a = new Artifact("org.apache.tinkerpop","tinkergraph-gremlin","3.0.0"); + final String b = " "; + + assertThat(a.equals(b), is(false)); + } + + @Test + public void shouldNotBeEqualIfTheGroupIsNotEqual() { + final Artifact a = new Artifact("org.apache.tinkerpop","tinkergraph-gremlin","3.0.0"); + final Artifact b = new Artifact("com.apacheTest.tinkerpop2","tinkergraph-gremlin","3.0.0"); + + assertThat(a.equals(b), is(false)); + } + + @Test + public void shouldNotBeEqualIfTheArtifactIsNotEqual() { + final Artifact a = new Artifact("org.apache.tinkerpop","tinkergraph-gremlin","3.0.0"); + final Artifact b = new Artifact("org.apache.tinkerpop","tinkergraph-artifact","3.0.0"); + + assertThat(a.equals(b), is(false)); + } + + @Test + public void shouldNotBeEqualIfTheVersionIsNotEqual() { + final Artifact a = new Artifact("org.apache.tinkerpop","tinkergraph-gremlin","3.0.0"); + final Artifact b = new Artifact("org.apache.tinkerpop","tinkergraph-gremlin","4.0.0"); + + assertThat(a.equals(b), is(false)); + } + + @Test + public void shouldBeEqual() { + final Artifact a = new Artifact("org.apache.tinkerpop","tinkergraph-gremlin","3.0.0"); + final Artifact b = new Artifact("org.apache.tinkerpop","tinkergraph-gremlin","3.0.0"); + + assertThat(a.equals(b), is(true)); + } +} From 6439d709c60a107b3e6a3b79231344f8a2ce2e7a Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Tue, 29 Nov 2016 10:13:04 -0500 Subject: [PATCH 31/45] TINKERPOP-1562 Deprecated the CredentialGraph and moved it to a new home so tht the "plugin" package can go away completely. --- .../dsl/credential/CredentialGraphTest.java | 121 ++++++++++++++++++ .../dsl/credential/CredentialGraphTest.java | 2 +- .../groovy/util/DependencyGrabber.groovy | 14 ++ .../dsl/credential/CredentialGraph.java | 121 ++++++++++++++++++ .../CredentialGraphGremlinPlugin.java | 6 +- .../dsl/credential/CredentialGraphTokens.java | 31 +++++ .../dsl/credential/CredentialGraph.java | 2 + .../dsl/credential/CredentialGraphTokens.java | 2 + ...che.tinkerpop.gremlin.jsr223.GremlinPlugin | 2 +- 9 files changed, 294 insertions(+), 7 deletions(-) create mode 100644 gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/dsl/credential/CredentialGraphTest.java create mode 100644 gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/dsl/credential/CredentialGraph.java rename gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/{plugin/dsl/credential/jsr223 => jsr223/dsl/credential}/CredentialGraphGremlinPlugin.java (90%) create mode 100644 gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/dsl/credential/CredentialGraphTokens.java diff --git a/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/dsl/credential/CredentialGraphTest.java b/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/dsl/credential/CredentialGraphTest.java new file mode 100644 index 00000000000..e3a713d63b6 --- /dev/null +++ b/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/dsl/credential/CredentialGraphTest.java @@ -0,0 +1,121 @@ +/* + * 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.dsl.credential; + +import org.apache.tinkerpop.gremlin.AbstractGremlinTest; +import org.apache.tinkerpop.gremlin.FeatureRequirement; +import org.apache.tinkerpop.gremlin.FeatureRequirementSet; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.hamcrest.MatcherAssert; +import org.junit.Test; + +import static org.apache.tinkerpop.gremlin.groovy.jsr223.dsl.credential.CredentialGraph.credentials; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class CredentialGraphTest extends AbstractGremlinTest { + + @Test + @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY) + public void shouldCreateUser() { + final Vertex v = credentials(graph).createUser("stephen", "secret"); + assertEquals("stephen", v.value("username")); + assertEquals("user", v.label()); + assertNotEquals("secret", v.value("password")); // hashed to something + assertThat(v.value("password").toString().length(), greaterThan(0)); + } + + @Test + @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY) + @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_REMOVE_VERTICES) + public void shouldRemoveUser() { + MatcherAssert.assertThat(graph.vertices().hasNext(), is(false)); + credentials(graph).createUser("stephen", "secret"); + MatcherAssert.assertThat(graph.vertices().hasNext(), is(true)); + + assertEquals(1, credentials(graph).removeUser("stephen")); + MatcherAssert.assertThat(graph.vertices().hasNext(), is(false)); + } + + @Test + @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY) + public void shouldNotRemoveUser() { + MatcherAssert.assertThat(graph.vertices().hasNext(), is(false)); + credentials(graph).createUser("stephen", "secret"); + MatcherAssert.assertThat(graph.vertices().hasNext(), is(true)); + + assertEquals(0, credentials(graph).removeUser("stephanie")); + MatcherAssert.assertThat(graph.vertices().hasNext(), is(true)); + } + + @Test + @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY) + public void shouldFindUser() { + MatcherAssert.assertThat(graph.vertices().hasNext(), is(false)); + credentials(graph).createUser("marko", "secret"); + final Vertex stephen = credentials(graph).createUser("stephen", "secret"); + credentials(graph).createUser("daniel", "secret"); + MatcherAssert.assertThat(graph.vertices().hasNext(), is(true)); + + assertEquals(stephen, credentials(graph).findUser("stephen")); + } + + @Test + @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY) + public void shouldNotFindUser() { + MatcherAssert.assertThat(graph.vertices().hasNext(), is(false)); + credentials(graph).createUser("marko", "secret"); + credentials(graph).createUser("stephen", "secret"); + credentials(graph).createUser("daniel", "secret"); + MatcherAssert.assertThat(graph.vertices().hasNext(), is(true)); + + assertNull(credentials(graph).findUser("stephanie")); + } + + @Test + @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY) + public void shouldCountUsers() { + MatcherAssert.assertThat(graph.vertices().hasNext(), is(false)); + credentials(graph).createUser("marko", "secret"); + credentials(graph).createUser("stephen", "secret"); + credentials(graph).createUser("daniel", "secret"); + MatcherAssert.assertThat(graph.vertices().hasNext(), is(true)); + + assertEquals(3, credentials(graph).countUsers()); + } + + @Test(expected = IllegalStateException.class) + @FeatureRequirementSet(FeatureRequirementSet.Package.VERTICES_ONLY) + public void shouldThrowIfFindingMultipleUsers() { + MatcherAssert.assertThat(graph.vertices().hasNext(), is(false)); + credentials(graph).createUser("stephen", "secret"); + credentials(graph).createUser("stephen", "secret"); + MatcherAssert.assertThat(graph.vertices().hasNext(), is(true)); + + assertNull(credentials(graph).findUser("stephen")); + } +} diff --git a/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/CredentialGraphTest.java b/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/CredentialGraphTest.java index d7d7f9dcde5..7cdf329e62b 100644 --- a/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/CredentialGraphTest.java +++ b/gremlin-groovy-test/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/CredentialGraphTest.java @@ -26,7 +26,7 @@ import org.hamcrest.MatcherAssert; import org.junit.Test; -import static org.apache.tinkerpop.gremlin.groovy.plugin.dsl.credential.CredentialGraph.*; +import static org.apache.tinkerpop.gremlin.groovy.plugin.dsl.credential.CredentialGraph.credentials; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.greaterThan; diff --git a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/util/DependencyGrabber.groovy b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/util/DependencyGrabber.groovy index f2bfe5c8bbe..4f895c86bd7 100644 --- a/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/util/DependencyGrabber.groovy +++ b/gremlin-groovy/src/main/groovy/org/apache/tinkerpop/gremlin/groovy/util/DependencyGrabber.groovy @@ -50,6 +50,13 @@ class DependencyGrabber { this.extensionDirectory = extensionDirectory } + /** + * @deprecated As of release 3.2.4, replaced by {@link #deleteDependenciesFromPath(Artifact)} + */ + def String deleteDependenciesFromPath(final org.apache.tinkerpop.gremlin.groovy.plugin.Artifact artifact) { + deleteDependenciesFromPath(new Artifact(artifact.group, artifact.artifact, artifact.version)) + } + def String deleteDependenciesFromPath(final Artifact artifact) { final def dep = makeDepsMap(artifact) final String extClassPath = getPathFromDependency(dep) @@ -63,6 +70,13 @@ class DependencyGrabber { } } + /** + * @deprecated As of release 3.2.4, replaced by {@link #copyDependenciesToPath(Artifact)} + */ + def String copyDependenciesToPath(final org.apache.tinkerpop.gremlin.groovy.plugin.Artifact artifact) { + copyDependenciesToPath(new Artifact(artifact.group, artifact.artifact, artifact.version)) + } + def Set copyDependenciesToPath(final Artifact artifact) { final def dep = makeDepsMap(artifact) final String extClassPath = getPathFromDependency(dep) diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/dsl/credential/CredentialGraph.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/dsl/credential/CredentialGraph.java new file mode 100644 index 00000000000..707e8167c4a --- /dev/null +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/dsl/credential/CredentialGraph.java @@ -0,0 +1,121 @@ +/* + * 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.dsl.credential; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.mindrot.BCrypt; + +import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.drop; + +/** + * A DSL for managing a "credentials graph" used by Gremlin Server for simple authentication functions. If the + * {@link Graph} is transactional, new transactions will be started for each method call. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class CredentialGraph { + + private final int BCRYPT_ROUNDS = 4; + private final Graph graph; + private final GraphTraversalSource g; + private final boolean supportsTransactions; + + public CredentialGraph(final Graph graph) { + this.graph = graph; + g = graph.traversal(); + supportsTransactions = graph.features().graph().supportsTransactions(); + } + + /** + * Finds a user by username and return {@code null} if one could not be found. + * + * @throws IllegalStateException if there is more than one user with a particular username. + */ + public Vertex findUser(final String username) { + if (supportsTransactions) g.tx().rollback(); + final GraphTraversal t = g.V().has(CredentialGraphTokens.PROPERTY_USERNAME, username); + final Vertex v = t.hasNext() ? t.next() : null; + if (t.hasNext()) throw new IllegalStateException(String.format("Multiple users with username %s", username)); + return v; + } + + /** + * Creates a new user. + * + * @return the newly created user vertex + */ + public Vertex createUser(final String username, final String password) { + if (findUser(username) != null) throw new IllegalStateException("User with this name already exists"); + if (supportsTransactions) graph.tx().rollback(); + + try { + final Vertex v = graph.addVertex(T.label, CredentialGraphTokens.VERTEX_LABEL_USER, + CredentialGraphTokens.PROPERTY_USERNAME, username, + CredentialGraphTokens.PROPERTY_PASSWORD, BCrypt.hashpw(password, BCrypt.gensalt(BCRYPT_ROUNDS))); + if (supportsTransactions) graph.tx().commit(); + return v; + } catch (Exception ex) { + if (supportsTransactions) graph.tx().rollback(); + throw new RuntimeException(ex); + } + } + + /** + * Removes a user by name. + * + * @return the number of users removed (which should be one or zero) + */ + public long removeUser(final String username) { + if (supportsTransactions) graph.tx().rollback(); + try { + final long count = g.V().has(CredentialGraphTokens.PROPERTY_USERNAME, username).sideEffect(drop()).count().next(); + if (supportsTransactions) graph.tx().commit(); + return count; + } catch (Exception ex) { + if (supportsTransactions) graph.tx().rollback(); + throw new RuntimeException(ex); + } + } + + /** + * Get a count of the number of users in the database. + */ + public long countUsers() { + if (supportsTransactions) graph.tx().rollback(); + return g.V().hasLabel(CredentialGraphTokens.VERTEX_LABEL_USER).count().next(); + } + + @Override + public String toString() { + return "CredentialGraph{" + + "graph=" + graph + + '}'; + } + + /** + * Wrap up any {@link Graph} instance in the {@code CredentialGraph} DSL. + */ + public static CredentialGraph credentials(final Graph graph) { + return new CredentialGraph(graph); + } +} diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/jsr223/CredentialGraphGremlinPlugin.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/dsl/credential/CredentialGraphGremlinPlugin.java similarity index 90% rename from gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/jsr223/CredentialGraphGremlinPlugin.java rename to gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/dsl/credential/CredentialGraphGremlinPlugin.java index 761567bdec3..7b6bd640b58 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/jsr223/CredentialGraphGremlinPlugin.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/dsl/credential/CredentialGraphGremlinPlugin.java @@ -16,17 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.tinkerpop.gremlin.groovy.plugin.dsl.credential.jsr223; +package org.apache.tinkerpop.gremlin.groovy.jsr223.dsl.credential; -import org.apache.tinkerpop.gremlin.groovy.plugin.dsl.credential.CredentialGraph; import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; import org.apache.tinkerpop.gremlin.structure.Graph; -import java.util.HashSet; -import java.util.Set; - /** * Plugin for the "credentials graph". This plugin imports the {@link CredentialGraph} to its environment. * diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/dsl/credential/CredentialGraphTokens.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/dsl/credential/CredentialGraphTokens.java new file mode 100644 index 00000000000..ac163023976 --- /dev/null +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/dsl/credential/CredentialGraphTokens.java @@ -0,0 +1,31 @@ +/* + * 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.dsl.credential; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public final class CredentialGraphTokens { + public static final String PROPERTY_USERNAME = "username"; + public static final String PROPERTY_PASSWORD = "password"; + + public static final String VERTEX_LABEL_USER = "user"; + + private CredentialGraphTokens() {} +} diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/CredentialGraph.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/CredentialGraph.java index 8c0277c7ac8..6a9058700ed 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/CredentialGraph.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/CredentialGraph.java @@ -32,7 +32,9 @@ * {@link Graph} is transactional, new transactions will be started for each method call. * * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.groovy.jsr223.dsl.credential.CredentialGraph}. */ +@Deprecated public class CredentialGraph { private final int BCRYPT_ROUNDS = 4; diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/CredentialGraphTokens.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/CredentialGraphTokens.java index 0cb254317b4..1f0d8cf44ef 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/CredentialGraphTokens.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/plugin/dsl/credential/CredentialGraphTokens.java @@ -20,7 +20,9 @@ /** * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.groovy.jsr223.dsl.credential.CredentialGraphTokens}. */ +@Deprecated public final class CredentialGraphTokens { public static final String PROPERTY_USERNAME = "username"; public static final String PROPERTY_PASSWORD = "password"; diff --git a/gremlin-groovy/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin b/gremlin-groovy/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin index 0004a807980..251250be9af 100644 --- a/gremlin-groovy/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin +++ b/gremlin-groovy/src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin @@ -1,2 +1,2 @@ org.apache.tinkerpop.gremlin.groovy.jsr223.SugarGremlinPlugin -org.apache.tinkerpop.gremlin.groovy.plugin.dsl.credential.jsr223.CredentialGraphGremlinPlugin \ No newline at end of file +org.apache.tinkerpop.gremlin.groovy.jsr223.dsl.credential.CredentialGraphGremlinPlugin \ No newline at end of file From 8dc9b1fbb3e0a6504b3d5d217dbb144031b30aab Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Tue, 29 Nov 2016 16:15:12 -0500 Subject: [PATCH 32/45] TINKERPOP-1562 Adapted CompilerCustomizerProvider to Customizer This hooks up all the groovy-specific configurations to the GremlinGroovyScriptEngine. --- .../jsr223/CustomizerProviderCustomizer.java | 40 +++++ .../jsr223/GremlinGroovyScriptEngine.java | 20 ++- .../jsr223/GroovyCompilerGremlinPlugin.java | 138 ++++++++++++++++++ .../groovy/jsr223/SugarGremlinPlugin.java | 8 +- .../CompileStaticCustomizerProvider.java | 1 + .../ConfigurationCustomizerProvider.java | 1 + .../InterpreterModeCustomizerProvider.java | 1 + .../ThreadInterruptCustomizerProvider.java | 1 + .../TimedInterruptCustomizerProvider.java | 1 + .../TypeCheckedCustomizerProvider.java | 1 + ...inGroovyScriptEngineCompileStaticTest.java | 72 ++++++++- .../GremlinGroovyScriptEngineConfigTest.java | 14 +- .../jsr223/GremlinGroovyScriptEngineTest.java | 50 ++++++- ...GroovyScriptEngineThreadInterruptTest.java | 23 ++- ...nGroovyScriptEngineTimedInterruptTest.java | 69 ++++++++- ...mlinGroovyScriptEngineTypeCheckedTest.java | 72 ++++++++- .../GroovyCompilerGremlinPluginTest.java | 128 ++++++++++++++++ 17 files changed, 617 insertions(+), 23 deletions(-) create mode 100644 gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/CustomizerProviderCustomizer.java create mode 100644 gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCompilerGremlinPlugin.java create mode 100644 gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCompilerGremlinPluginTest.java diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/CustomizerProviderCustomizer.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/CustomizerProviderCustomizer.java new file mode 100644 index 00000000000..73614a1fffe --- /dev/null +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/CustomizerProviderCustomizer.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.CompilerCustomizerProvider; +import org.apache.tinkerpop.gremlin.jsr223.Customizer; + +/** + * An adapter that allows existing {@link CompilerCustomizerProvider} instances to behave as a {#link Customizer}. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +class CustomizerProviderCustomizer implements Customizer { + + private final CompilerCustomizerProvider customizerProvider; + + CustomizerProviderCustomizer(final CompilerCustomizerProvider customizerProvider) { + this.customizerProvider = customizerProvider; + } + + CompilerCustomizerProvider getCustomizerProvider() { + return customizerProvider; + } +} 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 264587a9808..29967928f93 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 @@ -70,7 +70,6 @@ import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -233,16 +232,31 @@ public GremlinGroovyScriptEngine(final Customizer... customizers) { importCustomizerProvider = new EmptyImportCustomizerProvider(imports, staticImports); } - interpreterModeEnabled = false; + // this is a bit temporary - until CompilerCustomizerProvider is gone + final List customizerProviderCustomizer = listOfCustomizers.stream() + .filter(p -> p instanceof CustomizerProviderCustomizer) + .map(p -> ((CustomizerProviderCustomizer) p).getCustomizerProvider()) + .collect(Collectors.toList()); - customizerProviders = Collections.emptyList(); + // determine if interpreter mode should be enabled + interpreterModeEnabled = customizerProviderCustomizer.stream() + .anyMatch(p -> p.getClass().equals(InterpreterModeCustomizerProvider.class)); + + // remove used providers as the rest will be applied directly + customizerProviders = customizerProviderCustomizer.stream() + .filter(p -> p != null && + !((p instanceof ImportCustomizerProvider))) + .collect(Collectors.toList()); createClassLoader(); } /** * Creates a new instance with the specified {@link CompilerCustomizerProvider} objects. + * + * @deprecated As of release 3.2.4, replaced by {@link #GremlinGroovyScriptEngine(Customizer...)}. */ + @Deprecated public GremlinGroovyScriptEngine(final CompilerCustomizerProvider... compilerCustomizerProviders) { final List providers = Arrays.asList(compilerCustomizerProviders); diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCompilerGremlinPlugin.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCompilerGremlinPlugin.java new file mode 100644 index 00000000000..0d2e9c61734 --- /dev/null +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCompilerGremlinPlugin.java @@ -0,0 +1,138 @@ +/* + * 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.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.ThreadInterruptCustomizerProvider; +import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.TimedInterruptCustomizerProvider; +import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.TypeCheckedCustomizerProvider; +import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; +import org.apache.tinkerpop.gremlin.jsr223.Customizer; +import org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +/** + * A {@link GremlinPlugin} that provides access to low-level configuration options of the {@code GroovyScriptEngine} + * itself. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class GroovyCompilerGremlinPlugin extends AbstractGremlinPlugin { + + public enum Compilation { + COMPILE_STATIC, + TYPE_CHECKED, + NONE + } + + private static final String NAME = "tinkerpop.groovy"; + + private GroovyCompilerGremlinPlugin(final Builder builder) { + super(NAME, new HashSet<>(Collections.singletonList("gremlin-groovy")), builder.asCustomizers()); + } + + public static Builder build() { + return new Builder(); + } + + public static final class Builder { + + private boolean interpreterMode; + private boolean threadInterrupt; + private long timeInMillis = 0; + private Compilation compilation = Compilation.NONE; + private String extensions = null; + + private Map keyValues = Collections.emptyMap(); + + public Builder enableInterpreterMode(final boolean interpreterMode) { + this.interpreterMode = interpreterMode; + return this; + } + + public Builder compilerConfigurationOptions(final Map keyValues) { + this.keyValues = keyValues; + return this; + } + + public Builder enableThreadInterrupt(final boolean threadInterrupt) { + this.threadInterrupt = threadInterrupt; + return this; + } + + public Builder timedInterrupt(final long timeInMillis) { + this.timeInMillis = timeInMillis; + return this; + } + + public Builder compilation(final Compilation compilation) { + this.compilation = compilation; + return this; + } + + public Builder compilation(final String compilation) { + return compilation(Compilation.valueOf(compilation)); + } + + public Builder extensions(final String extensions) { + this.extensions = extensions; + return this; + } + + Customizer[] asCustomizers() { + final List list = new ArrayList<>(); + + if (interpreterMode) + list.add(new CustomizerProviderCustomizer(new InterpreterModeCustomizerProvider())); + + if (!keyValues.isEmpty()) + list.add(new CustomizerProviderCustomizer(new ConfigurationCustomizerProvider(keyValues))); + + if (threadInterrupt) + list.add(new CustomizerProviderCustomizer(new ThreadInterruptCustomizerProvider())); + + if (timeInMillis > 0) + list.add(new CustomizerProviderCustomizer(new TimedInterruptCustomizerProvider(timeInMillis))); + + if (compilation == Compilation.COMPILE_STATIC) + list.add(new CustomizerProviderCustomizer(new CompileStaticCustomizerProvider(extensions))); + else if (compilation == Compilation.TYPE_CHECKED) + list.add(new CustomizerProviderCustomizer(new TypeCheckedCustomizerProvider(extensions))); + else if (compilation != Compilation.NONE) + throw new IllegalStateException("Use of unknown compilation type: " + compilation); + + if (list.isEmpty()) throw new IllegalStateException("No customizer options have been selected for this plugin"); + + final Customizer[] customizers = new Customizer[list.size()]; + list.toArray(customizers); + return customizers; + } + + public GroovyCompilerGremlinPlugin create() { + return new GroovyCompilerGremlinPlugin(this); + } + } +} diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/SugarGremlinPlugin.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/SugarGremlinPlugin.java index 95c610f128d..82fbc8e8dc6 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/SugarGremlinPlugin.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/SugarGremlinPlugin.java @@ -23,6 +23,7 @@ import org.apache.tinkerpop.gremlin.jsr223.DefaultScriptCustomizer; import java.util.Collections; +import java.util.HashSet; /** * A plugin implementation which allows for the usage of Gremlin Groovy's syntactic sugar. @@ -34,12 +35,7 @@ public class SugarGremlinPlugin extends AbstractGremlinPlugin { private static final String NAME = "tinkerpop.sugar"; public SugarGremlinPlugin() { - super(NAME, new DefaultScriptCustomizer(Collections.singletonList( + super(NAME, new HashSet<>(Collections.singletonList("gremlin-groovy")), new DefaultScriptCustomizer(Collections.singletonList( Collections.singletonList(SugarLoader.class.getPackage().getName() + "." + SugarLoader.class.getSimpleName() + ".load()")))); } - - @Override - public String getName() { - return "tinkerpop.sugar"; - } } diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/CompileStaticCustomizerProvider.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/CompileStaticCustomizerProvider.java index 65e12245243..ab0bff55c10 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/CompileStaticCustomizerProvider.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/CompileStaticCustomizerProvider.java @@ -20,6 +20,7 @@ import groovy.transform.CompileStatic; import org.apache.tinkerpop.gremlin.groovy.CompilerCustomizerProvider; +import org.apache.tinkerpop.gremlin.jsr223.Customizer; import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer; import org.codehaus.groovy.control.customizers.CompilationCustomizer; 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 index ba9855cb85f..78357cbc84b 100644 --- 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 @@ -19,6 +19,7 @@ package org.apache.tinkerpop.gremlin.groovy.jsr223.customizer; import org.apache.tinkerpop.gremlin.groovy.CompilerCustomizerProvider; +import org.apache.tinkerpop.gremlin.jsr223.Customizer; import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.customizers.CompilationCustomizer; diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/InterpreterModeCustomizerProvider.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/InterpreterModeCustomizerProvider.java index e3f95f4a4ae..013ba8b9d9c 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/InterpreterModeCustomizerProvider.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/InterpreterModeCustomizerProvider.java @@ -20,6 +20,7 @@ import org.apache.tinkerpop.gremlin.groovy.CompilerCustomizerProvider; import org.apache.tinkerpop.gremlin.groovy.jsr223.ast.InterpreterMode; +import org.apache.tinkerpop.gremlin.jsr223.Customizer; import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer; import org.codehaus.groovy.control.customizers.CompilationCustomizer; diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/ThreadInterruptCustomizerProvider.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/ThreadInterruptCustomizerProvider.java index 8c942c60691..e46e9b72ceb 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/ThreadInterruptCustomizerProvider.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/ThreadInterruptCustomizerProvider.java @@ -20,6 +20,7 @@ import groovy.transform.ThreadInterrupt; import org.apache.tinkerpop.gremlin.groovy.CompilerCustomizerProvider; +import org.apache.tinkerpop.gremlin.jsr223.Customizer; import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer; import org.codehaus.groovy.control.customizers.CompilationCustomizer; diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TimedInterruptCustomizerProvider.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TimedInterruptCustomizerProvider.java index e4bdc625148..99130880854 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TimedInterruptCustomizerProvider.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TimedInterruptCustomizerProvider.java @@ -20,6 +20,7 @@ import groovy.transform.TimedInterrupt; import org.apache.tinkerpop.gremlin.groovy.CompilerCustomizerProvider; +import org.apache.tinkerpop.gremlin.jsr223.Customizer; import org.codehaus.groovy.ast.tools.GeneralUtils; import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer; import org.codehaus.groovy.control.customizers.CompilationCustomizer; diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TypeCheckedCustomizerProvider.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TypeCheckedCustomizerProvider.java index f0bc791f5dd..cf9147b766b 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TypeCheckedCustomizerProvider.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TypeCheckedCustomizerProvider.java @@ -20,6 +20,7 @@ import groovy.transform.TypeChecked; import org.apache.tinkerpop.gremlin.groovy.CompilerCustomizerProvider; +import org.apache.tinkerpop.gremlin.jsr223.Customizer; import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer; import org.codehaus.groovy.control.customizers.CompilationCustomizer; diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineCompileStaticTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineCompileStaticTest.java index 3ba3e0bb44e..b62f3f32456 100644 --- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineCompileStaticTest.java +++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineCompileStaticTest.java @@ -35,7 +35,7 @@ */ public class GremlinGroovyScriptEngineCompileStaticTest { @Test - public void shouldCompileStatic() throws Exception { + public void shouldCompileStaticDeprecated() throws Exception { // with no type checking this should pass try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine()) { assertEquals(255, scriptEngine.eval("((Object) new java.awt.Color(255, 255, 255)).getRed()")); @@ -53,7 +53,25 @@ public void shouldCompileStatic() throws Exception { } @Test - public void shouldCompileStaticWithExtension() throws Exception { + public void shouldCompileStatic() throws Exception { + // with no type checking this should pass + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine()) { + assertEquals(255, scriptEngine.eval("((Object) new java.awt.Color(255, 255, 255)).getRed()")); + } + + final CompileStaticCustomizerProvider provider = new CompileStaticCustomizerProvider(); + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(provider))) { + scriptEngine.eval("((Object) new java.awt.Color(255, 255, 255)).getRed()"); + fail("Should have failed type checking"); + } catch (ScriptException se) { + final Throwable root = ExceptionUtils.getRootCause(se); + assertEquals(MultipleCompilationErrorsException.class, root.getClass()); + assertThat(se.getMessage(), containsString("[Static type checking] - Cannot find matching method java.lang.Object#getRed(). Please check if the declared type is right and if the method exists.")); + } + } + + @Test + public void shouldCompileStaticWithExtensionDeprecated() throws Exception { // with no type checking extension this should pass final CompileStaticCustomizerProvider providerNoExtension = new CompileStaticCustomizerProvider(); try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(providerNoExtension)) { @@ -72,7 +90,26 @@ public void shouldCompileStaticWithExtension() throws Exception { } @Test - public void shouldCompileStaticWithMultipleExtension() throws Exception { + public void shouldCompileStaticWithExtension() throws Exception { + // with no type checking extension this should pass + final CompileStaticCustomizerProvider providerNoExtension = new CompileStaticCustomizerProvider(); + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(providerNoExtension))) { + assertEquals(255, scriptEngine.eval("def c = new java.awt.Color(255, 255, 255); c.red")); + } + + final CompileStaticCustomizerProvider providerWithExtension = new CompileStaticCustomizerProvider( + PrecompiledExtensions.PreventColorUsageExtension.class.getName()); + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(providerWithExtension))) { + scriptEngine.eval("def c = new java.awt.Color(255, 255, 255); c.red"); + fail("Should have failed type checking"); + } catch (ScriptException se) { + assertEquals(MultipleCompilationErrorsException.class, se.getCause().getClass()); + assertThat(se.getMessage(), containsString("Method call is not allowed!")); + } + } + + @Test + public void shouldCompileStaticWithMultipleExtensionDeprecated() throws Exception { // with no type checking extension this should pass final CompileStaticCustomizerProvider providerNoExtension = new CompileStaticCustomizerProvider(); try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(providerNoExtension)) { @@ -99,4 +136,33 @@ public void shouldCompileStaticWithMultipleExtension() throws Exception { assertThat(se.getMessage(), containsString("Method call is not allowed!")); } } + + @Test + public void shouldCompileStaticWithMultipleExtension() throws Exception { + // with no type checking extension this should pass + final CompileStaticCustomizerProvider providerNoExtension = new CompileStaticCustomizerProvider(); + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(providerNoExtension))) { + assertEquals(255, scriptEngine.eval("def c = new java.awt.Color(255, 255, 255); c.red")); + assertEquals(1l, scriptEngine.eval("def c = new java.util.concurrent.CountDownLatch(1); c.count")); + } + + final CompileStaticCustomizerProvider providerWithExtension = new CompileStaticCustomizerProvider( + PrecompiledExtensions.PreventColorUsageExtension.class.getName() + + "," + PrecompiledExtensions.PreventCountDownLatchUsageExtension.class.getName()); + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(providerWithExtension))) { + scriptEngine.eval("def c = new java.awt.Color(255, 255, 255); c.red"); + fail("Should have failed type checking"); + } catch (ScriptException se) { + assertEquals(MultipleCompilationErrorsException.class, se.getCause().getClass()); + assertThat(se.getMessage(), containsString("Method call is not allowed!")); + } + + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(providerWithExtension)) { + scriptEngine.eval("def c = new java.util.concurrent.CountDownLatch(1); c.count"); + fail("Should have failed type checking"); + } catch (ScriptException se) { + assertEquals(MultipleCompilationErrorsException.class, se.getCause().getClass()); + assertThat(se.getMessage(), containsString("Method call is not allowed!")); + } + } } 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 index d354ffa3834..6b18ece1e08 100644 --- 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 @@ -31,9 +31,19 @@ */ public class GremlinGroovyScriptEngineConfigTest { @Test - public void shouldAddBaseScriptClass() throws Exception { + public void shouldAddBaseScriptClassDeprecated() throws Exception { final ScriptEngine engine = new GremlinGroovyScriptEngine( - new ConfigurationCustomizerProvider("ScriptBaseClass", BaseScriptForTesting.class.getName()), new DefaultImportCustomizerProvider()); + new ConfigurationCustomizerProvider("ScriptBaseClass", BaseScriptForTesting.class.getName()), + new DefaultImportCustomizerProvider()); + + assertEquals("hello, stephen", engine.eval("hello('stephen')")); + } + + @Test + public void shouldAddBaseScriptClass() throws Exception { + final ScriptEngine engine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer( + new ConfigurationCustomizerProvider("ScriptBaseClass", BaseScriptForTesting.class.getName())), + new CustomizerProviderCustomizer(new DefaultImportCustomizerProvider())); assertEquals("hello, stephen", engine.eval("hello('stephen')")); } diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java index b18c020b21a..65dc56e8f95 100644 --- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java +++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java @@ -99,7 +99,7 @@ public void shouldEvalWithNoBindings() throws Exception { } @Test - public void shouldPromoteDefinedVarsInInterpreterModeWithNoBindings() throws Exception { + public void shouldPromoteDefinedVarsInInterpreterModeWithNoBindingsDeprecated() throws Exception { final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(new InterpreterModeCustomizerProvider()); engine.eval("def addItUp = { x, y -> x + y }"); assertEquals(3, engine.eval("int xxx = 1 + 2")); @@ -121,7 +121,29 @@ public void shouldPromoteDefinedVarsInInterpreterModeWithNoBindings() throws Exc } @Test - public void shouldPromoteDefinedVarsInInterpreterModeWithBindings() throws Exception { + public void shouldPromoteDefinedVarsInInterpreterModeWithNoBindings() throws Exception { + final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(new InterpreterModeCustomizerProvider())); + engine.eval("def addItUp = { x, y -> x + y }"); + assertEquals(3, engine.eval("int xxx = 1 + 2")); + assertEquals(4, engine.eval("yyy = xxx + 1")); + assertEquals(7, engine.eval("def zzz = yyy + xxx")); + assertEquals(4, engine.eval("zzz - xxx")); + assertEquals("accessible-globally", engine.eval("if (yyy > 0) { def inner = 'should-stay-local'; outer = 'accessible-globally' }\n outer")); + assertEquals("accessible-globally", engine.eval("outer")); + + try { + engine.eval("inner"); + fail("Should not have been able to access 'inner'"); + } catch (Exception ex) { + final Throwable root = ExceptionUtils.getRootCause(ex); + assertThat(root, instanceOf(MissingPropertyException.class)); + } + + assertEquals(10, engine.eval("addItUp(zzz,xxx)")); + } + + @Test + public void shouldPromoteDefinedVarsInInterpreterModeWithBindingsDeprecated() throws Exception { final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(new InterpreterModeCustomizerProvider()); final Bindings b = new SimpleBindings(); b.put("x", 2); @@ -144,6 +166,30 @@ public void shouldPromoteDefinedVarsInInterpreterModeWithBindings() throws Excep assertEquals(10, engine.eval("addItUp(zzz,xxx)", b)); } + @Test + public void shouldPromoteDefinedVarsInInterpreterModeWithBindings() throws Exception { + final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(new InterpreterModeCustomizerProvider())); + final Bindings b = new SimpleBindings(); + b.put("x", 2); + engine.eval("def addItUp = { x, y -> x + y }", b); + assertEquals(3, engine.eval("int xxx = 1 + x", b)); + assertEquals(4, engine.eval("yyy = xxx + 1", b)); + assertEquals(7, engine.eval("def zzz = yyy + xxx", b)); + assertEquals(4, engine.eval("zzz - xxx", b)); + assertEquals("accessible-globally", engine.eval("if (yyy > 0) { def inner = 'should-stay-local'; outer = 'accessible-globally' }\n outer", b)); + assertEquals("accessible-globally", engine.eval("outer", b)); + + try { + engine.eval("inner", b); + fail("Should not have been able to access 'inner'"); + } catch (Exception ex) { + final Throwable root = ExceptionUtils.getRootCause(ex); + assertThat(root, instanceOf(MissingPropertyException.class)); + } + + assertEquals(10, engine.eval("addItUp(zzz,xxx)", b)); + } + @Test public void shouldEvalWithBindings() throws Exception { final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(); diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineThreadInterruptTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineThreadInterruptTest.java index c00293954e5..ea778c6fb69 100644 --- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineThreadInterruptTest.java +++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineThreadInterruptTest.java @@ -33,7 +33,7 @@ */ public class GremlinGroovyScriptEngineThreadInterruptTest { @Test - public void shouldInterruptWhile() throws Exception { + public void shouldInterruptWhileDeprecated() throws Exception { final ScriptEngine engine = new GremlinGroovyScriptEngine(new ThreadInterruptCustomizerProvider()); final AtomicBoolean asserted = new AtomicBoolean(false); @@ -53,6 +53,27 @@ public void shouldInterruptWhile() throws Exception { assertTrue(asserted.get()); } + @Test + public void shouldInterruptWhile() throws Exception { + final ScriptEngine engine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(new ThreadInterruptCustomizerProvider())); + final AtomicBoolean asserted = new AtomicBoolean(false); + + final Thread t = new Thread(() -> { + try { + engine.eval("s = System.currentTimeMillis();\nwhile((System.currentTimeMillis() - s) < 10000) {}"); + } catch (ScriptException se) { + asserted.set(se.getCause().getCause() instanceof InterruptedException); + } + }); + + t.start(); + Thread.sleep(100); + t.interrupt(); + while(t.isAlive()) {} + + assertTrue(asserted.get()); + } + @Test public void shouldNotInterruptWhile() throws Exception { // companion to shouldInterruptWhile t diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTimedInterruptTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTimedInterruptTest.java index c4657321700..fa8d4efba7f 100644 --- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTimedInterruptTest.java +++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTimedInterruptTest.java @@ -35,22 +35,52 @@ */ public class GremlinGroovyScriptEngineTimedInterruptTest { + @Test + public void shouldTimeoutScriptOnTimedWhileDeprecated() throws Exception { + final ScriptEngine engine = new GremlinGroovyScriptEngine( + new TimedInterruptCustomizerProvider(1000), new DefaultImportCustomizerProvider()); + try { + engine.eval("s = System.currentTimeMillis();\nwhile((System.currentTimeMillis() - s) < 10000) {}"); + fail("This should have timed out"); + } catch (ScriptException se) { + assertEquals(TimedInterruptTimeoutException.class, se.getCause().getCause().getClass()); + } + } + @Test public void shouldTimeoutScriptOnTimedWhile() throws Exception { + final ScriptEngine engine = new GremlinGroovyScriptEngine( + new CustomizerProviderCustomizer(new TimedInterruptCustomizerProvider(1000)), + new CustomizerProviderCustomizer(new DefaultImportCustomizerProvider())); + try { + engine.eval("s = System.currentTimeMillis();\nwhile((System.currentTimeMillis() - s) < 10000) {}"); + fail("This should have timed out"); + } catch (ScriptException se) { + assertEquals(TimedInterruptTimeoutException.class, se.getCause().getCause().getClass()); + } + } + + @Test + public void shouldTimeoutScriptOnTimedWhileOnceEngineHasBeenAliveForLongerThanTimeoutDeprecated() throws Exception { final ScriptEngine engine = new GremlinGroovyScriptEngine( new TimedInterruptCustomizerProvider(1000), new DefaultImportCustomizerProvider()); + Thread.sleep(2000); try { engine.eval("s = System.currentTimeMillis();\nwhile((System.currentTimeMillis() - s) < 10000) {}"); fail("This should have timed out"); } catch (ScriptException se) { assertEquals(TimedInterruptTimeoutException.class, se.getCause().getCause().getClass()); } + + assertEquals(2, engine.eval("1+1")); } + @Test public void shouldTimeoutScriptOnTimedWhileOnceEngineHasBeenAliveForLongerThanTimeout() throws Exception { final ScriptEngine engine = new GremlinGroovyScriptEngine( - new TimedInterruptCustomizerProvider(1000), new DefaultImportCustomizerProvider()); + new CustomizerProviderCustomizer(new TimedInterruptCustomizerProvider(1000)), + new CustomizerProviderCustomizer(new DefaultImportCustomizerProvider())); Thread.sleep(2000); try { engine.eval("s = System.currentTimeMillis();\nwhile((System.currentTimeMillis() - s) < 10000) {}"); @@ -63,7 +93,7 @@ public void shouldTimeoutScriptOnTimedWhileOnceEngineHasBeenAliveForLongerThanTi } @Test - public void shouldContinueToEvalScriptsEvenWithTimedInterrupt() throws Exception { + public void shouldContinueToEvalScriptsEvenWithTimedInterruptDeprecated() throws Exception { final ScriptEngine engine = new GremlinGroovyScriptEngine( new TimedInterruptCustomizerProvider(1000), new DefaultImportCustomizerProvider()); @@ -84,7 +114,29 @@ public void shouldContinueToEvalScriptsEvenWithTimedInterrupt() throws Exception } @Test - public void shouldNotTimeoutStandaloneFunction() throws Exception { + public void shouldContinueToEvalScriptsEvenWithTimedInterrupt() throws Exception { + final ScriptEngine engine = new GremlinGroovyScriptEngine( + new CustomizerProviderCustomizer(new TimedInterruptCustomizerProvider(1000)), + new CustomizerProviderCustomizer(new DefaultImportCustomizerProvider())); + + for (int ix = 0; ix < 5; ix++) { + try { + // this script takes 1000 ms longer than the interruptionTimeout + engine.eval("s = System.currentTimeMillis();\nwhile((System.currentTimeMillis() - s) < 2000) {}"); + fail("This should have timed out"); + } catch (ScriptException se) { + assertEquals(TimedInterruptTimeoutException.class, se.getCause().getCause().getClass()); + } + + // this script takes 500 ms less than the interruptionTimeout + assertEquals("test", engine.eval("s = System.currentTimeMillis();\nwhile((System.currentTimeMillis() - s) < 500) {};'test'")); + } + + assertEquals(2, engine.eval("1+1")); + } + + @Test + public void shouldNotTimeoutStandaloneFunctionDeprecated() throws Exception { // use a super fast timeout which should not prevent the call of a cached function final ScriptEngine engine = new GremlinGroovyScriptEngine( new TimedInterruptCustomizerProvider(1), new DefaultImportCustomizerProvider()); @@ -92,4 +144,15 @@ public void shouldNotTimeoutStandaloneFunction() throws Exception { assertEquals(3, engine.eval("addItUp(1,2)")); } + + @Test + public void shouldNotTimeoutStandaloneFunction() throws Exception { + // use a super fast timeout which should not prevent the call of a cached function + final ScriptEngine engine = new GremlinGroovyScriptEngine( + new CustomizerProviderCustomizer(new TimedInterruptCustomizerProvider(1)), + new CustomizerProviderCustomizer(new DefaultImportCustomizerProvider())); + engine.eval("def addItUp(x,y) { x + y }"); + + assertEquals(3, engine.eval("addItUp(1,2)")); + } } diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTypeCheckedTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTypeCheckedTest.java index a9062a84a61..0ca12d71e63 100644 --- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTypeCheckedTest.java +++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTypeCheckedTest.java @@ -36,7 +36,7 @@ */ public class GremlinGroovyScriptEngineTypeCheckedTest { @Test - public void shouldTypeCheck() throws Exception { + public void shouldTypeCheckDeprecated() throws Exception { // with no type checking this should pass try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine()) { assertEquals(255, scriptEngine.eval("((Object) new java.awt.Color(255, 255, 255)).getRed()")); @@ -54,7 +54,25 @@ public void shouldTypeCheck() throws Exception { } @Test - public void shouldTypeCheckWithExtension() throws Exception { + public void shouldTypeCheck() throws Exception { + // with no type checking this should pass + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine()) { + assertEquals(255, scriptEngine.eval("((Object) new java.awt.Color(255, 255, 255)).getRed()")); + } + + final TypeCheckedCustomizerProvider provider = new TypeCheckedCustomizerProvider(); + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(provider))) { + scriptEngine.eval("((Object) new java.awt.Color(255, 255, 255)).getRed()"); + fail("Should have failed type checking"); + } catch (ScriptException se) { + final Throwable root = ExceptionUtils.getRootCause(se); + assertEquals(MultipleCompilationErrorsException.class, root.getClass()); + assertThat(se.getMessage(), containsString("[Static type checking] - Cannot find matching method java.lang.Object#getRed(). Please check if the declared type is right and if the method exists.")); + } + } + + @Test + public void shouldTypeCheckWithExtensionDeprecated() throws Exception { // with no type checking extension this should pass final TypeCheckedCustomizerProvider providerNoExtension = new TypeCheckedCustomizerProvider(); try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(providerNoExtension)) { @@ -73,7 +91,26 @@ public void shouldTypeCheckWithExtension() throws Exception { } @Test - public void shouldTypeCheckWithMultipleExtension() throws Exception { + public void shouldTypeCheckWithExtension() throws Exception { + // with no type checking extension this should pass + final TypeCheckedCustomizerProvider providerNoExtension = new TypeCheckedCustomizerProvider(); + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(providerNoExtension))) { + assertEquals(255, scriptEngine.eval("def c = new java.awt.Color(255, 255, 255); c.red")); + } + + final CompileStaticCustomizerProvider providerWithExtension = new CompileStaticCustomizerProvider( + PrecompiledExtensions.PreventColorUsageExtension.class.getName()); + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(providerWithExtension))) { + scriptEngine.eval("def c = new java.awt.Color(255, 255, 255); c.red"); + fail("Should have failed type checking"); + } catch (ScriptException se) { + assertEquals(MultipleCompilationErrorsException.class, se.getCause().getClass()); + assertThat(se.getMessage(), containsString("Method call is not allowed!")); + } + } + + @Test + public void shouldTypeCheckWithMultipleExtensionDeprecated() throws Exception { // with no type checking extension this should pass final TypeCheckedCustomizerProvider providerNoExtension = new TypeCheckedCustomizerProvider(); try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(providerNoExtension)) { @@ -100,4 +137,33 @@ public void shouldTypeCheckWithMultipleExtension() throws Exception { assertThat(se.getMessage(), containsString("Method call is not allowed!")); } } + + @Test + public void shouldTypeCheckWithMultipleExtension() throws Exception { + // with no type checking extension this should pass + final TypeCheckedCustomizerProvider providerNoExtension = new TypeCheckedCustomizerProvider(); + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(providerNoExtension))) { + assertEquals(255, scriptEngine.eval("def c = new java.awt.Color(255, 255, 255); c.red")); + assertEquals(1l, scriptEngine.eval("def c = new java.util.concurrent.CountDownLatch(1); c.count")); + } + + final TypeCheckedCustomizerProvider providerWithExtension = new TypeCheckedCustomizerProvider( + PrecompiledExtensions.PreventColorUsageExtension.class.getName() + + "," + PrecompiledExtensions.PreventCountDownLatchUsageExtension.class.getName()); + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(providerWithExtension))) { + scriptEngine.eval("def c = new java.awt.Color(255, 255, 255); c.red"); + fail("Should have failed type checking"); + } catch (ScriptException se) { + assertEquals(MultipleCompilationErrorsException.class, se.getCause().getClass()); + assertThat(se.getMessage(), containsString("Method call is not allowed!")); + } + + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(providerWithExtension)) { + scriptEngine.eval("def c = new java.util.concurrent.CountDownLatch(1); c.count"); + fail("Should have failed type checking"); + } catch (ScriptException se) { + assertEquals(MultipleCompilationErrorsException.class, se.getCause().getClass()); + assertThat(se.getMessage(), containsString("Method call is not allowed!")); + } + } } diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCompilerGremlinPluginTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCompilerGremlinPluginTest.java new file mode 100644 index 00000000000..8d2178fd3dc --- /dev/null +++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCompilerGremlinPluginTest.java @@ -0,0 +1,128 @@ +/* + * 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.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.ThreadInterruptCustomizerProvider; +import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.TimedInterruptCustomizerProvider; +import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.TypeCheckedCustomizerProvider; +import org.apache.tinkerpop.gremlin.jsr223.Customizer; +import org.codehaus.groovy.control.CompilerConfiguration; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import static org.apache.tinkerpop.gremlin.groovy.jsr223.GroovyCompilerGremlinPlugin.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsInstanceOf.instanceOf; +import static org.junit.Assert.assertEquals; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class GroovyCompilerGremlinPluginTest { + + @Test + public void shouldConfigureForGroovyOnly() { + final GroovyCompilerGremlinPlugin plugin = GroovyCompilerGremlinPlugin.build(). + compilation(Compilation.COMPILE_STATIC).create(); + final Optional customizers = plugin.getCustomizers("gremlin-not-real"); + assertThat(customizers.isPresent(), is(false)); + } + + @Test + public void shouldConfigureWithCompileStatic() { + final GroovyCompilerGremlinPlugin plugin = GroovyCompilerGremlinPlugin.build(). + compilation(Compilation.COMPILE_STATIC).create(); + final Optional customizers = plugin.getCustomizers("gremlin-groovy"); + assertThat(customizers.isPresent(), is(true)); + assertEquals(1, customizers.get().length); + assertThat(((CustomizerProviderCustomizer) customizers.get()[0]).getCustomizerProvider(), instanceOf(CompileStaticCustomizerProvider.class)); + } + + @Test + public void shouldConfigureWithTypeChecked() { + final GroovyCompilerGremlinPlugin plugin = GroovyCompilerGremlinPlugin.build(). + compilation(Compilation.TYPE_CHECKED).create(); + final Optional customizers = plugin.getCustomizers("gremlin-groovy"); + assertThat(customizers.isPresent(), is(true)); + assertEquals(1, customizers.get().length); + assertThat(((CustomizerProviderCustomizer) customizers.get()[0]).getCustomizerProvider(), instanceOf(TypeCheckedCustomizerProvider.class)); + } + + @Test + public void shouldConfigureWithCustomCompilerConfigurations() { + final Map conf = new HashMap<>(); + conf.put("Debug", true); + final GroovyCompilerGremlinPlugin plugin = GroovyCompilerGremlinPlugin.build(). + compilerConfigurationOptions(conf).create(); + final Optional customizers = plugin.getCustomizers("gremlin-groovy"); + assertThat(customizers.isPresent(), is(true)); + assertEquals(1, customizers.get().length); + assertThat(((CustomizerProviderCustomizer) customizers.get()[0]).getCustomizerProvider(), instanceOf(ConfigurationCustomizerProvider.class)); + + final CompilerConfiguration compilerConfiguration = new CompilerConfiguration(); + assertThat(compilerConfiguration.getDebug(), is(false)); + + final ConfigurationCustomizerProvider provider = (ConfigurationCustomizerProvider) ((CustomizerProviderCustomizer) customizers.get()[0]).getCustomizerProvider(); + provider.applyCustomization(compilerConfiguration); + + assertThat(compilerConfiguration.getDebug(), is(true)); + } + + @Test + public void shouldConfigureWithInterpreterMode() { + final GroovyCompilerGremlinPlugin plugin = GroovyCompilerGremlinPlugin.build(). + enableInterpreterMode(true).create(); + final Optional customizers = plugin.getCustomizers("gremlin-groovy"); + assertThat(customizers.isPresent(), is(true)); + assertEquals(1, customizers.get().length); + assertThat(((CustomizerProviderCustomizer) customizers.get()[0]).getCustomizerProvider(), instanceOf(InterpreterModeCustomizerProvider.class)); + } + + @Test + public void shouldConfigureWithThreadInterrupt() { + final GroovyCompilerGremlinPlugin plugin = GroovyCompilerGremlinPlugin.build(). + enableThreadInterrupt(true).create(); + final Optional customizers = plugin.getCustomizers("gremlin-groovy"); + assertThat(customizers.isPresent(), is(true)); + assertEquals(1, customizers.get().length); + assertThat(((CustomizerProviderCustomizer) customizers.get()[0]).getCustomizerProvider(), instanceOf(ThreadInterruptCustomizerProvider.class)); + } + + @Test + public void shouldConfigureWithTimedInterrupt() { + final GroovyCompilerGremlinPlugin plugin = GroovyCompilerGremlinPlugin.build(). + timedInterrupt(60000).create(); + final Optional customizers = plugin.getCustomizers("gremlin-groovy"); + assertThat(customizers.isPresent(), is(true)); + assertEquals(1, customizers.get().length); + assertThat(((CustomizerProviderCustomizer) customizers.get()[0]).getCustomizerProvider(), instanceOf(TimedInterruptCustomizerProvider.class)); + } + + @Test(expected = IllegalStateException.class) + public void shouldNotConfigureIfNoSettingsAreSupplied() { + GroovyCompilerGremlinPlugin.build().create(); + } +} From 2d49710b2f7a45453aa1165074440e704a007044 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Tue, 29 Nov 2016 16:29:10 -0500 Subject: [PATCH 33/45] TINKERPOP-1562 Use List of files to preserve order in script customzier --- .../tinkerpop/gremlin/jsr223/DefaultScriptCustomizer.java | 3 +-- .../tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java | 4 +++- .../gremlin/jsr223/DefaultScriptCustomizerTest.java | 5 ++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultScriptCustomizer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultScriptCustomizer.java index 9640f28ae24..c996caeb8e9 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultScriptCustomizer.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultScriptCustomizer.java @@ -24,7 +24,6 @@ import java.nio.file.Files; import java.util.Collection; import java.util.List; -import java.util.Set; import java.util.stream.Collectors; /** @@ -37,7 +36,7 @@ public class DefaultScriptCustomizer implements ScriptCustomizer { private final Collection> scripts; - public DefaultScriptCustomizer(final Set files) { + public DefaultScriptCustomizer(final List files) { this(files.stream().map(f -> { try { return Files.lines(f.toPath(), StandardCharsets.UTF_8).collect(Collectors.toList()); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java index 757001cffb4..93ad9d8c780 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java @@ -20,8 +20,10 @@ import java.io.File; import java.io.FileNotFoundException; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; /** @@ -44,7 +46,7 @@ public static Builder build() { public static final class Builder { private final Set appliesTo = new HashSet<>(); - private Set files = new HashSet<>(); + private List files = new ArrayList<>(); private Builder() {} diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/DefaultScriptCustomizerTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/DefaultScriptCustomizerTest.java index 3e4da135fee..07ab7709f67 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/DefaultScriptCustomizerTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/DefaultScriptCustomizerTest.java @@ -23,10 +23,9 @@ import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; import java.util.List; -import java.util.Set; import static org.junit.Assert.assertEquals; @@ -39,7 +38,7 @@ public class DefaultScriptCustomizerTest { public void shouldOpenViaPropertiesFileConfig() throws IOException { final File scriptFile1 = TestHelper.generateTempFileFromResource(DefaultScriptCustomizerTest.class, "script-customizer-1.groovy", ".groovy"); final File scriptFile2 = TestHelper.generateTempFileFromResource(DefaultScriptCustomizerTest.class, "script-customizer-2.groovy", ".groovy"); - final Set files = new HashSet<>(); + final List files = new ArrayList<>(); files.add(scriptFile1); files.add(scriptFile2); final ScriptCustomizer scripts = new DefaultScriptCustomizer(files); From 873ac619e935e226505f75bd6caacd76d820531b Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Tue, 29 Nov 2016 16:42:46 -0500 Subject: [PATCH 34/45] TINKERPOP-1562 Change Set to List to preserve order in script execution. --- .../tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java | 2 +- .../tinkerpop/gremlin/jsr223/ScriptFileGremlinPluginTest.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java index 93ad9d8c780..0131ca20aeb 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPlugin.java @@ -60,7 +60,7 @@ public Builder appliesTo(final Collection scriptEngineNames) { return this; } - public Builder files(final Set files) { + public Builder files(final List files) { for (String f : files) { final File file = new File(f); if (!file.exists()) throw new IllegalArgumentException(new FileNotFoundException(f)); diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPluginTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPluginTest.java index 81cf9e60a95..681d2ac669d 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPluginTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ScriptFileGremlinPluginTest.java @@ -23,6 +23,7 @@ import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; @@ -41,7 +42,7 @@ public class ScriptFileGremlinPluginTest { public void shouldOpenViaPropertiesFileConfig() throws IOException { final File scriptFile1 = TestHelper.generateTempFileFromResource(DefaultScriptCustomizerTest.class, "script-customizer-1.groovy", ".groovy"); final File scriptFile2 = TestHelper.generateTempFileFromResource(DefaultScriptCustomizerTest.class, "script-customizer-2.groovy", ".groovy"); - final Set files = new HashSet<>(); + final List files = new ArrayList<>(); files.add(scriptFile1.getAbsolutePath()); files.add(scriptFile2.getAbsolutePath()); final GremlinPlugin plugin = ScriptFileGremlinPlugin.build().files(files).create(); From 9218e8a1b4b532fbbfdcba00f005c8e9fdf95c59 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Tue, 29 Nov 2016 17:03:34 -0500 Subject: [PATCH 35/45] TINKERPOP-1562 Fix yet another compilation error. --- .../gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java b/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java index c3ade0492a2..cb6fa60f7aa 100644 --- a/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java +++ b/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/GremlinPluginAdapterTest.java @@ -78,7 +78,7 @@ public void shouldAdaptForImportCustomizer() throws Exception { public void shouldAdaptForScriptCustomizer() throws Exception { final File scriptFile1 = TestHelper.generateTempFileFromResource(GremlinPluginAdapterTest.class, "script-customizer-1.groovy", ".groovy"); final File scriptFile2 = TestHelper.generateTempFileFromResource(GremlinPluginAdapterTest.class, "script-customizer-2.groovy", ".groovy"); - final Set files = new HashSet<>(); + final List files = new ArrayList<>(); files.add(scriptFile1.getAbsolutePath()); files.add(scriptFile2.getAbsolutePath()); final ScriptFileGremlinPlugin plugin = ScriptFileGremlinPlugin.build().files(files).create(); From 6d1b97e0bb3f5567900124d3a549c066b7ff3605 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Wed, 30 Nov 2016 06:37:14 -0500 Subject: [PATCH 36/45] TINKERPOP-1562 Fixed a bad javadoc link --- .../tinkerpop/gremlin/jsr223/console/RemoteAcceptor.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/RemoteAcceptor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/RemoteAcceptor.java index ab4c81d98c9..9571b2ce9a4 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/RemoteAcceptor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/console/RemoteAcceptor.java @@ -28,8 +28,9 @@ * A "remote connection" does not necessarily have to be a remote server. It simply refers to a resource that is * external to the console. *

- * By implementing this interface and returning an instance of it through {@link ConsoleCustomizer#getRemoteAcceptor(Map)} - * a plugin can hook into those commands and provide remoting features. + * By implementing this interface and returning an instance of it through + * {@link ConsoleCustomizer#getRemoteAcceptor(GremlinShellEnvironment)} a plugin can hook into those commands and + * provide remoting features. * * @author Stephen Mallette (http://stephen.genoprime.com) */ From 83689211d05905fb4fd31375a8db225200a72ae5 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Wed, 30 Nov 2016 07:28:37 -0500 Subject: [PATCH 37/45] TINKERPOP-1562 Hook up Console to do core imports under v3d3 --- .../tinkerpop/gremlin/console/Console.groovy | 21 ++++++++++++------- .../ConsoleImportCustomizerProvider.groovy | 2 ++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy index d39e085afe9..a2e518c3056 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/Console.groovy @@ -31,6 +31,8 @@ import org.apache.tinkerpop.gremlin.console.commands.UninstallCommand import org.apache.tinkerpop.gremlin.console.plugin.PluggedIn import org.apache.tinkerpop.gremlin.groovy.loaders.GremlinLoader import org.apache.tinkerpop.gremlin.groovy.plugin.GremlinPlugin +import org.apache.tinkerpop.gremlin.jsr223.CoreGremlinPlugin +import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalExplanation import org.apache.tinkerpop.gremlin.structure.Edge import org.apache.tinkerpop.gremlin.structure.T @@ -103,13 +105,18 @@ class Console { // hide output temporarily while imports execute showShellEvaluationOutput(false) - // TODO: register CoreGremlinPlugin if using v3d3 - - // add the default imports - new ConsoleImportCustomizerProvider().getCombinedImports().stream() - .collect { IMPORT_SPACE + it }.each { groovy.execute(it) } - new ConsoleImportCustomizerProvider().getCombinedStaticImports().stream() - .collect { IMPORT_STATIC_SPACE + it }.each { groovy.execute(it) } + if (Mediator.useV3d3) { + def imports = (ImportCustomizer) CoreGremlinPlugin.instance().getCustomizers("gremlin-groovy").get()[0] + imports.classImports.collect { IMPORT_SPACE + it.canonicalName }.each { groovy.execute(it) } + imports.methodImports.collect { IMPORT_STATIC_SPACE + it.getDeclaringClass().getCanonicalName() + "." + it.name}.each{ groovy.execute(it) } + imports.enumImports.collect { IMPORT_STATIC_SPACE + it.getDeclaringClass().getCanonicalName() + "." + it.name()}.each{ groovy.execute(it) } + } else { + // add the default imports + new ConsoleImportCustomizerProvider().getCombinedImports().stream() + .collect { IMPORT_SPACE + it }.each { groovy.execute(it) } + new ConsoleImportCustomizerProvider().getCombinedStaticImports().stream() + .collect { IMPORT_STATIC_SPACE + it }.each { groovy.execute(it) } + } final InteractiveShellRunner runner = new InteractiveShellRunner(groovy, handlePrompt) runner.setErrorHandler(handleError) diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/ConsoleImportCustomizerProvider.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/ConsoleImportCustomizerProvider.groovy index 0404e935a65..85fda8ce3ef 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/ConsoleImportCustomizerProvider.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/ConsoleImportCustomizerProvider.groovy @@ -23,7 +23,9 @@ import groovy.sql.Sql /** * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, not replaced. */ +@Deprecated class ConsoleImportCustomizerProvider extends AbstractImportCustomizerProvider { public ConsoleImportCustomizerProvider() { // useful groovy bits that are good for the Console From 00ccf0fe0b728bd742f9b6f9c1cf6936f4f07b27 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Wed, 30 Nov 2016 18:42:49 -0500 Subject: [PATCH 38/45] TINKERPOP-1562 Update upgrade docs with ScriptEngine changes. --- .../upgrade/release-3.2.x-incubating.asciidoc | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/docs/src/upgrade/release-3.2.x-incubating.asciidoc b/docs/src/upgrade/release-3.2.x-incubating.asciidoc index 2b3890603d1..8a184b4f053 100644 --- a/docs/src/upgrade/release-3.2.x-incubating.asciidoc +++ b/docs/src/upgrade/release-3.2.x-incubating.asciidoc @@ -55,6 +55,56 @@ This has been changed to a regular `NoSuchElementException` that includes the st See: link:https://issues.apache.org/jira/browse/TINKERPOP-1330[TINKERPOP-1330] +ScriptEngine support in gremlin-core +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +`ScriptEngine` and `GremlinPlugin` infrastructure has been moved from gremlin-groovy to gremlin-core to allow for +better re-use across different Gremlin Language Variants. At this point, this change is non-breaking as it was +implemented through deprecation. + +The basic concept of a `ScriptEngine` has been replaced by the notion of a `GremlinScriptEngine` (i.e. a +"ScriptEngine" that is specifically tuned for executing Gremlin-related scripts). "ScriptEngine" infrastructure has +been developed to help support this new interface, specifically `GremlinScriptEngineFactory` and +`GremlinScriptEngineManager`. Prefer use of this infrastructure when instantiating a `GremlinScriptEngine` rather +than trying to instantiate directly. + +For example, rather than instantiate a `GremlinGroovyScriptEngine` with the constructor: + +[source,java] +---- +GremlinScriptEngine engine = new GremlinGroovyScriptEngine(); +---- + +prefer to instantiate it as follows: + +[source,java] +---- +GremlinScriptEngineManager manager = new CachedGremlinScriptEngineManager(); +GremlinScriptEngine engine = manager.getEngineByName("gremlin-groovy"); +---- + +Related to the addition of `GremlinScriptEngine`, `org.apache.tinkerpop.gremlin.groovy.plugin.GremlinPlugin` in +gremlin-groovy has been deprecated and then replaced by `org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin`. The new +version of `GremlinPlugin` is similar but does carry some new methods to implement that involves the new `Customizer` +interface. The `Customizer` interface is the way in which `GremlinScriptEngine` instance can be configured with +imports, initialization scripts, compiler options, etc. + +Note that a `GremlinPlugin` can be applied to a `GremlinScriptEngine` by adding it to the `GremlinScriptEngineManager` +that creates it. + +[source,java] +---- +GremlinScriptEngineManager manager = new CachedGremlinScriptEngineManager(); +manager.addPlugin(ImportGremlinPlugin.build().classImports(java.awt.Color.class).create()); +GremlinScriptEngine engine = manager.getEngineByName("gremlin-groovy"); +---- + +All of this new infrastructure is currently optional on the 3.2.x line of code. More detailed documentation will for +these changes will be supplied as part of 3.3.0 when these features become mandatory and the deprecated code is +removed. + +See: link:https://issues.apache.org/jira/browse/TINKERPOP-1562[TINKERPOP-1562] + Upgrading for Providers ~~~~~~~~~~~~~~~~~~~~~~~ From a82c56fad2500f49da265466d5e23f345749d348 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Wed, 30 Nov 2016 18:43:13 -0500 Subject: [PATCH 39/45] TINKERPOP-1562 Make all groovy customizer providers straight Customizer instances --- CHANGELOG.asciidoc | 1 + .../tinkerpop/gremlin/util/CoreImports.java | 34 +++++-- .../jsr223/CompileStaticGroovyCustomizer.java | 60 ++++++++++++ .../jsr223/ConfigurationGroovyCustomizer.java | 82 ++++++++++++++++ .../jsr223/GremlinGroovyScriptEngine.java | 97 ++++++++++--------- .../jsr223/GroovyCompilerGremlinPlugin.java | 18 ++-- ...rCustomizer.java => GroovyCustomizer.java} | 19 ++-- .../groovy/jsr223/ImportGroovyCustomizer.java | 66 +++++++++++++ .../InterpreterModeGroovyCustomizer.java | 36 +++++++ .../ThreadInterruptGroovyCustomizer.java | 35 +++++++ .../TimedInterruptGroovyCustomizer.java | 62 ++++++++++++ .../TimedInterruptTimeoutException.java | 38 ++++++++ .../jsr223/TypeCheckedGroovyCustomizer.java | 65 +++++++++++++ .../CompileStaticCustomizerProvider.java | 2 + .../ConfigurationCustomizerProvider.java | 2 + .../InterpreterModeCustomizerProvider.java | 4 + .../ThreadInterruptCustomizerProvider.java | 2 + .../TimedInterruptCustomizerProvider.java | 2 + .../TimedInterruptTimeoutException.java | 4 + .../TypeCheckedCustomizerProvider.java | 2 + .../VariableIdentificationCustomizer.java | 2 + ...inGroovyScriptEngineCompileStaticTest.java | 20 ++-- .../GremlinGroovyScriptEngineConfigTest.java | 5 +- .../jsr223/GremlinGroovyScriptEngineTest.java | 6 +- ...GroovyScriptEngineThreadInterruptTest.java | 2 +- ...nGroovyScriptEngineTimedInterruptTest.java | 22 ++--- ...mlinGroovyScriptEngineTypeCheckedTest.java | 18 ++-- .../GroovyCompilerGremlinPluginTest.java | 14 +-- .../server/op/AbstractEvalOpProcessor.java | 5 + 29 files changed, 598 insertions(+), 127 deletions(-) create mode 100644 gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/CompileStaticGroovyCustomizer.java create mode 100644 gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ConfigurationGroovyCustomizer.java rename gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/{CustomizerProviderCustomizer.java => GroovyCustomizer.java} (63%) create mode 100644 gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ImportGroovyCustomizer.java create mode 100644 gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/InterpreterModeGroovyCustomizer.java create mode 100644 gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ThreadInterruptGroovyCustomizer.java create mode 100644 gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/TimedInterruptGroovyCustomizer.java create mode 100644 gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/TimedInterruptTimeoutException.java create mode 100644 gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/TypeCheckedGroovyCustomizer.java diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index ebcc3186468..b4ba2a6c3c1 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -46,6 +46,7 @@ TinkerPop 3.2.4 (Release Date: NOT OFFICIALLY RELEASED YET) * Fixed a `NoSuchElementException` bug with `GroupXXXStep` where if the reduced `TraverserSet` is empty, don't add the key/value. * Fixed a `NullPointerException` bug with profiling `GroupSideEffectStep` in OLTP. * Improved ability to release resources in `GraphProvider` instances in the test suite. +* Factored `GremlinPlugin` functionality out of gremlin-groovy and into gremlin-core - related classes were deprecated. * Added a `force` option for killing sessions without waiting for transaction close or timeout of a currently running job or multiple jobs. * Deprecated `Session.kill()` and `Session.manualKill()`. * Added `choose(predicate,traversal)` and `choose(traversal,traversal)` to effect if/then-semantics (no else). Equivalent to `choose(x,y,identity())`. diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/CoreImports.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/CoreImports.java index e6c64fdb636..1c6a6e6a39c 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/CoreImports.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/CoreImports.java @@ -83,12 +83,15 @@ import org.apache.tinkerpop.gremlin.structure.io.IoCore; import org.apache.tinkerpop.gremlin.structure.io.Storage; import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph; +import org.javatuples.Pair; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.Stream; /** @@ -187,11 +190,11 @@ public final class CoreImports { // METHODS // ///////////// - Stream.of(IoCore.class.getMethods()).filter(m -> Modifier.isStatic(m.getModifiers())).forEach(METHOD_IMPORTS::add); - Stream.of(P.class.getMethods()).filter(m -> Modifier.isStatic(m.getModifiers())).forEach(METHOD_IMPORTS::add); - Stream.of(__.class.getMethods()).filter(m -> Modifier.isStatic(m.getModifiers())).filter(m -> !m.getName().equals("__")).forEach(METHOD_IMPORTS::add); - Stream.of(Computer.class.getMethods()).filter(m -> Modifier.isStatic(m.getModifiers())).forEach(METHOD_IMPORTS::add); - Stream.of(TimeUtil.class.getMethods()).filter(m -> Modifier.isStatic(m.getModifiers())).forEach(METHOD_IMPORTS::add); + uniqueMethods(IoCore.class).forEach(METHOD_IMPORTS::add); + uniqueMethods(P.class).forEach(METHOD_IMPORTS::add); + uniqueMethods(__.class).filter(m -> !m.getName().equals("__")).forEach(METHOD_IMPORTS::add); + uniqueMethods(Computer.class).forEach(METHOD_IMPORTS::add); + uniqueMethods(TimeUtil.class).forEach(METHOD_IMPORTS::add); /////////// // ENUMS // @@ -207,7 +210,6 @@ public final class CoreImports { Collections.addAll(ENUM_IMPORTS, Scope.values()); Collections.addAll(ENUM_IMPORTS, T.values()); Collections.addAll(ENUM_IMPORTS, TraversalOptionParent.Pick.values()); - } private CoreImports() { @@ -225,4 +227,24 @@ public static Set getMethodImports() { public static Set getEnumImports() { return Collections.unmodifiableSet(ENUM_IMPORTS); } + + /** + * Filters to unique method names on each class. + */ + private static Stream uniqueMethods(final Class clazz) { + final Set unique = new HashSet<>(); + return Stream.of(clazz.getMethods()) + .filter(m -> Modifier.isStatic(m.getModifiers())) + .map(m -> Pair.with(generateMethodDescriptor(m), m)) + .filter(p -> { + final boolean exists = unique.contains(p.getValue0()); + if (!exists) unique.add(p.getValue0()); + return !exists; + }) + .map(Pair::getValue1); + } + + private static String generateMethodDescriptor(final Method m) { + return m.getDeclaringClass().getCanonicalName() + "." + m.getName(); + } } diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/CompileStaticGroovyCustomizer.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/CompileStaticGroovyCustomizer.java new file mode 100644 index 00000000000..c57e64be7c5 --- /dev/null +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/CompileStaticGroovyCustomizer.java @@ -0,0 +1,60 @@ +/* + * 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.transform.CompileStatic; +import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer; +import org.codehaus.groovy.control.customizers.CompilationCustomizer; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Injects the {@code CompileStatic} transformer to enable type validation on script execution. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +class CompileStaticGroovyCustomizer implements GroovyCustomizer { + + private final String extensions; + + CompileStaticGroovyCustomizer() { + this(null); + } + + CompileStaticGroovyCustomizer(final String extensions) { + this.extensions = extensions; + } + + @Override + public CompilationCustomizer create() { + final Map annotationParams = new HashMap<>(); + if (extensions != null && !extensions.isEmpty()) { + if (extensions.contains(",")) + annotationParams.put("extensions", Stream.of(extensions.split(",")).collect(Collectors.toList())); + else + annotationParams.put("extensions", Collections.singletonList(extensions)); + } + + return new ASTTransformationCustomizer(annotationParams, CompileStatic.class); + } +} diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ConfigurationGroovyCustomizer.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ConfigurationGroovyCustomizer.java new file mode 100644 index 00000000000..23fe002eb0d --- /dev/null +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ConfigurationGroovyCustomizer.java @@ -0,0 +1,82 @@ +/* + * 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.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 GremlinGroovyScriptEngine} is initialized, providing fine-grained + * control over its internals. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +class ConfigurationGroovyCustomizer implements GroovyCustomizer { + + private final Map properties; + + /** + * Creates a new instance using configuration values specified + */ + ConfigurationGroovyCustomizer(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 + */ + ConfigurationGroovyCustomizer(final Map keyValues) { + properties = keyValues; + } + + public CompilerConfiguration applyCustomization(final CompilerConfiguration compilerConfiguration) { + final Class clazz = CompilerConfiguration.class; + final List methods = Arrays.asList(clazz.getMethods()); + for (Map.Entry 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"); + } +} 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 29967928f93..0f36dbfb54e 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 @@ -70,6 +70,7 @@ import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -177,6 +178,9 @@ protected Map initialValue() { private ImportCustomizerProvider importCustomizerProvider; private final List customizerProviders; + private final ImportGroovyCustomizer importGroovyCustomizer; + private final List groovyCustomizers; + private final Set artifactsToUse = new HashSet<>(); private final boolean interpreterModeEnabled; @@ -207,46 +211,21 @@ public GremlinGroovyScriptEngine(final Customizer... customizers) { .filter(p -> p instanceof ImportCustomizer) .map(p -> (ImportCustomizer) p) .collect(Collectors.toList()); - if (importCustomizers.isEmpty()) { - importCustomizerProvider = NoImportCustomizerProvider.INSTANCE; - } else { - final Set imports = new HashSet<>(); - final Set staticImports = new HashSet<>(); - importCustomizers.forEach(ic -> { - ic.getClassImports().forEach(c -> { - final String importStatement = c.getName(); - imports.add(importStatement); - }); - - ic.getEnumImports().forEach(e -> { - final String importStatement = e.getDeclaringClass().getCanonicalName() + ".*"; - staticImports.add(importStatement); - }); + final ImportCustomizer[] importCustomizerArray = new ImportCustomizer[importCustomizers.size()]; + importGroovyCustomizer = new ImportGroovyCustomizer(importCustomizers.toArray(importCustomizerArray)); - ic.getMethodImports().forEach(m -> { - final String importStatement = m.getDeclaringClass().getCanonicalName() + ".*"; - staticImports.add(importStatement); - }); - }); - - importCustomizerProvider = new EmptyImportCustomizerProvider(imports, staticImports); - } - - // this is a bit temporary - until CompilerCustomizerProvider is gone - final List customizerProviderCustomizer = listOfCustomizers.stream() - .filter(p -> p instanceof CustomizerProviderCustomizer) - .map(p -> ((CustomizerProviderCustomizer) p).getCustomizerProvider()) + groovyCustomizers = listOfCustomizers.stream() + .filter(p -> p instanceof GroovyCustomizer) + .map(p -> ((GroovyCustomizer) p)) .collect(Collectors.toList()); // determine if interpreter mode should be enabled - interpreterModeEnabled = customizerProviderCustomizer.stream() - .anyMatch(p -> p.getClass().equals(InterpreterModeCustomizerProvider.class)); + interpreterModeEnabled = groovyCustomizers.stream() + .anyMatch(p -> p.getClass().equals(InterpreterModeGroovyCustomizer.class)); - // remove used providers as the rest will be applied directly - customizerProviders = customizerProviderCustomizer.stream() - .filter(p -> p != null && - !((p instanceof ImportCustomizerProvider))) - .collect(Collectors.toList()); + // not using the old provider model so set that to empty list so that when createClassLoader is called + // it knows to use groovyCustomizers instead + customizerProviders = Collections.emptyList(); createClassLoader(); } @@ -273,10 +252,14 @@ public GremlinGroovyScriptEngine(final CompilerCustomizerProvider... compilerCus // remove used providers as the rest will be applied directly customizerProviders = providers.stream() - .filter(p -> p != null && - !((p instanceof ImportCustomizerProvider))) + .filter(p -> p != null && !(p instanceof ImportCustomizerProvider)) .collect(Collectors.toList()); + // groovy customizers are not used here - set to empty list so that the customizerProviders get used + // in createClassLoader + groovyCustomizers = Collections.emptyList(); + importGroovyCustomizer = null; + createClassLoader(); } @@ -350,7 +333,11 @@ public Map> imports() { */ @Override public synchronized void addImports(final Set importStatements) { - final Set staticImports = new HashSet<>(); + // can't use this feature because imports can't come in as String for the revised model + if (null == importCustomizerProvider) + throw new IllegalStateException("Imports cannot be added to a GremlinGroovyScriptEngine that uses Customizer instances"); + + final Set staticImports = new HashSet<>(); final Set imports = new HashSet<>(); importStatements.forEach(s -> { @@ -362,9 +349,8 @@ public synchronized void addImports(final Set importStatements) { imports.add(s.substring(6).trim()); }); - // use the EmptyImportCustomizer because it doesn't come with static initializers containing - // existing imports. importCustomizerProvider = new EmptyImportCustomizerProvider(importCustomizerProvider, imports, staticImports); + internalReset(); } @@ -697,17 +683,32 @@ private Object invokeImpl(final Object thiz, final String name, final Object arg } private synchronized void createClassLoader() { - final CompilerConfiguration conf = new CompilerConfiguration(CompilerConfiguration.DEFAULT); - conf.addCompilationCustomizers(this.importCustomizerProvider.create()); + // check for customizerProviders temporarily until this deprecated stuff is gone + if (groovyCustomizers.isEmpty() && null == importGroovyCustomizer) { + final CompilerConfiguration conf = new CompilerConfiguration(CompilerConfiguration.DEFAULT); + conf.addCompilationCustomizers(this.importCustomizerProvider.create()); - // ConfigurationCustomizerProvider is treated separately - customizerProviders.stream().filter(cp -> !(cp instanceof ConfigurationCustomizerProvider)) - .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)); + customizerProviders.stream().filter(cp -> cp instanceof ConfigurationCustomizerProvider).findFirst() + .ifPresent(cp -> ((ConfigurationCustomizerProvider) cp).applyCustomization(conf)); - this.loader = new GremlinGroovyClassLoader(getParentLoader(), conf); + this.loader = new GremlinGroovyClassLoader(getParentLoader(), conf); + } else { + final CompilerConfiguration conf = new CompilerConfiguration(CompilerConfiguration.DEFAULT); + conf.addCompilationCustomizers(this.importGroovyCustomizer.create()); + + // ConfigurationCustomizerProvider is treated separately + groovyCustomizers.stream().filter(cp -> !(cp instanceof ConfigurationGroovyCustomizer)) + .forEach(p -> conf.addCompilationCustomizers(p.create())); + + groovyCustomizers.stream().filter(cp -> cp instanceof ConfigurationGroovyCustomizer).findFirst() + .ifPresent(cp -> ((ConfigurationGroovyCustomizer) cp).applyCustomization(conf)); + + this.loader = new GremlinGroovyClassLoader(getParentLoader(), conf); + } } private void use(final Artifact artifact) { diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCompilerGremlinPlugin.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCompilerGremlinPlugin.java index 0d2e9c61734..71f5dc71f43 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCompilerGremlinPlugin.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCompilerGremlinPlugin.java @@ -18,12 +18,6 @@ */ package org.apache.tinkerpop.gremlin.groovy.jsr223; -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.ThreadInterruptCustomizerProvider; -import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.TimedInterruptCustomizerProvider; -import org.apache.tinkerpop.gremlin.groovy.jsr223.customizer.TypeCheckedCustomizerProvider; import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; import org.apache.tinkerpop.gremlin.jsr223.Customizer; import org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin; @@ -106,21 +100,21 @@ Customizer[] asCustomizers() { final List list = new ArrayList<>(); if (interpreterMode) - list.add(new CustomizerProviderCustomizer(new InterpreterModeCustomizerProvider())); + list.add(new InterpreterModeGroovyCustomizer()); if (!keyValues.isEmpty()) - list.add(new CustomizerProviderCustomizer(new ConfigurationCustomizerProvider(keyValues))); + list.add(new ConfigurationGroovyCustomizer(keyValues)); if (threadInterrupt) - list.add(new CustomizerProviderCustomizer(new ThreadInterruptCustomizerProvider())); + list.add(new ThreadInterruptGroovyCustomizer()); if (timeInMillis > 0) - list.add(new CustomizerProviderCustomizer(new TimedInterruptCustomizerProvider(timeInMillis))); + list.add(new TimedInterruptGroovyCustomizer(timeInMillis)); if (compilation == Compilation.COMPILE_STATIC) - list.add(new CustomizerProviderCustomizer(new CompileStaticCustomizerProvider(extensions))); + list.add(new CompileStaticGroovyCustomizer(extensions)); else if (compilation == Compilation.TYPE_CHECKED) - list.add(new CustomizerProviderCustomizer(new TypeCheckedCustomizerProvider(extensions))); + list.add(new TypeCheckedGroovyCustomizer(extensions)); else if (compilation != Compilation.NONE) throw new IllegalStateException("Use of unknown compilation type: " + compilation); diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/CustomizerProviderCustomizer.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCustomizer.java similarity index 63% rename from gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/CustomizerProviderCustomizer.java rename to gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCustomizer.java index 73614a1fffe..94426c02875 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/CustomizerProviderCustomizer.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCustomizer.java @@ -18,23 +18,16 @@ */ package org.apache.tinkerpop.gremlin.groovy.jsr223; -import org.apache.tinkerpop.gremlin.groovy.CompilerCustomizerProvider; import org.apache.tinkerpop.gremlin.jsr223.Customizer; +import org.codehaus.groovy.control.customizers.CompilationCustomizer; /** - * An adapter that allows existing {@link CompilerCustomizerProvider} instances to behave as a {#link Customizer}. - * * @author Stephen Mallette (http://stephen.genoprime.com) */ -class CustomizerProviderCustomizer implements Customizer { - - private final CompilerCustomizerProvider customizerProvider; - - CustomizerProviderCustomizer(final CompilerCustomizerProvider customizerProvider) { - this.customizerProvider = customizerProvider; - } +public interface GroovyCustomizer extends Customizer { - CompilerCustomizerProvider getCustomizerProvider() { - return customizerProvider; - } + /** + * Create a new instance of a {@code CompilationCustomizer} to add to the {@link GremlinGroovyScriptEngine}. + */ + public CompilationCustomizer create(); } diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ImportGroovyCustomizer.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ImportGroovyCustomizer.java new file mode 100644 index 00000000000..79243a00a25 --- /dev/null +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ImportGroovyCustomizer.java @@ -0,0 +1,66 @@ +/* + * 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.jsr223.ImportCustomizer; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.codehaus.groovy.control.customizers.CompilationCustomizer; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +class ImportGroovyCustomizer implements GroovyCustomizer { + + private final List customizers; + + ImportGroovyCustomizer(final ImportCustomizer... customizers) { + this.customizers = Arrays.asList(customizers); + } + + ImportGroovyCustomizer(final ImportGroovyCustomizer ic, final ImportCustomizer... customizers) { + this.customizers = new ArrayList<>(Arrays.asList(customizers)); + this.customizers.addAll(ic.customizers); + } + + @Override + public CompilationCustomizer create() { + final org.codehaus.groovy.control.customizers.ImportCustomizer ic = new org.codehaus.groovy.control.customizers.ImportCustomizer(); + + // there's something weird in groovy about doing specific imports instead of wildcard imports. with wildcard + // imports groovy seems to allow methods to be overloaded with enums such that things like Column.values and + // __.values() can be resolved by the compiler. if they are both directly in the imports then one or the other + // can't be found. the temporary fix is to hardcode a wildcard import __ and then filter out the core imports + // from the incoming customizer. ultimately, the fix should be to resolve the naming conflicts to ensure a + // unique space somehow. + ic.addStaticStars(__.class.getCanonicalName()); + for (ImportCustomizer customizer : customizers) { + customizer.getClassImports().forEach(i -> ic.addImports(i.getCanonicalName())); + customizer.getMethodImports().stream() + .filter(m -> !m.getDeclaringClass().equals(__.class)) + .forEach(m -> ic.addStaticImport(m.getDeclaringClass().getCanonicalName(), m.getName())); + customizer.getEnumImports().forEach(m -> ic.addStaticImport(m.getDeclaringClass().getCanonicalName(), m.name())); + } + + return ic; + } +} diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/InterpreterModeGroovyCustomizer.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/InterpreterModeGroovyCustomizer.java new file mode 100644 index 00000000000..89b471e4c11 --- /dev/null +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/InterpreterModeGroovyCustomizer.java @@ -0,0 +1,36 @@ +/* + * 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.jsr223.ast.InterpreterMode; +import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer; +import org.codehaus.groovy.control.customizers.CompilationCustomizer; + +/** + * Places the {@code ScriptEngine} in "interpreter mode" where local variables of a script are treated as global + * bindings. This implementation is technically not a true {@link GroovyCustomizer} instance as the + * "interpreter mode" feature does not require a {@code CompilerCustomizer}. This class merely acts as a flag that + * tells the {@link GremlinGroovyScriptEngine} to turn this feature on. + */ +class InterpreterModeGroovyCustomizer implements GroovyCustomizer { + @Override + public CompilationCustomizer create() { + return new ASTTransformationCustomizer(InterpreterMode.class); + } +} diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ThreadInterruptGroovyCustomizer.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ThreadInterruptGroovyCustomizer.java new file mode 100644 index 00000000000..4492c3c8a74 --- /dev/null +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/ThreadInterruptGroovyCustomizer.java @@ -0,0 +1,35 @@ +/* + * 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.transform.ThreadInterrupt; +import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer; +import org.codehaus.groovy.control.customizers.CompilationCustomizer; + +/** + * Injects checks for thread interruption into scripts. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +class ThreadInterruptGroovyCustomizer implements GroovyCustomizer { + @Override + public CompilationCustomizer create() { + return new ASTTransformationCustomizer(ThreadInterrupt.class); + } +} diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/TimedInterruptGroovyCustomizer.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/TimedInterruptGroovyCustomizer.java new file mode 100644 index 00000000000..c43216449c2 --- /dev/null +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/TimedInterruptGroovyCustomizer.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.groovy.jsr223; + +import groovy.transform.TimedInterrupt; +import org.codehaus.groovy.ast.tools.GeneralUtils; +import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer; +import org.codehaus.groovy.control.customizers.CompilationCustomizer; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * Injects a check in loops and other areas of code to interrupt script execution if the run time exceeds the + * specified time. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +class TimedInterruptGroovyCustomizer implements GroovyCustomizer { + private static final long DEFAULT_INTERRUPTION_TIMEOUT = 60000; + + private final long interruptionTimeout; + + TimedInterruptGroovyCustomizer() { + this(DEFAULT_INTERRUPTION_TIMEOUT); + } + + TimedInterruptGroovyCustomizer(final Long interruptionTimeout) { + this.interruptionTimeout = interruptionTimeout; + } + + TimedInterruptGroovyCustomizer(final Integer interruptionTimeout) { + this.interruptionTimeout = interruptionTimeout.longValue(); + } + + @Override + public CompilationCustomizer create() { + final Map timedInterruptAnnotationParams = new HashMap<>(); + timedInterruptAnnotationParams.put("value", interruptionTimeout); + timedInterruptAnnotationParams.put("unit", GeneralUtils.propX(GeneralUtils.classX(TimeUnit.class), TimeUnit.MILLISECONDS.toString())); + timedInterruptAnnotationParams.put("checkOnMethodStart", false); + timedInterruptAnnotationParams.put("thrown", GeneralUtils.classX(TimedInterruptTimeoutException.class)); + return new ASTTransformationCustomizer(timedInterruptAnnotationParams, TimedInterrupt.class); + } +} diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/TimedInterruptTimeoutException.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/TimedInterruptTimeoutException.java new file mode 100644 index 00000000000..bdd49b3cbbf --- /dev/null +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/TimedInterruptTimeoutException.java @@ -0,0 +1,38 @@ +/* + * 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.jsr223.customizer.TimedInterruptCustomizerProvider; + +import java.util.concurrent.TimeoutException; + +/** + * An exception thrown from the {@link TimedInterruptCustomizerProvider} when the timeout is exceeded. This exception + * allows differentiation from other "timeout exceptions" that might occur. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class TimedInterruptTimeoutException extends TimeoutException { + public TimedInterruptTimeoutException() { + } + + public TimedInterruptTimeoutException(final String message) { + super(message); + } +} diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/TypeCheckedGroovyCustomizer.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/TypeCheckedGroovyCustomizer.java new file mode 100644 index 00000000000..ac8dd1d5f26 --- /dev/null +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/TypeCheckedGroovyCustomizer.java @@ -0,0 +1,65 @@ +/* + * 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.transform.TypeChecked; +import org.apache.tinkerpop.gremlin.groovy.CompilerCustomizerProvider; +import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer; +import org.codehaus.groovy.control.customizers.CompilationCustomizer; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Injects the {@code TypeChecked} transformer to enable type validation on script execution. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +class TypeCheckedGroovyCustomizer implements GroovyCustomizer { + + private final String extensions; + + TypeCheckedGroovyCustomizer() { + this(null); + } + + /** + * Configures the {@code TypeChecked} annotation to use optional extensions. The argument should be one or more + * groovy scripts on the classpath or the fully qualified classname of a precompiled extension. If there are + * multiple extensions then extensions should be comma separated. + */ + TypeCheckedGroovyCustomizer(final String extensions) { + this.extensions = extensions; + } + + @Override + public CompilationCustomizer create() { + final Map annotationParams = new HashMap<>(); + if (extensions != null && !extensions.isEmpty()) { + if (extensions.contains(",")) + annotationParams.put("extensions", Stream.of(extensions.split(",")).collect(Collectors.toList())); + else + annotationParams.put("extensions", Collections.singletonList(extensions)); + } + return new ASTTransformationCustomizer(annotationParams, TypeChecked.class); + } +} diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/CompileStaticCustomizerProvider.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/CompileStaticCustomizerProvider.java index ab0bff55c10..4371e8a374a 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/CompileStaticCustomizerProvider.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/CompileStaticCustomizerProvider.java @@ -34,7 +34,9 @@ * Injects the {@code CompileStatic} transformer to enable type validation on script execution. * * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, not replaced by a public class. */ +@Deprecated public class CompileStaticCustomizerProvider implements CompilerCustomizerProvider { private final String extensions; 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 index 78357cbc84b..3c8b673a7a1 100644 --- 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 @@ -35,7 +35,9 @@ * control over its internals. * * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, not replaced by a public class. */ +@Deprecated public class ConfigurationCustomizerProvider implements CompilerCustomizerProvider { private final Map properties; diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/InterpreterModeCustomizerProvider.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/InterpreterModeCustomizerProvider.java index 013ba8b9d9c..30444746ce3 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/InterpreterModeCustomizerProvider.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/InterpreterModeCustomizerProvider.java @@ -29,7 +29,11 @@ * bindings. This implementation is technically not a true {@link CompilerCustomizerProvider} instance as the * "interpreter mode" feature does not require a {@code CompilerCustomizer}. This class merely acts as a flag that * tells the {@link org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine} to turn this feature on. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, not replaced by a public class. */ +@Deprecated public class InterpreterModeCustomizerProvider implements CompilerCustomizerProvider { @Override public CompilationCustomizer create() { diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/ThreadInterruptCustomizerProvider.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/ThreadInterruptCustomizerProvider.java index e46e9b72ceb..c5fc60c5bdf 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/ThreadInterruptCustomizerProvider.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/ThreadInterruptCustomizerProvider.java @@ -28,7 +28,9 @@ * Injects checks for thread interruption into scripts. * * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, not replaced by a public class. */ +@Deprecated public class ThreadInterruptCustomizerProvider implements CompilerCustomizerProvider { @Override public CompilationCustomizer create() { diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TimedInterruptCustomizerProvider.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TimedInterruptCustomizerProvider.java index 99130880854..f0e10805fb7 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TimedInterruptCustomizerProvider.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TimedInterruptCustomizerProvider.java @@ -34,7 +34,9 @@ * specified time. * * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, not replaced by a public class. */ +@Deprecated public class TimedInterruptCustomizerProvider implements CompilerCustomizerProvider { public static final long DEFAULT_INTERRUPTION_TIMEOUT = 60000; diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TimedInterruptTimeoutException.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TimedInterruptTimeoutException.java index 40abd59e68f..4063d4f962a 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TimedInterruptTimeoutException.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TimedInterruptTimeoutException.java @@ -23,7 +23,11 @@ /** * An exception thrown from the {@link TimedInterruptCustomizerProvider} when the timeout is exceeded. This exception * allows differentiation from other "timeout exceptions" that might occur. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.groovy.jsr223.TimedInterruptTimeoutException}. */ +@Deprecated public class TimedInterruptTimeoutException extends TimeoutException { public TimedInterruptTimeoutException() { } diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TypeCheckedCustomizerProvider.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TypeCheckedCustomizerProvider.java index cf9147b766b..b5c2729031e 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TypeCheckedCustomizerProvider.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/TypeCheckedCustomizerProvider.java @@ -34,7 +34,9 @@ * Injects the {@code TypeChecked} transformer to enable type validation on script execution. * * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, not replaced by a public class. */ +@Deprecated public class TypeCheckedCustomizerProvider implements CompilerCustomizerProvider { private final String extensions; diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/VariableIdentificationCustomizer.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/VariableIdentificationCustomizer.java index 79df57845c9..2c216d76b12 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/VariableIdentificationCustomizer.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/customizer/VariableIdentificationCustomizer.java @@ -35,7 +35,9 @@ /** * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, not replaced. */ +@Deprecated public class VariableIdentificationCustomizer extends CompilationCustomizer { private static final ThreadLocal> variables = new ThreadLocal>() { diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineCompileStaticTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineCompileStaticTest.java index b62f3f32456..6f3383eb929 100644 --- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineCompileStaticTest.java +++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineCompileStaticTest.java @@ -59,8 +59,8 @@ public void shouldCompileStatic() throws Exception { assertEquals(255, scriptEngine.eval("((Object) new java.awt.Color(255, 255, 255)).getRed()")); } - final CompileStaticCustomizerProvider provider = new CompileStaticCustomizerProvider(); - try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(provider))) { + final CompileStaticGroovyCustomizer provider = new CompileStaticGroovyCustomizer(); + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(provider)) { scriptEngine.eval("((Object) new java.awt.Color(255, 255, 255)).getRed()"); fail("Should have failed type checking"); } catch (ScriptException se) { @@ -92,14 +92,14 @@ public void shouldCompileStaticWithExtensionDeprecated() throws Exception { @Test public void shouldCompileStaticWithExtension() throws Exception { // with no type checking extension this should pass - final CompileStaticCustomizerProvider providerNoExtension = new CompileStaticCustomizerProvider(); - try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(providerNoExtension))) { + final CompileStaticGroovyCustomizer providerNoExtension = new CompileStaticGroovyCustomizer(); + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(providerNoExtension)) { assertEquals(255, scriptEngine.eval("def c = new java.awt.Color(255, 255, 255); c.red")); } - final CompileStaticCustomizerProvider providerWithExtension = new CompileStaticCustomizerProvider( + final CompileStaticGroovyCustomizer providerWithExtension = new CompileStaticGroovyCustomizer( PrecompiledExtensions.PreventColorUsageExtension.class.getName()); - try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(providerWithExtension))) { + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(providerWithExtension)) { scriptEngine.eval("def c = new java.awt.Color(255, 255, 255); c.red"); fail("Should have failed type checking"); } catch (ScriptException se) { @@ -140,16 +140,16 @@ public void shouldCompileStaticWithMultipleExtensionDeprecated() throws Exceptio @Test public void shouldCompileStaticWithMultipleExtension() throws Exception { // with no type checking extension this should pass - final CompileStaticCustomizerProvider providerNoExtension = new CompileStaticCustomizerProvider(); - try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(providerNoExtension))) { + final CompileStaticGroovyCustomizer providerNoExtension = new CompileStaticGroovyCustomizer(); + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(providerNoExtension)) { assertEquals(255, scriptEngine.eval("def c = new java.awt.Color(255, 255, 255); c.red")); assertEquals(1l, scriptEngine.eval("def c = new java.util.concurrent.CountDownLatch(1); c.count")); } - final CompileStaticCustomizerProvider providerWithExtension = new CompileStaticCustomizerProvider( + final CompileStaticGroovyCustomizer providerWithExtension = new CompileStaticGroovyCustomizer( PrecompiledExtensions.PreventColorUsageExtension.class.getName() + "," + PrecompiledExtensions.PreventCountDownLatchUsageExtension.class.getName()); - try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(providerWithExtension))) { + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(providerWithExtension)) { scriptEngine.eval("def c = new java.awt.Color(255, 255, 255); c.red"); fail("Should have failed type checking"); } catch (ScriptException se) { 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 index 6b18ece1e08..289b2ca1f39 100644 --- 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 @@ -41,9 +41,8 @@ public void shouldAddBaseScriptClassDeprecated() throws Exception { @Test public void shouldAddBaseScriptClass() throws Exception { - final ScriptEngine engine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer( - new ConfigurationCustomizerProvider("ScriptBaseClass", BaseScriptForTesting.class.getName())), - new CustomizerProviderCustomizer(new DefaultImportCustomizerProvider())); + final ScriptEngine engine = new GremlinGroovyScriptEngine( + new ConfigurationGroovyCustomizer("ScriptBaseClass", BaseScriptForTesting.class.getName())); assertEquals("hello, stephen", engine.eval("hello('stephen')")); } diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java index 65dc56e8f95..eb0a44b701c 100644 --- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java +++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTest.java @@ -122,7 +122,7 @@ public void shouldPromoteDefinedVarsInInterpreterModeWithNoBindingsDeprecated() @Test public void shouldPromoteDefinedVarsInInterpreterModeWithNoBindings() throws Exception { - final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(new InterpreterModeCustomizerProvider())); + final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(new InterpreterModeGroovyCustomizer()); engine.eval("def addItUp = { x, y -> x + y }"); assertEquals(3, engine.eval("int xxx = 1 + 2")); assertEquals(4, engine.eval("yyy = xxx + 1")); @@ -168,7 +168,7 @@ public void shouldPromoteDefinedVarsInInterpreterModeWithBindingsDeprecated() th @Test public void shouldPromoteDefinedVarsInInterpreterModeWithBindings() throws Exception { - final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(new InterpreterModeCustomizerProvider())); + final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(new InterpreterModeGroovyCustomizer()); final Bindings b = new SimpleBindings(); b.put("x", 2); engine.eval("def addItUp = { x, y -> x + y }", b); @@ -354,7 +354,7 @@ public void shouldReloadClassLoaderWhileDoingEvalInSeparateThread() throws Excep final CountDownLatch latch = new CountDownLatch(1); final AtomicReference color = new AtomicReference<>(Color.RED); - final GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(); + final GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(NoImportCustomizerProvider.INSTANCE); try { scriptEngine.eval("Color.BLACK"); diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineThreadInterruptTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineThreadInterruptTest.java index ea778c6fb69..499373fe858 100644 --- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineThreadInterruptTest.java +++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineThreadInterruptTest.java @@ -55,7 +55,7 @@ public void shouldInterruptWhileDeprecated() throws Exception { @Test public void shouldInterruptWhile() throws Exception { - final ScriptEngine engine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(new ThreadInterruptCustomizerProvider())); + final ScriptEngine engine = new GremlinGroovyScriptEngine(new ThreadInterruptGroovyCustomizer()); final AtomicBoolean asserted = new AtomicBoolean(false); final Thread t = new Thread(() -> { diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTimedInterruptTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTimedInterruptTest.java index fa8d4efba7f..1745b5d3b11 100644 --- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTimedInterruptTest.java +++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTimedInterruptTest.java @@ -25,7 +25,6 @@ import javax.script.ScriptEngine; import javax.script.ScriptException; -import java.util.concurrent.TimeoutException; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -49,14 +48,12 @@ public void shouldTimeoutScriptOnTimedWhileDeprecated() throws Exception { @Test public void shouldTimeoutScriptOnTimedWhile() throws Exception { - final ScriptEngine engine = new GremlinGroovyScriptEngine( - new CustomizerProviderCustomizer(new TimedInterruptCustomizerProvider(1000)), - new CustomizerProviderCustomizer(new DefaultImportCustomizerProvider())); + final ScriptEngine engine = new GremlinGroovyScriptEngine(new TimedInterruptGroovyCustomizer(1000)); try { engine.eval("s = System.currentTimeMillis();\nwhile((System.currentTimeMillis() - s) < 10000) {}"); fail("This should have timed out"); } catch (ScriptException se) { - assertEquals(TimedInterruptTimeoutException.class, se.getCause().getCause().getClass()); + assertEquals(org.apache.tinkerpop.gremlin.groovy.jsr223.TimedInterruptTimeoutException.class, se.getCause().getCause().getClass()); } } @@ -78,15 +75,13 @@ public void shouldTimeoutScriptOnTimedWhileOnceEngineHasBeenAliveForLongerThanTi @Test public void shouldTimeoutScriptOnTimedWhileOnceEngineHasBeenAliveForLongerThanTimeout() throws Exception { - final ScriptEngine engine = new GremlinGroovyScriptEngine( - new CustomizerProviderCustomizer(new TimedInterruptCustomizerProvider(1000)), - new CustomizerProviderCustomizer(new DefaultImportCustomizerProvider())); + final ScriptEngine engine = new GremlinGroovyScriptEngine(new TimedInterruptGroovyCustomizer(1000)); Thread.sleep(2000); try { engine.eval("s = System.currentTimeMillis();\nwhile((System.currentTimeMillis() - s) < 10000) {}"); fail("This should have timed out"); } catch (ScriptException se) { - assertEquals(TimedInterruptTimeoutException.class, se.getCause().getCause().getClass()); + assertEquals(org.apache.tinkerpop.gremlin.groovy.jsr223.TimedInterruptTimeoutException.class, se.getCause().getCause().getClass()); } assertEquals(2, engine.eval("1+1")); @@ -116,8 +111,7 @@ public void shouldContinueToEvalScriptsEvenWithTimedInterruptDeprecated() throws @Test public void shouldContinueToEvalScriptsEvenWithTimedInterrupt() throws Exception { final ScriptEngine engine = new GremlinGroovyScriptEngine( - new CustomizerProviderCustomizer(new TimedInterruptCustomizerProvider(1000)), - new CustomizerProviderCustomizer(new DefaultImportCustomizerProvider())); + new TimedInterruptGroovyCustomizer(1000)); for (int ix = 0; ix < 5; ix++) { try { @@ -125,7 +119,7 @@ public void shouldContinueToEvalScriptsEvenWithTimedInterrupt() throws Exception engine.eval("s = System.currentTimeMillis();\nwhile((System.currentTimeMillis() - s) < 2000) {}"); fail("This should have timed out"); } catch (ScriptException se) { - assertEquals(TimedInterruptTimeoutException.class, se.getCause().getCause().getClass()); + assertEquals(org.apache.tinkerpop.gremlin.groovy.jsr223.TimedInterruptTimeoutException.class, se.getCause().getCause().getClass()); } // this script takes 500 ms less than the interruptionTimeout @@ -148,9 +142,7 @@ public void shouldNotTimeoutStandaloneFunctionDeprecated() throws Exception { @Test public void shouldNotTimeoutStandaloneFunction() throws Exception { // use a super fast timeout which should not prevent the call of a cached function - final ScriptEngine engine = new GremlinGroovyScriptEngine( - new CustomizerProviderCustomizer(new TimedInterruptCustomizerProvider(1)), - new CustomizerProviderCustomizer(new DefaultImportCustomizerProvider())); + final ScriptEngine engine = new GremlinGroovyScriptEngine(new TimedInterruptGroovyCustomizer(1)); engine.eval("def addItUp(x,y) { x + y }"); assertEquals(3, engine.eval("addItUp(1,2)")); diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTypeCheckedTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTypeCheckedTest.java index 0ca12d71e63..6c70e8e161a 100644 --- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTypeCheckedTest.java +++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngineTypeCheckedTest.java @@ -60,8 +60,8 @@ public void shouldTypeCheck() throws Exception { assertEquals(255, scriptEngine.eval("((Object) new java.awt.Color(255, 255, 255)).getRed()")); } - final TypeCheckedCustomizerProvider provider = new TypeCheckedCustomizerProvider(); - try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(provider))) { + final TypeCheckedGroovyCustomizer provider = new TypeCheckedGroovyCustomizer(); + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(provider)) { scriptEngine.eval("((Object) new java.awt.Color(255, 255, 255)).getRed()"); fail("Should have failed type checking"); } catch (ScriptException se) { @@ -93,14 +93,14 @@ public void shouldTypeCheckWithExtensionDeprecated() throws Exception { @Test public void shouldTypeCheckWithExtension() throws Exception { // with no type checking extension this should pass - final TypeCheckedCustomizerProvider providerNoExtension = new TypeCheckedCustomizerProvider(); - try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(providerNoExtension))) { + final TypeCheckedGroovyCustomizer providerNoExtension = new TypeCheckedGroovyCustomizer(); + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(providerNoExtension)) { assertEquals(255, scriptEngine.eval("def c = new java.awt.Color(255, 255, 255); c.red")); } final CompileStaticCustomizerProvider providerWithExtension = new CompileStaticCustomizerProvider( PrecompiledExtensions.PreventColorUsageExtension.class.getName()); - try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(providerWithExtension))) { + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(providerWithExtension)) { scriptEngine.eval("def c = new java.awt.Color(255, 255, 255); c.red"); fail("Should have failed type checking"); } catch (ScriptException se) { @@ -141,16 +141,16 @@ public void shouldTypeCheckWithMultipleExtensionDeprecated() throws Exception { @Test public void shouldTypeCheckWithMultipleExtension() throws Exception { // with no type checking extension this should pass - final TypeCheckedCustomizerProvider providerNoExtension = new TypeCheckedCustomizerProvider(); - try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(providerNoExtension))) { + final TypeCheckedGroovyCustomizer providerNoExtension = new TypeCheckedGroovyCustomizer(); + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(providerNoExtension)) { assertEquals(255, scriptEngine.eval("def c = new java.awt.Color(255, 255, 255); c.red")); assertEquals(1l, scriptEngine.eval("def c = new java.util.concurrent.CountDownLatch(1); c.count")); } - final TypeCheckedCustomizerProvider providerWithExtension = new TypeCheckedCustomizerProvider( + final TypeCheckedGroovyCustomizer providerWithExtension = new TypeCheckedGroovyCustomizer( PrecompiledExtensions.PreventColorUsageExtension.class.getName() + "," + PrecompiledExtensions.PreventCountDownLatchUsageExtension.class.getName()); - try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(new CustomizerProviderCustomizer(providerWithExtension))) { + try (GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(providerWithExtension)) { scriptEngine.eval("def c = new java.awt.Color(255, 255, 255); c.red"); fail("Should have failed type checking"); } catch (ScriptException se) { diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCompilerGremlinPluginTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCompilerGremlinPluginTest.java index 8d2178fd3dc..f795fa76167 100644 --- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCompilerGremlinPluginTest.java +++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GroovyCompilerGremlinPluginTest.java @@ -58,7 +58,7 @@ public void shouldConfigureWithCompileStatic() { final Optional customizers = plugin.getCustomizers("gremlin-groovy"); assertThat(customizers.isPresent(), is(true)); assertEquals(1, customizers.get().length); - assertThat(((CustomizerProviderCustomizer) customizers.get()[0]).getCustomizerProvider(), instanceOf(CompileStaticCustomizerProvider.class)); + assertThat(customizers.get()[0], instanceOf(CompileStaticGroovyCustomizer.class)); } @Test @@ -68,7 +68,7 @@ public void shouldConfigureWithTypeChecked() { final Optional customizers = plugin.getCustomizers("gremlin-groovy"); assertThat(customizers.isPresent(), is(true)); assertEquals(1, customizers.get().length); - assertThat(((CustomizerProviderCustomizer) customizers.get()[0]).getCustomizerProvider(), instanceOf(TypeCheckedCustomizerProvider.class)); + assertThat(customizers.get()[0], instanceOf(TypeCheckedGroovyCustomizer.class)); } @Test @@ -80,12 +80,12 @@ public void shouldConfigureWithCustomCompilerConfigurations() { final Optional customizers = plugin.getCustomizers("gremlin-groovy"); assertThat(customizers.isPresent(), is(true)); assertEquals(1, customizers.get().length); - assertThat(((CustomizerProviderCustomizer) customizers.get()[0]).getCustomizerProvider(), instanceOf(ConfigurationCustomizerProvider.class)); + assertThat(customizers.get()[0], instanceOf(ConfigurationGroovyCustomizer.class)); final CompilerConfiguration compilerConfiguration = new CompilerConfiguration(); assertThat(compilerConfiguration.getDebug(), is(false)); - final ConfigurationCustomizerProvider provider = (ConfigurationCustomizerProvider) ((CustomizerProviderCustomizer) customizers.get()[0]).getCustomizerProvider(); + final ConfigurationGroovyCustomizer provider = (ConfigurationGroovyCustomizer) customizers.get()[0]; provider.applyCustomization(compilerConfiguration); assertThat(compilerConfiguration.getDebug(), is(true)); @@ -98,7 +98,7 @@ public void shouldConfigureWithInterpreterMode() { final Optional customizers = plugin.getCustomizers("gremlin-groovy"); assertThat(customizers.isPresent(), is(true)); assertEquals(1, customizers.get().length); - assertThat(((CustomizerProviderCustomizer) customizers.get()[0]).getCustomizerProvider(), instanceOf(InterpreterModeCustomizerProvider.class)); + assertThat(customizers.get()[0], instanceOf(InterpreterModeGroovyCustomizer.class)); } @Test @@ -108,7 +108,7 @@ public void shouldConfigureWithThreadInterrupt() { final Optional customizers = plugin.getCustomizers("gremlin-groovy"); assertThat(customizers.isPresent(), is(true)); assertEquals(1, customizers.get().length); - assertThat(((CustomizerProviderCustomizer) customizers.get()[0]).getCustomizerProvider(), instanceOf(ThreadInterruptCustomizerProvider.class)); + assertThat(customizers.get()[0], instanceOf(ThreadInterruptGroovyCustomizer.class)); } @Test @@ -118,7 +118,7 @@ public void shouldConfigureWithTimedInterrupt() { final Optional customizers = plugin.getCustomizers("gremlin-groovy"); assertThat(customizers.isPresent(), is(true)); assertEquals(1, customizers.get().length); - assertThat(((CustomizerProviderCustomizer) customizers.get()[0]).getCustomizerProvider(), instanceOf(TimedInterruptCustomizerProvider.class)); + assertThat(customizers.get()[0], instanceOf(TimedInterruptGroovyCustomizer.class)); } @Test(expected = IllegalStateException.class) diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java index b931b8c14f7..d5fe62a6b85 100644 --- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java +++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java @@ -279,6 +279,11 @@ protected void evalOpInternal(final Context context, final Supplier Date: Thu, 1 Dec 2016 06:40:59 -0500 Subject: [PATCH 40/45] TINKERPOP-1562 Moved CoreImports to jsr223 package. --- .../gremlin/jsr223/CoreGremlinModule.java | 2 - .../gremlin/jsr223/CoreGremlinPlugin.java | 2 - .../tinkerpop/gremlin/jsr223/CoreImports.java | 250 ++++++++++++++++++ .../DefaultGremlinScriptEngineManager.java | 2 - .../gremlin/jsr223/ImportCustomizer.java | 2 - .../tinkerpop/gremlin/util/CoreImports.java | 1 + .../python/TraversalSourceGenerator.groovy | 2 +- .../jsr223/GremlinJythonScriptEngine.java | 2 +- 8 files changed, 253 insertions(+), 10 deletions(-) create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.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 d398f89872f..369e171264b 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,8 +18,6 @@ */ package org.apache.tinkerpop.gremlin.jsr223; -import org.apache.tinkerpop.gremlin.util.CoreImports; - import java.util.Optional; /** diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinPlugin.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinPlugin.java index a3063cf7904..8882e36d0a2 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinPlugin.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreGremlinPlugin.java @@ -18,8 +18,6 @@ */ package org.apache.tinkerpop.gremlin.jsr223; -import org.apache.tinkerpop.gremlin.util.CoreImports; - import java.util.Optional; /** diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java new file mode 100644 index 00000000000..2244ae92f77 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java @@ -0,0 +1,250 @@ +/* + * 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.commons.configuration.Configuration; +import org.apache.tinkerpop.gremlin.process.computer.Computer; +import org.apache.tinkerpop.gremlin.process.computer.ComputerResult; +import org.apache.tinkerpop.gremlin.process.computer.GraphComputer; +import org.apache.tinkerpop.gremlin.process.computer.Memory; +import org.apache.tinkerpop.gremlin.process.computer.VertexProgram; +import org.apache.tinkerpop.gremlin.process.computer.bulkdumping.BulkDumperVertexProgram; +import org.apache.tinkerpop.gremlin.process.computer.bulkloading.BulkLoaderVertexProgram; +import org.apache.tinkerpop.gremlin.process.computer.clustering.peerpressure.PeerPressureVertexProgram; +import org.apache.tinkerpop.gremlin.process.computer.ranking.pagerank.PageRankVertexProgram; +import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy; +import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.optimization.GraphFilterStrategy; +import org.apache.tinkerpop.gremlin.process.remote.RemoteConnection; +import org.apache.tinkerpop.gremlin.process.remote.RemoteGraph; +import org.apache.tinkerpop.gremlin.process.traversal.Bindings; +import org.apache.tinkerpop.gremlin.process.traversal.Operator; +import org.apache.tinkerpop.gremlin.process.traversal.Order; +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.Pop; +import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions; +import org.apache.tinkerpop.gremlin.process.traversal.Scope; +import org.apache.tinkerpop.gremlin.process.traversal.Translator; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ConnectiveStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ElementIdStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.HaltedTraverserStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.MatchAlgorithmStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.ProfileStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.AdjacentToIncidentStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.FilterRankingStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.IdentityRemovalStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.IncidentToAdjacentStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.LazyBarrierStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.MatchPredicateStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.OrderLimitStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathProcessorStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RangeByIsCountStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ComputerVerificationStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.LambdaRestrictionStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.StandardVerificationStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalMetrics; +import org.apache.tinkerpop.gremlin.structure.Column; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Element; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Transaction; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; +import org.apache.tinkerpop.gremlin.structure.io.GraphReader; +import org.apache.tinkerpop.gremlin.structure.io.GraphWriter; +import org.apache.tinkerpop.gremlin.structure.io.Io; +import org.apache.tinkerpop.gremlin.structure.io.IoCore; +import org.apache.tinkerpop.gremlin.structure.io.Storage; +import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph; +import org.apache.tinkerpop.gremlin.util.Gremlin; +import org.apache.tinkerpop.gremlin.util.TimeUtil; +import org.javatuples.Pair; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; + +/** + * @author Marko A. Rodriguez (http://markorodriguez.com) + */ +public final class CoreImports { + + private final static Set CLASS_IMPORTS = new HashSet<>(); + private final static Set METHOD_IMPORTS = new HashSet<>(); + private final static Set ENUM_IMPORTS = new HashSet<>(); + + static { + ///////////// + // CLASSES // + ///////////// + + // graph + CLASS_IMPORTS.add(Edge.class); + CLASS_IMPORTS.add(Element.class); + CLASS_IMPORTS.add(Graph.class); + CLASS_IMPORTS.add(Property.class); + CLASS_IMPORTS.add(Transaction.class); + CLASS_IMPORTS.add(Vertex.class); + CLASS_IMPORTS.add(VertexProperty.class); + // tokens + CLASS_IMPORTS.add(SackFunctions.class); + CLASS_IMPORTS.add(SackFunctions.Barrier.class); + CLASS_IMPORTS.add(VertexProperty.Cardinality.class); + CLASS_IMPORTS.add(Column.class); + CLASS_IMPORTS.add(Direction.class); + CLASS_IMPORTS.add(Operator.class); + CLASS_IMPORTS.add(Order.class); + CLASS_IMPORTS.add(Pop.class); + CLASS_IMPORTS.add(Scope.class); + CLASS_IMPORTS.add(T.class); + CLASS_IMPORTS.add(TraversalOptionParent.class); + CLASS_IMPORTS.add(TraversalOptionParent.Pick.class); + CLASS_IMPORTS.add(P.class); + // remote + CLASS_IMPORTS.add(RemoteConnection.class); + CLASS_IMPORTS.add(RemoteGraph.class); + CLASS_IMPORTS.add(EmptyGraph.class); + // io + CLASS_IMPORTS.add(GraphReader.class); + CLASS_IMPORTS.add(GraphWriter.class); + CLASS_IMPORTS.add(Io.class); + CLASS_IMPORTS.add(IoCore.class); + CLASS_IMPORTS.add(Storage.class); + CLASS_IMPORTS.add(Configuration.class); + // strategies + CLASS_IMPORTS.add(ConnectiveStrategy.class); + CLASS_IMPORTS.add(ElementIdStrategy.class); + CLASS_IMPORTS.add(EventStrategy.class); + CLASS_IMPORTS.add(HaltedTraverserStrategy.class); + CLASS_IMPORTS.add(PartitionStrategy.class); + CLASS_IMPORTS.add(SubgraphStrategy.class); + CLASS_IMPORTS.add(LazyBarrierStrategy.class); + CLASS_IMPORTS.add(MatchAlgorithmStrategy.class); + CLASS_IMPORTS.add(ProfileStrategy.class); + CLASS_IMPORTS.add(AdjacentToIncidentStrategy.class); + CLASS_IMPORTS.add(FilterRankingStrategy.class); + CLASS_IMPORTS.add(IdentityRemovalStrategy.class); + CLASS_IMPORTS.add(IncidentToAdjacentStrategy.class); + CLASS_IMPORTS.add(MatchPredicateStrategy.class); + CLASS_IMPORTS.add(OrderLimitStrategy.class); + CLASS_IMPORTS.add(PathProcessorStrategy.class); + CLASS_IMPORTS.add(RangeByIsCountStrategy.class); + CLASS_IMPORTS.add(ComputerVerificationStrategy.class); + CLASS_IMPORTS.add(LambdaRestrictionStrategy.class); + CLASS_IMPORTS.add(ReadOnlyStrategy.class); + CLASS_IMPORTS.add(StandardVerificationStrategy.class); + // graph traversal + CLASS_IMPORTS.add(__.class); + CLASS_IMPORTS.add(GraphTraversal.class); + CLASS_IMPORTS.add(GraphTraversalSource.class); + CLASS_IMPORTS.add(TraversalMetrics.class); + CLASS_IMPORTS.add(Translator.class); + CLASS_IMPORTS.add(Bindings.class); + // graph computer + CLASS_IMPORTS.add(Computer.class); + CLASS_IMPORTS.add(ComputerResult.class); + CLASS_IMPORTS.add(GraphComputer.class); + CLASS_IMPORTS.add(Memory.class); + CLASS_IMPORTS.add(VertexProgram.class); + CLASS_IMPORTS.add(BulkDumperVertexProgram.class); + CLASS_IMPORTS.add(BulkLoaderVertexProgram.class); + CLASS_IMPORTS.add(PeerPressureVertexProgram.class); + CLASS_IMPORTS.add(PageRankVertexProgram.class); + CLASS_IMPORTS.add(GraphFilterStrategy.class); + CLASS_IMPORTS.add(VertexProgramStrategy.class); + // utils + CLASS_IMPORTS.add(Gremlin.class); + CLASS_IMPORTS.add(TimeUtil.class); + + ///////////// + // METHODS // + ///////////// + + uniqueMethods(IoCore.class).forEach(METHOD_IMPORTS::add); + uniqueMethods(P.class).forEach(METHOD_IMPORTS::add); + uniqueMethods(__.class).filter(m -> !m.getName().equals("__")).forEach(METHOD_IMPORTS::add); + uniqueMethods(Computer.class).forEach(METHOD_IMPORTS::add); + uniqueMethods(TimeUtil.class).forEach(METHOD_IMPORTS::add); + + /////////// + // ENUMS // + /////////// + + Collections.addAll(ENUM_IMPORTS, SackFunctions.Barrier.values()); + Collections.addAll(ENUM_IMPORTS, VertexProperty.Cardinality.values()); + Collections.addAll(ENUM_IMPORTS, Column.values()); + Collections.addAll(ENUM_IMPORTS, Direction.values()); + Collections.addAll(ENUM_IMPORTS, Operator.values()); + Collections.addAll(ENUM_IMPORTS, Order.values()); + Collections.addAll(ENUM_IMPORTS, Pop.values()); + Collections.addAll(ENUM_IMPORTS, Scope.values()); + Collections.addAll(ENUM_IMPORTS, T.values()); + Collections.addAll(ENUM_IMPORTS, TraversalOptionParent.Pick.values()); + } + + private CoreImports() { + // static methods only, do not instantiate class + } + + public static Set getClassImports() { + return Collections.unmodifiableSet(CLASS_IMPORTS); + } + + public static Set getMethodImports() { + return Collections.unmodifiableSet(METHOD_IMPORTS); + } + + public static Set getEnumImports() { + return Collections.unmodifiableSet(ENUM_IMPORTS); + } + + /** + * Filters to unique method names on each class. + */ + private static Stream uniqueMethods(final Class clazz) { + final Set unique = new HashSet<>(); + return Stream.of(clazz.getMethods()) + .filter(m -> Modifier.isStatic(m.getModifiers())) + .map(m -> Pair.with(generateMethodDescriptor(m), m)) + .filter(p -> { + final boolean exists = unique.contains(p.getValue0()); + if (!exists) unique.add(p.getValue0()); + return !exists; + }) + .map(Pair::getValue1); + } + + private static String generateMethodDescriptor(final Method m) { + return m.getDeclaringClass().getCanonicalName() + "." + m.getName(); + } +} 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 34ef9951c4b..86b72f2fa72 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,8 +18,6 @@ */ package org.apache.tinkerpop.gremlin.jsr223; -import org.apache.tinkerpop.gremlin.util.CoreImports; - import javax.script.Bindings; import javax.script.ScriptContext; import java.security.AccessController; 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 7b056ffcc9f..54c5d0c2556 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 @@ -18,8 +18,6 @@ */ package org.apache.tinkerpop.gremlin.jsr223; -import org.apache.tinkerpop.gremlin.util.CoreImports; - import java.lang.reflect.Method; import java.util.Set; diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/CoreImports.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/CoreImports.java index 1c6a6e6a39c..d5ec4184873 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/CoreImports.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/CoreImports.java @@ -96,6 +96,7 @@ /** * @author Marko A. Rodriguez (http://markorodriguez.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.jsr223.CoreImports}. */ public final class CoreImports { diff --git a/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/TraversalSourceGenerator.groovy b/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/TraversalSourceGenerator.groovy index b6f25e36021..73ffcb68598 100644 --- a/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/TraversalSourceGenerator.groovy +++ b/gremlin-python/src/main/groovy/org/apache/tinkerpop/gremlin/python/TraversalSourceGenerator.groovy @@ -19,9 +19,9 @@ package org.apache.tinkerpop.gremlin.python +import org.apache.tinkerpop.gremlin.jsr223.CoreImports import org.apache.tinkerpop.gremlin.process.traversal.P import org.apache.tinkerpop.gremlin.python.jsr223.SymbolHelper -import org.apache.tinkerpop.gremlin.util.CoreImports import java.lang.reflect.Modifier diff --git a/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngine.java b/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngine.java index 1a4b57a9cde..f6ada6e39a5 100644 --- a/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngine.java +++ b/gremlin-python/src/main/java/org/apache/tinkerpop/gremlin/python/jsr223/GremlinJythonScriptEngine.java @@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.python.jsr223; import org.apache.tinkerpop.gremlin.jsr223.CoreGremlinPlugin; +import org.apache.tinkerpop.gremlin.jsr223.CoreImports; import org.apache.tinkerpop.gremlin.jsr223.Customizer; import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine; import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngineFactory; @@ -28,7 +29,6 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; -import org.apache.tinkerpop.gremlin.util.CoreImports; import org.python.jsr223.PyScriptEngine; import org.python.jsr223.PyScriptEngineFactory; From ae45eca70de741a1a1872117e9f4f7dc3ddd6245 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Thu, 1 Dec 2016 06:48:22 -0500 Subject: [PATCH 41/45] TINKERPOP-1562 Moved ScriptEngineCache to jsr223 package. --- .../gremlin/jsr223/ScriptEngineCache.java | 54 +++++++++++++++++++ .../gremlin/util/ScriptEngineCache.java | 4 +- .../gremlin/jsr223/ScriptEngineCacheTest.java | 46 ++++++++++++++++ .../jsr223/JythonScriptEngineSetup.java | 2 +- .../python/jsr223/JythonTranslatorTest.java | 10 ---- .../jsr223/PythonGraphSONJavaTranslator.java | 2 +- .../gremlin/python/jsr223/PythonProvider.java | 5 +- 7 files changed, 106 insertions(+), 17 deletions(-) create mode 100644 gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptEngineCache.java create mode 100644 gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ScriptEngineCacheTest.java diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptEngineCache.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptEngineCache.java new file mode 100644 index 00000000000..9d2848dd732 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/ScriptEngineCache.java @@ -0,0 +1,54 @@ +/* + * 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 javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * A cache of standard {@code ScriptEngine} instances, instantiated by the standard {@code ScriptEngineManager}. + * These instances are NOT "Gremlin-enabled". See {@link SingleGremlinScriptEngineManager} for the analogous class + * that loads {@link GremlinScriptEngine} instances. + * + * @author Daniel Kuppitz (http://gremlin.guru) + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public final class ScriptEngineCache { + + private ScriptEngineCache() {} + + public final static String DEFAULT_SCRIPT_ENGINE = "gremlin-groovy"; + + private final static ScriptEngineManager SCRIPT_ENGINE_MANAGER = new ScriptEngineManager(); + private final static Map CACHED_ENGINES = new ConcurrentHashMap<>(); + + public static ScriptEngine get(final String engineName) { + return CACHED_ENGINES.compute(engineName, (key, engine) -> { + if (null == engine) { + engine = SCRIPT_ENGINE_MANAGER.getEngineByName(engineName); + if (null == engine) { + throw new IllegalArgumentException("There is no script engine with provided name: " + engineName); + } + } + return engine; + }); + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/ScriptEngineCache.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/ScriptEngineCache.java index 1ce7d05f514..dc4aeb72c63 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/ScriptEngineCache.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/ScriptEngineCache.java @@ -28,12 +28,14 @@ /** * A cache of standard {@code ScriptEngine} instances, instantiated by the standard {@code ScriptEngineManager}. - * These instances are not "Gremlin-enabled". See {@link SingleGremlinScriptEngineManager} for the analogous class + * These instances are NOT "Gremlin-enabled". See {@link SingleGremlinScriptEngineManager} for the analogous class * that loads {@link GremlinScriptEngine} instances. * * @author Daniel Kuppitz (http://gremlin.guru) * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, replaced by {@link org.apache.tinkerpop.gremlin.jsr223.ScriptEngineCache}. */ +@Deprecated public final class ScriptEngineCache { private ScriptEngineCache() {} diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ScriptEngineCacheTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ScriptEngineCacheTest.java new file mode 100644 index 00000000000..7cdbdc2daaa --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/jsr223/ScriptEngineCacheTest.java @@ -0,0 +1,46 @@ +/* + * 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.TestHelper; +import org.junit.Test; + +import static org.junit.Assert.assertSame; + +/** + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public class ScriptEngineCacheTest { + + @Test + public void shouldBeUtilityClass() throws Exception { + TestHelper.assertIsUtilityClass(ScriptEngineCache.class); + } + + @Test + public void shouldGetEngineFromCache() { + assertSame(ScriptEngineCache.get("nashorn"), ScriptEngineCache.get("nashorn")); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldThrowWhenScripEngineDoesNotExist() { + ScriptEngineCache.get("junk-that-no-one-would-ever-call-a-script-engine-83939473298432"); + } + +} diff --git a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/JythonScriptEngineSetup.java b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/JythonScriptEngineSetup.java index a16ce30914d..a4fe1ed35a7 100644 --- a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/JythonScriptEngineSetup.java +++ b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/JythonScriptEngineSetup.java @@ -19,7 +19,7 @@ package org.apache.tinkerpop.gremlin.python.jsr223; -import org.apache.tinkerpop.gremlin.util.ScriptEngineCache; +import org.apache.tinkerpop.gremlin.jsr223.ScriptEngineCache; import org.python.jsr223.PyScriptEngine; import javax.script.ScriptException; diff --git a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/JythonTranslatorTest.java b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/JythonTranslatorTest.java index 8bd32655bf5..5a6e30d0ea1 100644 --- a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/JythonTranslatorTest.java +++ b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/JythonTranslatorTest.java @@ -19,26 +19,16 @@ package org.apache.tinkerpop.gremlin.python.jsr223; -import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine; -import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.Traverser; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; -import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; -import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.TranslationStrategy; -import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory; -import org.apache.tinkerpop.gremlin.util.ScriptEngineCache; import org.apache.tinkerpop.gremlin.util.function.Lambda; import org.junit.Test; -import javax.script.Bindings; -import javax.script.SimpleBindings; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.List; import static org.junit.Assert.assertEquals; diff --git a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonGraphSONJavaTranslator.java b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonGraphSONJavaTranslator.java index cdeb4075d26..740fe1f1fe1 100644 --- a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonGraphSONJavaTranslator.java +++ b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonGraphSONJavaTranslator.java @@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.python.jsr223; import org.apache.tinkerpop.gremlin.jsr223.JavaTranslator; +import org.apache.tinkerpop.gremlin.jsr223.ScriptEngineCache; import org.apache.tinkerpop.gremlin.process.traversal.Bytecode; import org.apache.tinkerpop.gremlin.process.traversal.Translator; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; @@ -28,7 +29,6 @@ import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONReader; import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONVersion; import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONXModuleV2d0; -import org.apache.tinkerpop.gremlin.util.ScriptEngineCache; import javax.script.Bindings; import javax.script.ScriptContext; diff --git a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonProvider.java b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonProvider.java index fe04156a638..9e038844ae4 100644 --- a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonProvider.java +++ b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonProvider.java @@ -23,12 +23,10 @@ import org.apache.tinkerpop.gremlin.AbstractGraphProvider; import org.apache.tinkerpop.gremlin.LoadGraphWith; import org.apache.tinkerpop.gremlin.jsr223.JavaTranslator; +import org.apache.tinkerpop.gremlin.jsr223.ScriptEngineCache; import org.apache.tinkerpop.gremlin.process.traversal.TraversalInterruptionComputerTest; import org.apache.tinkerpop.gremlin.process.traversal.TraversalInterruptionTest; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; -import org.apache.tinkerpop.gremlin.process.traversal.step.map.PageRankTest; -import org.apache.tinkerpop.gremlin.process.traversal.step.map.PeerPressureTest; -import org.apache.tinkerpop.gremlin.process.traversal.step.map.ProfileTest; import org.apache.tinkerpop.gremlin.process.traversal.step.map.ProgramTest; import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ElementIdStrategyProcessTest; import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategyProcessTest; @@ -42,7 +40,6 @@ import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerProperty; import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerVertex; import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerVertexProperty; -import org.apache.tinkerpop.gremlin.util.ScriptEngineCache; import javax.script.ScriptException; import java.util.Arrays; From d68f69383464f8f54b98acecf99a39587f2e4839 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Thu, 1 Dec 2016 08:25:19 -0500 Subject: [PATCH 42/45] TINKERPOP-1562 Fixed the InstallCommand for new GremlinPlugin usage. DependencyGrabber wasn't using the right Artifact implementation and wasn't taking into account -Dplugins=v3d3 --- .../gremlin/console/commands/InstallCommand.groovy | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/commands/InstallCommand.groovy b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/commands/InstallCommand.groovy index a680757dace..c8a49df035d 100644 --- a/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/commands/InstallCommand.groovy +++ b/gremlin-console/src/main/groovy/org/apache/tinkerpop/gremlin/console/commands/InstallCommand.groovy @@ -21,9 +21,9 @@ package org.apache.tinkerpop.gremlin.console.commands import org.apache.tinkerpop.gremlin.console.ConsoleFs import org.apache.tinkerpop.gremlin.console.Mediator import org.apache.tinkerpop.gremlin.console.plugin.PluggedIn -import org.apache.tinkerpop.gremlin.groovy.plugin.Artifact import org.apache.tinkerpop.gremlin.groovy.plugin.GremlinPlugin import groovy.grape.Grape +import org.apache.tinkerpop.gremlin.groovy.util.Artifact import org.apache.tinkerpop.gremlin.groovy.util.DependencyGrabber import org.codehaus.groovy.tools.shell.CommandSupport import org.codehaus.groovy.tools.shell.Groovysh @@ -64,9 +64,17 @@ class InstallCommand extends CommandSupport { // note that the service loader utilized the classloader from the groovy shell as shell class are available // from within there given loading through Grape. - ServiceLoader.load(GremlinPlugin.class, shell.getInterp().getClassLoader()).forEach { plugin -> + def pluginClass = mediator.useV3d3 ? org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin : GremlinPlugin + ServiceLoader.load(pluginClass, shell.getInterp().getClassLoader()).forEach { plugin -> if (!mediator.availablePlugins.containsKey(plugin.class.name)) { - mediator.availablePlugins.put(plugin.class.name, new PluggedIn(plugin, shell, io, false)) + + if (Mediator.useV3d3) { + mediator.availablePlugins.put(plugin.class.name, new PluggedIn(new PluggedIn.GremlinPluginAdapter((org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin) plugin, shell, io), shell, io, false)) + } else { + mediator.availablePlugins.put(plugin.class.name, new PluggedIn((GremlinPlugin) plugin, shell, io, false)) + } + + //mediator.availablePlugins.put(plugin.class.name, new PluggedIn(plugin, shell, io, false)) if (plugin.requireRestart()) pluginsThatNeedRestart << plugin.name } From 338002ab79b7f651885ffd950f64f5d34655f0ef Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Thu, 1 Dec 2016 10:15:49 -0500 Subject: [PATCH 43/45] TINKERPOP-1562 Deprecated GremlinGroovyScriptEngine.close() In the future GremlinGroovyScriptEngine will not implement AutoCloseable - there was never really a need for that. --- .../gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java | 5 +++++ 1 file changed, 5 insertions(+) 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 0f36dbfb54e..1fb2efcf5e0 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 @@ -384,7 +384,12 @@ public Traversal.Admin eval(final Bytecode bytecode, final Bindings bindings) th return (Traversal.Admin) this.eval(GroovyTranslator.of(traversalSource).translate(bytecode), bindings); } + /** + * @deprecated As of release 3.2.4, not replaced as this class will not implement {@code AutoCloseable} in the + * future. + */ @Override + @Deprecated public void close() throws Exception { } From aa4a70a47c9c7b400983fb2fd55730b686cceea9 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Thu, 1 Dec 2016 10:19:40 -0500 Subject: [PATCH 44/45] TINKERPOP-1562 Deprecated the DependencyManager. This interface really isn't useful anymore. It is bound to ScriptEngines class which is also deprecated. The new model of the GremlinScriptEngine in gremlin-core sorta makes this obsolete. --- .../tinkerpop/gremlin/groovy/jsr223/DependencyManager.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/DependencyManager.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/DependencyManager.java index 5c1b8feba4f..4f1384d9232 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/DependencyManager.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/DependencyManager.java @@ -22,6 +22,7 @@ import org.apache.tinkerpop.gremlin.groovy.plugin.GremlinPlugin; import org.apache.tinkerpop.gremlin.groovy.plugin.GremlinPluginException; import org.apache.tinkerpop.gremlin.groovy.plugin.PluginAcceptor; +import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine; import java.util.List; import java.util.Map; @@ -33,7 +34,9 @@ * so this interface makes that possible to expose. * * @author Stephen Mallette (http://stephen.genoprime.com) + * @deprecated As of release 3.2.4, not replaced - no longer needed under the new {@link GremlinScriptEngine} model. */ +@Deprecated public interface DependencyManager { /** * Take maven coordinates and load the classes into the classloader used by the ScriptEngine. Those ScriptEngines From 9f84ac43bb71aff47fe4cfbbd9be277361191359 Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Wed, 7 Dec 2016 11:12:49 -0500 Subject: [PATCH 45/45] TINKERPOP-1562 Add more imports on Hadoop plugin. --- .../gremlin/hadoop/jsr223/HadoopGremlinPlugin.java | 6 ++++++ 1 file changed, 6 insertions(+) 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 b7403b6e253..0c9c2874af9 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 @@ -45,6 +45,9 @@ import org.apache.tinkerpop.gremlin.hadoop.structure.io.gryo.GryoRecordReader; import org.apache.tinkerpop.gremlin.hadoop.structure.io.gryo.GryoRecordWriter; import org.apache.tinkerpop.gremlin.hadoop.structure.io.script.ScriptInputFormat; +import org.apache.tinkerpop.gremlin.hadoop.structure.io.script.ScriptOutputFormat; +import org.apache.tinkerpop.gremlin.hadoop.structure.io.script.ScriptRecordReader; +import org.apache.tinkerpop.gremlin.hadoop.structure.io.script.ScriptRecordWriter; import org.apache.tinkerpop.gremlin.hadoop.structure.util.ConfUtil; import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; import org.apache.tinkerpop.gremlin.jsr223.BindingsCustomizer; @@ -108,6 +111,9 @@ public final class HadoopGremlinPlugin extends AbstractGremlinPlugin { GryoRecordReader.class, GryoRecordWriter.class, ScriptInputFormat.class, + ScriptOutputFormat.class, + ScriptRecordReader.class, + ScriptRecordWriter.class, MapReduceGraphComputer.class).create(); bindings = new LazyBindingsCustomizer(() -> {