blob: 4e2d808e23d988728db763f5dc1438d6a8147e43 [file] [log] [blame]
/*
* 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.driver.remote;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.tinkerpop.gremlin.driver.Client;
import org.apache.tinkerpop.gremlin.driver.Host;
import org.apache.tinkerpop.gremlin.driver.Result;
import org.apache.tinkerpop.gremlin.driver.Tokens;
import org.apache.tinkerpop.gremlin.driver.message.RequestMessage;
import org.apache.tinkerpop.gremlin.process.remote.traversal.AbstractRemoteTraversalSideEffects;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalSideEffects;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
/**
* Java driver implementation of {@link TraversalSideEffects}. This class is not thread safe.
*
* @author Stephen Mallette (http://stephen.genoprime.com)
* @deprecated As of release 3.3.8, not directly replaced, prefer use of {@link GraphTraversal#cap(String, String...)}
* to return the result as part of the traversal iteration.
*/
@Deprecated
public class DriverRemoteTraversalSideEffects extends AbstractRemoteTraversalSideEffects {
private final Client client;
private Set<String> keys = Collections.emptySet();
private final UUID serverSideEffect;
private final Host host;
private final Map<String, Object> sideEffects = new HashMap<>();
private boolean closed = false;
private boolean retrievedAllKeys = false;
private final CompletableFuture<Void> ready;
public DriverRemoteTraversalSideEffects(final Client client, final UUID serverSideEffect, final Host host,
final CompletableFuture<Void> ready) {
this.client = client;
this.serverSideEffect = serverSideEffect;
this.host = host;
this.ready = ready;
}
@Override
public <V> V get(final String key) throws IllegalArgumentException {
// wait for the read to complete (i.e. iteration on the server) before allowing the caller to get the
// side-effect. calling prior to this will result in the side-effect not being found. of course, the
// bad part here is that the method blocks indefinitely waiting for the result, but it prevents the
// test failure problems that happen on slower systems. in practice, it's unlikely that a user would
// try to get a side-effect prior to iteration, but since the API allows it, this at least prevents
// the error.
ready.join();
if (!keys().contains(key)) throw TraversalSideEffects.Exceptions.sideEffectKeyDoesNotExist(key);
if (!sideEffects.containsKey(key)) {
if (closed) throw new IllegalStateException("Traversal has been closed - no new side-effects can be retrieved");
// specify the ARGS_HOST so that the LoadBalancingStrategy is subverted and the connection is forced
// from the specified host (i.e. the host from the previous request as that host will hold the side-effects)
final RequestMessage msg = RequestMessage.build(Tokens.OPS_GATHER)
.addArg(Tokens.ARGS_SIDE_EFFECT, serverSideEffect)
.addArg(Tokens.ARGS_SIDE_EFFECT_KEY, key)
.addArg(Tokens.ARGS_HOST, host)
.processor("traversal").create();
try {
final Result result = client.submitAsync(msg).get().all().get().get(0);
sideEffects.put(key, null == result ? null : result.getObject());
} catch (Exception ex) {
// we use to try to catch "no found" situations returned from the server here and then null the
// side-effect for the requested key. doesn't seem like there is a need for that now because calls
// to get() now initially trigger a call the keys() so you would know all of the keys available on
// the server and would validate them up front throwing sideEffectKeyDoesNotExist(key) which thus
// produces behavior similar to the non-remote side-effect implementations. if we get an exception
// here at this point then we likely have a legit error in communicating to the remote server.
throw new RuntimeException("Could not get side-effect for " + serverSideEffect + " with key of " + key, ex);
}
}
return (V) sideEffects.get(key);
}
@Override
public Set<String> keys() {
// wait for the read to complete (i.e. iteration on the server) before allowing the caller to get the
// side-effect. calling prior to this will result in the side-effect not being found. of course, the
// bad part here is that the method blocks indefinitely waiting for the result, but it prevents the
// test failure problems that happen on slower systems. in practice, it's unlikely that a user would
// try to get a side-effect prior to iteration, but since the API allows it, this at least prevents
// the error.
ready.join();
if (closed && !retrievedAllKeys) throw new IllegalStateException("Traversal has been closed - side-effect keys cannot be retrieved");
if (!retrievedAllKeys) {
// specify the ARGS_HOST so that the LoadBalancingStrategy is subverted and the connection is forced
// from the specified host (i.e. the host from the previous request as that host will hold the side-effects)
final RequestMessage msg = RequestMessage.build(Tokens.OPS_KEYS)
.addArg(Tokens.ARGS_SIDE_EFFECT, serverSideEffect)
.addArg(Tokens.ARGS_HOST, host)
.processor("traversal").create();
try {
if (keys.equals(Collections.emptySet()))
keys = new HashSet<>();
client.submitAsync(msg).get().all().get().forEach(r -> keys.add(r.getString()));
// only need to retrieve all keys once
retrievedAllKeys = true;
} catch (Exception ex) {
final Throwable root = ExceptionUtils.getRootCause(ex);
throw new RuntimeException("Could not get keys", null == root ? ex : root);
}
}
return keys;
}
@Override
public void close() throws Exception {
if (!closed) {
final RequestMessage msg = RequestMessage.build(Tokens.OPS_CLOSE)
.addArg(Tokens.ARGS_SIDE_EFFECT, serverSideEffect)
.addArg(Tokens.ARGS_HOST, host)
.processor("traversal").create();
try {
client.submitAsync(msg).get();
closed = true;
} catch (Exception ex) {
final Throwable root = ExceptionUtils.getRootCause(ex);
throw new RuntimeException("Error on closing side effects", null == root ? ex : root);
}
}
}
@Override
public String toString() {
// have to override the implementation from TraversalSideEffects because it relies on calls to keys() as
// calling that too early can cause unintended failures (i.e. in the debugger, toString() gets called when
// introspecting the object from the moment of construction).
return "sideEffects[size:" + keys.size() + "]";
}
}