0
votes

I am building a Java application that needs to connect to a remote JanusGraph server and create graphs on the fly.

I have installed/configured a single node JanusGraph Server with a Berkeley database backend and ConfigurationManagementGraph support so that I can create/manage multiple graphs on the server.

In a Gremlin console I can connect to the remote server, create graphs, create vertexes, etc. Example:

gremlin> :remote connect tinkerpop.server conf/remote.yaml session
gremlin> :remote console

gremlin> map = new HashMap<String, Object>();
gremlin> map.put("storage.backend", "berkeleyje");
gremlin> map.put("storage.directory", "db/test");
gremlin> ConfiguredGraphFactory.createTemplateConfiguration(new MapConfiguration(map));
gremlin> ConfiguredGraphFactory.create("test");

gremlin> graph = ConfiguredGraphFactory.open("test");
gremlin> g = graph.traversal();
gremlin> g.addV("person").property("name", "peter");
gremlin> g.tx().commit();
gremlin> graph.vertices().size();
==>1
gremlin> g.V();
==>v[4288]
gremlin> g.V().count();
==>1
gremlin> g.close();

So far so good. On the Java side, I can connect to the remote server and issue commands via Client.submit() method. In the following example, I connect to the remote server and create a new graph called "test2":

Cluster cluster = Cluster.build()
    .addContactPoint(host)
    .port(port)
    .serializer(Serializers.GRYO_V3D0)
    .create();

String name = "test2";

String sessionId = UUID.randomUUID().toString();
Client client = cluster.connect(sessionId);
client.submit("map = new HashMap<String, Object>();");
client.submit("map.put(\"storage.backend\", \"berkeleyje\");");
client.submit("map.put(\"storage.directory\", \"db/" + name + "\");");
client.submit("ConfiguredGraphFactory.createTemplateConfiguration(new MapConfiguration(map));");
client.submit("ConfiguredGraphFactory.create(\"" + name + "\");");

I can confirm that the graph was created and see other graphs programmatically as well using the client.submit() method:

ResultSet results = client.submit("ConfiguredGraphFactory.getGraphNames()");
Iterator<Result> it = results.iterator();
while (it.hasNext()){
    Result result = it.next();
    String graphName = result.getString();
    System.out.println(graphName);
}

Next I want to connect to a graph and traverse the nodes programmatically (in Java). However, I can't seem to figure out how to do this. From what I've read, it should be something as simple as this:

DriverRemoteConnection conn = DriverRemoteConnection.using(client, name); //"_traversal"
GraphTraversalSource g = AnonymousTraversalSource.traversal().withRemote(conn);

These commands don't raise any errors but the GraphTraversalSource appears to be empty:

System.out.println(g.getGraph()); //always returns emptygraph[empty]
System.out.prinltn(g.V()); //Appears to be empty [GraphStep(vertex,[])]
Iterator<Vertex> it = g.getGraph().vertices(); //empty

Any suggestions of how to get a GraphTraversalSource for a remote JanusGraph server in Java? I suspect that my issue something to do with ConfigurationManagementGraph but I can't put my finger on it. Again, the client.submit() works. It would be cool if I could do something like this:

GraphTraversalSource g = (GraphTraversalSource) client.submit("ConfiguredGraphFactory.open(\"" + name + "\");").iterator().next();

...but of course, that doesn't work

UPDATE

Looking at the code, it appears that the graph name (remoteTraversalSourceName) passed to the DriverRemoteConnection is being ignored.

Starting with the DriverRemoteConnection:

DriverRemoteConnection conn = DriverRemoteConnection.using(client, name); 

Under the hood, the graph name (remoteTraversalSourceName) is simply used to set an alias (e.g. client.alias(name);)

Next, in the AnonymousTraversalSource.traversal().withRemote() method

GraphTraversalSource g = AnonymousTraversalSource.traversal().withRemote(conn);

Under the hood, withRemote() is calling:

traversalSourceClass.getConstructor(RemoteConnection.class).newInstance(remoteConnection);

Where traversalSourceClass is GraphTraversalSource.class

Which is the same as this:

g = GraphTraversalSource.class.getConstructor(RemoteConnection.class).newInstance(conn);

Finally, the constructor for the GraphTraversalSource looks like this:

public GraphTraversalSource(final RemoteConnection connection) {
    this(EmptyGraph.instance(), TraversalStrategies.GlobalCache.getStrategies(EmptyGraph.class).clone());
    this.connection = connection;
    this.strategies.addStrategies(new RemoteStrategy(connection));
}

As you can see, the graph variable in the GraphTraversalSource is never set.

I suspect that either (a) I shouldn't be using the AnonymousTraversalSource or (b) that I need to instantiate the GraphTraversalSource some other way, perhaps using a Graph object.

1

1 Answers

0
votes

Updated answer

Most likely the channelizer that you use is the Tinkerpop's channelizer org.apache.tinkerpop.gremlin.server.channel.WsAndHttpChannelizer. Replacing it with the Janus' channelizer org.janusgraph.channelizers.JanusGraphWsAndHttpChannelizer properly binds the graph to the connection.

Older answer

The workaround that was used along when the channelizer was org.apache.tinkerpop.gremlin.server.channel.WsAndHttpChannelizer.

I'm having the same issue. For now, the workaround that I found is to bind the traversals during the Janus startup. Additionally to the gremlin-server.yaml and janusgraph.properties, I also override the empty-sample.groovy with the content:

def globals = [:]
ConfiguredGraphFactory.getGraphNames().each { name ->
  globals << [ (name + "_traversal") : ConfiguredGraphFactory.open(name).traversal()]
}

Now the graph that you create is available at the yourgraphname_traversal:

import org.apache.tinkerpop.gremlin.driver.Cluster
import org.apache.tinkerpop.gremlin.driver.remote.DriverRemoteConnection
import org.apache.tinkerpop.gremlin.driver.ser.Serializers
import org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource
...
  val cluster = Cluster.build()
    .addContactPoint("your_load_balancer_host")
    .port(8182)
    .serializer(Serializers.GRAPHBINARY_V1D0.simpleInstance())
    .create()
  val remoteConnection = DriverRemoteConnection.using(cluster, "yourgraphname_traversal")
  val g = AnonymousTraversalSource.traversal().withRemote(remoteConnection)

The solution is not ideal, cause it requires all the Janus nodes to be restarted in order to update the bindings. Assuming that the graphs are rarely created, this solution is at least something.