【Corda4.4】ServiceHub.DiagnosticsServiceを使って、ノード起動時にバージョンとかを表示しよう


はじめに

どーも、のぶこふです。

今回は、Corda4.4の新機能であるServiceHub.DiagnosticsServiceを使用して、ノード起動時に色々と表示してみようぜ。という内容です。

※2020/07/21現在、最新は4.5です

ServiceHub.DiagnosticsServiceとは?

Corda4.4 で新規導入されたAPIです。
ざっくりとした説明をすると、実行しているCordaのエディション(OSS/Enterprise)だったり、バージョンを取得することが可能です。

リリースノートでは「開発者向け」と記述があり、ドキュメントには次のような記述があります。

  • ServiceHub.diagnosticsService
    • ノードのバージョンや現在実行中のアプリなど、ノードに関する診断情報を提供します。このデータは診断目的でのみ使用してください。

ふーむ、開発時での使用が望ましいようですね。

さて、これにより、何が嬉しいか・・・?
そうです、ノード起動時に「PartyAってどいつだ?」みたいなのを探す手間が(少し)減ります。
え?私だけ?そ、そんなことは・・・

いや、きっともっとメリットはあるとは思うんですけど、今はそれしか思いつかないですw
あ、ノード名を取得するだけだったら、DiagnosticsServiceを使わなくても大丈夫でした。

とりあえず、起動時のサンプル画面を見てください。
どうですか?黄色枠はデフォルト。赤枠が今回追記した箇所です。
ノード名がわかりやすくなったのでは無いでしょうか?
※ノード名を取得するだけだったら、DiagnosticsServiceを使わなくても大丈夫です

え?AAを見れば分かる情報じゃないか・・・って?
ほ、ほら、ノード名とか・・
※ノード名を取得するだけだったら、DiagnosticsServiceを使わなくても大丈b(ry

ということで、今回は、上記の実装テンプレートをご紹介します。

ソースコード

[PR]私のGithubにテンプレートを用意してあるので、そちらを使ってみてください。
https://github.com/nobkovskii/cordapp-template-kotlin_with_DiagnosticsService

説明

コレで終わると、流石にアレなので、簡単に説明します。

今回、新しく「MyService」というクラスを作成しました。
「init」内で、ノード起動時の処理を定義することが出来ます。(①)
これは、前々からありますね。

privateの関数(showNodeInfo)を定義し、その中でServiceHub.DiagnosticsServiceを使っています。(②)
この関数をinitで呼べば、ノード起動時に実行されるというわけです。

package com.template.services

import net.corda.core.node.AppServiceHub
import net.corda.core.node.services.CordaService
import net.corda.core.node.services.ServiceLifecycleEvent
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.loggerFor
import java.util.concurrent.Executors

@CordaService
class MyService(private val appServiceHub: AppServiceHub) : SingletonSerializeAsToken() {
    private companion object {
        // logger
        val log = loggerFor<MyService>()

        // Thread
        val executor = Executors.newFixedThreadPool(8)!!
    }

    init { // --------------------------------------------①
        showNodeInfo()
        appServiceHub.register { customEvent(it) }
    }

    /**
     * custom event
     */
    private fun customEvent(event: ServiceLifecycleEvent) {
        when (event) {
            ServiceLifecycleEvent.STATE_MACHINE_STARTED -> {
                // ノードが完全に起動したら実行される
                // executor.execute{ appServiceHub.startFlow(Initiator())}
            }
            else -> {
            }
        }
    }

    /**
     * show my node info
     */
    private fun showNodeInfo() {// ---------------------------------②
        val nodeInfo = appServiceHub.diagnosticsService.nodeVersionInfo()
        println("\n------ node info ------\n" +
                "Platform ver : ${nodeInfo.platformVersion} \n" +
                "Release ver  : ${nodeInfo.releaseVersion} \n" +
                "Version      : ${nodeInfo.revision} \n" +
                "Vendor       : ${nodeInfo.vendor} \n" +
                "Party Name   : ${appServiceHub.myInfo.legalIdentities[0].name} \n" +
                "------ node info ------\n")
    }
}

おまけ

お気づきの方もいるかもしれませんが、実は今回の実装は「ServiceHub.DiagnosticsService」だけではありません。
ちゃっかり「AppServiceHub.register」も実装しています。

こちらも同様にCorda4.4で新規導入されたAPIで、ノードが完全に起動したら実行するイベントを登録することが出来ます。

customEvent内のexecutor.executeをコメントアウトを外すだけで、使えるようになっています。

該当箇所
    init {
        appServiceHub.register { customEvent(it) }
    }

    /**
     * custom event
     */
    private fun customEvent(event: ServiceLifecycleEvent) {
        when (event) {
            ServiceLifecycleEvent.STATE_MACHINE_STARTED -> {
                // ノードが完全に起動したら実行される
                // executor.execute{ appServiceHub.startFlow(Initiator())}
            }
            else -> {
            }
        }
    }

Flowを呼び出すことも、VaultQueryを投げることも可能ですが、呼び出し先Classに「@StartableByService」アノテーションを付与する必要があるので注意が必要です。

package com.template.flows

import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.*
import net.corda.core.utilities.ProgressTracker

// *********
// * Flows *
// *********
@InitiatingFlow
@StartableByRPC
@StartableByService // <---------------------------- 忘れずに
class Initiator : FlowLogic<Unit>() {
    override val progressTracker = ProgressTracker()

    @Suspendable
    override fun call() {
        // Initiator flow logic goes here.
    }
}

@InitiatedBy(Initiator::class)
class Responder(val counterpartySession: FlowSession) : FlowLogic<Unit>() {
    @Suspendable
    override fun call() {
        // Responder flow logic goes here.
    }
}

おわりに

今回はCorda4.4の新機能である「ServiceHub.DiagnosticsService」と、おまけで「ServiceHub.DiagnosticsService」の紹介説明を行いました。
CordaのAAは好きなんですが、表形式で表示されるのも好きなので、起動時のオプションとかで設定できたら良いかもですね。

今回はここまでです。
ありがとうございました。

余談

QiitaにQ&Aや意見交換が追加されましたね。
個人的には、良い改修だなと思います。

「〇〇が解決できない」→「調べてみてもStackOverFlowとかしか出てこない。英語わからない」→「質問(解決)できない」
みたいな流れを払拭できるのではないでしょうか。
IT業界では「ggrks(ググレカス)」が「qiitarks(キタレカス)」になったりしてw

はい、本当に中身のない余談でした。