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 2016/01/22 20:18:34 UTC
incubator-tinkerpop git commit: Added alias support for sessions in
Gremlin Server/Driver.
Repository: incubator-tinkerpop
Updated Branches:
refs/heads/TINKERPOP-1096 [created] 02181880a
Added alias support for sessions in Gremlin Server/Driver.
Also added support for sessions in the console - TINKERPOP-1097
Project: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/commit/02181880
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/tree/02181880
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/diff/02181880
Branch: refs/heads/TINKERPOP-1096
Commit: 02181880a655a0e9baf7fd0c996c4dfc985ee7a1
Parents: 1d8642b
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Fri Jan 22 14:17:01 2016 -0500
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Fri Jan 22 14:17:01 2016 -0500
----------------------------------------------------------------------
.../groovy/plugin/DriverRemoteAcceptor.java | 24 ++-
.../DriverRemoteAcceptorIntegrateTest.java | 18 ++
.../apache/tinkerpop/gremlin/driver/Client.java | 214 ++++++++-----------
.../server/op/AbstractEvalOpProcessor.java | 1 -
.../server/op/session/SessionOpProcessor.java | 78 ++++++-
.../server/GremlinDriverIntegrateTest.java | 55 +++++
6 files changed, 251 insertions(+), 139 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/02181880/gremlin-console/src/main/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverRemoteAcceptor.java
----------------------------------------------------------------------
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 2be8b23..593ca6e 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
@@ -38,6 +38,7 @@ 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;
@@ -51,15 +52,17 @@ import java.util.stream.Stream;
*/
public class DriverRemoteAcceptor implements RemoteAcceptor {
private Cluster currentCluster;
- private Client.ClusteredClient currentClient;
+ private Client currentClient;
private int timeout = 180000;
private Map<String,String> aliases = new HashMap<>();
+ private Optional<String> session = Optional.empty();
private static final String TOKEN_RESET = "reset";
private static final String TOKEN_SHOW = "show";
private static final String TOKEN_MAX = "max";
private static final String TOKEN_TIMEOUT = "timeout";
private static final String TOKEN_ALIAS = "alias";
+ private static final String TOKEN_SESSION = "session";
private static final List<String> POSSIBLE_TOKENS = Arrays.asList(TOKEN_TIMEOUT, TOKEN_ALIAS);
private final Groovysh shell;
@@ -70,13 +73,20 @@ public class DriverRemoteAcceptor implements RemoteAcceptor {
@Override
public Object connect(final List<String> args) throws RemoteException {
- if (args.size() != 1) throw new RemoteException("Expects the location of a configuration file as an argument");
+ 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));
- this.currentClient = this.currentCluster.connect();
+ final boolean useSession = args.size() >= 2 && args.get(1).equals(TOKEN_SESSION);
+ if (useSession) {
+ final String sessionName = args.size() == 3 ? args.get(2) : UUID.randomUUID().toString();
+ session = Optional.of(sessionName);
+ this.currentClient = this.currentCluster.connect(sessionName);
+ } else {
+ this.currentClient = this.currentCluster.connect();
+ }
this.currentClient.init();
- return String.format("Connected - " + this.currentCluster);
+ return String.format("Connected - %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) {
@@ -178,7 +188,7 @@ public class DriverRemoteAcceptor implements RemoteAcceptor {
@Override
public String toString() {
- return "Gremlin Server - [" + this.currentCluster + "]";
+ return "Gremlin Server - [" + this.currentCluster + "]" + getSessionStringSegment();
}
private Optional<ResponseException> findResponseException(final Throwable ex) {
@@ -190,4 +200,8 @@ public class DriverRemoteAcceptor implements RemoteAcceptor {
return findResponseException(ex.getCause());
}
+
+ private String getSessionStringSegment() {
+ return session.isPresent() ? String.format("-[%s]", session.get()) : "";
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/02181880/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverRemoteAcceptorIntegrateTest.java
----------------------------------------------------------------------
diff --git a/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverRemoteAcceptorIntegrateTest.java b/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverRemoteAcceptorIntegrateTest.java
index b41432e..823d345 100644
--- a/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverRemoteAcceptorIntegrateTest.java
+++ b/gremlin-console/src/test/java/org/apache/tinkerpop/gremlin/console/groovy/plugin/DriverRemoteAcceptorIntegrateTest.java
@@ -118,4 +118,22 @@ public class DriverRemoteAcceptorIntegrateTest extends AbstractGremlinServerInte
assertThat(IteratorUtils.list(((Iterator<String>) acceptor.submit(Arrays.asList("g.traversal().V().drop().iterate();null")))), contains("null"));
assertThat(((List<Result>) groovysh.getInterp().getContext().getProperty(DriverRemoteAcceptor.RESULT)).stream().map(Result::getObject).collect(Collectors.toList()), contains("null"));
}
+
+ @Test
+ public void shouldConnectAndSubmitInSession() throws Exception {
+ assertThat(acceptor.connect(Arrays.asList(TestHelper.generateTempFileFromResource(this.getClass(), "remote.yaml", ".tmp").getAbsolutePath(), "session")).toString(), startsWith("Connected - "));
+ assertEquals("2", ((Iterator) acceptor.submit(Arrays.asList("x=1+1"))).next());
+ assertEquals("2", ((List<Result>) groovysh.getInterp().getContext().getProperty(DriverRemoteAcceptor.RESULT)).iterator().next().getString());
+ assertEquals("4", ((Iterator) acceptor.submit(Arrays.asList("x+2"))).next());
+ assertEquals("4", ((List<Result>) groovysh.getInterp().getContext().getProperty(DriverRemoteAcceptor.RESULT)).iterator().next().getString());
+ }
+
+ @Test
+ public void shouldConnectAndSubmitInNamedSession() throws Exception {
+ assertThat(acceptor.connect(Arrays.asList(TestHelper.generateTempFileFromResource(this.getClass(), "remote.yaml", ".tmp").getAbsolutePath(), "session", "AAA")).toString(), startsWith("Connected - "));
+ assertEquals("2", ((Iterator) acceptor.submit(Arrays.asList("x=1+1"))).next());
+ assertEquals("2", ((List<Result>) groovysh.getInterp().getContext().getProperty(DriverRemoteAcceptor.RESULT)).iterator().next().getString());
+ assertEquals("4", ((Iterator) acceptor.submit(Arrays.asList("x+2"))).next());
+ assertEquals("4", ((List<Result>) groovysh.getInterp().getContext().getProperty(DriverRemoteAcceptor.RESULT)).iterator().next().getString());
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/02181880/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
----------------------------------------------------------------------
diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
index 8c80b8a..fb110ae 100644
--- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
+++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Client.java
@@ -91,8 +91,9 @@ public abstract class Client {
* @deprecated As of release 3.1.0, replaced by {@link #alias(String)}
*/
@Deprecated
- public abstract Client rebind(final String graphOrTraversalSource);
-
+ public Client rebind(final String graphOrTraversalSource) {
+ return alias(graphOrTraversalSource);
+ }
/**
* Create a new {@code Client} that aliases the specified {@link Graph} or {@link TraversalSource} name on the
@@ -100,7 +101,28 @@ public abstract class Client {
*
* @param graphOrTraversalSource rebinds the specified global Gremlin Server variable to "g"
*/
- public abstract Client alias(final String graphOrTraversalSource);
+ public Client alias(String graphOrTraversalSource) {
+ return new AliasClusteredClient(this, graphOrTraversalSource);
+ }
+
+ /**
+ * Creates a {@code Client} that supplies the specified set of aliases, thus allowing the user to re-name
+ * one or more globally defined {@link Graph} or {@link TraversalSource} server bindings for the context of
+ * the created {@code Client}.
+ */
+ @Deprecated
+ public Client rebind(final Map<String,String> rebindings) {
+ return alias(rebindings);
+ }
+
+ /**
+ * Creates a {@code Client} that supplies the specified set of aliases, thus allowing the user to re-name
+ * one or more globally defined {@link Graph} or {@link TraversalSource} server bindings for the context of
+ * the created {@code Client}.
+ */
+ public Client alias(final Map<String,String> aliases) {
+ return new AliasClusteredClient(this, aliases);
+ }
/**
* Initializes the client which typically means that a connection is established to the server. Depending on the
@@ -175,6 +197,52 @@ public abstract class Client {
}
/**
+ * The asynchronous version of {@link #submit(String, Map)}} where the returned future will complete when the
+ * write of the request completes.
+ *
+ * @param gremlin the gremlin script to execute
+ * @param parameters a map of parameters that will be bound to the script on execution
+ * @param graphOrTraversalSource rebinds the specified global Gremlin Server variable to "g"
+ */
+ public CompletableFuture<ResultSet> submitAsync(final String gremlin, final String graphOrTraversalSource,
+ final Map<String, Object> parameters) {
+ final RequestMessage.Builder request = RequestMessage.build(Tokens.OPS_EVAL)
+ .add(Tokens.ARGS_GREMLIN, gremlin)
+ .add(Tokens.ARGS_BATCH_SIZE, cluster.connectionPoolSettings().resultIterationBatchSize);
+
+ Optional.ofNullable(parameters).ifPresent(params -> request.addArg(Tokens.ARGS_BINDINGS, parameters));
+
+ if (graphOrTraversalSource != null && !graphOrTraversalSource.isEmpty())
+ request.addArg(Tokens.ARGS_ALIASES, makeAliases(graphOrTraversalSource));
+
+ return submitAsync(buildMessage(request));
+ }
+
+ /**
+ * The asynchronous version of {@link #submit(String, Map)}} where the returned future will complete when the
+ * write of the request completes.
+ *
+ * @param gremlin the gremlin script to execute
+ * @param parameters a map of parameters that will be bound to the script on execution
+ * @param aliases aliases the specified global Gremlin Server variable some other name that then be used in the
+ * script where the key is the alias name and the value represents the global variable on the
+ * server
+ */
+ public CompletableFuture<ResultSet> submitAsync(final String gremlin, final Map<String,String> aliases,
+ final Map<String, Object> parameters) {
+ final RequestMessage.Builder request = RequestMessage.build(Tokens.OPS_EVAL)
+ .add(Tokens.ARGS_GREMLIN, gremlin)
+ .add(Tokens.ARGS_BATCH_SIZE, cluster.connectionPoolSettings().resultIterationBatchSize);
+
+ Optional.ofNullable(parameters).ifPresent(params -> request.addArg(Tokens.ARGS_BINDINGS, parameters));
+
+ if (aliases != null && !aliases.isEmpty())
+ request.addArg(Tokens.ARGS_ALIASES, aliases);
+
+ return submitAsync(buildMessage(request));
+ }
+
+ /**
* A low-level method that allows the submission of a manually constructed {@link RequestMessage}.
*/
public CompletableFuture<ResultSet> submitAsync(final RequestMessage msg) {
@@ -209,6 +277,12 @@ public abstract class Client {
closeAsync().join();
}
+ private Map<String,String> makeAliases(final String graphOrTraversalSource) {
+ final Map<String,String> aliases = new HashMap<>();
+ aliases.put("g", graphOrTraversalSource);
+ return aliases;
+ }
+
/**
* A {@code Client} implementation that does not operate in a session. Requests are sent to multiple servers
* given a {@link LoadBalancingStrategy}. Transactions are automatically committed
@@ -251,88 +325,6 @@ public abstract class Client {
}
/**
- * The asynchronous version of {@link #submit(String, Map)}} where the returned future will complete when the
- * write of the request completes.
- *
- * @param gremlin the gremlin script to execute
- * @param parameters a map of parameters that will be bound to the script on execution
- * @param graphOrTraversalSource rebinds the specified global Gremlin Server variable to "g"
- */
- public CompletableFuture<ResultSet> submitAsync(final String gremlin, final String graphOrTraversalSource,
- final Map<String, Object> parameters) {
- final RequestMessage.Builder request = RequestMessage.build(Tokens.OPS_EVAL)
- .add(Tokens.ARGS_GREMLIN, gremlin)
- .add(Tokens.ARGS_BATCH_SIZE, cluster.connectionPoolSettings().resultIterationBatchSize);
-
- Optional.ofNullable(parameters).ifPresent(params -> request.addArg(Tokens.ARGS_BINDINGS, parameters));
-
- if (graphOrTraversalSource != null && !graphOrTraversalSource.isEmpty())
- request.addArg(Tokens.ARGS_ALIASES, makeRebindings(graphOrTraversalSource));
-
- return submitAsync(buildMessage(request));
- }
-
- /**
- * The asynchronous version of {@link #submit(String, Map)}} where the returned future will complete when the
- * write of the request completes.
- *
- * @param gremlin the gremlin script to execute
- * @param parameters a map of parameters that will be bound to the script on execution
- * @param aliases aliases the specified global Gremlin Server variable some other name that then be used in the
- * script where the key is the alias name and the value represents the global variable on the
- * server
- */
- public CompletableFuture<ResultSet> submitAsync(final String gremlin, final Map<String,String> aliases,
- final Map<String, Object> parameters) {
- final RequestMessage.Builder request = RequestMessage.build(Tokens.OPS_EVAL)
- .add(Tokens.ARGS_GREMLIN, gremlin)
- .add(Tokens.ARGS_BATCH_SIZE, cluster.connectionPoolSettings().resultIterationBatchSize);
-
- Optional.ofNullable(parameters).ifPresent(params -> request.addArg(Tokens.ARGS_BINDINGS, parameters));
-
- if (aliases != null && !aliases.isEmpty())
- request.addArg(Tokens.ARGS_ALIASES, aliases);
-
- return submitAsync(buildMessage(request));
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- @Deprecated
- public Client rebind(final String graphOrTraversalSource) {
- return alias(graphOrTraversalSource);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Client alias(String graphOrTraversalSource) {
- return new AliasClusteredClient(this, graphOrTraversalSource);
- }
-
- /**
- * Creates a {@code Client} that supplies the specified set of aliases, thus allowing the user to re-name
- * one or more globally defined {@link Graph} or {@link TraversalSource} server bindings for the context of
- * the created {@code Client}.
- */
- @Deprecated
- public Client rebind(final Map<String,String> rebindings) {
- return alias(rebindings);
- }
-
- /**
- * Creates a {@code Client} that supplies the specified set of aliases, thus allowing the user to re-name
- * one or more globally defined {@link Graph} or {@link TraversalSource} server bindings for the context of
- * the created {@code Client}.
- */
- public Client alias(final Map<String,String> aliases) {
- return new AliasClusteredClient(this, aliases);
- }
-
- /**
* Uses a {@link LoadBalancingStrategy} to choose the best {@link Host} and then selects the best connection
* from that host's connection pool.
*/
@@ -374,12 +366,6 @@ public abstract class Client {
hostConnectionPools.values().stream().map(ConnectionPool::closeAsync).collect(Collectors.toList()).toArray(poolCloseFutures);
return CompletableFuture.allOf(poolCloseFutures);
}
-
- private Map<String,String> makeRebindings(final String graphOrTraversalSource) {
- final Map<String,String> rebindings = new HashMap<>();
- rebindings.put("g", graphOrTraversalSource);
- return rebindings;
- }
}
/**
@@ -387,11 +373,11 @@ public abstract class Client {
* specified {@link Graph} or {@link TraversalSource} instances on the server-side.
*/
public final static class AliasClusteredClient extends ReboundClusteredClient {
- public AliasClusteredClient(ClusteredClient clusteredClient, String graphOrTraversalSource) {
+ public AliasClusteredClient(Client clusteredClient, String graphOrTraversalSource) {
super(clusteredClient, graphOrTraversalSource);
}
- public AliasClusteredClient(ClusteredClient clusteredClient, Map<String, String> rebindings) {
+ public AliasClusteredClient(Client clusteredClient, Map<String, String> rebindings) {
super(clusteredClient, rebindings);
}
}
@@ -404,19 +390,19 @@ public abstract class Client {
*/
@Deprecated
public static class ReboundClusteredClient extends Client {
- private final ClusteredClient clusteredClient;
+ private final Client client;
private final Map<String,String> aliases = new HashMap<>();
final CompletableFuture<Void> close = new CompletableFuture<>();
- ReboundClusteredClient(final ClusteredClient clusteredClient, final String graphOrTraversalSource) {
- super(clusteredClient.cluster);
- this.clusteredClient = clusteredClient;
+ ReboundClusteredClient(final Client client, final String graphOrTraversalSource) {
+ super(client.cluster);
+ this.client = client;
aliases.put("g", graphOrTraversalSource);
}
- ReboundClusteredClient(final ClusteredClient clusteredClient, final Map<String,String> rebindings) {
- super(clusteredClient.cluster);
- this.clusteredClient = clusteredClient;
+ ReboundClusteredClient(final Client client, final Map<String,String> rebindings) {
+ super(client.cluster);
+ this.client = client;
this.aliases.putAll(rebindings);
}
@@ -425,7 +411,7 @@ public abstract class Client {
if (close.isDone()) throw new IllegalStateException("Client is closed");
// the underlying client may not have been init'd
- clusteredClient.init();
+ client.init();
return this;
}
@@ -451,7 +437,7 @@ public abstract class Client {
@Override
protected Connection chooseConnection(final RequestMessage msg) throws TimeoutException, ConnectionException {
if (close.isDone()) throw new IllegalStateException("Client is closed");
- return clusteredClient.chooseConnection(msg);
+ return client.chooseConnection(msg);
}
/**
@@ -479,7 +465,7 @@ public abstract class Client {
@Override
public Client alias(String graphOrTraversalSource) {
if (close.isDone()) throw new IllegalStateException("Client is closed");
- return new AliasClusteredClient(clusteredClient, graphOrTraversalSource);
+ return new AliasClusteredClient(client, graphOrTraversalSource);
}
}
@@ -503,28 +489,6 @@ public abstract class Client {
}
/**
- * The sessioned client does not support this feature.
- *
- * @throws UnsupportedOperationException
- * @deprecated As of release 3.1.0, replaced by {@link #alias(String)}
- */
- @Deprecated
- @Override
- public Client rebind(final String graphOrTraversalSourceName){
- throw new UnsupportedOperationException("Sessioned client does not support aliasing");
- }
-
- /**
- * The sessioned client does not support this feature.
- *
- * @throws UnsupportedOperationException
- */
- @Override
- public Client alias(String graphOrTraversalSource) {
- throw new UnsupportedOperationException("Sessioned client does not support aliasing");
- }
-
- /**
* Adds the {@link Tokens#ARGS_SESSION} value to every {@link RequestMessage}.
*/
@Override
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/02181880/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/AbstractEvalOpProcessor.java
----------------------------------------------------------------------
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 aab9950..dceb7fb 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
@@ -57,7 +57,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;
import java.util.regex.Pattern;
-import java.util.stream.Collectors;
import static com.codahale.metrics.MetricRegistry.name;
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/02181880/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/session/SessionOpProcessor.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/session/SessionOpProcessor.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/session/SessionOpProcessor.java
index d5e5e7c..916d82b 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/session/SessionOpProcessor.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/session/SessionOpProcessor.java
@@ -22,6 +22,7 @@ import org.apache.tinkerpop.gremlin.driver.Tokens;
import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage;
import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
import org.apache.tinkerpop.gremlin.server.Context;
import org.apache.tinkerpop.gremlin.server.GremlinServer;
import org.apache.tinkerpop.gremlin.server.Settings;
@@ -29,6 +30,7 @@ import org.apache.tinkerpop.gremlin.server.handler.StateKey;
import org.apache.tinkerpop.gremlin.server.op.AbstractEvalOpProcessor;
import org.apache.tinkerpop.gremlin.server.op.OpProcessorException;
import org.apache.tinkerpop.gremlin.server.util.MetricManager;
+import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.util.function.ThrowingConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,6 +40,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.function.Supplier;
import static com.codahale.metrics.MetricRegistry.name;
@@ -148,14 +152,7 @@ public class SessionOpProcessor extends AbstractEvalOpProcessor {
// is important given the threadlocal nature of Graph implementation transactions.
context.getChannelHandlerContext().channel().attr(StateKey.SESSION).set(session);
- evalOpInternal(context, session::getGremlinExecutor, () -> {
- final Bindings bindings = session.getBindings();
-
- // parameter bindings override session bindings if present
- Optional.ofNullable((Map<String, Object>) msg.getArgs().get(Tokens.ARGS_BINDINGS)).ifPresent(bindings::putAll);
-
- return bindings;
- });
+ evalOpInternal(context, session::getGremlinExecutor, getBindingMaker(session).apply(context));
}
/**
@@ -172,4 +169,69 @@ public class SessionOpProcessor extends AbstractEvalOpProcessor {
session.touch();
return session;
}
+
+ /**
+ * A useful method for those extending this class, where the means for binding construction can be supplied
+ * to this class. This function is used in {@link #evalOp(Context)} to create the final argument to
+ * {@link AbstractEvalOpProcessor#evalOpInternal(Context, Supplier, org.apache.tinkerpop.gremlin.server.op.AbstractEvalOpProcessor.BindingSupplier)}.
+ * In this way an extending class can use the default {@link org.apache.tinkerpop.gremlin.server.op.AbstractEvalOpProcessor.BindingSupplier}
+ * which carries a lot of re-usable functionality or provide a new one to override the existing approach.
+ */
+ protected Function<Context, BindingSupplier> getBindingMaker(final Session session) {
+ return context -> () -> {
+ final RequestMessage msg = context.getRequestMessage();
+ final Bindings bindings = session.getBindings();
+
+ // don't allow both rebindings and aliases parameters as they are the same thing. aliases were introduced
+ // as of 3.1.0 as a replacement for rebindings. this check can be removed when rebindings are completely
+ // removed from the protocol
+ final boolean hasRebindings = msg.getArgs().containsKey(Tokens.ARGS_REBINDINGS);
+ final boolean hasAliases = msg.getArgs().containsKey(Tokens.ARGS_ALIASES);
+ if (hasRebindings && hasAliases) {
+ final String error = "Prefer use of the 'aliases' parameter over 'rebindings' and do not use both";
+ throw new OpProcessorException(error, ResponseMessage.build(msg)
+ .code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).statusMessage(error).create());
+ }
+
+ final String rebindingOrAliasParameter = hasRebindings ? Tokens.ARGS_REBINDINGS : Tokens.ARGS_ALIASES;
+
+ // alias any global bindings to a different variable
+ if (msg.getArgs().containsKey(rebindingOrAliasParameter)) {
+ final Map<String, String> aliases = (Map<String, String>) msg.getArgs().get(rebindingOrAliasParameter);
+ for (Map.Entry<String,String> aliasKv : aliases.entrySet()) {
+ boolean found = false;
+
+ // first check if the alias refers to a Graph instance
+ final Map<String, Graph> graphs = context.getGraphManager().getGraphs();
+ if (graphs.containsKey(aliasKv.getValue())) {
+ bindings.put(aliasKv.getKey(), graphs.get(aliasKv.getValue()));
+ found = true;
+ }
+
+ // if the alias wasn't found as a Graph then perhaps it is a TraversalSource - it needs to be
+ // something
+ if (!found) {
+ final Map<String, TraversalSource> traversalSources = context.getGraphManager().getTraversalSources();
+ if (traversalSources.containsKey(aliasKv.getValue())) {
+ bindings.put(aliasKv.getKey(), traversalSources.get(aliasKv.getValue()));
+ found = true;
+ }
+ }
+
+ // this validation is important to calls to GraphManager.commit() and rollback() as they both
+ // expect that the aliases supplied are valid
+ if (!found) {
+ final String error = String.format("Could not alias [%s] to [%s] as [%s] not in the Graph or TraversalSource global bindings",
+ aliasKv.getKey(), aliasKv.getValue(), aliasKv.getValue());
+ throw new OpProcessorException(error, ResponseMessage.build(msg)
+ .code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS).statusMessage(error).create());
+ }
+ }
+ }
+
+ // add any bindings to override any other supplied
+ Optional.ofNullable((Map<String, Object>) msg.getArgs().get(Tokens.ARGS_BINDINGS)).ifPresent(bindings::putAll);
+ return bindings;
+ };
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/02181880/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
index a0d06f1..bce6bfa 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
@@ -86,6 +86,7 @@ public class GremlinDriverIntegrateTest extends AbstractGremlinServerIntegration
switch (nameOfTest) {
case "shouldAliasTraversalSourceVariables":
+ case "shouldAliasTraversalSourceVariablesInSession":
try {
final String p = TestHelper.generateTempFileFromResource(
GremlinDriverIntegrateTest.class, "generate-shouldRebindTraversalSourceVariables.groovy", "").getAbsolutePath();
@@ -1006,4 +1007,58 @@ public class GremlinDriverIntegrateTest extends AbstractGremlinServerIntegration
cluster.close();
}
+
+ @Test
+ public void shouldAliasGraphVariablesInSession() throws Exception {
+ final Cluster cluster = Cluster.build().create();
+ final Client client = cluster.connect(name.getMethodName());
+
+ try {
+ client.submit("g.addVertex('name','stephen');").all().get().get(0).getVertex();
+ fail("Should have tossed an exception because \"g\" does not have the addVertex method under default config");
+ } catch (Exception ex) {
+ final Throwable root = ExceptionUtils.getRootCause(ex);
+ assertThat(root, instanceOf(ResponseException.class));
+ final ResponseException re = (ResponseException) root;
+ assertEquals(ResponseStatusCode.SERVER_ERROR_SCRIPT_EVALUATION, re.getResponseStatusCode());
+ }
+
+ // keep the testing here until "rebind" is completely removed
+ final Client reboundLegacy = cluster.connect().rebind("graph");
+ final Vertex vLegacy = reboundLegacy.submit("g.addVertex('name','stephen')").all().get().get(0).getVertex();
+ assertEquals("stephen", vLegacy.value("name"));
+
+ final Client rebound = cluster.connect().alias("graph");
+ final Vertex v = rebound.submit("g.addVertex('name','jason')").all().get().get(0).getVertex();
+ assertEquals("jason", v.value("name"));
+
+ cluster.close();
+ }
+
+ @Test
+ public void shouldAliasTraversalSourceVariablesInSession() throws Exception {
+ final Cluster cluster = Cluster.build().create();
+ final Client client = cluster.connect(name.getMethodName());
+
+ try {
+ client.submit("g.addV('name','stephen')").all().get().get(0).getVertex();
+ fail("Should have tossed an exception because \"g\" is readonly in this context");
+ } catch (Exception ex) {
+ final Throwable root = ExceptionUtils.getRootCause(ex);
+ assertThat(root, instanceOf(ResponseException.class));
+ final ResponseException re = (ResponseException) root;
+ assertEquals(ResponseStatusCode.SERVER_ERROR, re.getResponseStatusCode());
+ }
+
+ // keep the testing here until "rebind" is completely removed
+ final Client clientLegacy = client.rebind("g1");
+ final Vertex vLegacy = clientLegacy.submit("g.addV('name','stephen')").all().get().get(0).getVertex();
+ assertEquals("stephen", vLegacy.value("name"));
+
+ final Client clientAliased = client.alias("g1");
+ final Vertex v = clientAliased.submit("g.addV('name','jason')").all().get().get(0).getVertex();
+ assertEquals("jason", v.value("name"));
+
+ cluster.close();
+ }
}