GremlinGroovyScriptEngine extends GroovyScriptEngineImpl implements GremlinScriptEngine { @Override public Traversal.Admin eval(final Bytecode bytecode, final Bindings bindings, final String traversalSource) throws ScriptException { if (traversalSource.equals(HIDDEN_G)) throw new IllegalArgumentException("The traversalSource cannot have the name " + HIDDEN_G + " - it is reserved"); if (bindings.containsKey(HIDDEN_G)) throw new IllegalArgumentException("Bindings cannot include " + HIDDEN_G + " - it is reserved"); if (!bindings.containsKey(traversalSource)) throw new IllegalArgumentException("The bindings available to the ScriptEngine do not contain a traversalSource named: " + traversalSource); final Object b = bindings.get(traversalSource); if (!(b instanceof TraversalSource)) throw new IllegalArgumentException(traversalSource + " is of type " + b.getClass().getSimpleName() + " and is not an instance of TraversalSource"); final Bindings inner = new SimpleBindings(); inner.putAll(bindings); inner.putAll(bytecode.getBindings()); inner.put(HIDDEN_G, b); org.apache.tinkerpop.gremlin.process.traversal.Script script = GroovyTranslator.of(HIDDEN_G, typeTranslator).translate(bytecode); script.getParameters().ifPresent(inner::putAll); return (Traversal.Admin) this.eval(script.getScript(), inner); } GremlinGroovyScriptEngine();  GremlinGroovyScriptEngine(final Customizer... customizers); Set getPlugins(); @Override Traversal.Admin eval(final Bytecode bytecode, final Bindings bindings, final String traversalSource); void reset(); @Override Object eval(final Reader reader, final ScriptContext context); @Override Object eval(final String script, final ScriptContext context); @Override Bindings createBindings(); @Override GremlinScriptEngineFactory getFactory(); @Override CompiledScript compile(final String scriptSource); @Override CompiledScript compile(final Reader reader); @Override Object invokeFunction(final String name, final Object... args); @Override Object invokeMethod(final Object thiz, final String name, final Object... args); @Override T getInterface(final Class<T> clazz); @Override T getInterface(final Object thiz, final Class<T> clazz); long getClassCacheLongRunCompilationCount(); long getClassCacheEstimatedSize(); double getClassCacheAverageLoadPenalty(); long getClassCacheEvictionCount(); long getClassCacheEvictionWeight(); long getClassCacheHitCount(); double getClassCacheHitRate(); long getClassCacheLoadCount(); long getClassCacheLoadFailureCount(); double getClassCacheLoadFailureRate(); long getClassCacheLoadSuccessCount(); long getClassCacheMissCount(); double getClassCacheMissRate(); long getClassCacheRequestCount(); long getClassCacheTotalLoadTime(); static final String COMPILE_OPTIONS_VAR_TYPES; static final String KEY_REFERENCE_TYPE; static final String REFERENCE_TYPE_PHANTOM; static final String REFERENCE_TYPE_WEAK; static final String REFERENCE_TYPE_SOFT; static final String REFERENCE_TYPE_HARD; static final String COLLECTED_BOUND_VARS_MAP_VARNAME; static final ThreadLocal<Map<String, Object>> COMPILE_OPTIONS; }
@Test public void shouldPromoteDefinedVarsInInterpreterModeWithBindings() throws Exception { 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); 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(); final Bindings b = new SimpleBindings(); b.put("x", 2); assertEquals(3, engine.eval("1+x", b)); }
@Test public void shouldEvalWithNullInBindings() throws Exception { final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(); final Bindings b = new SimpleBindings(); b.put("x", null); assertNull(engine.eval("x", b)); }
@Test public void shouldEvalSuccessfulAssert() throws Exception { final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(); assertNull(engine.eval("assert 1==1")); }
@Test(expected = AssertionError.class) public void shouldEvalFailingAssert() throws Exception { final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(); engine.eval("assert 1==0"); }
@Test public void shouldAllowVariableReuseAcrossThreads() throws Exception { final BasicThreadFactory testingThreadFactory = new BasicThreadFactory.Builder().namingPattern("test-gremlin-scriptengine-%d").build(); final ExecutorService service = Executors.newFixedThreadPool(8, testingThreadFactory); final GremlinGroovyScriptEngine scriptEngine = new GremlinGroovyScriptEngine(); final AtomicBoolean failed = new AtomicBoolean(false); final int max = 512; final List<Pair<Integer, List<Integer>>> futures = Collections.synchronizedList(new ArrayList<>(max)); IntStream.range(0, max).forEach(i -> { final int yValue = i * 2; final int zValue = i * -1; final Bindings b = new SimpleBindings(); b.put("x", i); b.put("y", yValue); final String script = "z=" + zValue + ";[x,y,z]"; try { service.submit(() -> { try { final List<Integer> result = (List<Integer>) scriptEngine.eval(script, b); futures.add(Pair.with(i, result)); } catch (Exception ex) { failed.set(true); } }); } catch (Exception ex) { throw new RuntimeException(ex); } }); service.shutdown(); assertThat(service.awaitTermination(120000, TimeUnit.MILLISECONDS), is(true)); assertThat(failed.get(), is(false)); assertEquals(max, futures.size()); futures.forEach(t -> { assertEquals(t.getValue0(), t.getValue1().get(0)); assertEquals(t.getValue0() * 2, t.getValue1().get(1).intValue()); assertEquals(t.getValue0() * -1, t.getValue1().get(2).intValue()); }); }
@Test public void shouldEnableEngineContextAccessibleToScript() throws Exception { final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(); final ScriptContext engineContext = engine.getContext(); engine.put("theEngineContext", engineContext); final String code = "[answer: theEngineContext.is(context)]"; assertThat(((Map) engine.eval(code)).get("answer"), is(true)); }
@Test public void shouldEnableContextBindingOverridesEngineContext() throws Exception { final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(); final ScriptContext engineContext = engine.getContext(); final Map<String,Object> otherContext = new HashMap<>(); otherContext.put("foo", "bar"); engine.put("context", otherContext); engine.put("theEngineContext", engineContext); final String code = "[answer: context.is(theEngineContext) ? \"wrong\" : context.foo]"; assertEquals("bar", ((Map) engine.eval(code)).get("answer")); }
@Test public void shouldEvalForLambda() throws Exception { final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(); final Lambda l = (Lambda) engine.eval(" org.apache.tinkerpop.gremlin.util.function.Lambda.function(\"{ it.get() }\")"); assertEquals("{ it.get() }", l.getLambdaScript()); }
@Test public void customizerShouldOverrideCoreImports() throws Exception { DefaultImportCustomizer customizer = DefaultImportCustomizer.build() .addClassImports(org.apache.tinkerpop.gremlin.groovy.jsr223.dsl.credential.__.class) .create(); final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(customizer); engine.eval("__.users();[]"); }
@Test public void shouldNotCacheGlobalFunctions() throws Exception { final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(CompilationOptionsCustomizer.build(). enableGlobalFunctionCache(false).create()); assertEquals(3, engine.eval("def addItUp(x,y){x+y};addItUp(1,2)")); try { engine.eval("addItUp(1,2)"); fail("Global functions should not be cached so the call to addItUp() should fail"); } catch (Exception ex) { final Throwable root = ExceptionUtils.getRootCause(ex); assertThat(root, instanceOf(MissingMethodException.class)); } }
@Test public void shouldEvalWithNoBindings() throws Exception { final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(); engine.eval("def addItUp(x,y){x+y}"); assertEquals(3, engine.eval("1+2")); assertEquals(3, engine.eval("addItUp(1,2)")); }
@Test public void shouldPromoteDefinedVarsInInterpreterModeWithNoBindings() throws Exception { final GremlinGroovyScriptEngine engine = new GremlinGroovyScriptEngine(new InterpreterModeGroovyCustomizer()); engine.eval("def addItUp = { x, y -> x + y }"); engine.eval("class A { def sub(int x, int 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(9, engine.eval("new A().sub(10, 1)")); assertEquals(10, engine.eval("addItUp(zzz,xxx)")); }