Remove the notion of submitting "remote" traversals to Gremlin Server.
Such features are no longer possible given that we can't truly serialize a lambda.
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index fafea67..64761e0 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -10,6 +10,7 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* All `Step` fields are now `private`/`protected` with respective getters as currently needed and will be added to as needed.
+* Gremlin Server no longer has the `traverse` operation as lambdas aren't really serialized.
* `Path` is now an interface with `ImmutablePath` and `MutablePath` as implementations (2x speedup on path calculations).
* `Traverser` now implements `Comparable`. If the underlying object doesn't implement `Comparable`, then a runtime exception.
* Added abstract `BarrierStep` which greatly simplifies implementing barriers like `AggregateStep`, `OrderStep`, etc.
diff --git a/docs/gremlin-applications.asciidoc b/docs/gremlin-applications.asciidoc
index e03388f..7850ac9 100644
--- a/docs/gremlin-applications.asciidoc
+++ b/docs/gremlin-applications.asciidoc
@@ -297,21 +297,6 @@
<4> Submit a script asynchronously without waiting for the request to be written to the server.
<5> Parameterized request are considered the most efficient way to send Gremlin to the server as they can be cached, which will boost performance and reduce resources required on the server.
-The above examples submit Gremlin `String` values to the server for evaluation. It is also possible to submit actual Java-based `Traversal` instances:
-
-[source,java]
-----
-public static class RemoteTraversal implements SFunction<Graph, Traversal> {
- public Traversal apply(final Graph g) {
- return g.V().out().range(0,9);
- }
-}
-
-List<Item> results = client.submit(new RemoteTraversal()).all().get();
-----
-
-The code above shows that a `Traversal` instance can be sent for remote execution in Gremlin Server. This feature is *highly experimental*, but has some interesting implications in that it provides a way to avoid maintaining "fat strings" of Gremlin in the application code base. While the "fat string" approach can also be avoided by developing a server-side DSL, the sending of an actual `Traversal` provides another method that might provide new and better patterns for developing graph-based applications.
-
Configuring
~~~~~~~~~~~
@@ -501,7 +486,6 @@
!=========================================================
!Key !Description
!`eval` !evaluate a Gremlin script provided as a `String`
-!`traverse` !evaluate a serialized `Traversal` object - available to JVM clients only
!=========================================================
|=========================================================
@@ -514,16 +498,6 @@
|language |String |The flavor used (e.g. `gremlin-groovy`)
|=========================================================
-'`traverse` operation arguments'
-[width="100%",cols="2,2,10",options="header"]
-|=========================================================
-|Key |Type |Description
-|gremlin |String | *Required* The Gremlin `Traversal` to evaluate
-|graphName |String | *Required* The name of graph to traverse. This graph name is set in the Gremlin Server configuration
-|bindings |Map |A set of key/value pairs to apply as variables in the context of the Gremlin script
-|language |String |The flavor used (e.g. `gremlin-groovy`)
-|=========================================================
-
Session OpProcessor
+++++++++++++++++++
@@ -538,7 +512,6 @@
!=========================================================
!Key !Description
!`eval` !evaluate a Gremlin script
-!`traverse` !evaluate a serialized `Traversal` object - available to JVM clients only
!=========================================================
|=========================================================
@@ -552,16 +525,6 @@
|language |String |The flavor used (e.g. `gremlin-groovy`)
|=========================================================
-'`traverse` operation arguments'
-[width="100%",cols="1,1,10",options="header"]
-|=========================================================
-|Key |Type |Description
-|gremlin |String | *Required* The Gremlin `Traversal` to evaluate
-|graphName |String | *Required* The name of graph to traverse. This graph name is set in the Gremlin Server configuration
-|bindings |Map |A set of key/value pairs to apply as variables in the context of the Gremlin script
-|language |String |The flavor used (e.g. `gremlin-groovy`)
-|=========================================================
-
[[gremlin-plugins]]
Gremlin Plugins
---------------
diff --git a/gremlin-driver/src/main/java/com/tinkerpop/gremlin/driver/Client.java b/gremlin-driver/src/main/java/com/tinkerpop/gremlin/driver/Client.java
index 5f178e8..fe94b99 100644
--- a/gremlin-driver/src/main/java/com/tinkerpop/gremlin/driver/Client.java
+++ b/gremlin-driver/src/main/java/com/tinkerpop/gremlin/driver/Client.java
@@ -2,13 +2,9 @@
import com.tinkerpop.gremlin.driver.exception.ConnectionException;
import com.tinkerpop.gremlin.driver.message.RequestMessage;
-import com.tinkerpop.gremlin.process.Traversal;
-import com.tinkerpop.gremlin.structure.Graph;
-import com.tinkerpop.gremlin.util.Serializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -18,7 +14,6 @@
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -74,7 +69,7 @@
}
public ResultSet submit(final String gremlin) {
- return submit(gremlin, (Map<String, Object>) null);
+ return submit(gremlin, null);
}
public ResultSet submit(final String gremlin, final Map<String, Object> parameters) {
@@ -85,39 +80,8 @@
}
}
- public ResultSet submit(final Function<Graph, Traversal> traversal) {
- return submit("g", traversal);
- }
-
- public ResultSet submit(final String graph, final Function<Graph, Traversal> traversal) {
- try {
- return submitAsync(graph, traversal).get();
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
-
- public CompletableFuture<ResultSet> submitAsync(final Function<Graph, Traversal> traversal) {
- return submitAsync("g", traversal);
- }
-
- public CompletableFuture<ResultSet> submitAsync(final String graph, final Function<Graph, Traversal> traversal) {
- try {
- final byte[] bytes = Serializer.serializeObject(traversal);
- final RequestMessage request = buildMessage(RequestMessage.build(Tokens.OPS_TRAVERSE)
- .add(Tokens.ARGS_GREMLIN, bytes)
- .add(Tokens.ARGS_GRAPH_NAME, graph)
- .add(Tokens.ARGS_BATCH_SIZE, cluster.connectionPoolSettings().resultIterationBatchSize));
-
- return submitAsync(request);
- } catch (IOException ioe) {
- ioe.printStackTrace();
- throw new RuntimeException(ioe);
- }
- }
-
public CompletableFuture<ResultSet> submitAsync(final String gremlin) {
- return submitAsync(gremlin, (Map<String, Object>) null);
+ return submitAsync(gremlin, null);
}
public CompletableFuture<ResultSet> submitAsync(final String gremlin, final Map<String, Object> parameters) {
diff --git a/gremlin-driver/src/main/java/com/tinkerpop/gremlin/driver/Tokens.java b/gremlin-driver/src/main/java/com/tinkerpop/gremlin/driver/Tokens.java
index 8ead168..4bca571 100644
--- a/gremlin-driver/src/main/java/com/tinkerpop/gremlin/driver/Tokens.java
+++ b/gremlin-driver/src/main/java/com/tinkerpop/gremlin/driver/Tokens.java
@@ -9,7 +9,6 @@
public class Tokens {
public static final String OPS_SHOW = "show";
public static final String OPS_EVAL = "eval";
- public static final String OPS_TRAVERSE = "traverse";
public static final String OPS_IMPORT = "import";
public static final String OPS_INVALID = "invalid";
public static final String OPS_RESET = "reset";
diff --git a/gremlin-server/src/main/java/com/tinkerpop/gremlin/server/op/session/SessionOpProcessor.java b/gremlin-server/src/main/java/com/tinkerpop/gremlin/server/op/session/SessionOpProcessor.java
index e680e6d..6598484 100644
--- a/gremlin-server/src/main/java/com/tinkerpop/gremlin/server/op/session/SessionOpProcessor.java
+++ b/gremlin-server/src/main/java/com/tinkerpop/gremlin/server/op/session/SessionOpProcessor.java
@@ -60,9 +60,6 @@
case Tokens.OPS_EVAL:
op = validateEvalMessage(message).orElse(SessionOps::evalOp);
break;
- case Tokens.OPS_TRAVERSE:
- op = validateTraverseMessage(message, ctx.getGraphs()).orElse(SessionOps::traverseOp);
- break;
case Tokens.OPS_INVALID:
final String msgInvalid = String.format("Message could not be parsed. Check the format of the request. [%s]", message);
throw new OpProcessorException(msgInvalid, ResponseMessage.build(message).code(ResponseStatusCode.REQUEST_ERROR_MALFORMED_REQUEST).result(msgInvalid).create());
diff --git a/gremlin-server/src/main/java/com/tinkerpop/gremlin/server/op/standard/StandardOpProcessor.java b/gremlin-server/src/main/java/com/tinkerpop/gremlin/server/op/standard/StandardOpProcessor.java
index 47bdfc3..b48b272 100644
--- a/gremlin-server/src/main/java/com/tinkerpop/gremlin/server/op/standard/StandardOpProcessor.java
+++ b/gremlin-server/src/main/java/com/tinkerpop/gremlin/server/op/standard/StandardOpProcessor.java
@@ -39,9 +39,6 @@
case Tokens.OPS_EVAL:
op = validateEvalMessage(message).orElse(StandardOps::evalOp);
break;
- case Tokens.OPS_TRAVERSE:
- op = validateTraverseMessage(message, ctx.getGraphs()).orElse(StandardOps::traverseOp);
- break;
case Tokens.OPS_INVALID:
final String msgInvalid = String.format("Message could not be parsed. Check the format of the request. [%s]", message);
throw new OpProcessorException(msgInvalid, ResponseMessage.build(message).code(ResponseStatusCode.REQUEST_ERROR_MALFORMED_REQUEST).result(msgInvalid).create());
@@ -61,24 +58,4 @@
return Optional.empty();
}
-
- private static Optional<ThrowingConsumer<Context>> validateTraverseMessage(final RequestMessage message, final Graphs graphs) throws OpProcessorException {
- if (!message.optionalArgs(Tokens.ARGS_GREMLIN).isPresent()) {
- final String msg = String.format("A message with an [%s] op code requires a [%s] argument.", Tokens.OPS_EVAL, Tokens.ARGS_GREMLIN);
- throw new OpProcessorException(msg, ResponseMessage.build(message).code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).result(msg).create());
- }
-
- if (!message.optionalArgs(Tokens.ARGS_GRAPH_NAME).isPresent()) {
- final String msg = String.format("A message with an [%s] op code requires a [%s] argument.", Tokens.OPS_EVAL, Tokens.ARGS_GRAPH_NAME);
- throw new OpProcessorException(msg, ResponseMessage.build(message).code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).result(msg).create());
- }
-
- final String graphName = message.getArgs().get(Tokens.ARGS_GRAPH_NAME).toString();
- if (!graphs.getGraphs().containsKey(graphName)) {
- final String msg = String.format("Requested a graph by the name of [%s] that is not configured on the server.", graphName);
- throw new OpProcessorException(msg, ResponseMessage.build(message).code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).result(msg).create());
- }
-
- return Optional.empty();
- }
}
diff --git a/gremlin-server/src/main/java/com/tinkerpop/gremlin/server/op/standard/StandardOps.java b/gremlin-server/src/main/java/com/tinkerpop/gremlin/server/op/standard/StandardOps.java
index 758adb9..7dffdd3 100644
--- a/gremlin-server/src/main/java/com/tinkerpop/gremlin/server/op/standard/StandardOps.java
+++ b/gremlin-server/src/main/java/com/tinkerpop/gremlin/server/op/standard/StandardOps.java
@@ -37,7 +37,6 @@
final class StandardOps {
private static final Logger logger = LoggerFactory.getLogger(StandardOps.class);
private static final Timer evalOpTimer = MetricManager.INSTANCE.getTimer(name(GremlinServer.class, "op", "eval"));
- private static final Timer traverseOpTimer = MetricManager.INSTANCE.getTimer(name(GremlinServer.class, "op", "traverse"));
public static void evalOp(final Context context) throws OpProcessorException {
final Timer.Context timerContext = evalOpTimer.time();
@@ -58,38 +57,6 @@
});
}
- public static void traverseOp(final Context context) throws OpProcessorException {
- final Timer.Context timerContext = traverseOpTimer.time();
- final ChannelHandlerContext ctx = context.getChannelHandlerContext();
- final RequestMessage msg = context.getRequestMessage();
-
- final Map<String, Object> args = msg.getArgs();
- final Map<String, Object> bindings = Optional.ofNullable((Map<String, Object>) args.get(Tokens.ARGS_BINDINGS)).orElse(new HashMap<>());
-
- final Function<Graph, Traversal> traversal;
- try {
- // deserialize the traversal and shove it into the bindings so that it can be executed within the
- // scriptengine. the scriptengine acts as a sandbox within which to execute the traversal.
- traversal = (Function<Graph, Traversal>) Serializer.deserializeObject((byte[]) args.get(Tokens.ARGS_GREMLIN));
- bindings.put("____trvrslScrpt", traversal);
- } catch (Exception ex) {
- logger.warn(String.format("Exception processing a traversal on request [%s].", msg), ex);
- ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TRAVERSAL_EVALUATION).statusMessage(ex.getMessage()).create());
- return;
- }
-
- // todo: different errors for traversal evaluation versus deserialization??
- final String script = String.format("____trvrslScrpt.apply(%s)", args.get(Tokens.ARGS_GRAPH_NAME));
- final CompletableFuture<Object> future = context.getGremlinExecutor().eval(script, bindings);
- future.handle((v, t) -> timerContext.stop());
- future.thenAccept(o -> ctx.write(Pair.with(msg, convertToIterator(o))));
- future.exceptionally(se -> {
- logger.warn(String.format("Exception processing a traversal on request [%s].", msg), se);
- ctx.writeAndFlush(ResponseMessage.build(msg).code(ResponseStatusCode.SERVER_ERROR_TRAVERSAL_EVALUATION).statusMessage(se.getMessage()).create());
- return null;
- });
- }
-
private static Iterator convertToIterator(final Object o) {
final Iterator itty;
if (o instanceof Iterable)
diff --git a/gremlin-server/src/test/java/com/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java b/gremlin-server/src/test/java/com/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
index f220f42..c9dcc53 100644
--- a/gremlin-server/src/test/java/com/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
+++ b/gremlin-server/src/test/java/com/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
@@ -42,23 +42,6 @@
@Rule
public TestName name = new TestName();
- public static class RemoteTraversal implements Function<Graph, Traversal> {
- public Traversal apply(final Graph g) {
- return g.V().out().range(0, 9);
- }
- }
-
- public static class ParameterizedRemoteTraversal implements Function<Graph, Traversal> {
- private String name;
- public ParameterizedRemoteTraversal(final String name) {
- this.name = name;
- }
-
- public Traversal apply(final Graph g) {
- return g.V().has("name", name).value("name");
- }
- }
-
/**
* Configure specific Gremlin Server settings for specific tests.
*/
@@ -78,27 +61,6 @@
}
@Test
- public void shouldSendTraversal() throws Exception {
- final Cluster cluster = Cluster.open();
- final Client client = cluster.connect();
-
- final List<Item> results = client.submit(new RemoteTraversal()).all().get();
- assertEquals(10, results.size());
- cluster.close();
- }
-
- @Test
- public void shouldSendParameterizedTraversal() throws Exception {
- final Cluster cluster = Cluster.open();
- final Client client = cluster.connect();
-
- final List<Item> results = client.submit(new ParameterizedRemoteTraversal("marko")).all().get();
- assertEquals(1, results.size());
- assertEquals("marko", results.get(0).getString());
- cluster.close();
- }
-
- @Test
public void shouldProcessRequestsOutOfOrder() throws Exception {
final Cluster cluster = Cluster.open();
final Client client = cluster.connect();