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 2020/12/11 20:41:58 UTC

[tinkerpop] branch TINKERPOP-2438 created (now f5cb7ec)

This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a change to branch TINKERPOP-2438
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git.


      at f5cb7ec  TINKERPOP-2438 Introduce GremlinScriptChecker

This branch includes the following new commits:

     new f5cb7ec  TINKERPOP-2438 Introduce GremlinScriptChecker

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[tinkerpop] 01/01: TINKERPOP-2438 Introduce GremlinScriptChecker

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch TINKERPOP-2438
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit f5cb7ec208f1355046b2c56f21dcc9ebfc309c01
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Fri Dec 11 15:38:33 2020 -0500

    TINKERPOP-2438 Introduce GremlinScriptChecker
    
    Even though TINKERPOP-2438 is already done this is a bit of a follow on to that change. The GremlinScriptChecker performs the same exact task as the GremlinAstChecker but it does so with regex parsing which should be faster than processing a full AST. I've left the GremlinASTChecker where it is because I wonder if there isn't yet some usage for more rigorous sort of checks we might come up with in the future or perhaps have checks that only work well with AST processing. It can be rem [...]
---
 CHANGELOG.asciidoc                                 |   2 +-
 .../gremlin/groovy/engine/GremlinExecutor.java     |   4 +-
 .../groovy/jsr223/GremlinScriptChecker.java        |  81 ++++++
 .../groovy/jsr223/GremlinScriptCheckerTest.java    | 300 +++++++++++++++++++++
 4 files changed, 384 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 3443432..fab59d3 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -23,7 +23,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 [[release-3-4-10]]
 === TinkerPop 3.4.10 (Release Date: NOT OFFICIALLY RELEASED YET)
 
-
+* Added `GremlinScriptChecker` to provide a way to extract properties of scripts before doing an actual `eval()`.
 
 [[release-3-4-9]]
 === TinkerPop 3.4.9 (Release Date: December 7, 2020)
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 ad11748..fe68ef9 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
@@ -21,7 +21,7 @@ package org.apache.tinkerpop.gremlin.groovy.engine;
 import org.apache.commons.lang3.ClassUtils;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.commons.lang3.concurrent.BasicThreadFactory;
-import org.apache.tinkerpop.gremlin.groovy.jsr223.ast.GremlinASTChecker;
+import org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinScriptChecker;
 import org.apache.tinkerpop.gremlin.jsr223.CachedGremlinScriptEngineManager;
 import org.apache.tinkerpop.gremlin.jsr223.ConcurrentBindings;
 import org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin;
@@ -258,7 +258,7 @@ public class GremlinExecutor implements AutoCloseable {
 
         // override the timeout if the lifecycle has a value assigned. if the script contains with(timeout)
         // options then allow that value to override what's provided on the lifecycle
-        final Optional<Long> timeoutDefinedInScript = GremlinASTChecker.parse(script).getTimeout();
+        final Optional<Long> timeoutDefinedInScript = GremlinScriptChecker.parse(script).getTimeout();
         final long scriptEvalTimeOut = timeoutDefinedInScript.orElse(
                 lifeCycle.getEvaluationTimeoutOverride().orElse(evaluationTimeout));
 
diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinScriptChecker.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinScriptChecker.java
new file mode 100644
index 0000000..41aa229
--- /dev/null
+++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinScriptChecker.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.groovy.jsr223;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Processes Gremlin strings using regex so as to try to detect certain properties from the script without actual
+ * having to execute a {@code eval()} on it.
+ */
+public class GremlinScriptChecker {
+
+    public static final Result EMPTY_RESULT = new Result(0);
+    private static final List<String> tokens = Arrays.asList("evaluationTimeout", "scriptEvaluationTimeout",
+                                                             "ARGS_EVAL_TIMEOUT", "ARGS_SCRIPT_EVAL_TIMEOUT");
+
+    private static final Pattern patternClean = Pattern.compile("//.*$|/\\*(.|[\\r\\n])*?\\*/|\\s", Pattern.MULTILINE);
+    private static final String timeoutTokens = "[\"']evaluationTimeout[\"']|[\"']scriptEvaluationTimeout[\"']|(?:Tokens\\.)?ARGS_EVAL_TIMEOUT|(?:Tokens\\.)?ARGS_SCRIPT_EVAL_TIMEOUT";
+    private static final Pattern patternTimeout = Pattern.compile("\\.with\\((?:" + timeoutTokens + "),(\\d*)(:?L|l)?\\)");
+
+    /**
+     * Parses a Gremlin script and extracts a {@code Result} containing properties that are relevant to the checker.
+     */
+    public static Result parse(final String gremlin) {
+        if (gremlin.isEmpty()) return EMPTY_RESULT;
+
+        // do a cheap check for tokens we care about - no need to parse unless one of these tokens is present in
+        // the string.
+        if (tokens.stream().noneMatch(gremlin::contains)) return EMPTY_RESULT;
+
+        // kill out comments/whitespace. for whitespace, ignoring the need to keep string literals together as that
+        // isn't currently a requirement
+        final String cleanGremlin = patternClean.matcher(gremlin).replaceAll("");
+
+        final Matcher m = patternTimeout.matcher(cleanGremlin);
+        if (!m.find()) return EMPTY_RESULT;
+
+        long l = Long.parseLong(m.group(1));
+        while (m.find()) {
+            l += Long.parseLong(m.group(1));
+        }
+
+        return new Result(l);
+    }
+
+    public static class Result {
+        private final long timeout;
+
+        private Result(final long timeout) {
+            this.timeout = timeout;
+        }
+
+        /**
+         * Gets the value of the timeouts that were set using the {@code with()} source step. If there are multiple
+         * commands using this step, the timeouts are summed together.
+         */
+        public final Optional<Long> getTimeout() {
+            return timeout == 0 ? Optional.empty() : Optional.of(timeout);
+        }
+    }
+}
diff --git a/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinScriptCheckerTest.java b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinScriptCheckerTest.java
new file mode 100644
index 0000000..3a745f5
--- /dev/null
+++ b/gremlin-groovy/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinScriptCheckerTest.java
@@ -0,0 +1,300 @@
+/*
+ * 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.junit.Test;
+
+import java.util.Optional;
+
+import static org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinScriptChecker.EMPTY_RESULT;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+public class GremlinScriptCheckerTest {
+
+    @Test
+    public void shouldNotFindTimeout() {
+        assertEquals(Optional.empty(), GremlinScriptChecker.parse("g.with(true).V().out('knows')").getTimeout());
+    }
+
+    @Test
+    public void shouldReturnEmpty() {
+        assertSame(EMPTY_RESULT, GremlinScriptChecker.parse(""));
+    }
+
+    @Test
+    public void shouldNotFindTimeoutCozWeCommentedItOut() {
+        assertEquals(Optional.empty(), GremlinScriptChecker.parse("g.\n" +
+                "                                                  // with('evaluationTimeout', 1000L).\n" +
+                "                                                  with(true).V().out('knows')").getTimeout());
+    }
+
+    @Test
+    public void shouldIdentifyTimeoutAsStringKeySingleQuoted() {
+        assertEquals(1000, GremlinScriptChecker.parse("g.with('evaluationTimeout', 1000L).with(true).V().out('knows')").
+                getTimeout().get().longValue());
+        assertEquals(1000, GremlinScriptChecker.parse("g.with('scriptEvaluationTimeout', 1000L).with(true).V().out('knows')").
+                getTimeout().get().longValue());
+    }
+
+    @Test
+    public void shouldIdentifyTimeoutAsStringKeyDoubleQuoted() {
+        assertEquals(1000, GremlinScriptChecker.parse("g.with(\"evaluationTimeout\", 1000L).with(true).V().out('knows')").
+                getTimeout().get().longValue());
+        assertEquals(1000, GremlinScriptChecker.parse("g.with(\"scriptEvaluationTimeout\", 1000L).with(true).V().out('knows')").
+                getTimeout().get().longValue());
+    }
+
+    @Test
+    public void shouldIdentifyTimeoutAsTokenKey() {
+        assertEquals(1000, GremlinScriptChecker.parse("g.with(Tokens.ARGS_EVAL_TIMEOUT, 1000L).with(true).V().out('knows')").
+                getTimeout().get().longValue());
+        assertEquals(1000, GremlinScriptChecker.parse("g.with(Tokens.ARGS_SCRIPT_EVAL_TIMEOUT, 1000L).with(true).V().out('knows')").
+                getTimeout().get().longValue());
+    }
+
+    @Test
+    public void shouldIdentifyTimeoutAsTokenKeyWithoutClassName() {
+        assertEquals(1000, GremlinScriptChecker.parse("g.with(ARGS_EVAL_TIMEOUT, 1000L).with(true).V().out('knows')").
+                getTimeout().get().longValue());
+        assertEquals(1000, GremlinScriptChecker.parse("g.with(ARGS_SCRIPT_EVAL_TIMEOUT, 1000L).with(true).V().out('knows')").
+                getTimeout().get().longValue());
+    }
+
+    @Test
+    public void shouldIdentifyMultipleTimeouts() {
+        assertEquals(6000, GremlinScriptChecker.parse("g.with('evaluationTimeout', 1000L).with(true).V().out('knows');" +
+                "g.with('evaluationTimeout', 1000L).with(true).V().out('knows')\n" +
+                "                                                   //g.with('evaluationTimeout', 1000L).with(true).V().out('knows')\n" +
+                "                                                   /* g.with('evaluationTimeout', 1000L).with(true).V().out('knows')*/\n" +
+                "                                                   /* \n" +
+                "g.with('evaluationTimeout', 1000L).with(true).V().out('knows') \n" +
+                "*/ \n" +
+                "                                                   g.with('evaluationTimeout', 1000L).with(true).V().out('knows')\n" +
+                "                                                   g.with(Tokens.ARGS_SCRIPT_EVAL_TIMEOUT, 1000L).with(true).V().out('knows')\n" +
+                "                                                   g.with(ARGS_EVAL_TIMEOUT, 1000L).with(true).V().out('knows')\n" +
+                "                                                   g.with('scriptEvaluationTimeout', 1000L).with(true).V().out('knows')").
+                getTimeout().get().longValue());
+    }
+
+    @Test
+    public void shouldParseLong() {
+        assertEquals(1000, GremlinScriptChecker.parse("g.with('evaluationTimeout', 1000L).addV().property(id, 'blue').as('b').\n" +
+                "  addV().property(id, 'orange').as('o').\n" +
+                "  addV().property(id, 'red').as('r').\n" +
+                "  addV().property(id, 'green').as('g').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('b').\n" +
+                "  addE('bridge').from('g').to('o').\n" +
+                "  addE('bridge').from('g').to('r').\n" +
+                "  addE('bridge').from('g').to('r').\n" +
+                "  addE('bridge').from('o').to('b').\n" +
+                "  addE('bridge').from('o').to('b').\n" +
+                "  addE('bridge').from('o').to('r').\n" +
+                "  addE('bridge').from('o').to('r').iterate()").
+                getTimeout().get().longValue());
+    }
+}