[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