%% 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]). start(Name, Mod) -> %% like start/3 but call Mod:init() for getting the initial state start(Name, Mod, Mod:init()). start(Name, Mod, State) -> %% start a server with the given initial State and register it as %% Name register(Name, spawn(fun() -> loop(Mod, State) end)). start_on_node(Node, Mod, State) -> %% link start/3 but start the process on the given Node and not %% register it (only local processes could be registered) spawn(Node, fun() -> loop(Mod, State) end). wait_response(_, Ref) -> %% wait for a specific reponse form the server receive {Ref, crash} -> exit(rpc); {Ref, ok, Response} -> Response end. rpc(Client, Request) -> %% make a call to the given client (make a reference to find the %% corresponding response) Ref = make_ref(), Client ! {Ref, self(), Request}, wait_response(Client, Ref). loop(Mod, OldState) -> %% main lopp receive %% pattern, that is matched if a message is directly sended to %% a server (like register and login called form the client %% over the dispatcher) {From, Request} -> try Mod:handle(Request, OldState) of {Response, NewState} -> From ! {ok, Response}, loop(Mod, NewState) catch %% log error if handle function is not working %% correctly and report the crash to the sender _: Why -> log_the_error(Request, Why), From ! {crash}, loop(Mod, OldState) end; %% pattern is matched if rpc/2 is used, response is send with %% the received reference to match the correct response {Ref, From, Request} when is_reference(Ref) -> try Mod:handle(Request, OldState) of {Response, NewState} -> From ! {Ref, ok, Response}, loop(Mod, NewState) catch %% log error if handle function is not working %% correctly and report the crash to the sender _: Why -> log_the_error(Request, Why), From ! {Ref, crash}, loop(Mod, OldState) end; %% handle messages if a linked process emits an error (no %% response to sender nessessary, sender is dead) {'EXIT', From, Why} -> try Mod:handle({'EXIT', From, Why}, OldState) of NewState -> loop(Mod, NewState) catch %% log error if handle function is not working %% correctly and report the crash to the sender _: Why -> log_the_error('EXIT', Why), loop(Mod, OldState) end end. log_the_error(Request, Why) -> %% helper function to log an error io:format("Server request ~p caused exception ~p~n", [Request, Why]).