You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2015/10/09 16:14:10 UTC

[2/5] incubator-tinkerpop git commit: Added an overload to GremlinExecutor.eval() to provide ways to override some default settings.

Added an overload to GremlinExecutor.eval() to provide ways to override some default settings.


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

Branch: refs/heads/master
Commit: 4773e872fbc3fb99c357547365b48a52aebad7e5
Parents: 37732ce
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Fri Oct 9 09:59:10 2015 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Fri Oct 9 09:59:10 2015 -0400

----------------------------------------------------------------------
 .../gremlin/groovy/engine/GremlinExecutor.java  | 150 ++++++++++++++++++-
 .../groovy/engine/GremlinExecutorTest.java      |  52 ++++++-
 2 files changed, 192 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4773e872/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/GremlinExecutor.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/GremlinExecutor.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/GremlinExecutor.java
index 3d2048b..0a8ae9e 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
@@ -227,6 +227,22 @@ public class GremlinExecutor implements AutoCloseable {
      */
     public CompletableFuture<Object> eval(final String script, final String language, final Bindings boundVars,
                                           final Function<Object, Object> transformResult, final Consumer<Object> withResult) {
+        final LifeCycle lifeCycle = LifeCycle.build()
+                .transformResult(transformResult)
+                .withResult(withResult).create();
+
+        return eval(script, language, boundVars, lifeCycle);
+    }
+
+    /**
+     * Evaluate a script and allow for the submission of alteration to the entire evaluation execution lifecycle.
+     *
+     * @param script the script to evaluate
+     * @param language the language to evaluate it in
+     * @param boundVars the bindings to evaluate in the context of the script
+     * @param lifeCycle a set of functions that can be applied at various stages of the evaluation process
+     */
+    public CompletableFuture<Object> eval(final String script, final String language, final Bindings boundVars,  final LifeCycle lifeCycle) {
         final String lang = Optional.ofNullable(language).orElse("gremlin-groovy");
 
         logger.debug("Preparing to evaluate script - {} - in thread [{}]", script, Thread.currentThread().getName());
@@ -234,7 +250,7 @@ public class GremlinExecutor implements AutoCloseable {
         final Bindings bindings = new SimpleBindings();
         bindings.putAll(globalBindings);
         bindings.putAll(boundVars);
-        beforeEval.accept(bindings);
+        lifeCycle.getBeforeEval().orElse(beforeEval).accept(bindings);
 
         final CompletableFuture<Object> evaluationFuture = new CompletableFuture<>();
         final FutureTask<Void> f = new FutureTask<>(() -> {
@@ -246,15 +262,16 @@ public class GremlinExecutor implements AutoCloseable {
                 // 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
                 // transactional constraints
-                final Object result = null == transformResult ? o : transformResult.apply(o);
+                final Object result = lifeCycle.getTransformResult().isPresent() ?
+                        lifeCycle.getTransformResult().get().apply(o) : o;
                 evaluationFuture.complete(result);
 
                 // a mechanism for taking the final result and doing something with it in the same thread, but
                 // AFTER the eval and transform are done and that future completed.  this provides a final means
                 // for working with the result in the same thread as it was eval'd
-                if (withResult != null) withResult.accept(result);
+                if (lifeCycle.getWithResult().isPresent()) lifeCycle.getWithResult().get().accept(result);
 
-                afterSuccess.accept(bindings);
+                lifeCycle.getAfterSuccess().orElse(afterSuccess).accept(bindings);
             } catch (Throwable ex) {
                 final Throwable root = null == ex.getCause() ? ex : ExceptionUtils.getRootCause(ex);
 
@@ -264,7 +281,7 @@ public class GremlinExecutor implements AutoCloseable {
                     evaluationFuture.completeExceptionally(new TimeoutException(
                             String.format("Script evaluation exceeded the configured threshold of %s ms for request [%s]: %s", scriptEvaluationTimeout, script, root.getMessage())));
                 else {
-                    afterFailure.accept(bindings, root);
+                    lifeCycle.getAfterFailure().orElse(afterFailure).accept(bindings, root);
                     evaluationFuture.completeExceptionally(root);
                 }
             }
@@ -279,7 +296,7 @@ public class GremlinExecutor implements AutoCloseable {
             final ScheduledFuture<?> sf = scheduledExecutorService.schedule(() -> {
                 logger.warn("Timing out script - {} - in thread [{}]", script, Thread.currentThread().getName());
                 if (!f.isDone()) {
-                    afterTimeout.accept(bindings);
+                    lifeCycle.getAfterTimeout().orElse(afterTimeout).accept(bindings);
                     f.cancel(true);
                 }
             }, scriptEvaluationTimeout, TimeUnit.MILLISECONDS);
@@ -670,4 +687,125 @@ public class GremlinExecutor implements AutoCloseable {
             return config;
         }
     }
+
+    /**
+     * The lifecycle of execution within the {@link #eval(String, String, Bindings, LifeCycle)} method. Since scripts
+     * are executed in a thread pool and graph transactions are bound to a thread all actions related to that script
+     * evaluation, both before and after that evaluation, need to be executed in the same thread.  This leads to a
+     * lifecycle of actions that can occur within that evaluation.  Note that some of these options can be globally
+     * set on the {@code GremlinExecutor} itself through the {@link GremlinExecutor.Builder}.  If specified here,
+     * they will override those global settings.
+     */
+    public static class LifeCycle {
+        private final Optional<Consumer<Bindings>> beforeEval;
+        private final Optional<Function<Object, Object>> transformResult;
+        private final Optional<Consumer<Object>> withResult;
+        private final Optional<Consumer<Bindings>> afterSuccess;
+        private final Optional<Consumer<Bindings>> afterTimeout;
+        private final Optional<BiConsumer<Bindings, Throwable>> afterFailure;
+
+        private LifeCycle(final Builder builder) {
+            beforeEval = Optional.ofNullable(builder.beforeEval);
+            transformResult = Optional.ofNullable(builder.transformResult);
+            withResult = Optional.ofNullable(builder.withResult);
+            afterSuccess = Optional.ofNullable(builder.afterSuccess);
+            afterTimeout = Optional.ofNullable(builder.afterTimeout);
+            afterFailure = Optional.ofNullable(builder.afterFailure);
+        }
+
+        public Optional<Consumer<Bindings>> getBeforeEval() {
+            return beforeEval;
+        }
+
+        public Optional<Function<Object, Object>> getTransformResult() {
+            return transformResult;
+        }
+
+        public Optional<Consumer<Object>> getWithResult() {
+            return withResult;
+        }
+
+        public Optional<Consumer<Bindings>> getAfterSuccess() {
+            return afterSuccess;
+        }
+
+        public Optional<Consumer<Bindings>> getAfterTimeout() {
+            return afterTimeout;
+        }
+
+        public Optional<BiConsumer<Bindings, Throwable>> getAfterFailure() {
+            return afterFailure;
+        }
+
+        public static Builder build() {
+            return new Builder();
+        }
+
+        public static class Builder {
+            private Consumer<Bindings> beforeEval = null;
+            private Function<Object, Object> transformResult = null;
+            private Consumer<Object> withResult = null;
+            private Consumer<Bindings> afterSuccess = null;
+            private Consumer<Bindings> afterTimeout = null;
+            private BiConsumer<Bindings, Throwable> afterFailure = null;
+
+            /**
+             * Specifies the function to execute prior to the script being evaluated.  This function can also be
+             * specified globally on {@link GremlinExecutor.Builder#beforeEval(Consumer)}.
+             */
+            public Builder beforeEval(final Consumer<Bindings> beforeEval) {
+                this.beforeEval = beforeEval;
+                return this;
+            }
+
+            /**
+             * Specifies the function to execute on the result of the script evaluation just after script evaluation
+             * returns but before the script evaluation is marked as complete.
+             */
+            public Builder transformResult(final Function<Object, Object> transformResult) {
+                this.transformResult = transformResult;
+                return this;
+            }
+
+            /**
+             * Specifies the function to execute on the result of the script evaluation just after script evaluation
+             * returns but after the script evaluation is marked as complete.
+             */
+            public Builder withResult(final Consumer<Object> withResult) {
+                this.withResult = withResult;
+                return this;
+            }
+
+            /**
+             * Specifies the function to execute after result transformations.  This function can also be
+             * specified globally on {@link GremlinExecutor.Builder#afterSuccess(Consumer)}.
+             */
+            public Builder afterSuccess(final Consumer<Bindings> afterSuccess) {
+                this.afterSuccess = afterSuccess;
+                return this;
+            }
+
+            /**
+             * Specifies the function to execute if the script evaluation times out.  This function can also be
+             * specified globally on {@link GremlinExecutor.Builder#afterTimeout(Consumer)}.
+             */
+            public Builder afterTimeout(final Consumer<Bindings> afterTimeout) {
+                this.afterTimeout = afterTimeout;
+                return this;
+            }
+
+            /**
+             * Specifies the function to execute if the script evaluation fails.  This function can also be
+             * specified globally on {@link GremlinExecutor.Builder#afterFailure(BiConsumer)}.
+             */
+            public Builder afterFailure(final BiConsumer<Bindings, Throwable> afterFailure) {
+                this.afterFailure = afterFailure;
+                return this;
+            }
+
+            public LifeCycle create() {
+                return new LifeCycle(this);
+            }
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/4773e872/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/engine/GremlinExecutorTest.java
----------------------------------------------------------------------
diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/engine/GremlinExecutorTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/engine/GremlinExecutorTest.java
index 1268a3e..a8a8984 100644
--- a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/engine/GremlinExecutorTest.java
+++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/engine/GremlinExecutorTest.java
@@ -247,6 +247,50 @@ public class GremlinExecutorTest {
     }
 
     @Test
+    public void shouldOverrideBeforeEval() throws Exception {
+        final AtomicInteger called = new AtomicInteger(0);
+        final GremlinExecutor gremlinExecutor = GremlinExecutor.build().beforeEval(b -> called.set(1)).create();
+        assertEquals(2, gremlinExecutor.eval("1+1", null, new SimpleBindings(),
+                GremlinExecutor.LifeCycle.build().beforeEval(b -> called.set(200)).create()).get());
+
+        // need to wait long enough for the callback to register
+        Thread.sleep(500);
+
+        assertEquals(200, called.get());
+    }
+
+    @Test
+    public void shouldOverrideAfterSuccess() throws Exception {
+        final AtomicInteger called = new AtomicInteger(0);
+        final GremlinExecutor gremlinExecutor = GremlinExecutor.build().afterSuccess(b -> called.set(1)).create();
+        assertEquals(2, gremlinExecutor.eval("1+1", null, new SimpleBindings(),
+                GremlinExecutor.LifeCycle.build().afterSuccess(b -> called.set(200)).create()).get());
+
+        // need to wait long enough for the callback to register
+        Thread.sleep(500);
+
+        assertEquals(200, called.get());
+    }
+
+    @Test
+    public void shouldOverrideAfterFailure() throws Exception {
+        final AtomicInteger called = new AtomicInteger(0);
+        final GremlinExecutor gremlinExecutor = GremlinExecutor.build().afterFailure((b,t) -> called.set(1)).create();
+        try {
+            gremlinExecutor.eval("10/0", null, new SimpleBindings(),
+                    GremlinExecutor.LifeCycle.build().afterFailure((b,t) -> called.set(200)).create()).get();
+            fail("Should have failed with division by zero");
+        } catch (Exception ignored) {
+
+        }
+
+        // need to wait long enough for the callback to register
+        Thread.sleep(500);
+
+        assertEquals(200, called.get());
+    }
+
+    @Test
     public void shouldCallFail() throws Exception {
         final AtomicBoolean timeoutCalled = new AtomicBoolean(false);
         final AtomicBoolean successCalled = new AtomicBoolean(false);
@@ -260,8 +304,8 @@ public class GremlinExecutorTest {
             fail();
         } catch (Exception ignored) { }
 
-        // need to wait long enough for the script to complete
-        Thread.sleep(750);
+        // need to wait long enough for the callback to register
+        Thread.sleep(500);
 
         assertFalse(timeoutCalled.get());
         assertFalse(successCalled.get());
@@ -280,8 +324,8 @@ public class GremlinExecutorTest {
                 .afterTimeout((b) -> timeoutCalled.set(true)).create();
         assertEquals(2, gremlinExecutor.eval("1+1").get());
 
-        // need to wait long enough for the script to complete
-        Thread.sleep(750);
+        // need to wait long enough for the callback to register
+        Thread.sleep(500);
 
         assertFalse(timeoutCalled.get());
         assertTrue(successCalled.get());