Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial Code Commit

  • Loading branch information...
commit d42b59bd3ae6a44a8b1728cb04c9de5b3e52b667 0 parents
@ericbmerritt ericbmerritt authored
Showing with 8,710 additions and 0 deletions.
  1. +3 −0  README
  2. +8 −0 chapter_01/README
  3. +18 −0 chapter_01/pingpong.erl
  4. +1 −0  chapter_02/README
  5. +84 −0 chapter_02/my_module.erl
  6. +23 −0 chapter_03/README
  7. +33 −0 chapter_03/gen_server_skel.erl
  8. +135 −0 chapter_03/tr_server.erl
  9. +26 −0 chapter_04/tcp_rpc/README
  10. +4 −0 chapter_04/tcp_rpc/doc/.gitignore
  11. +1 −0  chapter_04/tcp_rpc/ebin/.gitignore
  12. +12 −0 chapter_04/tcp_rpc/ebin/tcp_rpc.app
  13. 0  chapter_04/tcp_rpc/include/.gitignore
  14. 0  chapter_04/tcp_rpc/priv/.gitignore
  15. +19 −0 chapter_04/tcp_rpc/src/tr_app.erl
  16. +126 −0 chapter_04/tcp_rpc/src/tr_server.erl
  17. +21 −0 chapter_04/tcp_rpc/src/tr_sup.erl
  18. +2 −0  chapter_05/README
  19. +12 −0 chapter_06/simple_cache/README
  20. +4 −0 chapter_06/simple_cache/doc/.gitignore
  21. +1 −0  chapter_06/simple_cache/ebin/.gitignore
  22. +11 −0 chapter_06/simple_cache/ebin/simple_cache.app
  23. 0  chapter_06/simple_cache/include/.gitignore
  24. 0  chapter_06/simple_cache/priv/.gitignore
  25. +17 −0 chapter_06/simple_cache/src/sc_app.erl
  26. +83 −0 chapter_06/simple_cache/src/sc_element.erl
  27. +26 −0 chapter_06/simple_cache/src/sc_store.erl
  28. +24 −0 chapter_06/simple_cache/src/sc_sup.erl
  29. +30 −0 chapter_06/simple_cache/src/simple_cache.erl
  30. +60 −0 chapter_07/custom_error_report.erl
  31. +36 −0 chapter_07/die_please.erl
  32. +10 −0 chapter_07/die_please2.erl
  33. +15 −0 chapter_07/simple_cache/README
  34. +4 −0 chapter_07/simple_cache/doc/.gitignore
  35. +1 −0  chapter_07/simple_cache/ebin/.gitignore
  36. +15 −0 chapter_07/simple_cache/ebin/simple_cache.app
  37. 0  chapter_07/simple_cache/include/.gitignore
  38. 0  chapter_07/simple_cache/priv/.gitignore
  39. +18 −0 chapter_07/simple_cache/src/sc_app.erl
  40. +83 −0 chapter_07/simple_cache/src/sc_element.erl
  41. +24 −0 chapter_07/simple_cache/src/sc_element_sup.erl
  42. +32 −0 chapter_07/simple_cache/src/sc_event.erl
  43. +45 −0 chapter_07/simple_cache/src/sc_event_logger.erl
  44. +26 −0 chapter_07/simple_cache/src/sc_store.erl
  45. +25 −0 chapter_07/simple_cache/src/sc_sup.erl
  46. +34 −0 chapter_07/simple_cache/src/simple_cache.erl
  47. +123 −0 chapter_08/resource_discovery.erl
  48. +36 −0 chapter_09/README
  49. +44 −0 chapter_09/create_tables.erl
  50. +12 −0 chapter_09/resource_discovery/README
  51. +4 −0 chapter_09/resource_discovery/doc/.gitignore
  52. +1 −0  chapter_09/resource_discovery/ebin/.gitignore
  53. +11 −0 chapter_09/resource_discovery/ebin/resource_discovery.app
  54. 0  chapter_09/resource_discovery/include/.gitignore
  55. 0  chapter_09/resource_discovery/priv/.gitignore
  56. +16 −0 chapter_09/resource_discovery/src/rd_app.erl
  57. +123 −0 chapter_09/resource_discovery/src/rd_server.erl
  58. +21 −0 chapter_09/resource_discovery/src/rd_sup.erl
  59. +20 −0 chapter_09/resource_discovery/src/resource_discovery.erl
  60. +4 −0 chapter_09/simple_cache/doc/.gitignore
  61. +1 −0  chapter_09/simple_cache/ebin/.gitignore
  62. +15 −0 chapter_09/simple_cache/ebin/simple_cache.app
  63. 0  chapter_09/simple_cache/include/.gitignore
  64. 0  chapter_09/simple_cache/priv/.gitignore
  65. +67 −0 chapter_09/simple_cache/src/sc_app.erl
  66. +83 −0 chapter_09/simple_cache/src/sc_element.erl
  67. +24 −0 chapter_09/simple_cache/src/sc_element_sup.erl
  68. +32 −0 chapter_09/simple_cache/src/sc_event.erl
  69. +45 −0 chapter_09/simple_cache/src/sc_event_logger.erl
  70. +58 −0 chapter_09/simple_cache/src/sc_store.erl
  71. +25 −0 chapter_09/simple_cache/src/sc_sup.erl
  72. +34 −0 chapter_09/simple_cache/src/simple_cache.erl
  73. +3 −0  chapter_10/.gitignore
  74. +18 −0 chapter_10/README
  75. +7 −0 chapter_10/install.sh
  76. +4 −0 chapter_10/resource_discovery/doc/.gitignore
  77. +1 −0  chapter_10/resource_discovery/ebin/.gitignore
  78. +11 −0 chapter_10/resource_discovery/ebin/resource_discovery.app
  79. 0  chapter_10/resource_discovery/include/.gitignore
  80. 0  chapter_10/resource_discovery/priv/.gitignore
  81. +16 −0 chapter_10/resource_discovery/src/rd_app.erl
  82. +123 −0 chapter_10/resource_discovery/src/rd_server.erl
  83. +21 −0 chapter_10/resource_discovery/src/rd_sup.erl
  84. +20 −0 chapter_10/resource_discovery/src/resource_discovery.erl
  85. +10 −0 chapter_10/simple_cache.rel
  86. +9 −0 chapter_10/simple_cache.sh
  87. +4 −0 chapter_10/simple_cache/doc/.gitignore
  88. +1 −0  chapter_10/simple_cache/ebin/.gitignore
  89. +15 −0 chapter_10/simple_cache/ebin/simple_cache.app
  90. 0  chapter_10/simple_cache/include/.gitignore
  91. 0  chapter_10/simple_cache/priv/.gitignore
  92. +67 −0 chapter_10/simple_cache/src/sc_app.erl
  93. +83 −0 chapter_10/simple_cache/src/sc_element.erl
  94. +24 −0 chapter_10/simple_cache/src/sc_element_sup.erl
  95. +32 −0 chapter_10/simple_cache/src/sc_event.erl
  96. +45 −0 chapter_10/simple_cache/src/sc_event_logger.erl
  97. +58 −0 chapter_10/simple_cache/src/sc_store.erl
  98. +25 −0 chapter_10/simple_cache/src/sc_sup.erl
  99. +34 −0 chapter_10/simple_cache/src/simple_cache.erl
  100. +12 −0 chapter_10/sys.config
  101. +59 −0 chapter_11/README
  102. +18 −0 chapter_11/active_once.erl
  103. +6 −0 chapter_11/compile.sh
  104. +1 −0  chapter_11/gen_web_server/ebin/.gitignore
  105. +9 −0 chapter_11/gen_web_server/ebin/gen_web_server.app
  106. +57 −0 chapter_11/gen_web_server/src/gen_web_server.erl
  107. +40 −0 chapter_11/gen_web_server/src/gws_connection_sup.erl
  108. +113 −0 chapter_11/gen_web_server/src/gws_server.erl
  109. +10 −0 chapter_11/http_interface/ebin/http_interface.app
  110. +22 −0 chapter_11/http_interface/src/hi_app.erl
  111. +58 −0 chapter_11/http_interface/src/hi_server.erl
  112. +24 −0 chapter_11/http_interface/src/hi_sup.erl
  113. +7 −0 chapter_11/install.sh
  114. +12 −0 chapter_11/old/gen_web_server/ebin/gen_web_server.app
  115. +153 −0 chapter_11/old/gen_web_server/src/gen_web_server.erl
  116. +94 −0 chapter_11/old/gen_web_server/src/gws_connection_sup.erl
  117. +196 −0 chapter_11/old/gen_web_server/src/gws_server.erl
  118. +12 −0 chapter_11/old/restful_interface/ebin/restful_interface.app
  119. +63 −0 chapter_11/old/restful_interface/src/ri_app.erl
  120. +87 −0 chapter_11/old/restful_interface/src/ri_gws_impl.erl
  121. +77 −0 chapter_11/old/restful_interface/src/ri_sup.erl
  122. +11 −0 chapter_11/old/tcp_interface/ebin/tcp_interface.app
  123. +71 −0 chapter_11/old/tcp_interface/src/ti_app.erl
  124. +151 −0 chapter_11/old/tcp_interface/src/ti_server.erl
  125. +80 −0 chapter_11/old/tcp_interface/src/ti_sup.erl
  126. +1 −0  chapter_11/put.txt
  127. +4 −0 chapter_11/resource_discovery/doc/.gitignore
  128. +1 −0  chapter_11/resource_discovery/ebin/.gitignore
  129. +11 −0 chapter_11/resource_discovery/ebin/resource_discovery.app
  130. 0  chapter_11/resource_discovery/include/.gitignore
  131. 0  chapter_11/resource_discovery/priv/.gitignore
  132. +16 −0 chapter_11/resource_discovery/src/rd_app.erl
  133. +123 −0 chapter_11/resource_discovery/src/rd_server.erl
  134. +21 −0 chapter_11/resource_discovery/src/rd_sup.erl
  135. +20 −0 chapter_11/resource_discovery/src/resource_discovery.erl
  136. BIN  chapter_11/simple_cache.boot
  137. +10 −0 chapter_11/simple_cache.rel
  138. +224 −0 chapter_11/simple_cache.script
  139. +9 −0 chapter_11/simple_cache.sh
  140. +4 −0 chapter_11/simple_cache/doc/.gitignore
  141. +1 −0  chapter_11/simple_cache/ebin/.gitignore
  142. +15 −0 chapter_11/simple_cache/ebin/simple_cache.app
  143. 0  chapter_11/simple_cache/include/.gitignore
  144. 0  chapter_11/simple_cache/priv/.gitignore
  145. +67 −0 chapter_11/simple_cache/src/sc_app.erl
  146. +83 −0 chapter_11/simple_cache/src/sc_element.erl
  147. +24 −0 chapter_11/simple_cache/src/sc_element_sup.erl
  148. +32 −0 chapter_11/simple_cache/src/sc_event.erl
  149. +45 −0 chapter_11/simple_cache/src/sc_event_logger.erl
  150. +58 −0 chapter_11/simple_cache/src/sc_store.erl
  151. +25 −0 chapter_11/simple_cache/src/sc_sup.erl
  152. +34 −0 chapter_11/simple_cache/src/simple_cache.erl
  153. +12 −0 chapter_11/sys.config
  154. +1 −0  chapter_11/tcp_interface/ebin/.gitignore
  155. +10 −0 chapter_11/tcp_interface/ebin/tcp_interface.app
  156. +24 −0 chapter_11/tcp_interface/src/ti_app.erl
  157. +53 −0 chapter_11/tcp_interface/src/ti_server.erl
  158. +24 −0 chapter_11/tcp_interface/src/ti_sup.erl
  159. +9 −0 chapter_13/.gitignore
  160. +44 −0 chapter_13/JInterfaceExample.java
  161. +104 −0 chapter_13/README
  162. +29 −0 chapter_13/Test.java
  163. +6 −0 chapter_13/compile.sh
  164. +5 −0 chapter_13/create-table.sh
  165. +7 −0 chapter_13/install.sh
  166. +4 −0 chapter_13/resource_discovery/doc/.gitignore
  167. +1 −0  chapter_13/resource_discovery/ebin/.gitignore
  168. +11 −0 chapter_13/resource_discovery/ebin/resource_discovery.app
  169. 0  chapter_13/resource_discovery/include/.gitignore
  170. 0  chapter_13/resource_discovery/priv/.gitignore
  171. +16 −0 chapter_13/resource_discovery/src/rd_app.erl
  172. +123 −0 chapter_13/resource_discovery/src/rd_server.erl
  173. +21 −0 chapter_13/resource_discovery/src/rd_sup.erl
  174. +20 −0 chapter_13/resource_discovery/src/resource_discovery.erl
  175. +11 −0 chapter_13/run.sh
  176. +10 −0 chapter_13/simple_cache.rel
  177. +9 −0 chapter_13/simple_cache.sh
  178. +4 −0 chapter_13/simple_cache/doc/.gitignore
  179. +1 −0  chapter_13/simple_cache/ebin/.gitignore
  180. +16 −0 chapter_13/simple_cache/ebin/simple_cache.app
  181. 0  chapter_13/simple_cache/include/.gitignore
  182. +32 −0 chapter_13/simple_cache/java_src/HBaseConnector.java
  183. +58 −0 chapter_13/simple_cache/java_src/HBaseNode.java
  184. +72 −0 chapter_13/simple_cache/java_src/HBaseTask.java
  185. 0  chapter_13/simple_cache/priv/.gitignore
  186. +1 −0  chapter_13/simple_cache/priv/java/.gitignore
  187. +67 −0 chapter_13/simple_cache/src/sc_app.erl
  188. +83 −0 chapter_13/simple_cache/src/sc_element.erl
  189. +24 −0 chapter_13/simple_cache/src/sc_element_sup.erl
  190. +32 −0 chapter_13/simple_cache/src/sc_event.erl
  191. +45 −0 chapter_13/simple_cache/src/sc_event_logger.erl
  192. +36 −0 chapter_13/simple_cache/src/sc_hbase.erl
  193. +58 −0 chapter_13/simple_cache/src/sc_store.erl
  194. +25 −0 chapter_13/simple_cache/src/sc_sup.erl
  195. +44 −0 chapter_13/simple_cache/src/simple_cache.erl
  196. +3 −0  chapter_13/start-hbase.sh
  197. +3 −0  chapter_13/stop-hbase.sh
  198. +12 −0 chapter_13/sys.config
  199. +23 −0 chapter_14/profile_ex.erl
  200. +14 −0 hello_erlware/_build.cfg
  201. +26 −0 hello_erlware/bin/erlware_release_start_helper
  202. +15 −0 hello_erlware/bin/hello_erlware
  203. +14 −0 hello_erlware/config/sys.config
  204. +4 −0 hello_erlware/lib/my_app/doc/overview.edoc
  205. +13 −0 hello_erlware/lib/my_app/ebin/my_app.app
  206. +59 −0 hello_erlware/lib/my_app/src/ma_hello_server.erl
  207. +62 −0 hello_erlware/lib/my_app/src/my_app_app.erl
  208. +41 −0 hello_erlware/lib/my_app/src/my_app_sup.erl
  209. +39 −0 link_ex.erl
  210. +48 −0 link_ex2.erl
  211. +196 −0 profile_ex
  212. +161 −0 profile_ex-with_concurrent_cutoff
  213. +55 −0 profile_ex.erl
  214. +5 −0 simple_cache/_build.cfg
  215. +25 −0 simple_cache/bin/simple_cache
  216. +40 −0 simple_cache/config/sys.config
  217. +14 −0 simple_cache/lib/resource_discovery/ebin/resource_discovery.app
  218. +60 −0 simple_cache/lib/resource_discovery/src/rd_app.erl
  219. +246 −0 simple_cache/lib/resource_discovery/src/rd_server.erl
  220. +72 −0 simple_cache/lib/resource_discovery/src/rd_sup.erl
  221. +65 −0 simple_cache/lib/resource_discovery/src/resource_discovery.erl
  222. +4 −0 simple_cache/lib/simple_cache/doc/overview.edoc
  223. +18 −0 simple_cache/lib/simple_cache/ebin/simple_cache.app
  224. +106 −0 simple_cache/lib/simple_cache/src/sc_app.erl
  225. +211 −0 simple_cache/lib/simple_cache/src/sc_element.erl
  226. +83 −0 simple_cache/lib/simple_cache/src/sc_element_sup.erl
  227. +87 −0 simple_cache/lib/simple_cache/src/sc_event.erl
  228. +136 −0 simple_cache/lib/simple_cache/src/sc_event_logger.erl
  229. +104 −0 simple_cache/lib/simple_cache/src/sc_store.erl
  230. +65 −0 simple_cache/lib/simple_cache/src/sc_sup.erl
  231. +82 −0 simple_cache/lib/simple_cache/src/simple_cache.erl
  232. BIN  simple_cache/release/simple_cache.boot
  233. +5 −0 simple_cache/release/simple_cache.rel
  234. +137 −0 simple_cache/release/simple_cache.script
  235. +9 −0 tcp_rpc/README
  236. +4 −0 tcp_rpc/lib/tcp_rpc/doc/.gitignore
  237. +4 −0 tcp_rpc/lib/tcp_rpc/doc/overview.edoc
  238. +1 −0  tcp_rpc/lib/tcp_rpc/ebin/.gitignore
  239. +13 −0 tcp_rpc/lib/tcp_rpc/ebin/tcp_rpc.app
  240. 0  tcp_rpc/lib/tcp_rpc/include/.gitignore
  241. 0  tcp_rpc/lib/tcp_rpc/priv/.gitignore
  242. +61 −0 tcp_rpc/lib/tcp_rpc/src/tr_app.erl
  243. +197 −0 tcp_rpc/lib/tcp_rpc/src/tr_server.erl
  244. +59 −0 tcp_rpc/lib/tcp_rpc/src/tr_sup.erl
  245. +4 −0 tcp_rpc/sinan.cfg
3  README
@@ -0,0 +1,3 @@
+This repository contains the source code for the book Erlang and OTP in
+Action, by Martin Logan, Eric Merritt, and Richard Carlsson.
+Manning Publications, 2010. http://www.manning.com/logan/
8 chapter_01/README
@@ -0,0 +1,8 @@
+To run the program, start Erlang (in the same directory),
+then run the following in the Erlang shell:
+
+1> c(pingpong).
+{ok,pingpong}
+2> pingpong:run().
+ok
+3>
18 chapter_01/pingpong.erl
@@ -0,0 +1,18 @@
+%% ---------------------------------------------------------------------
+%% File: pingpong.erl
+
+-module(pingpong).
+
+-export([run/0]).
+
+run() ->
+ Pid = spawn(fun ping/0),
+ Pid ! self(),
+ receive
+ pong -> ok
+ end.
+
+ping() ->
+ receive
+ From -> From ! pong
+ end.
1  chapter_02/README
@@ -0,0 +1 @@
+The file my_module.erl contains all the example code from chapter 2.
84 chapter_02/my_module.erl
@@ -0,0 +1,84 @@
+%% This is a simple Erlang module
+
+-module(my_module).
+
+-export([pie/0, print/1, either_or_both/2, area/1, sign/1, yesno/1,
+ render/0, sum/1, do_sum/1, rev/1, tailrev/1]).
+
+
+pie() ->
+ 3.14.
+
+
+print(Term) ->
+ io:format("The value of Term is: ~w.~n", [Term]).
+
+
+either_or_both(true, B) when is_boolean(B) ->
+ true;
+either_or_both(A, true) when is_boolean(A) ->
+ true;
+either_or_both(false, false) ->
+ false.
+
+
+area(Shape) ->
+ case Shape of
+ {circle, Radius} ->
+ Radius * Radius * math:pi();
+ {square, Side} ->
+ Side * Side;
+ {rectangle, Height, Width} ->
+ Height * Width
+ end.
+
+
+sign(N) when is_number(N) ->
+ if
+ N > 0 -> positive;
+ N < 0 -> negative;
+ true -> zero
+ end.
+
+
+yesno(F) ->
+ case F(true, false) of
+ true -> io:format("yes~n");
+ false -> io:format("no~n")
+ end.
+
+
+to_html(Items, F) ->
+ ["<dl>\n",
+ [io_lib:format("<dt>~s:\n<dd>~s\n", [F(T),F(D)]) || {T, D} <- Items],
+ "</dl>"
+ ].
+
+render(Items, Em) ->
+ to_html(Items,
+ fun (Text) ->
+ "<" ++ Em ++ ">" ++ Text ++ "</" ++ Em ++ ">"
+ end).
+
+render() ->
+ render([{"D&D", "Dungeons and Dragons"}], "b").
+
+
+sum(0) -> 0;
+sum(N) -> sum(N-1) + N.
+
+
+do_sum(N) -> do_sum(N, 0).
+
+do_sum(0, Total) -> Total;
+do_sum(N, Total) -> do_sum(N-1, Total+N).
+
+
+rev([X | TheRest]) -> rev(TheRest) ++ [X];
+rev([]) -> [].
+
+
+tailrev(List) -> tailrev(List, []).
+
+tailrev([X | TheRest], Acc) -> tailrev(TheRest, [X | Acc]);
+tailrev([], Acc) -> Acc.
23 chapter_03/README
@@ -0,0 +1,23 @@
+To build this code, run the following command:
+
+erlc *.erl
+
+To run the program, start Erlang (in the same directory),
+then run the following in the Erlang shell:
+
+1> tr_server:start_link().
+{ok,<0.34.0>}
+2>
+
+After that, open another terminal window and use telnet
+to connect to the application, like this:
+
+$ telnet localhost 1055
+Trying 127.0.0.1...
+Connected to localhost.localdomain.
+Escape character is '^]'.
+lists:reverse([1,2,3]).
+[3,2,1]
+init:stop().
+ok
+Connection closed by foreign host.
33 chapter_03/gen_server_skel.erl
@@ -0,0 +1,33 @@
+%% ---------------------------------------------------------------------
+%% File: gen_server_skel.erl
+%%
+%% This is a skeleton implementation of a gen_server callback module.
+
+-module(gen_server_skel).
+
+
+-behaviour(gen_server).
+
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-record(state, { }).
+
+init([]) ->
+ {ok, #state{}}.
+
+handle_call(_Request, _From, State) ->
+ Reply = ok,
+ {reply, Reply, State}.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
135 chapter_03/tr_server.erl
@@ -0,0 +1,135 @@
+%%%-------------------------------------------------------------------
+%%% @author Martin & Eric <erlware-dev@googlegroups.com>
+%%% [http://www.erlware.org]
+%%% @copyright 2008-2010 Erlware
+%%% @doc RPC over TCP server. This module defines a server process that
+%%% listens for incoming TCP connections and allows the user to
+%%% execute RPC commands via that TCP stream.
+%%% @end
+%%%-------------------------------------------------------------------
+
+-module(tr_server).
+
+-behaviour(gen_server).
+
+-include_lib("eunit/include/eunit.hrl").
+
+%% API
+-export([
+ start_link/1,
+ start_link/0,
+ get_count/0,
+ stop/0
+ ]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(SERVER, ?MODULE).
+-define(DEFAULT_PORT, 1055).
+
+-record(state, {port, lsock, request_count = 0}).
+
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+
+%%--------------------------------------------------------------------
+%% @doc Starts the server.
+%%
+%% @spec start_link(Port::integer()) -> {ok, Pid}
+%% where
+%% Pid = pid()
+%% @end
+%%--------------------------------------------------------------------
+start_link(Port) ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []).
+
+%% @spec start_link() -> {ok, Pid}
+%% @doc Calls `start_link(Port)' using the default port.
+start_link() ->
+ start_link(?DEFAULT_PORT).
+
+%%--------------------------------------------------------------------
+%% @doc Fetches the number of requests made to this server.
+%% @spec get_count() -> {ok, Count}
+%% where
+%% Count = integer()
+%% @end
+%%--------------------------------------------------------------------
+get_count() ->
+ gen_server:call(?SERVER, get_count).
+
+%%--------------------------------------------------------------------
+%% @doc Stops the server.
+%% @spec stop() -> ok
+%% @end
+%%--------------------------------------------------------------------
+stop() ->
+ gen_server:cast(?SERVER, stop).
+
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+
+init([Port]) ->
+ {ok, LSock} = gen_tcp:listen(Port, [{active, true}]),
+ {ok, #state{port = Port, lsock = LSock}, 0}.
+
+handle_call(get_count, _From, State) ->
+ {reply, {ok, State#state.request_count}, State}.
+
+handle_cast(stop, State) ->
+ {stop, normal, State}.
+
+handle_info({tcp, Socket, RawData}, State) ->
+ do_rpc(Socket, RawData),
+ RequestCount = State#state.request_count,
+ {noreply, State#state{request_count = RequestCount + 1}};
+handle_info(timeout, #state{lsock = LSock} = State) ->
+ {ok, _Sock} = gen_tcp:accept(LSock),
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+
+do_rpc(Socket, RawData) ->
+ try
+ {M, F, A} = split_out_mfa(RawData),
+ Result = apply(M, F, A),
+ gen_tcp:send(Socket, io_lib:fwrite("~p~n", [Result]))
+ catch
+ _Class:Err ->
+ gen_tcp:send(Socket, io_lib:fwrite("~p~n", [Err]))
+ end.
+
+split_out_mfa(RawData) ->
+ MFA = re:replace(RawData, "\r\n$", "", [{return, list}]),
+ {match, [M, F, A]} =
+ re:run(MFA,
+ "(.*):(.*)\s*\\((.*)\s*\\)\s*.\s*$",
+ [{capture, [1,2,3], list}, ungreedy]),
+ {list_to_atom(M), list_to_atom(F), args_to_terms(A)}.
+
+args_to_terms(RawArgs) ->
+ {ok, Toks, _Line} = erl_scan:string("[" ++ RawArgs ++ "]. ", 1),
+ {ok, Args} = erl_parse:parse_term(Toks),
+ Args.
+
+
+%% test
+
+startstop_test() ->
+ {ok, _} = tr_server:start_link(1055),
+ ok = tr_server:stop().
26 chapter_04/tcp_rpc/README
@@ -0,0 +1,26 @@
+To build this code, run the following command:
+
+erlc -o ./ebin ./src/*.erl
+
+To run the program, first start Erlang like this:
+
+erl -pa ./ebin
+
+Then, run the following in the Erlang shell:
+
+1> application:start(tcp_rpc).
+ok
+2>
+
+After that, open another terminal window and use telnet
+to connect to the application, like this:
+
+$ telnet localhost 1055
+Trying 127.0.0.1...
+Connected to localhost.localdomain.
+Escape character is '^]'.
+lists:reverse([1,2,3]).
+[3,2,1]
+init:stop().
+ok
+Connection closed by foreign host.
4 chapter_04/tcp_rpc/doc/.gitignore
@@ -0,0 +1,4 @@
+edoc-info
+stylesheet.css
+erlang.png
+*.html
1  chapter_04/tcp_rpc/ebin/.gitignore
@@ -0,0 +1 @@
+*.beam
12 chapter_04/tcp_rpc/ebin/tcp_rpc.app
@@ -0,0 +1,12 @@
+%% -*- mode: Erlang; fill-column: 75; comment-column: 50; -*-
+
+{application, tcp_rpc,
+ [{description, "RPC server for Erlang and OTP in action"},
+ {vsn, "0.1.0"},
+ {modules, [tr_app,
+ tr_sup,
+ tr_server]},
+ {registered, [tr_sup]},
+ {applications, [kernel, stdlib]},
+ {mod, {tr_app, []}}
+ ]}.
0  chapter_04/tcp_rpc/include/.gitignore
No changes.
0  chapter_04/tcp_rpc/priv/.gitignore
No changes.
19 chapter_04/tcp_rpc/src/tr_app.erl
@@ -0,0 +1,19 @@
+-module(tr_app).
+
+-behaviour(application).
+
+-export([
+ start/2,
+ stop/1
+ ]).
+
+start(_Type, _StartArgs) ->
+ case tr_sup:start_link() of
+ {ok, Pid} ->
+ {ok, Pid};
+ Other ->
+ {error, Other}
+ end.
+
+stop(_State) ->
+ ok.
126 chapter_04/tcp_rpc/src/tr_server.erl
@@ -0,0 +1,126 @@
+%%%-------------------------------------------------------------------
+%%% @author Martin & Eric <erlware-dev@googlegroups.com>
+%%% [http://www.erlware.org]
+%%% @copyright 2008-2010 Erlware
+%%% @doc RPC over TCP server. This module defines a server process that
+%%% listens for incoming TCP connections and allows the user to
+%%% execute RPC commands via that TCP stream.
+%%% @end
+%%%-------------------------------------------------------------------
+
+-module(tr_server).
+
+-behaviour(gen_server).
+
+%% API
+-export([
+ start_link/1,
+ start_link/0,
+ get_count/0,
+ stop/0
+ ]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(SERVER, ?MODULE).
+-define(DEFAULT_PORT, 1055).
+
+-record(state, {port, lsock, request_count = 0}).
+
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+
+%%--------------------------------------------------------------------
+%% @doc Starts the server.
+%%
+%% @spec start_link(Port::integer()) -> {ok, Pid}
+%% where
+%% Pid = pid()
+%% @end
+%%--------------------------------------------------------------------
+start_link(Port) ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []).
+
+%% @spec start_link() -> {ok, Pid}
+%% @doc Calls `start_link(Port)' using the default port.
+start_link() ->
+ start_link(?DEFAULT_PORT).
+
+%%--------------------------------------------------------------------
+%% @doc Fetches the number of requests made to this server.
+%% @spec get_count() -> {ok, Count}
+%% where
+%% Count = integer()
+%% @end
+%%--------------------------------------------------------------------
+get_count() ->
+ gen_server:call(?SERVER, get_count).
+
+%%--------------------------------------------------------------------
+%% @doc Stops the server.
+%% @spec stop() -> ok
+%% @end
+%%--------------------------------------------------------------------
+stop() ->
+ gen_server:cast(?SERVER, stop).
+
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+
+init([Port]) ->
+ {ok, LSock} = gen_tcp:listen(Port, [{active, true}]),
+ {ok, #state{port = Port, lsock = LSock}, 0}.
+
+handle_call(get_count, _From, State) ->
+ {reply, {ok, State#state.request_count}, State}.
+
+handle_cast(stop, State) ->
+ {stop, normal, State}.
+
+handle_info({tcp, Socket, RawData}, State) ->
+ do_rpc(Socket, RawData),
+ RequestCount = State#state.request_count,
+ {noreply, State#state{request_count = RequestCount + 1}};
+handle_info(timeout, #state{lsock = LSock} = State) ->
+ {ok, _Sock} = gen_tcp:accept(LSock),
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+
+do_rpc(Socket, RawData) ->
+ try
+ {M, F, A} = split_out_mfa(RawData),
+ Result = apply(M, F, A),
+ gen_tcp:send(Socket, io_lib:fwrite("~p~n", [Result]))
+ catch
+ _Class:Err ->
+ gen_tcp:send(Socket, io_lib:fwrite("~p~n", [Err]))
+ end.
+
+split_out_mfa(RawData) ->
+ MFA = re:replace(RawData, "\r\n$", "", [{return, list}]),
+ {match, [M, F, A]} =
+ re:run(MFA,
+ "(.*):(.*)\s*\\((.*)\s*\\)\s*.\s*$",
+ [{capture, [1,2,3], list}, ungreedy]),
+ {list_to_atom(M), list_to_atom(F), args_to_terms(A)}.
+
+args_to_terms(RawArgs) ->
+ {ok, Toks, _Line} = erl_scan:string("[" ++ RawArgs ++ "]. ", 1),
+ {ok, Args} = erl_parse:parse_term(Toks),
+ Args.
21 chapter_04/tcp_rpc/src/tr_sup.erl
@@ -0,0 +1,21 @@
+-module(tr_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+
+%% Supervisor callbacks
+-export([init/1]).
+
+-define(SERVER, ?MODULE).
+
+start_link() ->
+ supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+
+init([]) ->
+ Server = {tr_server, {tr_server, start_link, []},
+ permanent, 2000, worker, [tr_server]},
+ Children = [Server],
+ RestartStrategy = {one_for_one, 0, 1},
+ {ok, {RestartStrategy, Children}}.
2  chapter_05/README
@@ -0,0 +1,2 @@
+There is no new code for chapter 5. For the examples,
+use the code for chapter 04.
12 chapter_06/simple_cache/README
@@ -0,0 +1,12 @@
+To build this code, run the following command:
+
+erlc -o ./ebin ./src/*.erl
+
+To run the program, first start Erlang like this:
+
+erl -pa ./ebin
+
+Then, run the following in the Erlang shell:
+
+1> application:start(simple_cache).
+ok
4 chapter_06/simple_cache/doc/.gitignore
@@ -0,0 +1,4 @@
+edoc-info
+stylesheet.css
+erlang.png
+*.html
1  chapter_06/simple_cache/ebin/.gitignore
@@ -0,0 +1 @@
+*.beam
11 chapter_06/simple_cache/ebin/simple_cache.app
@@ -0,0 +1,11 @@
+{application, simple_cache,
+ [{description, "A simple caching system"},
+ {vsn, "0.1.0"},
+ {modules, [
+ sc_app,
+ sc_sup
+ ]},
+ {registered, [sc_sup]},
+ {applications, [kernel, stdlib]},
+ {mod, {sc_app, []}}
+ ]}.
0  chapter_06/simple_cache/include/.gitignore
No changes.
0  chapter_06/simple_cache/priv/.gitignore
No changes.
17 chapter_06/simple_cache/src/sc_app.erl
@@ -0,0 +1,17 @@
+-module(sc_app).
+
+-behaviour(application).
+
+-export([start/2, stop/1]).
+
+start(_StartType, _StartArgs) ->
+ sc_store:init(),
+ case sc_sup:start_link() of
+ {ok, Pid} ->
+ {ok, Pid};
+ Other ->
+ {error, Other}
+ end.
+
+stop(_State) ->
+ ok.
83 chapter_06/simple_cache/src/sc_element.erl
@@ -0,0 +1,83 @@
+-module(sc_element).
+
+-behaviour(gen_server).
+
+-export([
+ start_link/2,
+ create/2,
+ create/1,
+ fetch/1,
+ replace/2,
+ delete/1
+ ]).
+
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(SERVER, ?MODULE).
+-define(DEFAULT_LEASE_TIME, (60 * 60 * 24)).
+
+-record(state, {value, lease_time, start_time}).
+
+start_link(Value, LeaseTime) ->
+ gen_server:start_link(?MODULE, [Value, LeaseTime], []).
+
+create(Value, LeaseTime) ->
+ sc_sup:start_child(Value, LeaseTime).
+
+create(Value) ->
+ create(Value, ?DEFAULT_LEASE_TIME).
+
+fetch(Pid) ->
+ gen_server:call(Pid, fetch).
+
+replace(Pid, Value) ->
+ gen_server:cast(Pid, {replace, Value}).
+
+delete(Pid) ->
+ gen_server:cast(Pid, delete).
+
+init([Value, LeaseTime]) ->
+ Now = calendar:local_time(),
+ StartTime = calendar:datetime_to_gregorian_seconds(Now),
+ {ok,
+ #state{value = Value,
+ lease_time = LeaseTime,
+ start_time = StartTime},
+ time_left(StartTime, LeaseTime)}.
+
+time_left(_StartTime, infinity) ->
+ infinity;
+time_left(StartTime, LeaseTime) ->
+ Now = calendar:local_time(),
+ CurrentTime = calendar:datetime_to_gregorian_seconds(Now),
+ TimeElapsed = CurrentTime - StartTime,
+ case LeaseTime - TimeElapsed of
+ Time when Time =< 0 -> 0;
+ Time -> Time * 1000
+ end.
+
+handle_call(fetch, _From, State) ->
+ #state{value = Value,
+ lease_time = LeaseTime,
+ start_time = StartTime} = State,
+ TimeLeft = time_left(StartTime, LeaseTime),
+ {reply, {ok, Value}, State, TimeLeft}.
+
+handle_cast({replace, Value}, State) ->
+ #state{lease_time = LeaseTime,
+ start_time = StartTime} = State,
+ TimeLeft = time_left(StartTime, LeaseTime),
+ {noreply, State#state{value = Value}, TimeLeft};
+handle_cast(delete, State) ->
+ {stop, normal, State}.
+
+handle_info(timeout, State) ->
+ {stop, normal, State}.
+
+terminate(_Reason, _State) ->
+ sc_store:delete(self()),
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
26 chapter_06/simple_cache/src/sc_store.erl
@@ -0,0 +1,26 @@
+-module(sc_store).
+
+-export([
+ init/0,
+ insert/2,
+ delete/1,
+ lookup/1
+ ]).
+
+-define(TABLE_ID, ?MODULE).
+
+init() ->
+ ets:new(?TABLE_ID, [public, named_table]),
+ ok.
+
+insert(Key, Pid) ->
+ ets:insert(?TABLE_ID, {Key, Pid}).
+
+lookup(Key) ->
+ case ets:lookup(?TABLE_ID, Key) of
+ [{Key, Pid}] -> {ok, Pid};
+ [] -> {error, not_found}
+ end.
+
+delete(Pid) ->
+ ets:match_delete(?TABLE_ID, {'_', Pid}).
24 chapter_06/simple_cache/src/sc_sup.erl
@@ -0,0 +1,24 @@
+-module(sc_sup).
+
+-behaviour(supervisor).
+
+-export([start_link/0,
+ start_child/2
+ ]).
+
+-export([init/1]).
+
+-define(SERVER, ?MODULE).
+
+start_link() ->
+ supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+
+start_child(Value, LeaseTime) ->
+ supervisor:start_child(?SERVER, [Value, LeaseTime]).
+
+init([]) ->
+ Element = {sc_element, {sc_element, start_link, []},
+ temporary, brutal_kill, worker, [sc_element]},
+ Children = [Element],
+ RestartStrategy = {simple_one_for_one, 0, 1},
+ {ok, {RestartStrategy, Children}}.
30 chapter_06/simple_cache/src/simple_cache.erl
@@ -0,0 +1,30 @@
+-module(simple_cache).
+
+-export([insert/2, lookup/1, delete/1]).
+
+insert(Key, Value) ->
+ case sc_store:lookup(Key) of
+ {ok, Pid} ->
+ sc_element:replace(Pid, Value);
+ {error, _} ->
+ {ok, Pid} = sc_element:create(Value),
+ sc_store:insert(Key, Pid)
+ end.
+
+lookup(Key) ->
+ try
+ {ok, Pid} = sc_store:lookup(Key),
+ {ok, Value} = sc_element:fetch(Pid),
+ {ok, Value}
+ catch
+ _Class:_Exception ->
+ {error, not_found}
+ end.
+
+delete(Key) ->
+ case sc_store:lookup(Key) of
+ {ok, Pid} ->
+ sc_element:delete(Pid);
+ {error, _Reason} ->
+ ok
+ end.
60 chapter_07/custom_error_report.erl
@@ -0,0 +1,60 @@
+-module(custom_error_report).
+
+-behaviour(gen_event).
+
+%% API
+-export([register_with_logger/0]).
+
+-export([init/1, handle_event/2, handle_call/2,
+ handle_info/2, terminate/2, code_change/3]).
+
+-record(state, {}).
+
+register_with_logger() ->
+ error_logger:add_report_handler(?MODULE).
+
+init([]) ->
+ {ok, #state{}}.
+
+handle_event({error, _Gleader, {Pid,Format,Data}}, State) ->
+ io:fwrite("ERROR <~p> ~s", [Pid, io_lib:format(Format, Data)]),
+ {ok, State};
+handle_event({error_report, _Gleader, {Pid, std_error, Report}}, State) ->
+ io:fwrite("ERROR <~p> ~p", [Pid, Report]),
+ {ok, State};
+handle_event({error_report, _Gleader, {Pid, Type, Report}}, State) ->
+ io:fwrite("ERROR <~p> ~p ~p", [Pid, Type, Report]),
+ {ok, State};
+handle_event({warning_msg, _Gleader, {Pid, Format, Data}}, State) ->
+ io:fwrite("WARNING <~p> ~s", [Pid, io_lib:format(Format, Data)]),
+ {ok, State};
+handle_event({warning_report,_Gleader,{Pid,std_warning,Report}}, State) ->
+ io:fwrite("WARNING <~p> ~p", [Pid, Report]),
+ {ok, State};
+handle_event({warning_report,_Gleader,{Pid, Type, Report}}, State) ->
+ io:fwrite("WARNING <~p> ~p ~p", [Pid, Type, Report]),
+ {ok, State};
+handle_event({info_msg, _Gleader, {Pid, Format, Data}}, State) ->
+ io:fwrite("INFO <~p> ~s", [Pid, io_lib:format(Format, Data)]),
+ {ok, State};
+handle_event({info_report, _Gleader, {Pid, std_info, Report}}, State) ->
+ io:fwrite("INFO <~p> ~p", [Pid, Report]),
+ {ok, State};
+handle_event({info_report, _Gleader, {Pid, Type, Report}}, State) ->
+ io:fwrite("INFO <~p> ~p ~p", [Pid, Type, Report]),
+ {ok, State};
+handle_event(_Event, State) ->
+ {ok, State}.
+
+handle_call(_Request, State) ->
+ Reply = ok,
+ {ok, Reply, State}.
+
+handle_info(_Info, State) ->
+ {ok, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
36 chapter_07/die_please.erl
@@ -0,0 +1,36 @@
+-module(die_please).
+
+-behaviour(gen_server).
+
+-export([start_link/0]).
+
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(SERVER, ?MODULE).
+-define(SLEEP_TIME, (2*1000)).
+
+-record(state, {}).
+
+start_link() ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
+
+init([]) ->
+ {ok, #state{}, ?SLEEP_TIME}.
+
+handle_call(_Request, _From, State) ->
+ Reply = ok,
+ {reply, Reply, State}.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+handle_info(timeout, State) ->
+ i_want_to_die = right_now,
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
10 chapter_07/die_please2.erl
@@ -0,0 +1,10 @@
+-module(die_please2).
+
+-export([go/0]).
+
+-define(SLEEP_TIME, 2000).
+
+go() ->
+ %% just sleep for a while, then crash
+ timer:sleep(?SLEEP_TIME),
+ i_really_want_to_die = right_now.
15 chapter_07/simple_cache/README
@@ -0,0 +1,15 @@
+To build this code, run the following command:
+
+erlc -o ./ebin ./src/*.erl
+
+To run the program, first start Erlang like this:
+
+erl -pa ./ebin
+
+Then, run the following in the Erlang shell:
+
+1> application:start(sasl).
+ok
+2> application:start(simple_cache).
+ok
+3>
4 chapter_07/simple_cache/doc/.gitignore
@@ -0,0 +1,4 @@
+edoc-info
+stylesheet.css
+erlang.png
+*.html
1  chapter_07/simple_cache/ebin/.gitignore
@@ -0,0 +1 @@
+*.beam
15 chapter_07/simple_cache/ebin/simple_cache.app
@@ -0,0 +1,15 @@
+{application, simple_cache,
+ [{description, "A simple caching system"},
+ {vsn, "0.1.0"},
+ {modules, [simple_cache,
+ sc_app,
+ sc_sup,
+ sc_element_sup,
+ sc_store,
+ sc_element,
+ sc_event,
+ sc_event_logger]},
+ {registered, [sc_sup]},
+ {applications, [kernel, sasl, stdlib]},
+ {mod, {sc_app, []}}
+ ]}.
0  chapter_07/simple_cache/include/.gitignore
No changes.
0  chapter_07/simple_cache/priv/.gitignore
No changes.
18 chapter_07/simple_cache/src/sc_app.erl
@@ -0,0 +1,18 @@
+-module(sc_app).
+
+-behaviour(application).
+
+-export([start/2, stop/1]).
+
+start(_StartType, _StartArgs) ->
+ sc_store:init(),
+ case sc_sup:start_link() of
+ {ok, Pid} ->
+ sc_event_logger:add_handler(),
+ {ok, Pid};
+ Other ->
+ {error, Other}
+ end.
+
+stop(_State) ->
+ ok.
83 chapter_07/simple_cache/src/sc_element.erl
@@ -0,0 +1,83 @@
+-module(sc_element).
+
+-behaviour(gen_server).
+
+-export([
+ start_link/2,
+ create/1,
+ create/2,
+ fetch/1,
+ replace/2,
+ delete/1
+ ]).
+
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(SERVER, ?MODULE).
+-define(DEFAULT_LEASE_TIME, (60 * 60 * 24)).
+
+-record(state, {value, lease_time, start_time}).
+
+start_link(Value, LeaseTime) ->
+ gen_server:start_link(?MODULE, [Value, LeaseTime], []).
+
+create(Value, LeaseTime) ->
+ sc_element_sup:start_child(Value, LeaseTime).
+
+create(Value) ->
+ create(Value, ?DEFAULT_LEASE_TIME).
+
+fetch(Pid) ->
+ gen_server:call(Pid, fetch).
+
+replace(Pid, Value) ->
+ gen_server:cast(Pid, {replace, Value}).
+
+delete(Pid) ->
+ gen_server:cast(Pid, delete).
+
+init([Value, LeaseTime]) ->
+ Now = calendar:local_time(),
+ StartTime = calendar:datetime_to_gregorian_seconds(Now),
+ {ok,
+ #state{value = Value,
+ lease_time = LeaseTime,
+ start_time = StartTime},
+ time_left(StartTime, LeaseTime)}.
+
+time_left(_StartTime, infinity) ->
+ infinity;
+time_left(StartTime, LeaseTime) ->
+ Now = calendar:local_time(),
+ CurrentTime = calendar:datetime_to_gregorian_seconds(Now),
+ TimeElapsed = CurrentTime - StartTime,
+ case LeaseTime - TimeElapsed of
+ Time when Time =< 0 -> 0;
+ Time -> Time * 1000
+ end.
+
+handle_call(fetch, _From, State) ->
+ #state{value = Value,
+ lease_time = LeaseTime,
+ start_time = StartTime} = State,
+ TimeLeft = time_left(StartTime, LeaseTime),
+ {reply, {ok, Value}, State, TimeLeft}.
+
+handle_cast({replace, Value}, State) ->
+ #state{lease_time = LeaseTime,
+ start_time = StartTime} = State,
+ TimeLeft = time_left(StartTime, LeaseTime),
+ {noreply, State#state{value = Value}, TimeLeft};
+handle_cast(delete, State) ->
+ {stop, normal, State}.
+
+handle_info(timeout, State) ->
+ {stop, normal, State}.
+
+terminate(_Reason, _State) ->
+ sc_store:delete(self()),
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
24 chapter_07/simple_cache/src/sc_element_sup.erl
@@ -0,0 +1,24 @@
+-module(sc_element_sup).
+
+-behaviour(supervisor).
+
+-export([start_link/0,
+ start_child/2
+ ]).
+
+-export([init/1]).
+
+-define(SERVER, ?MODULE).
+
+start_link() ->
+ supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+
+start_child(Value, LeaseTime) ->
+ supervisor:start_child(?SERVER, [Value, LeaseTime]).
+
+init([]) ->
+ Element = {sc_element, {sc_element, start_link, []},
+ temporary, brutal_kill, worker, [sc_element]},
+ Children = [Element],
+ RestartStrategy = {simple_one_for_one, 0, 1},
+ {ok, {RestartStrategy, Children}}.
32 chapter_07/simple_cache/src/sc_event.erl
@@ -0,0 +1,32 @@
+-module(sc_event).
+
+-export([start_link/0,
+ add_handler/2,
+ delete_handler/2,
+ lookup/1,
+ create/2,
+ replace/2,
+ delete/1]).
+
+-define(SERVER, ?MODULE).
+
+start_link() ->
+ gen_event:start_link({local, ?SERVER}).
+
+add_handler(Handler, Args) ->
+ gen_event:add_handler(?SERVER, Handler, Args).
+
+delete_handler(Handler, Args) ->
+ gen_event:delete_handler(?SERVER, Handler, Args).
+
+lookup(Key) ->
+ gen_event:notify(?SERVER, {lookup, Key}).
+
+create(Key, Value) ->
+ gen_event:notify(?SERVER, {create, {Key, Value}}).
+
+replace(Key, Value) ->
+ gen_event:notify(?SERVER, {replace, {Key, Value}}).
+
+delete(Key) ->
+ gen_event:notify(?SERVER, {delete, Key}).
45 chapter_07/simple_cache/src/sc_event_logger.erl
@@ -0,0 +1,45 @@
+-module(sc_event_logger).
+
+-behaviour(gen_event).
+
+-export([add_handler/0, delete_handler/0]).
+
+-export([init/1, handle_event/2, handle_call/2,
+ handle_info/2, code_change/3, terminate/2]).
+
+-record(state, {}).
+
+add_handler() ->
+ sc_event:add_handler(?MODULE, []).
+
+delete_handler() ->
+ sc_event:delete_handler(?MODULE, []).
+
+init([]) ->
+ {ok, #state{}}.
+
+handle_event({create, {Key, Value}}, State) ->
+ error_logger:info_msg("create(~w, ~w)~n", [Key, Value]),
+ {ok, State};
+handle_event({lookup, Key}, State) ->
+ error_logger:info_msg("lookup(~w)~n", [Key]),
+ {ok, State};
+handle_event({delete, Key}, State) ->
+ error_logger:info_msg("delete(~w)~n", [Key]),
+ {ok, State};
+handle_event({replace, {Key, Value}}, State) ->
+ error_logger:info_msg("replace(~w, ~w)~n", [Key, Value]),
+ {ok, State}.
+
+handle_call(_Request, State) ->
+ Reply = ok,
+ {ok, Reply, State}.
+
+handle_info(_Info, State) ->
+ {ok, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
26 chapter_07/simple_cache/src/sc_store.erl
@@ -0,0 +1,26 @@
+-module(sc_store).
+
+-export([
+ init/0,
+ insert/2,
+ delete/1,
+ lookup/1
+ ]).
+
+-define(TABLE_ID, ?MODULE).
+
+init() ->
+ ets:new(?TABLE_ID, [public, named_table]),
+ ok.
+
+insert(Key, Pid) ->
+ ets:insert(?TABLE_ID, {Key, Pid}).
+
+lookup(Key) ->
+ case ets:lookup(?TABLE_ID, Key) of
+ [{Key, Pid}] -> {ok, Pid};
+ [] -> {error, not_found}
+ end.
+
+delete(Pid) ->
+ ets:match_delete(?TABLE_ID, {'_', Pid}).
25 chapter_07/simple_cache/src/sc_sup.erl
@@ -0,0 +1,25 @@
+-module(sc_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+
+%% Supervisor callbacks
+-export([init/1]).
+
+-define(SERVER, ?MODULE).
+
+start_link() ->
+ supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+
+init([]) ->
+ ElementSup = {sc_element_sup, {sc_element_sup, start_link, []},
+ permanent, 2000, supervisor, [sc_element]},
+
+ EventManager = {sc_event, {sc_event, start_link, []},
+ permanent, 2000, worker, [sc_event]},
+
+ Children = [ElementSup, EventManager],
+ RestartStrategy = {one_for_one, 4, 3600},
+ {ok, {RestartStrategy, Children}}.
34 chapter_07/simple_cache/src/simple_cache.erl
@@ -0,0 +1,34 @@
+-module(simple_cache).
+
+-export([insert/2, lookup/1, delete/1]).
+
+insert(Key, Value) ->
+ case sc_store:lookup(Key) of
+ {ok, Pid} ->
+ sc_event:replace(Key, Value),
+ sc_element:replace(Pid, Value);
+ {error, _} ->
+ {ok, Pid} = sc_element:create(Value),
+ sc_store:insert(Key, Pid),
+ sc_event:create(Key, Value)
+ end.
+
+lookup(Key) ->
+ sc_event:lookup(Key),
+ try
+ {ok, Pid} = sc_store:lookup(Key),
+ {ok, Value} = sc_element:fetch(Pid),
+ {ok, Value}
+ catch
+ _Class:_Exception ->
+ {error, not_found}
+ end.
+
+delete(Key) ->
+ sc_event:delete(Key),
+ case sc_store:lookup(Key) of
+ {ok, Pid} ->
+ sc_element:delete(Pid);
+ {error, _Reason} ->
+ ok
+ end.
123 chapter_08/resource_discovery.erl
@@ -0,0 +1,123 @@
+-module(resource_discovery).
+
+-behaviour(gen_server).
+
+-export([
+ start_link/0,
+ add_target_resource_type/1,
+ add_local_resource/2,
+ fetch_resources/1,
+ trade_resources/0
+ ]).
+
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(SERVER, ?MODULE).
+
+-record(state, {target_resource_types,
+ local_resources,
+ resources}).
+
+%% API
+
+start_link() ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
+
+add_target_resource_type(Type) ->
+ gen_server:cast(?SERVER, {add_target_resource_type, Type}).
+
+add_local_resource(Type, Instance) ->
+ gen_server:cast(?SERVER, {add_local_resource, {Type, Instance}}).
+
+fetch_resources(Type) ->
+ gen_server:call(?SERVER, {fetch_resources, Type}).
+
+trade_resources() ->
+ gen_server:cast(?SERVER, trade_resources).
+
+%% Callbacks
+
+init([]) ->
+ {ok, #state{target_resource_types = [],
+ local_resources = dict:new(),
+ resources = dict:new()}}.
+
+handle_call({fetch_resources, Type}, _From, State) ->
+ {reply, dict:find(Type, State#state.resources), State};
+handle_call(_Request, _From, State) ->
+ Reply = ok,
+ {reply, Reply, State}.
+
+handle_cast({add_target_resource_type, Type}, State) ->
+ TargetTypes = State#state.target_resource_types,
+ NewTargetTypes = [Type | lists:delete(Type, TargetTypes)],
+ {noreply, State#state{target_resource_types = NewTargetTypes}};
+handle_cast({add_local_resource, {Type, Instance}}, State) ->
+ LocalResources = State#state.local_resources,
+ NewLocalResources = add_resource(Type, Instance, LocalResources),
+ {noreply, State#state{local_resources = NewLocalResources}};
+handle_cast(trade_resources, State) ->
+ LocalResources = State#state.local_resources,
+ AllNodes = [node() | nodes()],
+ lists:foreach(
+ fun(Node) ->
+ gen_server:cast({?SERVER, Node},
+ {trade_resources, {node(), LocalResources}})
+ end,
+ AllNodes),
+ {noreply, State};
+handle_cast({trade_resources, {ReplyTo, RemoteResources}},
+ #state{local_resources = LocalResources,
+ target_resource_types = TargetTypes,
+ resources = Resources} = State) ->
+ ResourceList = resources_for_types(TargetTypes, RemoteResources),
+ NewResources = add_resources(ResourceList, Resources),
+ case ReplyTo of
+ noreply ->
+ ok;
+ _ ->
+ gen_server:cast({?SERVER, ReplyTo},
+ {trade_resources, {noreply, LocalResources}})
+ end,
+ {noreply, State#state{resources = NewResources}};
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+
+%% Utilities
+
+add_resources([{Type, Identifier}|T], Dict) ->
+ add_resources(T, add_resource(Type, Identifier, Dict));
+add_resources([], Dict) ->
+ Dict.
+
+add_resource(Type, Resource, Dict) ->
+ case dict:find(Type, Dict) of
+ {ok, ResourceList} ->
+ NewList = [Resource | lists:delete(Resource, ResourceList)],
+ dict:store(Type, NewList, Dict);
+ error ->
+ dict:store(Type, [Resource], Dict)
+ end.
+
+resources_for_types(Types, Resources) ->
+ Fun =
+ fun(Type, Acc) ->
+ case dict:find(Type, Resources) of
+ {ok, List} ->
+ [{Type, Instance} || Instance <- List] ++ Acc;
+ error ->
+ Acc
+ end
+ end,
+ lists:foldl(Fun, [], Types).
36 chapter_09/README
@@ -0,0 +1,36 @@
+To build this code, run the following commands:
+
+erlc -o ./simple_cache/ebin ./simple_cache/src/*.erl
+erlc -o ./resource_discovery/ebin ./resource_discovery/src/*.erl
+
+First start one or two contact nodes. Ensure that the default
+node names in sc_app:ensure_contact() match your host name, or
+use -sname instead of -name in order to use 'localhost'. (Edit
+and recompile if necessary.) For example, in a separate terminal
+window, do:
+
+erl -name contact1
+
+To run the program, start Erlang like this:
+
+erl -name mynode -pa ./ebin -pa ../resource_discovery/ebin/
+
+Then, run the following in the Erlang shell:
+
+1> application:start(sasl).
+ok
+2> mnesia:start().
+ok
+3> application:start(resource_discovery).
+ok
+4> application:start(simple_cache).
+ok
+5>
+
+(Remember that the resource discovery will make simple_cache
+wait several seconds when it starts.)
+
+For more fun, start one or two other nodes in the same
+way (with different names, of course), and start the same
+applications on them. Then try inserting something in the
+cache on one node, and looking it up on another node.
44 chapter_09/create_tables.erl
@@ -0,0 +1,44 @@
+-module(create_tables).
+
+-export([init_tables/0, insert_user/3, insert_project/2]).
+
+-record(user, {
+ id,
+ name
+ }).
+
+-record(project, {
+ title,
+ description
+ }).
+
+-record(contributor, {
+ user_id,
+ project_title
+ }).
+
+init_tables() ->
+ mnesia:create_table(user,
+ [{attributes, record_info(fields, user)}]),
+ mnesia:create_table(project,
+ [{attributes, record_info(fields, project)}]),
+ mnesia:create_table(contributor,
+ [{type, bag}, {attributes, record_info(fields, contributor)}]).
+
+insert_user(Id, Name, ProjectTitles) when ProjectTitles =/= [] ->
+ User = #user{id = Id, name = Name},
+ Fun = fun() ->
+ mnesia:write(User),
+ lists:foreach(
+ fun(Title) ->
+ [#project{title = Title}] = mnesia:read(project, Title),
+ mnesia:write(#contributor{user_id = Id,
+ project_title = Title})
+ end,
+ ProjectTitles)
+ end,
+ mnesia:transaction(Fun).
+
+insert_project(Title, Description) ->
+ mnesia:dirty_write(#project{title = Title,
+ description = Description}).
12 chapter_09/resource_discovery/README
@@ -0,0 +1,12 @@
+To build this code, run the following command:
+
+erlc -o ./ebin ./src/*.erl
+
+To run the program, first start Erlang like this:
+
+erl -pa ./ebin
+
+Then, run the following in the Erlang shell:
+
+1> application:start(resource_discovery).
+ok
4 chapter_09/resource_discovery/doc/.gitignore
@@ -0,0 +1,4 @@
+edoc-info
+stylesheet.css
+erlang.png
+*.html
1  chapter_09/resource_discovery/ebin/.gitignore
@@ -0,0 +1 @@
+*.beam
11 chapter_09/resource_discovery/ebin/resource_discovery.app
@@ -0,0 +1,11 @@
+{application, resource_discovery,
+ [{description, "A simple resource discovery system"},
+ {vsn, "0.1.0"},
+ {modules, [resource_discovery,
+ rd_app,
+ rd_sup,
+ rd_server]},
+ {registered, [rd_sup]},
+ {applications, [kernel, stdlib]},
+ {mod, {rd_app, []}}
+ ]}.
0  chapter_09/resource_discovery/include/.gitignore
No changes.
0  chapter_09/resource_discovery/priv/.gitignore
No changes.
16 chapter_09/resource_discovery/src/rd_app.erl
@@ -0,0 +1,16 @@
+-module(rd_app).
+
+-behaviour(application).
+
+-export([start/2, stop/1]).
+
+start(_StartType, _StartArgs) ->
+ case rd_sup:start_link() of
+ {ok, Pid} ->
+ {ok, Pid};
+ Other ->
+ {error, Other}
+ end.
+
+stop(_State) ->
+ ok.
123 chapter_09/resource_discovery/src/rd_server.erl
@@ -0,0 +1,123 @@
+-module(rd_server).
+
+-behaviour(gen_server).
+
+-export([
+ start_link/0,
+ add_target_resource_type/1,
+ add_local_resource/2,
+ fetch_resources/1,
+ trade_resources/0
+ ]).
+
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(SERVER, ?MODULE).
+
+-record(state, {target_resource_types,
+ local_resources,
+ resources}).
+
+%% API
+
+start_link() ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
+
+add_target_resource_type(Type) ->
+ gen_server:cast(?SERVER, {add_target_resource_type, Type}).
+
+add_local_resource(Type, Instance) ->
+ gen_server:cast(?SERVER, {add_local_resource, {Type, Instance}}).
+
+fetch_resources(Type) ->
+ gen_server:call(?SERVER, {fetch_resources, Type}).
+
+trade_resources() ->
+ gen_server:cast(?SERVER, trade_resources).
+
+%% Callbacks
+
+init([]) ->
+ {ok, #state{target_resource_types = [],
+ local_resources = dict:new(),
+ resources = dict:new()}}.
+
+handle_call({fetch_resources, Type}, _From, State) ->
+ {reply, dict:find(Type, State#state.resources), State};
+handle_call(_Request, _From, State) ->
+ Reply = ok,
+ {reply, Reply, State}.
+
+handle_cast({add_target_resource_type, Type}, State) ->
+ TargetTypes = State#state.target_resource_types,
+ NewTargetTypes = [Type | lists:delete(Type, TargetTypes)],
+ {noreply, State#state{target_resource_types = NewTargetTypes}};
+handle_cast({add_local_resource, {Type, Instance}}, State) ->
+ LocalResources = State#state.local_resources,
+ NewLocalResources = add_resource(Type, Instance, LocalResources),
+ {noreply, State#state{local_resources = NewLocalResources}};
+handle_cast(trade_resources, State) ->
+ LocalResources = State#state.local_resources,
+ AllNodes = [node() | nodes()],
+ lists:foreach(
+ fun(Node) ->
+ gen_server:cast({?SERVER, Node},
+ {trade_resources, {node(), LocalResources}})
+ end,
+ AllNodes),
+ {noreply, State};
+handle_cast({trade_resources, {ReplyTo, RemoteResources}},
+ #state{local_resources = LocalResources,
+ target_resource_types = TargetTypes,
+ resources = Resources} = State) ->
+ ResourceList = resources_for_types(TargetTypes, RemoteResources),
+ NewResources = add_resources(ResourceList, Resources),
+ case ReplyTo of
+ noreply ->
+ ok;
+ _ ->
+ gen_server:cast({?SERVER, ReplyTo},
+ {trade_resources, {noreply, LocalResources}})
+ end,
+ {noreply, State#state{resources = NewResources}};
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+
+%% Utilities
+
+add_resources([{Type, Identifier}|T], Dict) ->
+ add_resources(T, add_resource(Type, Identifier, Dict));
+add_resources([], Dict) ->
+ Dict.
+
+add_resource(Type, Resource, Dict) ->
+ case dict:find(Type, Dict) of
+ {ok, ResourceList} ->
+ NewList = [Resource | lists:delete(Resource, ResourceList)],
+ dict:store(Type, NewList, Dict);
+ error ->
+ dict:store(Type, [Resource], Dict)
+ end.
+
+resources_for_types(Types, Resources) ->
+ Fun =
+ fun(Type, Acc) ->
+ case dict:find(Type, Resources) of
+ {ok, List} ->
+ [{Type, Instance} || Instance <- List] ++ Acc;
+ error ->
+ Acc
+ end
+ end,
+ lists:foldl(Fun, [], Types).
21 chapter_09/resource_discovery/src/rd_sup.erl
@@ -0,0 +1,21 @@
+-module(rd_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+
+%% Supervisor callbacks
+-export([init/1]).
+
+-define(SERVER, ?MODULE).
+
+start_link() ->
+ supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+
+init([]) ->
+ Server = {rd_server, {rd_server, start_link, []},
+ permanent, 2000, worker, [rd_server]},
+ Children = [Server],
+ RestartStrategy = {one_for_one, 0, 1},
+ {ok, {RestartStrategy, Children}}.
20 chapter_09/resource_discovery/src/resource_discovery.erl
@@ -0,0 +1,20 @@
+-module(resource_discovery).
+
+-export([
+ add_target_resource_type/1,
+ add_local_resource/2,
+ fetch_resources/1,
+ trade_resources/0
+ ]).
+
+add_target_resource_type(Type) ->
+ rd_server:add_target_resource_type(Type).
+
+add_local_resource(Type, Instance) ->
+ rd_server:add_local_resource(Type, Instance).
+
+fetch_resources(Type) ->
+ rd_server:fetch_resources(Type).
+
+trade_resources() ->
+ rd_server:trade_resources().
4 chapter_09/simple_cache/doc/.gitignore
@@ -0,0 +1,4 @@
+edoc-info
+stylesheet.css
+erlang.png
+*.html
1  chapter_09/simple_cache/ebin/.gitignore
@@ -0,0 +1 @@
+*.beam
15 chapter_09/simple_cache/ebin/simple_cache.app
@@ -0,0 +1,15 @@
+{application, simple_cache,
+ [{description, "A simple caching system"},
+ {vsn, "0.3.0"},
+ {modules, [simple_cache,
+ sc_app,
+ sc_sup,
+ sc_element_sup,
+ sc_store,
+ sc_element,
+ sc_event,
+ sc_event_logger]},
+ {registered, [sc_sup]},
+ {applications, [kernel, sasl, stdlib, mnesia, resource_discovery]},
+ {mod, {sc_app, []}}
+ ]}.
0  chapter_09/simple_cache/include/.gitignore
No changes.
0  chapter_09/simple_cache/priv/.gitignore
No changes.
67 chapter_09/simple_cache/src/sc_app.erl
@@ -0,0 +1,67 @@
+-module(sc_app).
+
+-behaviour(application).
+
+-export([start/2, stop/1]).
+
+-define(WAIT_FOR_RESOURCES, 2500).
+
+start(_StartType, _StartArgs) ->
+ ok = ensure_contact(),
+ resource_discovery:add_local_resource(simple_cache, node()),
+ resource_discovery:add_target_resource_type(simple_cache),
+ resource_discovery:trade_resources(),
+ timer:sleep(?WAIT_FOR_RESOURCES),
+ sc_store:init(),
+ case sc_sup:start_link() of
+ {ok, Pid} ->
+ sc_event_logger:add_handler(),
+ {ok, Pid};
+ Other ->
+ {error, Other}
+ end.
+
+stop(_State) ->
+ ok.
+
+ensure_contact() ->
+ DefaultNodes = ['contact1@localhost', 'contact2@localhost'],
+ case get_env(simple_cache, contact_nodes, DefaultNodes) of
+ [] ->
+ {error, no_contact_nodes};
+ ContactNodes ->
+ ensure_contact(ContactNodes)
+ end.
+
+ensure_contact(ContactNodes) ->
+ Answering = [N || N <- ContactNodes, net_adm:ping(N) =:= pong],
+ case Answering of
+ [] ->
+ {error, no_contact_nodes_reachable};
+ _ ->
+ DefaultTime = 6000,
+ WaitTime = get_env(simple_cache, wait_time, DefaultTime),
+ wait_for_nodes(length(Answering), WaitTime)
+ end.
+
+wait_for_nodes(MinNodes, WaitTime) ->
+ Slices = 10,
+ SliceTime = round(WaitTime/Slices),
+ wait_for_nodes(MinNodes, SliceTime, Slices).
+
+wait_for_nodes(_MinNodes, _SliceTime, 0) ->
+ ok;
+wait_for_nodes(MinNodes, SliceTime, Iterations) ->
+ case length(nodes()) > MinNodes of
+ true ->
+ ok;
+ false ->
+ timer:sleep(SliceTime),
+ wait_for_nodes(MinNodes, SliceTime, Iterations - 1)
+ end.
+
+get_env(AppName, Key, Default) ->
+ case application:get_env(AppName, Key) of
+ undefined -> Default;
+ {ok, Value} -> Value
+ end.
83 chapter_09/simple_cache/src/sc_element.erl
@@ -0,0 +1,83 @@
+-module(sc_element).
+
+-behaviour(gen_server).
+
+-export([
+ start_link/2,
+ create/1,
+ create/2,
+ fetch/1,
+ replace/2,
+ delete/1
+ ]).
+
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(SERVER, ?MODULE).
+-define(DEFAULT_LEASE_TIME, (60 * 60 * 24)).
+
+-record(state, {value, lease_time, start_time}).
+
+start_link(Value, LeaseTime) ->
+ gen_server:start_link(?MODULE, [Value, LeaseTime], []).
+
+create(Value, LeaseTime) ->
+ sc_element_sup:start_child(Value, LeaseTime).
+
+create(Value) ->
+ create(Value, ?DEFAULT_LEASE_TIME).
+
+fetch(Pid) ->
+ gen_server:call(Pid, fetch).
+
+replace(Pid, Value) ->
+ gen_server:cast(Pid, {replace, Value}).
+
+delete(Pid) ->
+ gen_server:cast(Pid, delete).
+
+init([Value, LeaseTime]) ->
+ Now = calendar:local_time(),
+ StartTime = calendar:datetime_to_gregorian_seconds(Now),
+ {ok,
+ #state{value = Value,
+ lease_time = LeaseTime,
+ start_time = StartTime},
+ time_left(StartTime, LeaseTime)}.
+
+time_left(_StartTime, infinity) ->
+ infinity;
+time_left(StartTime, LeaseTime) ->
+ Now = calendar:local_time(),
+ CurrentTime = calendar:datetime_to_gregorian_seconds(Now),
+ TimeElapsed = CurrentTime - StartTime,
+ case LeaseTime - TimeElapsed of
+ Time when Time =< 0 -> 0;
+ Time -> Time * 1000
+ end.
+
+handle_call(fetch, _From, State) ->
+ #state{value = Value,
+ lease_time = LeaseTime,
+ start_time = StartTime} = State,
+ TimeLeft = time_left(StartTime, LeaseTime),
+ {reply, {ok, Value}, State, TimeLeft}.
+
+handle_cast({replace, Value}, State) ->
+ #state{lease_time = LeaseTime,
+ start_time = StartTime} = State,
+ TimeLeft = time_left(StartTime, LeaseTime),
+ {noreply, State#state{value = Value}, TimeLeft};
+handle_cast(delete, State) ->
+ {stop, normal, State}.
+
+handle_info(timeout, State) ->
+ {stop, normal, State}.
+
+terminate(_Reason, _State) ->
+ sc_store:delete(self()),
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
24 chapter_09/simple_cache/src/sc_element_sup.erl
@@ -0,0 +1,24 @@
+-module(sc_element_sup).
+
+-behaviour(supervisor).
+
+-export([start_link/0,
+ start_child/2
+ ]).
+
+-export([init/1]).
+