%% Inspired by "Programming Erlang",
%% published by The Pragmatic Bookshelf.
%% Copyrights apply to this code.
%% We make no guarantees that this code is fit for any purpose.
%% Visit http://www.pragmaticprogrammer.com/titles/jaerlang for more book information.
-module(server).
-export([start/2, start/3, start_on_node/3, rpc/2, send/2, registration/2]).
start(Name, Mod) ->
register(Name, spawn(fun() -> loop(Name, Mod, Mod:init()) end)).
start(Name, Mod, State) ->
register(Name, spawn(fun() -> loop(Name, Mod, State) end)).
start_on_node(Node, Mod, State) ->
Ref = make_ref(),
Pid = spawn(Node, fun() -> loop(Ref, Mod, State) end),
{Pid, Ref}.
send({Client, _}, Data) ->
send(Client, Data);
send(Client, Data) ->
Client ! Data.
wait_response({_, Name}) ->
wait_response(Name);
wait_response(Name) ->
receive
{Name, crash} -> exit(rpc);
{Name, ok, Response} -> Response
end.
rpc(Client, Request) ->
send(Client, {self(), Request}),
wait_response(Client).
registration(Client, NewName) ->
send(Client, {register, NewName}).
loop(Name, Mod, OldState) ->
receive
{register, NewName} ->
register(NewName, self()),
loop(NewName, Mod, OldState);
{From, Request} ->
try Mod:handle(Request, OldState) of
{Response, NewState} ->
From ! {Name, ok, Response},
loop(Name, Mod, NewState)
catch
_: Why ->
log_the_error(Name, Request, Why),
From ! {Name, crash},
loop(Name, Mod, OldState)
end;
{'EXIT', From, Why} ->
try Mod:handle({'EXIT', From, Why}, OldState) of
NewState ->
loop(Name, Mod, NewState)
catch
_: Why ->
log_the_error(From, 'EXIT', Why),
loop(Name, Mod, OldState)
end
end.
log_the_error(Name, Request, Why) ->
io:format("Server ~p request ~p ~n"
"caused exception ~p~n",
[Name, Request, Why]).