ErlangとJavaをつなぐ

この記事では、ErlangJavaの連携方法を説明します。

使い道

Erlangで出来ているプログラムを使いたいが、今Erlangが分かる人がいない。
でも、Javaが分かる人はいる。こういった状況でErlang + Javaが使えるのではないでしょうか。

また、Erlangがネットワークや並行処理に強い反面、
他の言語に比べてパフォーマンスがよくないところ(演算など)があるようで、
そういった部分はJavaに担当させるのもいいでしょう。

GUIRDB操作はJavaに任せて、分散処理はErlangに担当させるなどもありだと思います。

実際、Erlang + Javaではありませんが、
ある商用のメール配信エンジン(?)は、
メインをErlangにして、計算速度が必要な部分は
C言語で開発したとの記事を見たことがあります。

Jinterface

JavaからErlang プログラムを呼び出すための、Erlang 公式Java APIパッケージです。
APIは実際通信を行う部分とErlangデータタイプをJavaクラスとして実装した部分で構成されています。
このAPIパッケージを使えば、Erlangプログラムと連携できます。

jinterfaceのjarファイルは、${ERLANG_HOME}/lib/jinterface(又はjinterface-x.y.z)/privディレクトリ配下にOtpErlang.jarという名前で配置されています。

サンプル

受け取ったメッセージをそのまま返すエコサーバのようなErlangプログラムと、
そのエコサーバを呼び出すJavaプログラムを作成してみます。

まず、Erlangプログラムです。
Erlang 基礎ポイント7 - 分散処理 - DukeLabに出たサンプルソースと同じです。

-module(echo).
-export([start/0, send/2]).

start() -> register(echo, spawn(fun() -> loop() end)).

send(Msg, Receiver) ->
    Receiver ! {self(), {server, Msg}},
    receive
        {client, Res} ->
            Res
    end.

loop() ->
    receive
        {From, {server, Msg}} ->
            From ! {client, string:concat("Echo response : ", Msg)},
            loop()
    end.   

このErlangプログラムをコンパイルして、実行しておきます。
今回は、VirtualBox上に動くUbuntu 12.04で実行しました。
REPL環境上でテストもしてみます。
Javaからアクセスする時、識別に必要なため、erl起動時にnameとsetcookieの指定が必要です。
nameとsetcookieはメモしておいて下さい。

$ erl -name echoserver@vpc1.com -setcookie echocookie
(echoserver@vpc1.com)1> c(echo).
{ok,echo}
(echoserver@vpc1.com)2> echo:start().
true
(echoserver@vpc1.com)3> echo:send("Hello!!", echo). % 自分自身への呼出でテスト。
"Echo response : Hello!!"

次は、Javaプログラムです。
ビルドパスにjinterfaceのjarファイル(OtpErlang.jar)を追加して下さい。
今回は、Windows 7環境とEclipseで作成しました。
こちら側のPCにもErlangがインストールされていなければなりません。
jinterfaceのjarだけでは動かないので、注意して下さい。

package dukelab.erlangjavatest;

import java.io.IOException;

import com.ericsson.otp.erlang.OtpErlangAtom;
import com.ericsson.otp.erlang.OtpErlangDecodeException;
import com.ericsson.otp.erlang.OtpErlangExit;
import com.ericsson.otp.erlang.OtpErlangObject;
import com.ericsson.otp.erlang.OtpErlangString;
import com.ericsson.otp.erlang.OtpErlangTuple;
import com.ericsson.otp.erlang.OtpMbox;
import com.ericsson.otp.erlang.OtpNode;

public class ErlangJavaTest {
    public static void main(String[] args) throws IOException, OtpErlangDecodeException, OtpErlangExit {
        // javanode : Javaプログラム側のホスト名です。任意で指定します。
        // echocookie : Erlangプログラム側でerl実行時、指定したクッキー名と同じです。
        OtpNode node = new OtpNode("javanode", "echocookie");
        OtpMbox mBox = node.createMbox();

        // メッセージをErlangプログラム側に送ります。
        // これをErlangプログラムで表現すると、
        // { echo, 'echoserver@vpc1.com' } ! { self(), {server, "Hello Erlang, I am Java."} }になります。
        {
            OtpErlangTuple tuple = createTuple(
                mBox.self(),
                createTuple(
                    new OtpErlangAtom("server"),
                    new OtpErlangString("Hello Erlang, I am Java.")
                )
            );
            // echo : Erlangプログラム側で登録したプロセス名です(register(echo, spawn(fun() -> loop() end)))。
            // echoserver@vpc1.com : Erlangプログラム側でerl実行時、指定したホスト名です。
            mBox.send("echo", "echoserver@vpc1.com", tuple);
        }

        // 応答を受け取ります。
        {
            OtpErlangObject result = mBox.receive();
            OtpErlangTuple tuple = (OtpErlangTuple) result;
            OtpErlangAtom atom = (OtpErlangAtom) tuple.elementAt(0);
            OtpErlangString str = (OtpErlangString) tuple.elementAt(1);

            System.out.println("Response from Erlang : " + str);
        }
    }

    private static OtpErlangTuple createTuple(OtpErlangObject... msg) {
        return new OtpErlangTuple(msg);
    }
}

その後、JavaプログラムのPCにあるhostsファイルに、
先ほどのErlang実行時にnameに指定したドメイン名とIPアドレス(Erlangを実行したPC)の対応を追加します。
例えば、以下のようにです。

192.168.56.101  vpc1.com

さあ、Javaプログラムを実行してみましょう。

Response from Erlang : "Echo response : Hello Erlang, I am Java."

できました!

整理

jinterfaceを使うと、Erlangプログラムと同じく動作するプログラムを
Javaで作成することができます。
サーバ役割のプログラムもクライアント役割のプログラムもできます。
いくつかのErlang機能に対応するJavaクラスを以下に並べます。

  • Erlang起動(Erlang仮想マシン) : OtpNode
  • メッセージをやりとりする(Erlangでの!やreceiveなど) : OtpMbox
  • タプル : OtpErlangTuple
  • 文字列 : OtpErlangString
  • アトム : OtpErlangAtom

この記事が理解できたら、後は、参考資料を読みながら、掘り下げていけばいいと思います。