Janusgraphに対して、JavaのTinkerpop APIで接続する

# やりたいこと
[前のエントリ](https://qiita.com/Takashi_Kasuya/items/dbca2086649e6594f5e8)で、JanusGraphをインメモリで立ち上げるということやりました。立ち上げたサーバに対しては、Gramlin-consoleに加え、websocket等を介して様々な言語からアクセスできます。

Pythonでは、それほど考えずにできたのですが、scalaでの接続にはちょっと工夫が必要です。

# シリアライズ
クライアント用のプログラムと、JanusGraphのサーバ間の通信はwebsocketのようですが、平文で流れているわけではなくて、適宜シリアライズされています。これは、サーバ起動時のパラメータに指定があります。例えば、以下はデフォルトの設定で、`serializers`というのが確認できます。速さに定評がある[Kryo](https://github.com/EsotericSoftware/kryo)が採用されているようです。（設定ファイルでの名前はGryoになっています。これって違うものかしら…？）

```gremlin-server.yaml
host: 0.0.0.0
port: 8182
scriptEvaluationTimeout: 30000
channelizer: org.apache.tinkerpop.gremlin.server.channel.WebSocketChannelizer
graphs: {
  graph: conf/gremlin-server/janusgraph-cql-es-server.properties
}
scriptEngines: {
  gremlin-groovy: {
    plugins: { org.janusgraph.graphdb.tinkerpop.plugin.JanusGraphGremlinPlugin: {},
               org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {},
               org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin: {},
               org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin: {classImports: [java.lang.Math], methodImports: [java.lang.Math#*]},
               org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: {files: [scripts/empty-sample.groovy]}}}}
serializers:
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { serializeResultToString: true }}
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV3d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
  # Older serialization versions for backwards compatibility:
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoLiteMessageSerializerV1d0, config: {ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0, config: { serializeResultToString: true }}
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerGremlinV2d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }}
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerGremlinV1d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistryV1d0] }}
  - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV1d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistryV1d0] }}
processors:
  - { className: org.apache.tinkerpop.gremlin.server.op.session.SessionOpProcessor, config: { sessionTimeout: 28800000 }}
  - { className: org.apache.tinkerpop.gremlin.server.op.traversal.TraversalOpProcessor, config: { cacheExpirationTime: 600000, cacheMaxSize: 1000 }}
metrics: {
  consoleReporter: {enabled: true, interval: 180000},
  csvReporter: {enabled: true, interval: 180000, fileName: /tmp/gremlin-server-metrics.csv},
  jmxReporter: {enabled: true},
  slf4jReporter: {enabled: true, interval: 180000},
  gangliaReporter: {enabled: false, interval: 180000, addressingMode: MULTICAST},
  graphiteReporter: {enabled: false, interval: 180000}}
maxInitialLineLength: 4096
maxHeaderSize: 8192
maxChunkSize: 8192
maxContentLength: 65536
maxAccumulationBufferComponents: 1024
resultIterationBatchSize: 64
writeBufferLowWaterMark: 32768
writeBufferHighWaterMark: 65536
```


# Scalaのクライアント
今回のクライアントは、[gremlin-scala](https://github.com/mpollmeier/gremlin-scala)を使います。わかりやすいDSLを提供していていい感じです。容易に接続できるのですが、上記と同じ仕様のデシリアライザをクライアント側でも使う必要があります。

そのために、必要ライブラリをインポートしておきます。`Janus-core`に付属しているシリアライザを使うのがポイントです。[サンプル](https://github.com/mpollmeier/gremlin-scala-examples/blob/master/gremlin-server/src/test/scala/SimpleSpec.scala)にあるように`TinkerIoRegistryV3d0`を使うと失敗します。ここ重要。

```build.sbt
name := "BotImporter"

version := "0.1"

scalaVersion := "2.13.0"

libraryDependencies ++= Seq("org.apache.jena" % "jena-core" % "3.12.0",
  "com.typesafe" % "config" % "1.3.4",
  "com.michaelpollmeier" %% "gremlin-scala" % "3.4.1.8",
  "org.apache.tinkerpop" % "gremlin-driver" % "3.3.1",
  "org.apache.tinkerpop" % "tinkergraph-gremlin" % "3.3.1",
  "ch.qos.logback" % "logback-classic" % "1.2.3",
  "org.janusgraph" % "janusgraph-core" % "0.3.1",
)

fork in Test := true
resolvers += Resolver.mavenLocal
resolvers += "Apache public" at "https://repository.apache.org/content/groups/public/"
```

後は、サンプルを参考に組み立てます。最後に適当にトレースしていますが、きちんと変更が反映されているのが分かります。

``` TinkerpopTest.scala
object TinkerpopTest {

  val level = "WARN"
  val context: LoggerContext = LoggerFactory.getILoggerFactory.asInstanceOf[LoggerContext]
  context.getLogger(this.getClass).setLevel(Level.valueOf(level))

  def main(args: Array[String]): Unit = {
    val serializer = new GryoMessageSerializerV3d0(GryoMapper.build.addRegistry(JanusGraphIoRegistry.getInstance()))
    val cluster = Cluster.build.addContactPoint("localhost").port(8182).serializer(serializer).create
    implicit val graph = EmptyGraph.instance.asScala.configure(_.withRemote(DriverRemoteConnection.using(cluster, "g")))
    val g = graph.traversal
    // see org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerFactory.generateClassic()
    // Keys for properties which can later be used for type safe traversals
    val Founded = Key[String]("founded")
    val Distance = Key[Int]("distance")

    // create labelled vertex
    val paris = graph + "Paris"

    // create vertex with typed properties
    val london = graph + ("London", Founded -> "43 AD")

    // create labelled edges
    paris --- "OneWayRoad" --> london
    paris <-- "OtherWayAround" --- london
    paris <-- "Eurostar" --> london

    g.V().toList().foreach(e => println(e.label()))

    cluster.close()
    System.exit(0)
  }
}
```

# 参考
- https://stackoverflow.com/questions/55457307/negativearraysizeexception-error-is-raised-when-trying-to-create-an-edge-between