[erlang-questions] Reliable way to handle Java node crashs?

Robert Raschke rtrlists@REDACTED
Fri Jun 18 11:06:44 CEST 2010


On Thu, Jun 17, 2010 at 3:59 PM, zabrane Mikael <zabrane3@REDACTED> wrote:

> Thanks guys for the advices.
>
> 2010/6/17 Robert Raschke <rtrlists@REDACTED>
>
>  I kick off the Java node as a monitored port from within Erlang. That
>> way I am in control of starting, noticing crashes, and restarting. I
>> also have the port set up so that messages written to stdout in the
>> Java node feed into the logging in Erlang.
>>
>> If you want I can dig out some example code when I'm back in the
>> office next week.
>>
>
>
> Robert, I'll be more than happy to read some code!
> Thanks
>
>
> --
> Regards
> Zabrane
>


In Erlang I have a gen_server that interfaces to my Java Node with messages.
This kicks off the port manager and then does a handshake to transmit some
data the Java program needs to do its thing. Please note that I have not
actually compiled or run the example code below, this has been adapted from
some production code.

The port manager is a process that starts the Java node as a port program:

-module(java_manager).

-export([start_link/1, init/3]).

start_link(Node_Name) ->
    spawn_link(?MODULE, init, [Node_Name]).

init(Node_Name) ->
    process_flag(trap_exit, true),
    Cmd = mk_java_cmdline(os:type(), Node_Name),
    error_logger:info_report([{module, ?MODULE}, {java_port, Node_Name},
{'START', Cmd}]),
    P = open_port({spawn, Cmd}, [stream, {line, 100}, exit_status]),
    loop(P, Node_Name, [], []).


loop(Port, Name, Response, Line) ->
    receive
        % These three messages mean that the Java program is no longer
running.
        % So we stop looping.
        % The first is normal termination, the other two are abnormal.
        {Port, {exit_status, 0}} ->
            error_logger:info_report([{module, ?MODULE}, {java_port, Name},
                    {'EXIT', {status, 0}}]);

        {Port, {exit_status, N}} ->
            error_logger:error_report([{module, ?MODULE}, {java_port, Name},
                    {'EXIT', {status, N}}]),
            erlang:error({port_status, N});
        {'EXIT', Port, Reason} ->
            error_logger:error_report([{module, ?MODULE}, {java_port, Name},
                    {'EXIT', Reason}]),
            erlang:error({port_exit, Reason});


        % The data messages come from the standard output of the Java
program.
        % We accumulate the output line by line; potentially having to
assemble
        % each line from pieces. Everything is accumulated through list
cons'ing.
        % Thus all results have to be reversed before use.

        % Unfinished output lines are tagged with noeol.
        % We accumulate the line.
        {Port, {data, {noeol, S}}} ->
            loop(Port, Name, Response, [S | Line]);

        % Finished lines are tagged with eol.
        {Port, {data, {eol, S}}} ->
            case {S, Line} of
                % The convention in the Java program is to send a solitary
"." to signal
                % that this particular bit of output is complete.
                {".", []} ->
                    process_port_data(Name, lists:reverse(Response)),
                    loop(Port, Name, [], []);
                {S, Line} ->
                    Full_Line = lists:flatten(lists:reverse([S | Line])),
                    loop(Port, Name, [Full_Line | Response], [])
            end
    end.

% Messages from the Java program are logged as info messages.
process_port_data(Name, Text) ->
    error_logger:info_report([{module, ?MODULE}, {java_port, Name},
            {'STDOUT', string:join(Text, "\n")}]).


mk_java_cmdline({Ostype, _}, Node_Name) ->
    mk_java_cmdline(Ostype, Node_Name);
mk_java_cmdline(Ostype, Node_Name) ->
    lists:flatten([
        os:find_executable("java"),
            " -Xrs -classpath myPackage ",
            " myPackage.myJavaNode ",
                quote(Ostype, Node_Name), " ",
                quote(Ostype, my_cookie())
    ]).

my_cookie() ->
    atom_to_list(erlang:get_cookie()).

quote(win32, S) ->
    ["\"", S, "\""];
quote(unix, S) ->
    ["'", S, "'"].



And the Java node looks something like this:

package myPackage;

import com.ericsson.otp.erlang.*;

public class myJavaNode {

    private static void print(String s)
    {
        System.out.println(s);
        System.out.println(".");
        System.out.flush();
    }

    public static void main(String[] args)
    {
        // System.setProperty("OtpConnection.trace", "4");

        if (args.length != 2) {
            print("Invalid arguments.");
            System.exit(10);
        }

        String node_name = args[0];
        String cookie = args[1];

        OtpNode node = null;
        try {
            node = new OtpNode(node_name, cookie);
        } catch(java.io.IOException ioe) {
            String msg = ioe.getMessage();
            if (msg != null) {
                print("OtpNode creation error: " + msg);
            }
            System.exit(11);
        }

        try {
            OtpMbox mbox = node.createMbox("myBox");
            shake_hands(mbox);

            // go into your message loop

            node.closeMbox(mbox);
        } catch (Exception e) {
            e.printStackTrace(System.out);
            System.out.println("."); System.out.flush();
            System.exit(12);
        }

        node.close();
    }

    private static void shake_hands(OtpMbox mbox)
        throws Exception
    {
        OtpErlangTuple msg = (OtpErlangTuple) mbox.receive(5000);
        if (msg == null) {
            print("Handshake timed out.");
            System.exit(13);
        }

        OtpErlangPid from = (OtpErlangPid) msg.elementAt(0);
        OtpErlangAtom hand = (OtpErlangAtom) msg.elementAt(1);

        if ("ok".equals(hand.atomValue())) {
            mbox.link(from);
            mbox.send(from, new OtpErlangTuple(new OtpErlangObject[] {
                new OtpErlangAtom("ok"),
                mbox.self(),
            }));
        } else {
            mbox.send(from, new OtpErlangAtom("who are you?"));
            print("Not received ok: " + msg);
            System.exit(14);
        }
    }

}


I hope this'll give you a few ideas.

Robby


More information about the erlang-questions mailing list