%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
-module(jinterface_SUITE).

-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, 
	 init_per_suite/1, end_per_suite/1,
	 init_per_testcase/2, end_per_testcase/2]).

-export([nodename/1, register_and_whereis/1, get_names/1, boolean_atom/1,
	 node_ping/1, mbox_ping/1,
	 java_erlang_send_receive/1,
	 java_internal_send_receive_same_node/1,
	 java_internal_send_receive_different_nodes/1,
	 java_internal_send_receive_self/1,
	 java_link_and_exit/1, erl_link_and_exit/1,
	 erl_link_java_exit/1, java_link_erl_exit/1,
	 internal_link_linking_exits/1, internal_link_linked_exits/1,
	 internal_unlink_linking_exits/1, internal_unlink_linked_exits/1,
	 normal_exit/1, kill_mbox/1,kill_erl_proc_from_java/1,
	 kill_mbox_from_erlang/1,
	 erl_exit_with_reason_any_term/1,
	 java_exit_with_reason_any_term/1,
	 status_handler_localStatus/1, status_handler_remoteStatus/1,
	 status_handler_connAttempt/1]).

-include_lib("common_test/include/ct.hrl").
-include("test_server_line.hrl").

-define(debug,true).
-ifdef(debug).
-define(dbg(Str,Args), io:format(Str,Args)).
-else.
-define(dbg(Str,Args), ok).
-endif.

-define(link_test_reason,link_test_reason).

%% Test cases in MboxSendReceive.java
-define(java_erlang_send_receive,1).
-define(java_internal_send_receive_same_node,2).
-define(java_internal_send_receive_different_nodes,3).
-define(java_internal_send_receive_self,4).

%% Test cases in MboxLinkUnlink.java
-define(java_link_and_exit, 1).
-define(erl_link_and_exit, 2).
-define(erl_link_java_exit, 3).
-define(java_link_erl_exit, 4).
-define(internal_link_linking_exits, 5).
-define(internal_link_linked_exits, 6).
-define(internal_unlink_linking_exits,7).
-define(internal_unlink_linked_exits,8).
-define(normal_exit,9).
-define(kill_mbox,10).
-define(kill_erl_proc_from_java,11).
-define(kill_mbox_from_erlang,12).
-define(erl_exit_with_reason_any_term,13).
-define(java_exit_with_reason_any_term,14).


%% Test cases in NodeStatusHandler.java
-define(status_handler_localStatus,1).
-define(status_handler_remoteStatus,2).
-define(status_handler_connAttempt,3).

%%%-----------------------------------------------------------------
%%% INIT/END
%%%-----------------------------------------------------------------
suite() -> [{ct_hooks,[ts_install_cth]}].

all() -> 
    lists:append([fundamental(), ping(), send_receive(),
		  link_unlink(), status_handler()]).

groups() -> 
    [].

init_per_group(_GroupName, Config) ->
    Config.

end_per_group(_GroupName, Config) ->
    Config.


fundamental() ->
    [
     nodename,             % Nodename.java
     register_and_whereis, % RegisterAndWhereis.java
     get_names,            % GetNames.java
     boolean_atom          % BooleanAtom.java
    ].

ping() ->
    [
     %% Implemented in NodePing.java
     node_ping,

     %% Implemented in MboxPing.java
     mbox_ping
     ].


send_receive() ->
    [
     %% Implemented in MboxSendReceive.java
     java_erlang_send_receive,
     java_internal_send_receive_same_node,
     java_internal_send_receive_different_nodes,
     java_internal_send_receive_self
    ].

%% Note:
%%
%% The test cases in MboxLinkUnlink.java and in
%% NodePing.java, all uses default cookie, and if there
%% is a problem with having the same default cookie in
%% erlang vs jinterface, e.g because the home directory
%% does not get the same in some cases on Windows
%% - they will all fail.

link_unlink() ->
    [
     %% Implemented in MboxLinkUnlink.java
     java_link_and_exit,
     erl_link_and_exit,
     erl_link_java_exit,
     java_link_erl_exit,
     internal_link_linking_exits,
     internal_link_linked_exits,
     internal_unlink_linking_exits,
     internal_unlink_linked_exits,
     normal_exit,
     kill_mbox,
     kill_erl_proc_from_java,
     kill_mbox_from_erlang,
     erl_exit_with_reason_any_term,
     java_exit_with_reason_any_term
    ].

status_handler() ->
    [
     %% Implemented in NodeStatusHandler.java
     status_handler_localStatus,
     status_handler_remoteStatus,
     status_handler_connAttempt
    ].


init_per_suite(Config) when is_list(Config) ->
    case case code:priv_dir(jinterface) of
	     {error,bad_name} -> false;
	     P -> filelib:is_dir(P) end of
	true ->
	    jitu:init_all(Config);
	false ->
	    {skip,"No jinterface application"}
    end.

end_per_suite(Config) when is_list(Config) ->
    jitu:finish_all(Config).

init_per_testcase(Case, _Config) 
  when Case =:= kill_mbox;
       Case =:= kill_mbox_from_erlang ->
    {skip, "Not yet implemented"};
init_per_testcase(_Case,Config) ->
    Dog = ?t:timetrap({seconds,10}),
    [{watch_dog,Dog}|Config].

end_per_testcase(_Case,Config) ->
    ?t:timetrap_cancel(?config(watch_dog,Config)),
    ok.


%%%-----------------------------------------------------------------
%%% TEST CASES
%%%-----------------------------------------------------------------
nodename(doc) ->
    ["Nodename.java: "
     "Test OtpNode.node(), OtpNode.alive() and OtpNode.host()"];
nodename(suite) ->
    [];
nodename(Config) when is_list(Config) ->
    [_,Host] = string:tokens(atom_to_list(node()),"@"),
    ok = jitu:java(?config(java, Config),
		   ?config(data_dir, Config),
		   "Nodename",
		   [list_to_atom(Host)]).

%%%-----------------------------------------------------------------
register_and_whereis(doc) ->
    ["RegisterAndWhereis.java: "
     "Test OtpNode.registerName(...), OtpMbox.registerName(...) and "
     "OtpNode.whereis(...)"];
register_and_whereis(suite) ->
    [];
register_and_whereis(Config) when is_list(Config) ->
    ok = jitu:java(?config(java, Config),
		   ?config(data_dir, Config),
		   "RegisterAndWhereis",
		   []).

%%%-----------------------------------------------------------------
get_names(doc) ->
    ["GetNames.java: "
     "Test OtpNode.getNames()"];
get_names(suite) ->
    [];
get_names(Config) when is_list(Config) ->
    ok = jitu:java(?config(java, Config),
		   ?config(data_dir, Config),
		   "GetNames",
		   []).

%%%-----------------------------------------------------------------
boolean_atom(doc) ->
    ["BooleanAtom.java: "
     "Test OtpErlangAtom.booleanValue()"];
boolean_atom(suite) ->
    [];
boolean_atom(Config) when is_list(Config) ->
    ok = jitu:java(?config(java, Config),
		   ?config(data_dir, Config),
		   "BooleanAtom",
		   []).

%%%-----------------------------------------------------------------
node_ping(doc) ->
    ["NodePing.java: "
     "Test OtpNode.ping(java.lang.String node, long timeout)"];
node_ping(suite) ->
    [];
node_ping(Config) when is_list(Config) ->
    ok = jitu:java(?config(java, Config),
		   ?config(data_dir, Config),
		   "NodePing",
		   [erlang:get_cookie(),node()]).

%%%-----------------------------------------------------------------
mbox_ping(doc) ->
    ["MboxPing.java: "
     "Test OtpNode.createMbox(...) and OtpMbox.ping(...)"];
mbox_ping(suite) ->
    [];
mbox_ping(Config) when is_list(Config) ->
    ok = jitu:java(?config(java, Config),
		   ?config(data_dir, Config),
		   "MboxPing",
		   [erlang:get_cookie(),node()]).

%%%-----------------------------------------------------------------
java_erlang_send_receive(doc) ->
    ["Test sending/receiving of erlang messages between erlang and java"];
java_erlang_send_receive(suite) ->
    [];
java_erlang_send_receive(Config) when is_list(Config) ->
    send_receive(?java_erlang_send_receive, fun echo_loop/0, Config).

echo_loop() ->
    receive
	{From,Msg} ->
	    ?dbg("erl_send_receive_server received ~p",[{From,Msg}]),
	    ?dbg("erl_send_receive_server sending ~p",[Msg]),
	    From ! Msg,
	    echo_loop();
	done ->
	    ok
    end.


%%%-----------------------------------------------------------------
java_internal_send_receive_same_node(doc) ->
    ["MboxSendReceive.java: "
     "Test sending/receiving of erlang messages between mboxes "
     "on the same java node."];
java_internal_send_receive_same_node(suite) ->
    [];
java_internal_send_receive_same_node(Config) when is_list(Config) ->
    send_receive(?java_internal_send_receive_same_node,
		 fun() -> receive done -> ok end end,
		 Config).

%%%-----------------------------------------------------------------
java_internal_send_receive_different_nodes(doc) ->
    ["MboxSendReceive.java: "
     "Test sending/receiving of erlang messages between mboxes "
     "on different java nodes."];
java_internal_send_receive_different_nodes(suite) ->
    [];
java_internal_send_receive_different_nodes(Config) when is_list(Config) ->
    send_receive(?java_internal_send_receive_different_nodes,
		 fun() -> receive done -> ok end end,
		 Config).

%%%-----------------------------------------------------------------
java_internal_send_receive_self(doc) ->
    ["MboxSendReceive.java: "
     "Test sending/receiving of erlang messages from an mbox to itself"];
java_internal_send_receive_self(suite) ->
    [];
java_internal_send_receive_self(Config) when is_list(Config) ->
    send_receive(?java_internal_send_receive_self,
		 fun() -> receive done -> ok end end,
		 Config).

%%%-----------------------------------------------------------------
java_link_and_exit(doc) ->
    ["MboxLinkUnlink.java: "
     "Test link between erlang process and java mailbox."
     "Java mailbox links and exits"];
java_link_and_exit(suite) ->
    [];
java_link_and_exit(Config) when is_list(Config) ->
    LinkFun =
	fun(Mbox) ->
		Mbox ! {?java_link_and_exit,self(),?link_test_reason},
		receive after infinity -> ok end
	end,
    erl_java_link(LinkFun,java_link_and_exit,Config).


%%%-----------------------------------------------------------------
erl_link_and_exit(doc) ->
    ["MboxLinkUnlink.java: "
     "Test link between erlang process and java mailbox."
     "Erlang process links and exits"];
erl_link_and_exit(suite) ->
    [];
erl_link_and_exit(Config) when is_list(Config) ->
    LinkFun = fun(Mbox) ->
		      link(Mbox),
		      Mbox ! {?erl_link_and_exit,self(),?link_test_reason},
		      receive ok -> ok end,
		      exit(?link_test_reason)
	      end,
    erl_java_link(LinkFun,erl_link_and_exit,Config).

%%%-----------------------------------------------------------------
erl_link_java_exit(doc) ->
    ["MboxLinkUnlink.java: "
     "Test link between erlang process and java mailbox."
     "Erlang process links and java mailbox exits"];
erl_link_java_exit(suite) ->
    [];
erl_link_java_exit(Config) when is_list(Config) ->
    LinkFun = fun(Mbox) ->
		      link(Mbox),
		      Mbox ! {?erl_link_java_exit,self(),?link_test_reason},
		      receive after infinity -> ok end
	      end,
    erl_java_link(LinkFun,erl_link_java_exit,Config).


%%%-----------------------------------------------------------------
java_link_erl_exit(doc) ->
    ["MboxLinkUnlink.java: "
     "Test link between erlang process and java mailbox."
     "Java mailbox links and erlang process exits"];
java_link_erl_exit(suite) ->
    [];
java_link_erl_exit(Config) when is_list(Config) ->
    LinkFun =
	fun(Mbox) ->
		Mbox ! {?java_link_erl_exit,self(),?link_test_reason},
		receive ok -> ok end,
		exit(?link_test_reason)
	end,
    erl_java_link(LinkFun,java_link_erl_exit,Config).

%%%-----------------------------------------------------------------
internal_link_linking_exits(doc) ->
    ["MboxLinkUnlink.java: "
     "Test link between two java mailboxes."
     "The mailbox which creates the link is the one exiting"];
internal_link_linking_exits(suite) ->
    [];
internal_link_linking_exits(Config) when is_list(Config) ->
     internal_link_unlink(?internal_link_linking_exits,
			  internal_link_linking_exits,
			  Config).

%%%-----------------------------------------------------------------
internal_link_linked_exits(doc) ->
    ["MboxLinkUnlink.java: "
     "Test link between two java mailboxes."
     "The mailbox which dies not create the link is the one exiting"];
internal_link_linked_exits(suite) ->
    [];
internal_link_linked_exits(Config) when is_list(Config) ->
    internal_link_unlink(?internal_link_linked_exits,
			 internal_link_linked_exits,
			 Config).

%%%-----------------------------------------------------------------
internal_unlink_linking_exits(doc) ->
    ["MboxLinkUnlink.java: "
     "Test link and unlink between two java mailboxes. "
     "Mailbox1 creates a link to mailbox2 and removes it. "
     "Then it creates another link and mailbox2 removes is. "
     "Finally mailbox1 exits - mailbox2 must survive"];
internal_unlink_linking_exits(suite) ->
    [];
internal_unlink_linking_exits(Config) when is_list(Config) ->
     internal_link_unlink(?internal_unlink_linking_exits,
			  internal_unlink_linking_exits,
			  Config).

%%%-----------------------------------------------------------------
internal_unlink_linked_exits(doc) ->
    ["MboxLinkUnlink.java: "
     "Test link and unlink between two java mailboxes. "
     "Mailbox1 creates a link to mailbox2 and removes it. "
     "Then it creates another link and mailbox2 removes is. "
     "Finally mailbox2 exits - mailbox1 must survive"];
internal_unlink_linked_exits(suite) ->
    [];
internal_unlink_linked_exits(Config) when is_list(Config) ->
    internal_link_unlink(?internal_unlink_linked_exits,
			 internal_unlink_linked_exits,
			 Config).

%%%-----------------------------------------------------------------
normal_exit(doc) ->
    ["MboxLinkUnlink.java: "
     "Test that mbox.close() uses exit reason 'normal', i.e. "
     "that linked processes are not terminated."];
normal_exit(suite) ->
    [];
normal_exit(Config) when is_list(Config) ->
    Fun =
	fun() ->
		register(erl_link_server,self()),
		process_flag(trap_exit,true),
		receive
		    {Main,Mbox} when is_pid(Main), is_pid(Mbox) ->
			?dbg("Erlang sending \"~p\"",[normal_exit]),
			link(Mbox),
			Pid = spawn_link(fun() ->
						 link(Mbox),
						 Mbox ! {?normal_exit},
						 receive after infinity -> ok end
					 end),
			receive
			    {'EXIT',Mbox,normal} ->
				%% Make sure that we don't get the
				%% exit signal from Pid, and Pid
				%% should still be alive.
				receive
				    {'EXIT',Pid,Reason} ->
					?dbg("Got unexpected exit signal: ~p",
					     [{'EXIT',Pid,Reason}]),
					exit({unexpected,{'EXIT',Pid,Reason}})
				after 500 ->
					true = erlang:is_process_alive(Pid),
					exit(Pid,kill)
				end,
				receive done -> Main ! done end
			after 1000 ->
				receive
				    Other ->
					?dbg("Got garbage when waiting for exit:"
					     " ~p", [Other]),
					Main ! done,
					exit({got_unexpected,Other})
				after 0 ->
					ok
				end
			end;
		    Other ->
			?dbg("Got garbage: ~p",[Other]),
			exit(Other)
		end
	end,

    spawn_link(Fun),
    ok = jitu:java(?config(java, Config),
		   ?config(data_dir, Config),
		   "MboxLinkUnlink",
		   [erlang:get_cookie(),node()]).


%%%-----------------------------------------------------------------
kill_mbox(doc) ->
    ["MboxLinkUnlink.java: "
     "Test that mbox.exit(new OtpErlangAtom(\"kill\") causes linked "
     "processes to exit with reason 'killed', which can be trapped."];
kill_mbox(suite) ->
    {skip, "Not yet implemented"};
kill_mbox(Config) when is_list(Config) ->
    Fun =
	fun() ->
		register(erl_link_server,self()),
		process_flag(trap_exit,true),
		receive
		    {Main,Mbox} when is_pid(Main), is_pid(Mbox) ->
			?dbg("Erlang sending \"~p\"",[kill_mbox]),
			Pid = spawn_link(fun() ->
						 process_flag(trap_exit,true),
						 link(Mbox),
						 Mbox ! {?kill_mbox},
						 receive
						     {'EXIT',Mbox,killed} ->
							 exit(correct_reason);
						     {'EXIT',Mbox,R} ->
							 exit({faulty_reason,R})
						 end
					 end),
			receive
			    {'EXIT',Pid,{faulty_reason,Reason}} ->
				receive done -> Main ! done end,
				exit({faulty_reason,Reason});
			    {'EXIT',Pid,im_killed} ->
				receive done -> Main ! done end
			after 1000 ->
				receive
				    Other ->
					?dbg("Got garbage when waiting for exit:"
					     " ~p", [Other]),
					Main ! done,
					exit({got_unexpected,Other})
				after 0 ->
					ok
				end
			end;
		    Other ->
			?dbg("Got garbage: ~p",[Other]),
			exit(Other)
		end
	end,

    spawn_link(Fun),
    ok = jitu:java(?config(java, Config),
		   ?config(data_dir, Config),
		   "MboxLinkUnlink",
		   [erlang:get_cookie(),node()]).

%%%-----------------------------------------------------------------
kill_erl_proc_from_java(doc) ->
    ["MboxLinkUnlink.java: "
     "Test that mbox.exit(pid, new OtpErlangAtom(\"kill\") causes erlang "
     "processes <pid> to be killed, even if trapping exits"];
kill_erl_proc_from_java(suite) ->
    [];
kill_erl_proc_from_java(Config) when is_list(Config) ->
    LinkFun = fun(Mbox) ->
		      process_flag(trap_exit,true),
		      link(Mbox),
		      Mbox ! {?kill_erl_proc_from_java, self()},
		      receive after infinity -> ok end
	      end,
    erl_java_link(LinkFun,kill_erl_proc_from_java,killed,Config).

%%%-----------------------------------------------------------------
kill_mbox_from_erlang(doc) ->
    ["MboxLinkUnlink.java: "
     "Test that exit(Mbox,kill) causes linked the Mbox to be killed, and"
     "linked processes to exit with reason 'killed', even if trapping exits"];
kill_mbox_from_erlang(suite) ->
    {skip, "Not yet implemented"};
kill_mbox_from_erlang(Config) when is_list(Config) ->
    LinkFun = fun(Mbox) ->
		      link(Mbox),
		      Mbox ! {?kill_mbox_from_erlang},
		      exit(Mbox,kill),
		      receive after infinity -> ok end
	      end,
    erl_java_link(LinkFun,kill_mbox_from_erlang,killed,Config).

%%%-----------------------------------------------------------------
erl_exit_with_reason_any_term(doc) ->
    ["MboxLinkUnlink.java: "
     "Test that any erlang term can be used as exit reason when erlang "
     "process exits and is linked to an mbox."];
erl_exit_with_reason_any_term(suite) ->
    [];
erl_exit_with_reason_any_term(Config) when is_list(Config) ->
    Reason = [hei,self(),{this,is,"a",[different,"reason"]}],
    LinkFun = fun(Mbox) ->
		      link(Mbox),
		      Mbox ! {?erl_exit_with_reason_any_term,self(),Reason},
		      receive ok -> ok end,
		      exit(Reason)
	      end,
    erl_java_link(LinkFun,erl_exit_with_reason_any_term,Reason,Config).

%%%-----------------------------------------------------------------
java_exit_with_reason_any_term(doc) ->
    ["MboxLinkUnlink.java: "
     "Test that any erlang term can be used as exit reason when mbox "
     "exits and is linked to an erlang process."];
java_exit_with_reason_any_term(suite) ->
    [];
java_exit_with_reason_any_term(Config) when is_list(Config) ->
    Reason = [hei,self(),{this,is,"a",[different,"reason"]}],
    LinkFun =
	fun(Mbox) ->
		Mbox ! {?java_exit_with_reason_any_term,self(),Reason},
		receive after infinity -> ok end
	end,
    erl_java_link(LinkFun,java_exit_with_reason_any_term,Reason,Config).


%%%-----------------------------------------------------------------
status_handler_localStatus(doc) ->
    ["NodeStatusHandler.java: "
     "Test OtpNode.registerStatusHandler(...) and the callback "
     "OtpNodeStatus.localStatus(...)"];
status_handler_localStatus(suite) ->
    [];
status_handler_localStatus(Config) when is_list(Config) ->
    spawn_link(fun() ->
		       erl_status_server([{opt,{localStatus,"javanode1",true}},
					  {localStatus,"javanode1",false}])
	       end),
    ok = jitu:java(?config(java, Config),
		   ?config(data_dir, Config),
		   "NodeStatusHandler",
		   [erlang:get_cookie(),node(),?status_handler_localStatus]).

%%%-----------------------------------------------------------------
status_handler_remoteStatus(doc) ->
    ["NodeStatusHandler.java: "
     "Test OtpNode.registerStatusHandler(...) and the callback "
     "OtpNodeStatus.remoteStatus(...)"];
status_handler_remoteStatus(suite) ->
    [];
status_handler_remoteStatus(Config) when is_list(Config) ->
    spawn_link(fun() ->
		       erl_status_server([{opt,{localStatus,"javanode1",true}},
					  {remoteStatus,"javanode2",true},
					  {remoteStatus,"javanode2",false}])
	       end),
    ok = jitu:java(?config(java, Config),
		   ?config(data_dir, Config),
		   "NodeStatusHandler",
		   [erlang:get_cookie(),node(),?status_handler_remoteStatus]).


%%%-----------------------------------------------------------------
status_handler_connAttempt(doc) ->
    ["NodeStatusHandler.java: "
     "Test OtpNode.registerStatusHandler(...) and the callback "
     "OtpNodeStatus.connAttempt(...)"];
status_handler_connAttempt(suite) ->
    [];
status_handler_connAttempt(Config) when is_list(Config) ->
    spawn_link(fun() ->
		       erl_status_server([{opt,{localStatus,"javanode1",true}},
					  {connAttempt,"unknown",true},
					  {connAttempt,"javanode3",false}])
	       end),
    ok = jitu:java(?config(java, Config),
		   ?config(data_dir, Config),
		   "NodeStatusHandler",
		   [erlang:get_cookie(),node(),?status_handler_connAttempt]).


%%%-----------------------------------------------------------------
%%% INTERNAL FUNCTIONS
%%%-----------------------------------------------------------------
send_receive(TestCaseTag,Fun,Config) ->
    spawn(fun() ->
		  register(erl_send_receive_server,self()),
		  receive
		      From when is_pid(From) ->
			  JavaNode = node(From),
			  [JavaNode] = nodes(hidden),
			  From ! {TestCaseTag,self()},
			  Fun(),
			  unregister(erl_send_receive_server)
		  end
	  end),
    ok = jitu:java(?config(java, Config),
		   ?config(data_dir, Config),
		   "MboxSendReceive",
		   [erlang:get_cookie(),node()]).


internal_link_unlink(Tag,Msg,Config) ->
    Fun =
	fun() ->
		register(erl_link_server,self()),
		process_flag(trap_exit,true),
		receive
		    {Main,Mbox} when is_pid(Main), is_pid(Mbox) ->
			?dbg("Erlang sending \"~p\"",[Msg]),
			Mbox ! {Tag,self(),?link_test_reason},
			receive done -> Main ! done end;
		    Other ->
			?dbg("Got garbage: ~p",[Other]),
			exit(Other)
		end
	end,

    spawn_link(Fun),
    ok = jitu:java(?config(java, Config),
		   ?config(data_dir, Config),
		   "MboxLinkUnlink",
		   [erlang:get_cookie(),node()]).


erl_java_link(LinkFun,Msg,Config) ->
    erl_java_link(LinkFun,Msg,?link_test_reason,Config).

erl_java_link(LinkFun,Msg,Reason,Config) ->
    Fun =
	fun() ->
		register(erl_link_server,self()),
		process_flag(trap_exit,true),
		receive
		    {Main,Mbox} when is_pid(Mbox), is_pid(Mbox) ->
			?dbg("Erlang sending \"~p\"",[Msg]),
			Pid = spawn_link(fun() -> LinkFun(Mbox) end),
			receive
			    {'EXIT',Pid,Reason} ->
				receive done -> Main ! done end
			after 1000 ->
				receive
				    Other ->
					?dbg("Got garbage when waiting for exit:"
					     " ~p", [Other]),
					Main ! done,
					exit({got_unexpected,Other})
				after 0 ->
					ok
				end
			end;
		    Other ->
			?dbg("Got garbage: ~p",[Other]),
			exit(Other)
		end
	end,

    spawn_link(Fun),
    ok = jitu:java(?config(java, Config),
		   ?config(data_dir, Config),
		   "MboxLinkUnlink",
		   [erlang:get_cookie(),node()]).

erl_status_server(List) ->
    register(erl_status_server,self()),
    erl_status_server(List,undefined).
erl_status_server([{opt,{Tag,NodeName,Up}},{Tag2,NodeName2,Up2}|Rest],_) ->
    receive
	{Tag,Node,Up,From} = M ->
	    ?dbg("erl_status_server got: ~p",[M]),
	    true = lists:prefix(NodeName,Node),
	    erl_status_server([{Tag2,NodeName2,Up2}|Rest],From);
	{Tag2,Node2,Up2,From2} = M2 ->
	    ?dbg("erl_status_server got: ~p",[M2]),
	    true = lists:prefix(NodeName2,Node2),
	    erl_status_server(Rest,From2)
    end;
erl_status_server([{Tag,NodeName,Up}|Rest],_) ->
    receive
	{Tag,Node,Up,From} = M ->
	    ?dbg("erl_status_server got: ~p",[M]),
	    true = lists:prefix(NodeName,Node),
	    erl_status_server(Rest,From);
	Other ->
	    ?dbg("erl_status_server got garbage: ~p",[Other]),
	    exit(Other)
    end;
erl_status_server([],From) ->
    From ! done.