/Original-Code/chapter_12/port_driver/json_parser/src/jp_server.erl

https://github.com/sschaefer/Notebook-for-Erlang-And-OTP-in-Action · Erlang · 108 lines · 50 code · 23 blank · 35 comment · 0 complexity · 869f36618a315ea735d5a79fff753c29 MD5 · raw file

  1. %%%-------------------------------------------------------------------
  2. %%% @doc Server for the JSON parser integration.
  3. %%% @end
  4. %%%-------------------------------------------------------------------
  5. -module(jp_server).
  6. -behaviour(gen_server).
  7. %% API
  8. -export([
  9. start_link/0,
  10. parse_document/1,
  11. stop/0
  12. ]).
  13. %% gen_server callbacks
  14. -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
  15. terminate/2, code_change/3]).
  16. -define(SERVER, ?MODULE).
  17. -define(APPNAME, json_parser).
  18. -record(state, {port}).
  19. %%%===================================================================
  20. %%% API
  21. %%%===================================================================
  22. %%--------------------------------------------------------------------
  23. %% @doc Starts the server.
  24. %%
  25. %% @spec start_link() -> {ok, Pid}
  26. %% where
  27. %% Pid = pid()
  28. %% @end
  29. %%--------------------------------------------------------------------
  30. start_link() ->
  31. gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
  32. %%--------------------------------------------------------------------
  33. %% @doc Parses a document.
  34. %% @spec parse_document(Data) -> ok
  35. %% where
  36. %% Data = binary()
  37. %% @end
  38. %%--------------------------------------------------------------------
  39. parse_document(Data) when is_binary(Data) ->
  40. gen_server:call(?SERVER, {parse_document, Data}).
  41. %%--------------------------------------------------------------------
  42. %% @doc Stops the server.
  43. %% @spec stop() -> ok
  44. %% @end
  45. %%--------------------------------------------------------------------
  46. stop() ->
  47. gen_server:cast(?SERVER, stop).
  48. %%%===================================================================
  49. %%% gen_server callbacks
  50. %%%===================================================================
  51. init([]) ->
  52. Port = create_port(),
  53. {ok, #state{port=Port}}.
  54. handle_call({parse_document, Msg}, _From, #state{port=Port}=State) ->
  55. Port ! {self(),{command, term_to_binary(Msg)}},
  56. receive
  57. {Port, {data, Data}} ->
  58. {reply, binary_to_term(Data), State}
  59. end.
  60. handle_cast(stop, State) ->
  61. {stop, normal, State}.
  62. handle_info({Port, {exit_status, Status}}, #state{port=Port}=State) ->
  63. error_logger:format("port exited with status ~p; restarting~n",
  64. [Status]),
  65. NewPort = create_port(),
  66. {noreply, State#state{port=NewPort}}.
  67. terminate(_Reason, _State) ->
  68. ok.
  69. code_change(_OldVsn, State, _Extra) ->
  70. {ok, State}.
  71. %%%===================================================================
  72. %%% Internal functions
  73. %%%===================================================================
  74. create_port() ->
  75. case code:priv_dir(?APPNAME) of
  76. {error, _} ->
  77. error_logger:format("~w priv dir not found~n", [?APPNAME]),
  78. exit(error);
  79. PrivDir ->
  80. case erl_ddll:load(PrivDir, "jp_driver") of
  81. ok -> ok;
  82. Other -> exit(Other)
  83. end,
  84. open_port({spawn, "jp_driver"}, [binary])
  85. end.