LoginSignup
2
1

More than 5 years have passed since last update.

KotlinでJava RMIを利用する。

Posted at

Kotlinを使ったWebアプリケーションサーバーを作成している時に、サーバーを分散できるように、複数モジュールでの通信を行おうと考えました。
KotlinはJVMで動いており、Javaの資産は全て使えます。
つまり、JVMでのORB/RPCであるRMIも使えるということになります。
基本はJavaと同じですがメモ書き程度にやったことを残しておきます。

構成

Projectは、以下の3つのモジュールに分割されています。

  • RMI Interface
  • RMI Server
  • RMI Client

RMI Client及びRMI Serverは共にRMI Interfaceに依存しており、RMI ClientとRMI Serverにおける直接の依存関係はありません。

RMI Interface

RMI Interfaceモジュールは、下記のExampleRMIInterfaceのみを持ちます。

ExampleRMIInterface.kt

import java.rmi.Remote
import java.rmi.RemoteException

interface ExampleRMIInterface: Remote {
    @Throws(RemoteException::class)
    fun echo(text: String): String
}

RMIに用いるInterfaceは全てjava.rmi.Remoteを継承します。
また、RMIに用いるInterfaceの全てのメソッドはthrows RemoteException宣言されている必要があります。
Kotlinにおいては、@Throwsアノテーションをつけることで、コンパイル時にthrows宣言と同等の扱いになります。

RMI Server

RMI Serverは、ExampleRMIInterfaceの実装であるExampleRMIImplと、メインクラスとなるExampleRMIServer.ktを持ちます。

ExampleRMIImple.kt

class ExampleRMIImpl: ExampleRMIInterface, UnicastRemoteObject(23456) {
    override fun echo(text: String): String {
        println(text)
        return text
    }
}

RMIに用いるInterfaceの実装には、@Throwsアノテーションをつける必要はありません。
また、UnicastRemoteObjectを継承することで、exportする手間を省くことができます。
UnicastRemoteObjectのコンストラクタに入れている数値はポート番号で、省略した場合匿名ポートが使われます。

ExampleRMIServer.kt
import java.rmi.registry.LocateRegistry

fun main(args: Array<String>) {
    val registry = LocateRegistry.createRegistry(23450)
    val remote = ExampleRMIImpl()
    registry.bind("example", remote)
}

まず、LocateRegistry.createRegistry()でRMIのRegistryを作成します。
このコードは、rmiregistryコマンドを実行することの完全な代替で、示す挙動はほぼ同じです。
独立したプロセスで行われるか、Server内の1スレッドとして行われるかの違いです。
また、引数はRegistryのポート番号で、クライアント側はこのポート番号を指定することでRegistryを取得できます。
そして、registry.bind()でRegistryにRemoteインターフェースを実装したオブジェクトを登録します。
第一引数は識別名で、LocateRegistryからこの識別名を用いてRMIのオブジェクトを取得できます。

RMI Client

RMI Clientは、RMIを用いてメソッドを呼び出す、メイン関数/クラスのみを持ちます。

ExampleClient.kt

fun main(args: Array<String>) {
    val registry = LocateRegistry.getRegistry(23450)
    val example = registry.lookup("example") as ExampleRMIInterface
    example.echo("Hello, RMI!")
}

まず、ポートを指定してLocateRegistryを取得します。
同一物理サーバー内でない場合、第一引数にRegistryのホストのアドレスを文字列で入れることで、対象を指定できます。
次に、LocateRegistryに登録されているRemoteオブジェクトを取得し、使用しているRMI用のInterfaceにキャストします。
対象の検索はRegistryへの登録時に使った識別名を用います。
そして、取得したRemoteオブジェクトのメソッドを呼び出すと、RMI Serverへの通信が行われ、RMI Server側の実装が呼び出されます。

最後に

RMIについてわかりやすくまとめたドキュメントが少ない気がする(この記事がわかりやすいとは言っていない)
特に、rmiregistryを使わずにコードベースだけでやる方法は、中途半端だったりして情報を集めるのに多少苦労しました。
が、仕組みが分かってしまうと非常に簡単に実装することができ、通信をほとんど意識すること無く通信ができるので、非常に便利ですね。RMI。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1