├── LICENSE
├── README
├── chapter_01
├── README
└── pingpong.erl
├── chapter_02
├── README
└── my_module.erl
├── chapter_03
├── README
├── gen_server_skel.erl
└── tr_server.erl
├── chapter_04
└── tcp_rpc
│ ├── README
│ ├── doc
│ └── .gitignore
│ ├── ebin
│ ├── .gitignore
│ └── tcp_rpc.app
│ ├── include
│ └── .gitignore
│ ├── priv
│ └── .gitignore
│ └── src
│ ├── tr_app.erl
│ ├── tr_server.erl
│ └── tr_sup.erl
├── chapter_05
└── README
├── chapter_06
└── simple_cache
│ ├── README
│ ├── doc
│ └── .gitignore
│ ├── ebin
│ ├── .gitignore
│ └── simple_cache.app
│ ├── include
│ └── .gitignore
│ ├── priv
│ └── .gitignore
│ └── src
│ ├── sc_app.erl
│ ├── sc_element.erl
│ ├── sc_store.erl
│ ├── sc_sup.erl
│ └── simple_cache.erl
├── chapter_07
├── custom_error_report.erl
├── die_please.erl
├── die_please2.erl
└── simple_cache
│ ├── README
│ ├── doc
│ └── .gitignore
│ ├── ebin
│ ├── .gitignore
│ └── simple_cache.app
│ ├── include
│ └── .gitignore
│ ├── priv
│ └── .gitignore
│ └── src
│ ├── sc_app.erl
│ ├── sc_element.erl
│ ├── sc_element_sup.erl
│ ├── sc_event.erl
│ ├── sc_event_logger.erl
│ ├── sc_store.erl
│ ├── sc_sup.erl
│ └── simple_cache.erl
├── chapter_08
└── resource_discovery.erl
├── chapter_09
├── README
├── create_tables.erl
├── resource_discovery
│ ├── README
│ ├── doc
│ │ └── .gitignore
│ ├── ebin
│ │ ├── .gitignore
│ │ └── resource_discovery.app
│ ├── include
│ │ └── .gitignore
│ ├── priv
│ │ └── .gitignore
│ └── src
│ │ ├── rd_app.erl
│ │ ├── rd_server.erl
│ │ ├── rd_sup.erl
│ │ └── resource_discovery.erl
└── simple_cache
│ ├── doc
│ └── .gitignore
│ ├── ebin
│ ├── .gitignore
│ └── simple_cache.app
│ ├── include
│ └── .gitignore
│ ├── priv
│ └── .gitignore
│ └── src
│ ├── sc_app.erl
│ ├── sc_element.erl
│ ├── sc_element_sup.erl
│ ├── sc_event.erl
│ ├── sc_event_logger.erl
│ ├── sc_store.erl
│ ├── sc_sup.erl
│ └── simple_cache.erl
├── chapter_10
├── .gitignore
├── README
├── install.sh
├── resource_discovery
│ ├── doc
│ │ └── .gitignore
│ ├── ebin
│ │ ├── .gitignore
│ │ └── resource_discovery.app
│ ├── include
│ │ └── .gitignore
│ ├── priv
│ │ └── .gitignore
│ └── src
│ │ ├── rd_app.erl
│ │ ├── rd_server.erl
│ │ ├── rd_sup.erl
│ │ └── resource_discovery.erl
├── simple_cache.rel
├── simple_cache.sh
├── simple_cache
│ ├── doc
│ │ └── .gitignore
│ ├── ebin
│ │ ├── .gitignore
│ │ └── simple_cache.app
│ ├── include
│ │ └── .gitignore
│ ├── priv
│ │ └── .gitignore
│ └── src
│ │ ├── sc_app.erl
│ │ ├── sc_element.erl
│ │ ├── sc_element_sup.erl
│ │ ├── sc_event.erl
│ │ ├── sc_event_logger.erl
│ │ ├── sc_store.erl
│ │ ├── sc_sup.erl
│ │ └── simple_cache.erl
└── sys.config
├── chapter_11
├── .gitignore
├── README
├── active_once.erl
├── compile.sh
├── gen_web_server
│ ├── ebin
│ │ ├── .gitignore
│ │ └── gen_web_server.app
│ └── src
│ │ ├── gen_web_server.erl
│ │ ├── gws_connection_sup.erl
│ │ └── gws_server.erl
├── http_interface
│ ├── ebin
│ │ └── http_interface.app
│ └── src
│ │ ├── hi_app.erl
│ │ ├── hi_server.erl
│ │ └── hi_sup.erl
├── install.sh
├── old
│ ├── gen_web_server
│ │ ├── ebin
│ │ │ └── gen_web_server.app
│ │ └── src
│ │ │ ├── gen_web_server.erl
│ │ │ ├── gws_connection_sup.erl
│ │ │ └── gws_server.erl
│ ├── restful_interface
│ │ ├── ebin
│ │ │ └── restful_interface.app
│ │ └── src
│ │ │ ├── ri_app.erl
│ │ │ ├── ri_gws_impl.erl
│ │ │ └── ri_sup.erl
│ └── tcp_interface
│ │ ├── ebin
│ │ └── tcp_interface.app
│ │ └── src
│ │ ├── ti_app.erl
│ │ ├── ti_server.erl
│ │ └── ti_sup.erl
├── put.txt
├── resource_discovery
│ ├── doc
│ │ └── .gitignore
│ ├── ebin
│ │ ├── .gitignore
│ │ └── resource_discovery.app
│ ├── include
│ │ └── .gitignore
│ ├── priv
│ │ └── .gitignore
│ └── src
│ │ ├── rd_app.erl
│ │ ├── rd_server.erl
│ │ ├── rd_sup.erl
│ │ └── resource_discovery.erl
├── simple_cache.rel
├── simple_cache.sh
├── simple_cache
│ ├── doc
│ │ └── .gitignore
│ ├── ebin
│ │ ├── .gitignore
│ │ └── simple_cache.app
│ ├── include
│ │ └── .gitignore
│ ├── priv
│ │ └── .gitignore
│ └── src
│ │ ├── sc_app.erl
│ │ ├── sc_element.erl
│ │ ├── sc_element_sup.erl
│ │ ├── sc_event.erl
│ │ ├── sc_event_logger.erl
│ │ ├── sc_store.erl
│ │ ├── sc_sup.erl
│ │ └── simple_cache.erl
├── sys.config
└── tcp_interface
│ ├── ebin
│ ├── .gitignore
│ └── tcp_interface.app
│ └── src
│ ├── ti_app.erl
│ ├── ti_server.erl
│ └── ti_sup.erl
├── chapter_12
├── lloyd-yajl-1.0.9-0-g9c15d72.tar.gz
├── nifs
│ └── json_parser
│ │ ├── README
│ │ ├── c_src
│ │ ├── jp_nifs_R13.c
│ │ └── jp_nifs_R14.c
│ │ ├── doc
│ │ └── .gitignore
│ │ ├── ebin
│ │ ├── .gitignore
│ │ └── json_parser.app
│ │ ├── include
│ │ └── .gitignore
│ │ ├── priv
│ │ └── .gitignore
│ │ └── src
│ │ └── json_parser.erl
├── plain_port
│ └── json_parser
│ │ ├── README
│ │ ├── c_src
│ │ └── jp_prog.c
│ │ ├── doc
│ │ └── .gitignore
│ │ ├── ebin
│ │ ├── .gitignore
│ │ └── json_parser.app
│ │ ├── include
│ │ └── .gitignore
│ │ ├── priv
│ │ └── .gitignore
│ │ └── src
│ │ ├── jp_app.erl
│ │ ├── jp_server.erl
│ │ ├── jp_sup.erl
│ │ └── json_parser.erl
└── port_driver
│ └── json_parser
│ ├── README
│ ├── c_src
│ └── jp_driver.c
│ ├── doc
│ └── .gitignore
│ ├── ebin
│ ├── .gitignore
│ └── json_parser.app
│ ├── include
│ └── .gitignore
│ ├── priv
│ └── .gitignore
│ └── src
│ ├── jp_app.erl
│ ├── jp_server.erl
│ ├── jp_sup.erl
│ └── json_parser.erl
├── chapter_13
├── .gitignore
├── JInterfaceExample.java
├── README
├── Test.java
├── compile.sh
├── create-table.sh
├── install.sh
├── resource_discovery
│ ├── doc
│ │ └── .gitignore
│ ├── ebin
│ │ ├── .gitignore
│ │ └── resource_discovery.app
│ ├── include
│ │ └── .gitignore
│ ├── priv
│ │ └── .gitignore
│ └── src
│ │ ├── rd_app.erl
│ │ ├── rd_server.erl
│ │ ├── rd_sup.erl
│ │ └── resource_discovery.erl
├── run.sh
├── simple_cache.rel
├── simple_cache.sh
├── simple_cache
│ ├── doc
│ │ └── .gitignore
│ ├── ebin
│ │ ├── .gitignore
│ │ └── simple_cache.app
│ ├── include
│ │ └── .gitignore
│ ├── java_src
│ │ ├── HBaseConnector.java
│ │ ├── HBaseNode.java
│ │ └── HBaseTask.java
│ ├── priv
│ │ ├── .gitignore
│ │ └── java
│ │ │ └── .gitignore
│ └── src
│ │ ├── sc_app.erl
│ │ ├── sc_element.erl
│ │ ├── sc_element_sup.erl
│ │ ├── sc_event.erl
│ │ ├── sc_event_logger.erl
│ │ ├── sc_hbase.erl
│ │ ├── sc_store.erl
│ │ ├── sc_sup.erl
│ │ └── simple_cache.erl
├── start-hbase.sh
├── stop-hbase.sh
└── sys.config
├── chapter_14
└── profile_ex.erl
├── hello_erlware
├── _build.cfg
├── bin
│ ├── erlware_release_start_helper
│ └── hello_erlware
├── config
│ └── sys.config
└── lib
│ └── my_app
│ ├── doc
│ └── overview.edoc
│ ├── ebin
│ └── my_app.app
│ └── src
│ ├── ma_hello_server.erl
│ ├── my_app_app.erl
│ └── my_app_sup.erl
├── link_ex.erl
├── link_ex2.erl
├── profile_ex
├── profile_ex-with_concurrent_cutoff
├── profile_ex.erl
├── simple_cache
├── _build.cfg
├── bin
│ └── simple_cache
├── config
│ └── sys.config
├── lib
│ ├── resource_discovery
│ │ ├── ebin
│ │ │ └── resource_discovery.app
│ │ └── src
│ │ │ ├── rd_app.erl
│ │ │ ├── rd_server.erl
│ │ │ ├── rd_sup.erl
│ │ │ └── resource_discovery.erl
│ └── simple_cache
│ │ ├── doc
│ │ └── overview.edoc
│ │ ├── ebin
│ │ └── simple_cache.app
│ │ └── src
│ │ ├── sc_app.erl
│ │ ├── sc_element.erl
│ │ ├── sc_element_sup.erl
│ │ ├── sc_event.erl
│ │ ├── sc_event_logger.erl
│ │ ├── sc_store.erl
│ │ ├── sc_sup.erl
│ │ └── simple_cache.erl
└── release
│ ├── simple_cache.boot
│ ├── simple_cache.rel
│ └── simple_cache.script
└── tcp_rpc
├── README
├── lib
└── tcp_rpc
│ ├── doc
│ ├── .gitignore
│ └── overview.edoc
│ ├── ebin
│ ├── .gitignore
│ └── tcp_rpc.app
│ ├── include
│ └── .gitignore
│ ├── priv
│ └── .gitignore
│ └── src
│ ├── tr_app.erl
│ ├── tr_server.erl
│ └── tr_sup.erl
└── sinan.cfg
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009-2011 Martin Logan, Eric Merritt, Richard Carlsson
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to
5 | deal in the Software without restriction, including without limitation the
6 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 | sell copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 | IN THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | This repository contains the source code for the book Erlang and OTP in
2 | Action, by Martin Logan, Eric Merritt, and Richard Carlsson.
3 | Manning Publications, 2010. http://www.manning.com/logan/
4 |
5 | Follow us on twitter here:
6 | Martin J Logan: http://twitter.com/martinjlogan
7 | Eric Merritt http://twitter.com/cyberlync
8 | Richard Carlsson http://twitter.com/rich_4711
9 |
--------------------------------------------------------------------------------
/chapter_01/README:
--------------------------------------------------------------------------------
1 | To run the program, start Erlang (in the same directory),
2 | then run the following in the Erlang shell:
3 |
4 | 1> c(pingpong).
5 | {ok,pingpong}
6 | 2> pingpong:run().
7 | ok
8 | 3>
9 |
--------------------------------------------------------------------------------
/chapter_01/pingpong.erl:
--------------------------------------------------------------------------------
1 | %% ---------------------------------------------------------------------
2 | %% File: pingpong.erl
3 |
4 | -module(pingpong).
5 |
6 | -export([run/0]).
7 |
8 | run() ->
9 | Pid = spawn(fun ping/0),
10 | Pid ! self(),
11 | receive
12 | pong -> ok
13 | end.
14 |
15 | ping() ->
16 | receive
17 | From -> From ! pong
18 | end.
19 |
--------------------------------------------------------------------------------
/chapter_02/README:
--------------------------------------------------------------------------------
1 | The file my_module.erl contains all the example code from chapter 2.
2 |
--------------------------------------------------------------------------------
/chapter_02/my_module.erl:
--------------------------------------------------------------------------------
1 | %% This is a simple Erlang module
2 |
3 | -module(my_module).
4 |
5 | -export([pie/0, print/1, either_or_both/2, area/1, sign/1, yesno/1,
6 | render/0, sum/1, do_sum/1, rev/1, tailrev/1]).
7 |
8 |
9 | pie() ->
10 | 3.14.
11 |
12 |
13 | print(Term) ->
14 | io:format("The value of Term is: ~w.~n", [Term]).
15 |
16 |
17 | either_or_both(true, B) when is_boolean(B) ->
18 | true;
19 | either_or_both(A, true) when is_boolean(A) ->
20 | true;
21 | either_or_both(false, false) ->
22 | false.
23 |
24 |
25 | area(Shape) ->
26 | case Shape of
27 | {circle, Radius} ->
28 | Radius * Radius * math:pi();
29 | {square, Side} ->
30 | Side * Side;
31 | {rectangle, Height, Width} ->
32 | Height * Width
33 | end.
34 |
35 |
36 | sign(N) when is_number(N) ->
37 | if
38 | N > 0 -> positive;
39 | N < 0 -> negative;
40 | true -> zero
41 | end.
42 |
43 |
44 | yesno(F) ->
45 | case F(true, false) of
46 | true -> io:format("yes~n");
47 | false -> io:format("no~n")
48 | end.
49 |
50 |
51 | to_html(Items, F) ->
52 | ["
\n",
53 | [io_lib:format("- ~s:\n
- ~s\n", [F(T),F(D)]) || {T, D} <- Items],
54 | "
"
55 | ].
56 |
57 | render(Items, Em) ->
58 | to_html(Items,
59 | fun (Text) ->
60 | "<" ++ Em ++ ">" ++ Text ++ "" ++ Em ++ ">"
61 | end).
62 |
63 | render() ->
64 | render([{"D&D", "Dungeons and Dragons"}], "b").
65 |
66 |
67 | sum(0) -> 0;
68 | sum(N) -> sum(N-1) + N.
69 |
70 |
71 | do_sum(N) -> do_sum(N, 0).
72 |
73 | do_sum(0, Total) -> Total;
74 | do_sum(N, Total) -> do_sum(N-1, Total+N).
75 |
76 |
77 | rev([X | TheRest]) -> rev(TheRest) ++ [X];
78 | rev([]) -> [].
79 |
80 |
81 | tailrev(List) -> tailrev(List, []).
82 |
83 | tailrev([X | TheRest], Acc) -> tailrev(TheRest, [X | Acc]);
84 | tailrev([], Acc) -> Acc.
85 |
--------------------------------------------------------------------------------
/chapter_03/README:
--------------------------------------------------------------------------------
1 | To build this code, run the following command:
2 |
3 | erlc *.erl
4 |
5 | To run the program, start Erlang (in the same directory),
6 | then run the following in the Erlang shell:
7 |
8 | 1> tr_server:start_link().
9 | {ok,<0.34.0>}
10 | 2>
11 |
12 | After that, open another terminal window and use telnet
13 | to connect to the application, like this:
14 |
15 | $ telnet localhost 1055
16 | Trying 127.0.0.1...
17 | Connected to localhost.localdomain.
18 | Escape character is '^]'.
19 | lists:reverse([1,2,3]).
20 | [3,2,1]
21 | init:stop().
22 | ok
23 | Connection closed by foreign host.
24 |
--------------------------------------------------------------------------------
/chapter_03/gen_server_skel.erl:
--------------------------------------------------------------------------------
1 | %% ---------------------------------------------------------------------
2 | %% File: gen_server_skel.erl
3 | %%
4 | %% This is a skeleton implementation of a gen_server callback module.
5 |
6 | -module(gen_server_skel).
7 |
8 |
9 | -behaviour(gen_server).
10 |
11 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
12 | terminate/2, code_change/3]).
13 |
14 | -record(state, { }).
15 |
16 | init([]) ->
17 | {ok, #state{}}.
18 |
19 | handle_call(_Request, _From, State) ->
20 | Reply = ok,
21 | {reply, Reply, State}.
22 |
23 | handle_cast(_Msg, State) ->
24 | {noreply, State}.
25 |
26 | handle_info(_Info, State) ->
27 | {noreply, State}.
28 |
29 | terminate(_Reason, _State) ->
30 | ok.
31 |
32 | code_change(_OldVsn, State, _Extra) ->
33 | {ok, State}.
34 |
--------------------------------------------------------------------------------
/chapter_04/tcp_rpc/README:
--------------------------------------------------------------------------------
1 | To build this code, run the following command:
2 |
3 | erlc -o ./ebin ./src/*.erl
4 |
5 | To run the program, first start Erlang like this:
6 |
7 | erl -pa ./ebin
8 |
9 | Then, run the following in the Erlang shell:
10 |
11 | 1> application:start(tcp_rpc).
12 | ok
13 | 2>
14 |
15 | After that, open another terminal window and use telnet
16 | to connect to the application, like this:
17 |
18 | $ telnet localhost 1055
19 | Trying 127.0.0.1...
20 | Connected to localhost.localdomain.
21 | Escape character is '^]'.
22 | lists:reverse([1,2,3]).
23 | [3,2,1]
24 | init:stop().
25 | ok
26 | Connection closed by foreign host.
27 |
--------------------------------------------------------------------------------
/chapter_04/tcp_rpc/doc/.gitignore:
--------------------------------------------------------------------------------
1 | edoc-info
2 | stylesheet.css
3 | erlang.png
4 | *.html
5 |
--------------------------------------------------------------------------------
/chapter_04/tcp_rpc/ebin/.gitignore:
--------------------------------------------------------------------------------
1 | *.beam
2 |
--------------------------------------------------------------------------------
/chapter_04/tcp_rpc/ebin/tcp_rpc.app:
--------------------------------------------------------------------------------
1 | %% -*- mode: Erlang; fill-column: 75; comment-column: 50; -*-
2 |
3 | {application, tcp_rpc,
4 | [{description, "RPC server for Erlang and OTP in action"},
5 | {vsn, "0.1.0"},
6 | {modules, [tr_app,
7 | tr_sup,
8 | tr_server]},
9 | {registered, [tr_sup, tr_server]},
10 | {applications, [kernel, stdlib]},
11 | {mod, {tr_app, []}}
12 | ]}.
13 |
--------------------------------------------------------------------------------
/chapter_04/tcp_rpc/include/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_04/tcp_rpc/include/.gitignore
--------------------------------------------------------------------------------
/chapter_04/tcp_rpc/priv/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_04/tcp_rpc/priv/.gitignore
--------------------------------------------------------------------------------
/chapter_04/tcp_rpc/src/tr_app.erl:
--------------------------------------------------------------------------------
1 | -module(tr_app).
2 |
3 | -behaviour(application).
4 |
5 | -export([
6 | start/2,
7 | stop/1
8 | ]).
9 |
10 | start(_Type, _StartArgs) ->
11 | case tr_sup:start_link() of
12 | {ok, Pid} ->
13 | {ok, Pid};
14 | Other ->
15 | {error, Other}
16 | end.
17 |
18 | stop(_State) ->
19 | ok.
20 |
--------------------------------------------------------------------------------
/chapter_04/tcp_rpc/src/tr_sup.erl:
--------------------------------------------------------------------------------
1 | -module(tr_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | %% API
6 | -export([start_link/0]).
7 |
8 | %% Supervisor callbacks
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
15 |
16 | init([]) ->
17 | Server = {tr_server, {tr_server, start_link, []},
18 | permanent, 2000, worker, [tr_server]},
19 | Children = [Server],
20 | RestartStrategy = {one_for_one, 0, 1},
21 | {ok, {RestartStrategy, Children}}.
22 |
--------------------------------------------------------------------------------
/chapter_05/README:
--------------------------------------------------------------------------------
1 | There is no new code for chapter 5. For the examples,
2 | use the code for chapter 04.
3 |
--------------------------------------------------------------------------------
/chapter_06/simple_cache/README:
--------------------------------------------------------------------------------
1 | To build this code, run the following command:
2 |
3 | erlc -o ./ebin ./src/*.erl
4 |
5 | To run the program, first start Erlang like this:
6 |
7 | erl -pa ./ebin
8 |
9 | Then, run the following in the Erlang shell:
10 |
11 | 1> application:start(simple_cache).
12 | ok
13 |
--------------------------------------------------------------------------------
/chapter_06/simple_cache/doc/.gitignore:
--------------------------------------------------------------------------------
1 | edoc-info
2 | stylesheet.css
3 | erlang.png
4 | *.html
5 |
--------------------------------------------------------------------------------
/chapter_06/simple_cache/ebin/.gitignore:
--------------------------------------------------------------------------------
1 | *.beam
2 |
--------------------------------------------------------------------------------
/chapter_06/simple_cache/ebin/simple_cache.app:
--------------------------------------------------------------------------------
1 | {application, simple_cache,
2 | [{description, "A simple caching system"},
3 | {vsn, "0.1.0"},
4 | {modules, [
5 | sc_app,
6 | sc_sup
7 | ]},
8 | {registered, [sc_sup]},
9 | {applications, [kernel, stdlib]},
10 | {mod, {sc_app, []}}
11 | ]}.
12 |
--------------------------------------------------------------------------------
/chapter_06/simple_cache/include/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_06/simple_cache/include/.gitignore
--------------------------------------------------------------------------------
/chapter_06/simple_cache/priv/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_06/simple_cache/priv/.gitignore
--------------------------------------------------------------------------------
/chapter_06/simple_cache/src/sc_app.erl:
--------------------------------------------------------------------------------
1 | -module(sc_app).
2 |
3 | -behaviour(application).
4 |
5 | -export([start/2, stop/1]).
6 |
7 | start(_StartType, _StartArgs) ->
8 | sc_store:init(),
9 | case sc_sup:start_link() of
10 | {ok, Pid} ->
11 | {ok, Pid};
12 | Other ->
13 | {error, Other}
14 | end.
15 |
16 | stop(_State) ->
17 | ok.
18 |
--------------------------------------------------------------------------------
/chapter_06/simple_cache/src/sc_element.erl:
--------------------------------------------------------------------------------
1 | -module(sc_element).
2 |
3 | -behaviour(gen_server).
4 |
5 | -export([
6 | start_link/2,
7 | create/2,
8 | create/1,
9 | fetch/1,
10 | replace/2,
11 | delete/1
12 | ]).
13 |
14 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
15 | terminate/2, code_change/3]).
16 |
17 | -define(SERVER, ?MODULE).
18 | -define(DEFAULT_LEASE_TIME, (60 * 60 * 24)).
19 |
20 | -record(state, {value, lease_time, start_time}).
21 |
22 | start_link(Value, LeaseTime) ->
23 | gen_server:start_link(?MODULE, [Value, LeaseTime], []).
24 |
25 | create(Value, LeaseTime) ->
26 | sc_sup:start_child(Value, LeaseTime).
27 |
28 | create(Value) ->
29 | create(Value, ?DEFAULT_LEASE_TIME).
30 |
31 | fetch(Pid) ->
32 | gen_server:call(Pid, fetch).
33 |
34 | replace(Pid, Value) ->
35 | gen_server:cast(Pid, {replace, Value}).
36 |
37 | delete(Pid) ->
38 | gen_server:cast(Pid, delete).
39 |
40 | init([Value, LeaseTime]) ->
41 | Now = calendar:local_time(),
42 | StartTime = calendar:datetime_to_gregorian_seconds(Now),
43 | {ok,
44 | #state{value = Value,
45 | lease_time = LeaseTime,
46 | start_time = StartTime},
47 | time_left(StartTime, LeaseTime)}.
48 |
49 | time_left(_StartTime, infinity) ->
50 | infinity;
51 | time_left(StartTime, LeaseTime) ->
52 | Now = calendar:local_time(),
53 | CurrentTime = calendar:datetime_to_gregorian_seconds(Now),
54 | TimeElapsed = CurrentTime - StartTime,
55 | case LeaseTime - TimeElapsed of
56 | Time when Time =< 0 -> 0;
57 | Time -> Time * 1000
58 | end.
59 |
60 | handle_call(fetch, _From, State) ->
61 | #state{value = Value,
62 | lease_time = LeaseTime,
63 | start_time = StartTime} = State,
64 | TimeLeft = time_left(StartTime, LeaseTime),
65 | {reply, {ok, Value}, State, TimeLeft}.
66 |
67 | handle_cast({replace, Value}, State) ->
68 | #state{lease_time = LeaseTime,
69 | start_time = StartTime} = State,
70 | TimeLeft = time_left(StartTime, LeaseTime),
71 | {noreply, State#state{value = Value}, TimeLeft};
72 | handle_cast(delete, State) ->
73 | {stop, normal, State}.
74 |
75 | handle_info(timeout, State) ->
76 | {stop, normal, State}.
77 |
78 | terminate(_Reason, _State) ->
79 | sc_store:delete(self()),
80 | ok.
81 |
82 | code_change(_OldVsn, State, _Extra) ->
83 | {ok, State}.
84 |
--------------------------------------------------------------------------------
/chapter_06/simple_cache/src/sc_store.erl:
--------------------------------------------------------------------------------
1 | -module(sc_store).
2 |
3 | -export([
4 | init/0,
5 | insert/2,
6 | delete/1,
7 | lookup/1
8 | ]).
9 |
10 | -define(TABLE_ID, ?MODULE).
11 |
12 | init() ->
13 | ets:new(?TABLE_ID, [public, named_table]),
14 | ok.
15 |
16 | insert(Key, Pid) ->
17 | ets:insert(?TABLE_ID, {Key, Pid}).
18 |
19 | lookup(Key) ->
20 | case ets:lookup(?TABLE_ID, Key) of
21 | [{Key, Pid}] -> {ok, Pid};
22 | [] -> {error, not_found}
23 | end.
24 |
25 | delete(Pid) ->
26 | ets:match_delete(?TABLE_ID, {'_', Pid}).
27 |
--------------------------------------------------------------------------------
/chapter_06/simple_cache/src/sc_sup.erl:
--------------------------------------------------------------------------------
1 | -module(sc_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | -export([start_link/0,
6 | start_child/2
7 | ]).
8 |
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
15 |
16 | start_child(Value, LeaseTime) ->
17 | supervisor:start_child(?SERVER, [Value, LeaseTime]).
18 |
19 | init([]) ->
20 | Element = {sc_element, {sc_element, start_link, []},
21 | temporary, brutal_kill, worker, [sc_element]},
22 | Children = [Element],
23 | RestartStrategy = {simple_one_for_one, 0, 1},
24 | {ok, {RestartStrategy, Children}}.
25 |
--------------------------------------------------------------------------------
/chapter_06/simple_cache/src/simple_cache.erl:
--------------------------------------------------------------------------------
1 | -module(simple_cache).
2 |
3 | -export([insert/2, lookup/1, delete/1]).
4 |
5 | insert(Key, Value) ->
6 | case sc_store:lookup(Key) of
7 | {ok, Pid} ->
8 | sc_element:replace(Pid, Value);
9 | {error, _} ->
10 | {ok, Pid} = sc_element:create(Value),
11 | sc_store:insert(Key, Pid)
12 | end.
13 |
14 | lookup(Key) ->
15 | try
16 | {ok, Pid} = sc_store:lookup(Key),
17 | {ok, Value} = sc_element:fetch(Pid),
18 | {ok, Value}
19 | catch
20 | _Class:_Exception ->
21 | {error, not_found}
22 | end.
23 |
24 | delete(Key) ->
25 | case sc_store:lookup(Key) of
26 | {ok, Pid} ->
27 | sc_element:delete(Pid);
28 | {error, _Reason} ->
29 | ok
30 | end.
31 |
--------------------------------------------------------------------------------
/chapter_07/custom_error_report.erl:
--------------------------------------------------------------------------------
1 | -module(custom_error_report).
2 |
3 | -behaviour(gen_event).
4 |
5 | %% API
6 | -export([register_with_logger/0]).
7 |
8 | -export([init/1, handle_event/2, handle_call/2,
9 | handle_info/2, terminate/2, code_change/3]).
10 |
11 | -record(state, {}).
12 |
13 | register_with_logger() ->
14 | error_logger:add_report_handler(?MODULE).
15 |
16 | init([]) ->
17 | {ok, #state{}}.
18 |
19 | handle_event({error, _Gleader, {Pid,Format,Data}}, State) ->
20 | io:fwrite("ERROR <~p> ~s", [Pid, io_lib:format(Format, Data)]),
21 | {ok, State};
22 | handle_event({error_report, _Gleader, {Pid, std_error, Report}}, State) ->
23 | io:fwrite("ERROR <~p> ~p", [Pid, Report]),
24 | {ok, State};
25 | handle_event({error_report, _Gleader, {Pid, Type, Report}}, State) ->
26 | io:fwrite("ERROR <~p> ~p ~p", [Pid, Type, Report]),
27 | {ok, State};
28 | handle_event({warning_msg, _Gleader, {Pid, Format, Data}}, State) ->
29 | io:fwrite("WARNING <~p> ~s", [Pid, io_lib:format(Format, Data)]),
30 | {ok, State};
31 | handle_event({warning_report,_Gleader,{Pid,std_warning,Report}}, State) ->
32 | io:fwrite("WARNING <~p> ~p", [Pid, Report]),
33 | {ok, State};
34 | handle_event({warning_report,_Gleader,{Pid, Type, Report}}, State) ->
35 | io:fwrite("WARNING <~p> ~p ~p", [Pid, Type, Report]),
36 | {ok, State};
37 | handle_event({info_msg, _Gleader, {Pid, Format, Data}}, State) ->
38 | io:fwrite("INFO <~p> ~s", [Pid, io_lib:format(Format, Data)]),
39 | {ok, State};
40 | handle_event({info_report, _Gleader, {Pid, std_info, Report}}, State) ->
41 | io:fwrite("INFO <~p> ~p", [Pid, Report]),
42 | {ok, State};
43 | handle_event({info_report, _Gleader, {Pid, Type, Report}}, State) ->
44 | io:fwrite("INFO <~p> ~p ~p", [Pid, Type, Report]),
45 | {ok, State};
46 | handle_event(_Event, State) ->
47 | {ok, State}.
48 |
49 | handle_call(_Request, State) ->
50 | Reply = ok,
51 | {ok, Reply, State}.
52 |
53 | handle_info(_Info, State) ->
54 | {ok, State}.
55 |
56 | terminate(_Reason, _State) ->
57 | ok.
58 |
59 | code_change(_OldVsn, State, _Extra) ->
60 | {ok, State}.
61 |
--------------------------------------------------------------------------------
/chapter_07/die_please.erl:
--------------------------------------------------------------------------------
1 | -module(die_please).
2 |
3 | -behaviour(gen_server).
4 |
5 | -export([start_link/0]).
6 |
7 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
8 | terminate/2, code_change/3]).
9 |
10 | -define(SERVER, ?MODULE).
11 | -define(SLEEP_TIME, (2*1000)).
12 |
13 | -record(state, {}).
14 |
15 | start_link() ->
16 | gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
17 |
18 | init([]) ->
19 | {ok, #state{}, ?SLEEP_TIME}.
20 |
21 | handle_call(_Request, _From, State) ->
22 | Reply = ok,
23 | {reply, Reply, State}.
24 |
25 | handle_cast(_Msg, State) ->
26 | {noreply, State}.
27 |
28 | handle_info(timeout, State) ->
29 | i_want_to_die = right_now,
30 | {noreply, State}.
31 |
32 | terminate(_Reason, _State) ->
33 | ok.
34 |
35 | code_change(_OldVsn, State, _Extra) ->
36 | {ok, State}.
37 |
--------------------------------------------------------------------------------
/chapter_07/die_please2.erl:
--------------------------------------------------------------------------------
1 | -module(die_please2).
2 |
3 | -export([go/0]).
4 |
5 | -define(SLEEP_TIME, 2000).
6 |
7 | go() ->
8 | %% just sleep for a while, then crash
9 | timer:sleep(?SLEEP_TIME),
10 | i_really_want_to_die = right_now.
11 |
--------------------------------------------------------------------------------
/chapter_07/simple_cache/README:
--------------------------------------------------------------------------------
1 | To build this code, run the following command:
2 |
3 | erlc -o ./ebin ./src/*.erl
4 |
5 | To run the program, first start Erlang like this:
6 |
7 | erl -pa ./ebin
8 |
9 | Then, run the following in the Erlang shell:
10 |
11 | 1> application:start(sasl).
12 | ok
13 | 2> application:start(simple_cache).
14 | ok
15 | 3>
16 |
--------------------------------------------------------------------------------
/chapter_07/simple_cache/doc/.gitignore:
--------------------------------------------------------------------------------
1 | edoc-info
2 | stylesheet.css
3 | erlang.png
4 | *.html
5 |
--------------------------------------------------------------------------------
/chapter_07/simple_cache/ebin/.gitignore:
--------------------------------------------------------------------------------
1 | *.beam
2 |
--------------------------------------------------------------------------------
/chapter_07/simple_cache/ebin/simple_cache.app:
--------------------------------------------------------------------------------
1 | {application, simple_cache,
2 | [{description, "A simple caching system"},
3 | {vsn, "0.1.0"},
4 | {modules, [simple_cache,
5 | sc_app,
6 | sc_sup,
7 | sc_element_sup,
8 | sc_store,
9 | sc_element,
10 | sc_event,
11 | sc_event_logger]},
12 | {registered, [sc_sup, sc_element_sup, sc_event]},
13 | {applications, [kernel, sasl, stdlib]},
14 | {mod, {sc_app, []}}
15 | ]}.
16 |
--------------------------------------------------------------------------------
/chapter_07/simple_cache/include/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_07/simple_cache/include/.gitignore
--------------------------------------------------------------------------------
/chapter_07/simple_cache/priv/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_07/simple_cache/priv/.gitignore
--------------------------------------------------------------------------------
/chapter_07/simple_cache/src/sc_app.erl:
--------------------------------------------------------------------------------
1 | -module(sc_app).
2 |
3 | -behaviour(application).
4 |
5 | -export([start/2, stop/1]).
6 |
7 | start(_StartType, _StartArgs) ->
8 | sc_store:init(),
9 | case sc_sup:start_link() of
10 | {ok, Pid} ->
11 | sc_event_logger:add_handler(),
12 | {ok, Pid};
13 | Other ->
14 | {error, Other}
15 | end.
16 |
17 | stop(_State) ->
18 | ok.
19 |
--------------------------------------------------------------------------------
/chapter_07/simple_cache/src/sc_element.erl:
--------------------------------------------------------------------------------
1 | -module(sc_element).
2 |
3 | -behaviour(gen_server).
4 |
5 | -export([
6 | start_link/2,
7 | create/1,
8 | create/2,
9 | fetch/1,
10 | replace/2,
11 | delete/1
12 | ]).
13 |
14 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
15 | terminate/2, code_change/3]).
16 |
17 | -define(SERVER, ?MODULE).
18 | -define(DEFAULT_LEASE_TIME, (60 * 60 * 24)).
19 |
20 | -record(state, {value, lease_time, start_time}).
21 |
22 | start_link(Value, LeaseTime) ->
23 | gen_server:start_link(?MODULE, [Value, LeaseTime], []).
24 |
25 | create(Value, LeaseTime) ->
26 | sc_element_sup:start_child(Value, LeaseTime).
27 |
28 | create(Value) ->
29 | create(Value, ?DEFAULT_LEASE_TIME).
30 |
31 | fetch(Pid) ->
32 | gen_server:call(Pid, fetch).
33 |
34 | replace(Pid, Value) ->
35 | gen_server:cast(Pid, {replace, Value}).
36 |
37 | delete(Pid) ->
38 | gen_server:cast(Pid, delete).
39 |
40 | init([Value, LeaseTime]) ->
41 | Now = calendar:local_time(),
42 | StartTime = calendar:datetime_to_gregorian_seconds(Now),
43 | {ok,
44 | #state{value = Value,
45 | lease_time = LeaseTime,
46 | start_time = StartTime},
47 | time_left(StartTime, LeaseTime)}.
48 |
49 | time_left(_StartTime, infinity) ->
50 | infinity;
51 | time_left(StartTime, LeaseTime) ->
52 | Now = calendar:local_time(),
53 | CurrentTime = calendar:datetime_to_gregorian_seconds(Now),
54 | TimeElapsed = CurrentTime - StartTime,
55 | case LeaseTime - TimeElapsed of
56 | Time when Time =< 0 -> 0;
57 | Time -> Time * 1000
58 | end.
59 |
60 | handle_call(fetch, _From, State) ->
61 | #state{value = Value,
62 | lease_time = LeaseTime,
63 | start_time = StartTime} = State,
64 | TimeLeft = time_left(StartTime, LeaseTime),
65 | {reply, {ok, Value}, State, TimeLeft}.
66 |
67 | handle_cast({replace, Value}, State) ->
68 | #state{lease_time = LeaseTime,
69 | start_time = StartTime} = State,
70 | TimeLeft = time_left(StartTime, LeaseTime),
71 | {noreply, State#state{value = Value}, TimeLeft};
72 | handle_cast(delete, State) ->
73 | {stop, normal, State}.
74 |
75 | handle_info(timeout, State) ->
76 | {stop, normal, State}.
77 |
78 | terminate(_Reason, _State) ->
79 | sc_store:delete(self()),
80 | ok.
81 |
82 | code_change(_OldVsn, State, _Extra) ->
83 | {ok, State}.
84 |
--------------------------------------------------------------------------------
/chapter_07/simple_cache/src/sc_element_sup.erl:
--------------------------------------------------------------------------------
1 | -module(sc_element_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | -export([start_link/0,
6 | start_child/2
7 | ]).
8 |
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
15 |
16 | start_child(Value, LeaseTime) ->
17 | supervisor:start_child(?SERVER, [Value, LeaseTime]).
18 |
19 | init([]) ->
20 | Element = {sc_element, {sc_element, start_link, []},
21 | temporary, brutal_kill, worker, [sc_element]},
22 | Children = [Element],
23 | RestartStrategy = {simple_one_for_one, 0, 1},
24 | {ok, {RestartStrategy, Children}}.
25 |
--------------------------------------------------------------------------------
/chapter_07/simple_cache/src/sc_event.erl:
--------------------------------------------------------------------------------
1 | -module(sc_event).
2 |
3 | -export([start_link/0,
4 | add_handler/2,
5 | delete_handler/2,
6 | lookup/1,
7 | create/2,
8 | replace/2,
9 | delete/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | gen_event:start_link({local, ?SERVER}).
15 |
16 | add_handler(Handler, Args) ->
17 | gen_event:add_handler(?SERVER, Handler, Args).
18 |
19 | delete_handler(Handler, Args) ->
20 | gen_event:delete_handler(?SERVER, Handler, Args).
21 |
22 | lookup(Key) ->
23 | gen_event:notify(?SERVER, {lookup, Key}).
24 |
25 | create(Key, Value) ->
26 | gen_event:notify(?SERVER, {create, {Key, Value}}).
27 |
28 | replace(Key, Value) ->
29 | gen_event:notify(?SERVER, {replace, {Key, Value}}).
30 |
31 | delete(Key) ->
32 | gen_event:notify(?SERVER, {delete, Key}).
33 |
--------------------------------------------------------------------------------
/chapter_07/simple_cache/src/sc_event_logger.erl:
--------------------------------------------------------------------------------
1 | -module(sc_event_logger).
2 |
3 | -behaviour(gen_event).
4 |
5 | -export([add_handler/0, delete_handler/0]).
6 |
7 | -export([init/1, handle_event/2, handle_call/2,
8 | handle_info/2, code_change/3, terminate/2]).
9 |
10 | -record(state, {}).
11 |
12 | add_handler() ->
13 | sc_event:add_handler(?MODULE, []).
14 |
15 | delete_handler() ->
16 | sc_event:delete_handler(?MODULE, []).
17 |
18 | init([]) ->
19 | {ok, #state{}}.
20 |
21 | handle_event({create, {Key, Value}}, State) ->
22 | error_logger:info_msg("create(~w, ~w)~n", [Key, Value]),
23 | {ok, State};
24 | handle_event({lookup, Key}, State) ->
25 | error_logger:info_msg("lookup(~w)~n", [Key]),
26 | {ok, State};
27 | handle_event({delete, Key}, State) ->
28 | error_logger:info_msg("delete(~w)~n", [Key]),
29 | {ok, State};
30 | handle_event({replace, {Key, Value}}, State) ->
31 | error_logger:info_msg("replace(~w, ~w)~n", [Key, Value]),
32 | {ok, State}.
33 |
34 | handle_call(_Request, State) ->
35 | Reply = ok,
36 | {ok, Reply, State}.
37 |
38 | handle_info(_Info, State) ->
39 | {ok, State}.
40 |
41 | terminate(_Reason, _State) ->
42 | ok.
43 |
44 | code_change(_OldVsn, State, _Extra) ->
45 | {ok, State}.
46 |
--------------------------------------------------------------------------------
/chapter_07/simple_cache/src/sc_store.erl:
--------------------------------------------------------------------------------
1 | -module(sc_store).
2 |
3 | -export([
4 | init/0,
5 | insert/2,
6 | delete/1,
7 | lookup/1
8 | ]).
9 |
10 | -define(TABLE_ID, ?MODULE).
11 |
12 | init() ->
13 | ets:new(?TABLE_ID, [public, named_table]),
14 | ok.
15 |
16 | insert(Key, Pid) ->
17 | ets:insert(?TABLE_ID, {Key, Pid}).
18 |
19 | lookup(Key) ->
20 | case ets:lookup(?TABLE_ID, Key) of
21 | [{Key, Pid}] -> {ok, Pid};
22 | [] -> {error, not_found}
23 | end.
24 |
25 | delete(Pid) ->
26 | ets:match_delete(?TABLE_ID, {'_', Pid}).
27 |
--------------------------------------------------------------------------------
/chapter_07/simple_cache/src/sc_sup.erl:
--------------------------------------------------------------------------------
1 | -module(sc_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | %% API
6 | -export([start_link/0]).
7 |
8 | %% Supervisor callbacks
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
15 |
16 | init([]) ->
17 | ElementSup = {sc_element_sup, {sc_element_sup, start_link, []},
18 | permanent, 2000, supervisor, [sc_element]},
19 |
20 | EventManager = {sc_event, {sc_event, start_link, []},
21 | permanent, 2000, worker, [sc_event]},
22 |
23 | Children = [ElementSup, EventManager],
24 | RestartStrategy = {one_for_one, 4, 3600},
25 | {ok, {RestartStrategy, Children}}.
26 |
--------------------------------------------------------------------------------
/chapter_07/simple_cache/src/simple_cache.erl:
--------------------------------------------------------------------------------
1 | -module(simple_cache).
2 |
3 | -export([insert/2, lookup/1, delete/1]).
4 |
5 | insert(Key, Value) ->
6 | case sc_store:lookup(Key) of
7 | {ok, Pid} ->
8 | sc_event:replace(Key, Value),
9 | sc_element:replace(Pid, Value);
10 | {error, _} ->
11 | {ok, Pid} = sc_element:create(Value),
12 | sc_store:insert(Key, Pid),
13 | sc_event:create(Key, Value)
14 | end.
15 |
16 | lookup(Key) ->
17 | sc_event:lookup(Key),
18 | try
19 | {ok, Pid} = sc_store:lookup(Key),
20 | {ok, Value} = sc_element:fetch(Pid),
21 | {ok, Value}
22 | catch
23 | _Class:_Exception ->
24 | {error, not_found}
25 | end.
26 |
27 | delete(Key) ->
28 | sc_event:delete(Key),
29 | case sc_store:lookup(Key) of
30 | {ok, Pid} ->
31 | sc_element:delete(Pid);
32 | {error, _Reason} ->
33 | ok
34 | end.
35 |
--------------------------------------------------------------------------------
/chapter_09/README:
--------------------------------------------------------------------------------
1 | To build this code, run the following commands:
2 |
3 | erlc -o ./simple_cache/ebin ./simple_cache/src/*.erl
4 | erlc -o ./resource_discovery/ebin ./resource_discovery/src/*.erl
5 |
6 | First start one or two contact nodes. Ensure that the default
7 | node names in sc_app:ensure_contact() match your host name, and/or
8 | use -sname instead of -name. (Edit and recompile if necessary.)
9 | For example, in a separate terminal window, do:
10 |
11 | erl -name contact1
12 |
13 | To run the program, start Erlang like this:
14 |
15 | erl -name mynode -pa ./simple_cache/ebin -pa ./resource_discovery/ebin/
16 |
17 | Then, run the following in the Erlang shell:
18 |
19 | 1> application:start(sasl).
20 | ok
21 | 2> mnesia:start().
22 | ok
23 | 3> application:start(resource_discovery).
24 | ok
25 | 4> application:start(simple_cache).
26 | ok
27 | 5>
28 |
29 | (Remember that the resource discovery will make simple_cache
30 | wait several seconds when it starts.)
31 |
32 | For more fun, start one or two other nodes in the same
33 | way (with different names, of course), and start the same
34 | applications on them. Then try inserting something in the
35 | cache on one node, and looking it up on another node.
36 |
--------------------------------------------------------------------------------
/chapter_09/create_tables.erl:
--------------------------------------------------------------------------------
1 | -module(create_tables).
2 |
3 | -export([init_tables/0, insert_user/3, insert_project/2]).
4 |
5 | -record(user, {
6 | id,
7 | name
8 | }).
9 |
10 | -record(project, {
11 | title,
12 | description
13 | }).
14 |
15 | -record(contributor, {
16 | user_id,
17 | project_title
18 | }).
19 |
20 | init_tables() ->
21 | mnesia:create_table(user,
22 | [{attributes, record_info(fields, user)}]),
23 | mnesia:create_table(project,
24 | [{attributes, record_info(fields, project)}]),
25 | mnesia:create_table(contributor,
26 | [{type, bag}, {attributes, record_info(fields, contributor)}]).
27 |
28 | insert_user(Id, Name, ProjectTitles) when ProjectTitles =/= [] ->
29 | User = #user{id = Id, name = Name},
30 | Fun = fun() ->
31 | mnesia:write(User),
32 | lists:foreach(
33 | fun(Title) ->
34 | [#project{title = Title}] = mnesia:read(project, Title),
35 | mnesia:write(#contributor{user_id = Id,
36 | project_title = Title})
37 | end,
38 | ProjectTitles)
39 | end,
40 | mnesia:transaction(Fun).
41 |
42 | insert_project(Title, Description) ->
43 | mnesia:dirty_write(#project{title = Title,
44 | description = Description}).
45 |
--------------------------------------------------------------------------------
/chapter_09/resource_discovery/README:
--------------------------------------------------------------------------------
1 | To build this code, run the following command:
2 |
3 | erlc -o ./ebin ./src/*.erl
4 |
5 | To run the program, first start Erlang like this:
6 |
7 | erl -pa ./ebin
8 |
9 | Then, run the following in the Erlang shell:
10 |
11 | 1> application:start(resource_discovery).
12 | ok
13 |
--------------------------------------------------------------------------------
/chapter_09/resource_discovery/doc/.gitignore:
--------------------------------------------------------------------------------
1 | edoc-info
2 | stylesheet.css
3 | erlang.png
4 | *.html
5 |
--------------------------------------------------------------------------------
/chapter_09/resource_discovery/ebin/.gitignore:
--------------------------------------------------------------------------------
1 | *.beam
2 |
--------------------------------------------------------------------------------
/chapter_09/resource_discovery/ebin/resource_discovery.app:
--------------------------------------------------------------------------------
1 | {application, resource_discovery,
2 | [{description, "A simple resource discovery system"},
3 | {vsn, "0.1.0"},
4 | {modules, [resource_discovery,
5 | rd_app,
6 | rd_sup,
7 | rd_server]},
8 | {registered, [rd_sup, rd_server]},
9 | {applications, [kernel, stdlib]},
10 | {mod, {rd_app, []}}
11 | ]}.
12 |
--------------------------------------------------------------------------------
/chapter_09/resource_discovery/include/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_09/resource_discovery/include/.gitignore
--------------------------------------------------------------------------------
/chapter_09/resource_discovery/priv/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_09/resource_discovery/priv/.gitignore
--------------------------------------------------------------------------------
/chapter_09/resource_discovery/src/rd_app.erl:
--------------------------------------------------------------------------------
1 | -module(rd_app).
2 |
3 | -behaviour(application).
4 |
5 | -export([start/2, stop/1]).
6 |
7 | start(_StartType, _StartArgs) ->
8 | case rd_sup:start_link() of
9 | {ok, Pid} ->
10 | {ok, Pid};
11 | Other ->
12 | {error, Other}
13 | end.
14 |
15 | stop(_State) ->
16 | ok.
17 |
--------------------------------------------------------------------------------
/chapter_09/resource_discovery/src/rd_sup.erl:
--------------------------------------------------------------------------------
1 | -module(rd_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | %% API
6 | -export([start_link/0]).
7 |
8 | %% Supervisor callbacks
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
15 |
16 | init([]) ->
17 | Server = {rd_server, {rd_server, start_link, []},
18 | permanent, 2000, worker, [rd_server]},
19 | Children = [Server],
20 | RestartStrategy = {one_for_one, 0, 1},
21 | {ok, {RestartStrategy, Children}}.
22 |
--------------------------------------------------------------------------------
/chapter_09/resource_discovery/src/resource_discovery.erl:
--------------------------------------------------------------------------------
1 | -module(resource_discovery).
2 |
3 | -export([
4 | add_target_resource_type/1,
5 | add_local_resource/2,
6 | fetch_resources/1,
7 | trade_resources/0
8 | ]).
9 |
10 | add_target_resource_type(Type) ->
11 | rd_server:add_target_resource_type(Type).
12 |
13 | add_local_resource(Type, Resource) ->
14 | rd_server:add_local_resource(Type, Resource).
15 |
16 | fetch_resources(Type) ->
17 | rd_server:fetch_resources(Type).
18 |
19 | trade_resources() ->
20 | rd_server:trade_resources().
21 |
--------------------------------------------------------------------------------
/chapter_09/simple_cache/doc/.gitignore:
--------------------------------------------------------------------------------
1 | edoc-info
2 | stylesheet.css
3 | erlang.png
4 | *.html
5 |
--------------------------------------------------------------------------------
/chapter_09/simple_cache/ebin/.gitignore:
--------------------------------------------------------------------------------
1 | *.beam
2 |
--------------------------------------------------------------------------------
/chapter_09/simple_cache/ebin/simple_cache.app:
--------------------------------------------------------------------------------
1 | {application, simple_cache,
2 | [{description, "A simple caching system"},
3 | {vsn, "0.3.0"},
4 | {modules, [simple_cache,
5 | sc_app,
6 | sc_sup,
7 | sc_element_sup,
8 | sc_store,
9 | sc_element,
10 | sc_event,
11 | sc_event_logger]},
12 | {registered, [sc_sup, sc_element_sup, sc_event]},
13 | {applications, [kernel, sasl, stdlib, mnesia, resource_discovery]},
14 | {mod, {sc_app, []}}
15 | ]}.
16 |
--------------------------------------------------------------------------------
/chapter_09/simple_cache/include/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_09/simple_cache/include/.gitignore
--------------------------------------------------------------------------------
/chapter_09/simple_cache/priv/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_09/simple_cache/priv/.gitignore
--------------------------------------------------------------------------------
/chapter_09/simple_cache/src/sc_app.erl:
--------------------------------------------------------------------------------
1 | -module(sc_app).
2 |
3 | -behaviour(application).
4 |
5 | -export([start/2, stop/1]).
6 |
7 | -define(WAIT_FOR_RESOURCES, 2500).
8 |
9 | start(_StartType, _StartArgs) ->
10 | ok = ensure_contact(),
11 | resource_discovery:add_local_resource(simple_cache, node()),
12 | resource_discovery:add_target_resource_type(simple_cache),
13 | resource_discovery:trade_resources(),
14 | timer:sleep(?WAIT_FOR_RESOURCES),
15 | sc_store:init(),
16 | case sc_sup:start_link() of
17 | {ok, Pid} ->
18 | sc_event_logger:add_handler(),
19 | {ok, Pid};
20 | Other ->
21 | {error, Other}
22 | end.
23 |
24 | stop(_State) ->
25 | ok.
26 |
27 | ensure_contact() ->
28 | DefaultNodes = ['contact1@localhost', 'contact2@localhost'],
29 | case get_env(simple_cache, contact_nodes, DefaultNodes) of
30 | [] ->
31 | {error, no_contact_nodes};
32 | ContactNodes ->
33 | ensure_contact(ContactNodes)
34 | end.
35 |
36 | ensure_contact(ContactNodes) ->
37 | Answering = [N || N <- ContactNodes, net_adm:ping(N) =:= pong],
38 | case Answering of
39 | [] ->
40 | {error, no_contact_nodes_reachable};
41 | _ ->
42 | DefaultTime = 6000,
43 | WaitTime = get_env(simple_cache, wait_time, DefaultTime),
44 | wait_for_nodes(length(Answering), WaitTime)
45 | end.
46 |
47 | wait_for_nodes(MinNodes, WaitTime) ->
48 | Slices = 10,
49 | SliceTime = round(WaitTime/Slices),
50 | wait_for_nodes(MinNodes, SliceTime, Slices).
51 |
52 | wait_for_nodes(_MinNodes, _SliceTime, 0) ->
53 | ok;
54 | wait_for_nodes(MinNodes, SliceTime, Iterations) ->
55 | case length(nodes()) > MinNodes of
56 | true ->
57 | ok;
58 | false ->
59 | timer:sleep(SliceTime),
60 | wait_for_nodes(MinNodes, SliceTime, Iterations - 1)
61 | end.
62 |
63 | get_env(AppName, Key, Default) ->
64 | case application:get_env(AppName, Key) of
65 | undefined -> Default;
66 | {ok, Value} -> Value
67 | end.
68 |
--------------------------------------------------------------------------------
/chapter_09/simple_cache/src/sc_element.erl:
--------------------------------------------------------------------------------
1 | -module(sc_element).
2 |
3 | -behaviour(gen_server).
4 |
5 | -export([
6 | start_link/2,
7 | create/1,
8 | create/2,
9 | fetch/1,
10 | replace/2,
11 | delete/1
12 | ]).
13 |
14 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
15 | terminate/2, code_change/3]).
16 |
17 | -define(SERVER, ?MODULE).
18 | -define(DEFAULT_LEASE_TIME, (60 * 60 * 24)).
19 |
20 | -record(state, {value, lease_time, start_time}).
21 |
22 | start_link(Value, LeaseTime) ->
23 | gen_server:start_link(?MODULE, [Value, LeaseTime], []).
24 |
25 | create(Value, LeaseTime) ->
26 | sc_element_sup:start_child(Value, LeaseTime).
27 |
28 | create(Value) ->
29 | create(Value, ?DEFAULT_LEASE_TIME).
30 |
31 | fetch(Pid) ->
32 | gen_server:call(Pid, fetch).
33 |
34 | replace(Pid, Value) ->
35 | gen_server:cast(Pid, {replace, Value}).
36 |
37 | delete(Pid) ->
38 | gen_server:cast(Pid, delete).
39 |
40 | init([Value, LeaseTime]) ->
41 | Now = calendar:local_time(),
42 | StartTime = calendar:datetime_to_gregorian_seconds(Now),
43 | {ok,
44 | #state{value = Value,
45 | lease_time = LeaseTime,
46 | start_time = StartTime},
47 | time_left(StartTime, LeaseTime)}.
48 |
49 | time_left(_StartTime, infinity) ->
50 | infinity;
51 | time_left(StartTime, LeaseTime) ->
52 | Now = calendar:local_time(),
53 | CurrentTime = calendar:datetime_to_gregorian_seconds(Now),
54 | TimeElapsed = CurrentTime - StartTime,
55 | case LeaseTime - TimeElapsed of
56 | Time when Time =< 0 -> 0;
57 | Time -> Time * 1000
58 | end.
59 |
60 | handle_call(fetch, _From, State) ->
61 | #state{value = Value,
62 | lease_time = LeaseTime,
63 | start_time = StartTime} = State,
64 | TimeLeft = time_left(StartTime, LeaseTime),
65 | {reply, {ok, Value}, State, TimeLeft}.
66 |
67 | handle_cast({replace, Value}, State) ->
68 | #state{lease_time = LeaseTime,
69 | start_time = StartTime} = State,
70 | TimeLeft = time_left(StartTime, LeaseTime),
71 | {noreply, State#state{value = Value}, TimeLeft};
72 | handle_cast(delete, State) ->
73 | {stop, normal, State}.
74 |
75 | handle_info(timeout, State) ->
76 | {stop, normal, State}.
77 |
78 | terminate(_Reason, _State) ->
79 | sc_store:delete(self()),
80 | ok.
81 |
82 | code_change(_OldVsn, State, _Extra) ->
83 | {ok, State}.
84 |
--------------------------------------------------------------------------------
/chapter_09/simple_cache/src/sc_element_sup.erl:
--------------------------------------------------------------------------------
1 | -module(sc_element_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | -export([start_link/0,
6 | start_child/2
7 | ]).
8 |
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
15 |
16 | start_child(Value, LeaseTime) ->
17 | supervisor:start_child(?SERVER, [Value, LeaseTime]).
18 |
19 | init([]) ->
20 | Element = {sc_element, {sc_element, start_link, []},
21 | temporary, brutal_kill, worker, [sc_element]},
22 | Children = [Element],
23 | RestartStrategy = {simple_one_for_one, 0, 1},
24 | {ok, {RestartStrategy, Children}}.
25 |
--------------------------------------------------------------------------------
/chapter_09/simple_cache/src/sc_event.erl:
--------------------------------------------------------------------------------
1 | -module(sc_event).
2 |
3 | -export([start_link/0,
4 | add_handler/2,
5 | delete_handler/2,
6 | lookup/1,
7 | create/2,
8 | replace/2,
9 | delete/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | gen_event:start_link({local, ?SERVER}).
15 |
16 | add_handler(Handler, Args) ->
17 | gen_event:add_handler(?SERVER, Handler, Args).
18 |
19 | delete_handler(Handler, Args) ->
20 | gen_event:delete_handler(?SERVER, Handler, Args).
21 |
22 | lookup(Key) ->
23 | gen_event:notify(?SERVER, {lookup, Key}).
24 |
25 | create(Key, Value) ->
26 | gen_event:notify(?SERVER, {create, {Key, Value}}).
27 |
28 | replace(Key, Value) ->
29 | gen_event:notify(?SERVER, {replace, {Key, Value}}).
30 |
31 | delete(Key) ->
32 | gen_event:notify(?SERVER, {delete, Key}).
33 |
--------------------------------------------------------------------------------
/chapter_09/simple_cache/src/sc_event_logger.erl:
--------------------------------------------------------------------------------
1 | -module(sc_event_logger).
2 |
3 | -behaviour(gen_event).
4 |
5 | -export([add_handler/0, delete_handler/0]).
6 |
7 | -export([init/1, handle_event/2, handle_call/2,
8 | handle_info/2, code_change/3, terminate/2]).
9 |
10 | -record(state, {}).
11 |
12 | add_handler() ->
13 | sc_event:add_handler(?MODULE, []).
14 |
15 | delete_handler() ->
16 | sc_event:delete_handler(?MODULE, []).
17 |
18 | init([]) ->
19 | {ok, #state{}}.
20 |
21 | handle_event({create, {Key, Value}}, State) ->
22 | error_logger:info_msg("create(~w, ~w)~n", [Key, Value]),
23 | {ok, State};
24 | handle_event({lookup, Key}, State) ->
25 | error_logger:info_msg("lookup(~w)~n", [Key]),
26 | {ok, State};
27 | handle_event({delete, Key}, State) ->
28 | error_logger:info_msg("delete(~w)~n", [Key]),
29 | {ok, State};
30 | handle_event({replace, {Key, Value}}, State) ->
31 | error_logger:info_msg("replace(~w, ~w)~n", [Key, Value]),
32 | {ok, State}.
33 |
34 | handle_call(_Request, State) ->
35 | Reply = ok,
36 | {ok, Reply, State}.
37 |
38 | handle_info(_Info, State) ->
39 | {ok, State}.
40 |
41 | terminate(_Reason, _State) ->
42 | ok.
43 |
44 | code_change(_OldVsn, State, _Extra) ->
45 | {ok, State}.
46 |
--------------------------------------------------------------------------------
/chapter_09/simple_cache/src/sc_store.erl:
--------------------------------------------------------------------------------
1 | -module(sc_store).
2 |
3 | -export([
4 | init/0,
5 | insert/2,
6 | delete/1,
7 | lookup/1
8 | ]).
9 |
10 | -define(TABLE_ID, ?MODULE).
11 | -define(WAIT_FOR_TABLES, 5000).
12 |
13 | -record(key_to_pid, {key, pid}).
14 |
15 | init() ->
16 | mnesia:stop(),
17 | mnesia:delete_schema([node()]),
18 | mnesia:start(),
19 | {ok, CacheNodes} = resource_discovery:fetch_resources(simple_cache),
20 | dynamic_db_init(lists:delete(node(), CacheNodes)).
21 |
22 | insert(Key, Pid) ->
23 | mnesia:dirty_write(#key_to_pid{key = Key, pid = Pid}).
24 |
25 | lookup(Key) ->
26 | case mnesia:dirty_read(key_to_pid, Key) of
27 | [{key_to_pid, Key, Pid}] ->
28 | case is_pid_alive(Pid) of
29 | true -> {ok, Pid};
30 | false -> {error, not_found}
31 | end;
32 | [] ->
33 | {error, not_found}
34 | end.
35 |
36 | delete(Pid) ->
37 | case mnesia:dirty_index_read(key_to_pid, Pid, #key_to_pid.pid) of
38 | [#key_to_pid{} = Record] ->
39 | mnesia:dirty_delete_object(Record);
40 | _ ->
41 | ok
42 | end.
43 |
44 |
45 | %% Internal Functions
46 |
47 | dynamic_db_init([]) ->
48 | mnesia:create_table(key_to_pid,
49 | [{index, [pid]},
50 | {attributes, record_info(fields, key_to_pid)}
51 | ]);
52 | dynamic_db_init(CacheNodes) ->
53 | add_extra_nodes(CacheNodes).
54 |
55 | add_extra_nodes([Node|T]) ->
56 | case mnesia:change_config(extra_db_nodes, [Node]) of
57 | {ok, [Node]} ->
58 | mnesia:add_table_copy(key_to_pid, node(), ram_copies),
59 |
60 | Tables = mnesia:system_info(tables),
61 | mnesia:wait_for_tables(Tables, ?WAIT_FOR_TABLES);
62 | _ ->
63 | add_extra_nodes(T)
64 | end.
65 |
66 | is_pid_alive(Pid) when node(Pid) =:= node() ->
67 | is_process_alive(Pid);
68 | is_pid_alive(Pid) ->
69 | case lists:member(node(Pid), nodes()) of
70 | false ->
71 | false;
72 | true ->
73 | case rpc:call(node(Pid), erlang, is_process_alive, [Pid]) of
74 | true ->
75 | true;
76 | false ->
77 | false;
78 | {badrpc, _Reason} ->
79 | false
80 | end
81 | end.
82 |
83 |
--------------------------------------------------------------------------------
/chapter_09/simple_cache/src/sc_sup.erl:
--------------------------------------------------------------------------------
1 | -module(sc_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | %% API
6 | -export([start_link/0]).
7 |
8 | %% Supervisor callbacks
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
15 |
16 | init([]) ->
17 | ElementSup = {sc_element_sup, {sc_element_sup, start_link, []},
18 | permanent, 2000, supervisor, [sc_element]},
19 |
20 | EventManager = {sc_event, {sc_event, start_link, []},
21 | permanent, 2000, worker, [sc_event]},
22 |
23 | Children = [ElementSup, EventManager],
24 | RestartStrategy = {one_for_one, 4, 3600},
25 | {ok, {RestartStrategy, Children}}.
26 |
--------------------------------------------------------------------------------
/chapter_09/simple_cache/src/simple_cache.erl:
--------------------------------------------------------------------------------
1 | -module(simple_cache).
2 |
3 | -export([insert/2, lookup/1, delete/1]).
4 |
5 | insert(Key, Value) ->
6 | case sc_store:lookup(Key) of
7 | {ok, Pid} ->
8 | sc_event:replace(Key, Value),
9 | sc_element:replace(Pid, Value);
10 | {error, _} ->
11 | {ok, Pid} = sc_element:create(Value),
12 | sc_store:insert(Key, Pid),
13 | sc_event:create(Key, Value)
14 | end.
15 |
16 | lookup(Key) ->
17 | sc_event:lookup(Key),
18 | try
19 | {ok, Pid} = sc_store:lookup(Key),
20 | {ok, Value} = sc_element:fetch(Pid),
21 | {ok, Value}
22 | catch
23 | _Class:_Exception ->
24 | {error, not_found}
25 | end.
26 |
27 | delete(Key) ->
28 | sc_event:delete(Key),
29 | case sc_store:lookup(Key) of
30 | {ok, Pid} ->
31 | sc_element:delete(Pid);
32 | {error, _Reason} ->
33 | ok
34 | end.
35 |
--------------------------------------------------------------------------------
/chapter_10/.gitignore:
--------------------------------------------------------------------------------
1 | *.boot
2 | *.script
3 | *.tar.gz
4 |
--------------------------------------------------------------------------------
/chapter_10/README:
--------------------------------------------------------------------------------
1 | To build this code, run the following commands:
2 |
3 | erlc -o ./simple_cache/ebin ./simple_cache/src/*.erl
4 | erlc -o ./resource_discovery/ebin ./resource_discovery/src/*.erl
5 |
6 | Then run the following (all on a single line) to generate the .boot file:
7 |
8 | erl -noshell -pa ./simple_cache/ebin -pa ./resource_discovery/ebin -eval 'systools:make_script("simple_cache", [local])' -s init stop
9 |
10 | First start one or two contact nodes. Ensure that the node names
11 | in the sys.config files match your host name, and/or use -sname
12 | instead of -name. For example, do:
13 |
14 | erl -sname contact1 -detached
15 |
16 | Then, start the system like this (all on a single line):
17 |
18 | erl -sname mynode -pa ./simple_cache/ebin -pa ./resource_discovery/ebin/ -boot ./simple_cache -config sys
19 |
20 | (Recall that the initial node discovery waits a few seconds at startup.)
21 |
22 | You can now e.g. run appmon:start() to inspect your running system.
23 |
--------------------------------------------------------------------------------
/chapter_10/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # rename this file to "install" and set the executable flag,
3 | # then copy it into the bin directory of your release package
4 | # (update the erts version number below to match your release)
5 | ROOT=`pwd`
6 | DIR=./erts-5.7.4/bin
7 | sed s:%FINAL_ROOTDIR%:$ROOT: $DIR/erl.src > $DIR/erl
8 |
--------------------------------------------------------------------------------
/chapter_10/resource_discovery/doc/.gitignore:
--------------------------------------------------------------------------------
1 | edoc-info
2 | stylesheet.css
3 | erlang.png
4 | *.html
5 |
--------------------------------------------------------------------------------
/chapter_10/resource_discovery/ebin/.gitignore:
--------------------------------------------------------------------------------
1 | *.beam
2 |
--------------------------------------------------------------------------------
/chapter_10/resource_discovery/ebin/resource_discovery.app:
--------------------------------------------------------------------------------
1 | {application, resource_discovery,
2 | [{description, "A simple resource discovery system"},
3 | {vsn, "0.1.0"},
4 | {modules, [resource_discovery,
5 | rd_app,
6 | rd_sup,
7 | rd_server]},
8 | {registered, [rd_sup, rd_server]},
9 | {applications, [kernel, stdlib]},
10 | {mod, {rd_app, []}}
11 | ]}.
12 |
--------------------------------------------------------------------------------
/chapter_10/resource_discovery/include/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_10/resource_discovery/include/.gitignore
--------------------------------------------------------------------------------
/chapter_10/resource_discovery/priv/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_10/resource_discovery/priv/.gitignore
--------------------------------------------------------------------------------
/chapter_10/resource_discovery/src/rd_app.erl:
--------------------------------------------------------------------------------
1 | -module(rd_app).
2 |
3 | -behaviour(application).
4 |
5 | -export([start/2, stop/1]).
6 |
7 | start(_StartType, _StartArgs) ->
8 | case rd_sup:start_link() of
9 | {ok, Pid} ->
10 | {ok, Pid};
11 | Other ->
12 | {error, Other}
13 | end.
14 |
15 | stop(_State) ->
16 | ok.
17 |
--------------------------------------------------------------------------------
/chapter_10/resource_discovery/src/rd_sup.erl:
--------------------------------------------------------------------------------
1 | -module(rd_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | %% API
6 | -export([start_link/0]).
7 |
8 | %% Supervisor callbacks
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
15 |
16 | init([]) ->
17 | Server = {rd_server, {rd_server, start_link, []},
18 | permanent, 2000, worker, [rd_server]},
19 | Children = [Server],
20 | RestartStrategy = {one_for_one, 0, 1},
21 | {ok, {RestartStrategy, Children}}.
22 |
--------------------------------------------------------------------------------
/chapter_10/resource_discovery/src/resource_discovery.erl:
--------------------------------------------------------------------------------
1 | -module(resource_discovery).
2 |
3 | -export([
4 | add_target_resource_type/1,
5 | add_local_resource/2,
6 | fetch_resources/1,
7 | trade_resources/0
8 | ]).
9 |
10 | add_target_resource_type(Type) ->
11 | rd_server:add_target_resource_type(Type).
12 |
13 | add_local_resource(Type, Resource) ->
14 | rd_server:add_local_resource(Type, Resource).
15 |
16 | fetch_resources(Type) ->
17 | rd_server:fetch_resources(Type).
18 |
19 | trade_resources() ->
20 | rd_server:trade_resources().
21 |
--------------------------------------------------------------------------------
/chapter_10/simple_cache.rel:
--------------------------------------------------------------------------------
1 | {release,
2 | {"simple_cache", "0.1.0"},
3 | {erts, "5.7.3"},
4 | [{kernel, "2.13.3"},
5 | {stdlib, "1.16.3"},
6 | {sasl, "2.1.7"},
7 | {mnesia, "4.4.11"},
8 | {resource_discovery, "0.1.0"},
9 | {simple_cache, "0.3.0"}
10 | ]}.
11 |
--------------------------------------------------------------------------------
/chapter_10/simple_cache.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # rename this file to "simple_cache" and set the executable flag,
3 | # then copy it into the bin directory of your release package
4 | # (update the erts version number below to match your release)
5 | ./erts-5.7.4/bin/erl \
6 | -sname cache \
7 | -boot ./releases/0.1.0/start \
8 | -config ./releases/0.1.0/sys \
9 | -detached
10 |
--------------------------------------------------------------------------------
/chapter_10/simple_cache/doc/.gitignore:
--------------------------------------------------------------------------------
1 | edoc-info
2 | stylesheet.css
3 | erlang.png
4 | *.html
5 |
--------------------------------------------------------------------------------
/chapter_10/simple_cache/ebin/.gitignore:
--------------------------------------------------------------------------------
1 | *.beam
2 |
--------------------------------------------------------------------------------
/chapter_10/simple_cache/ebin/simple_cache.app:
--------------------------------------------------------------------------------
1 | {application, simple_cache,
2 | [{description, "A simple caching system"},
3 | {vsn, "0.3.0"},
4 | {modules, [simple_cache,
5 | sc_app,
6 | sc_sup,
7 | sc_element_sup,
8 | sc_store,
9 | sc_element,
10 | sc_event,
11 | sc_event_logger]},
12 | {registered, [sc_sup, sc_element_sup, sc_event]},
13 | {applications, [kernel, sasl, stdlib, mnesia, resource_discovery]},
14 | {mod, {sc_app, []}}
15 | ]}.
16 |
--------------------------------------------------------------------------------
/chapter_10/simple_cache/include/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_10/simple_cache/include/.gitignore
--------------------------------------------------------------------------------
/chapter_10/simple_cache/priv/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_10/simple_cache/priv/.gitignore
--------------------------------------------------------------------------------
/chapter_10/simple_cache/src/sc_app.erl:
--------------------------------------------------------------------------------
1 | -module(sc_app).
2 |
3 | -behaviour(application).
4 |
5 | -export([start/2, stop/1]).
6 |
7 | -define(WAIT_FOR_RESOURCES, 2500).
8 |
9 | start(_StartType, _StartArgs) ->
10 | ok = ensure_contact(),
11 | resource_discovery:add_local_resource(simple_cache, node()),
12 | resource_discovery:add_target_resource_type(simple_cache),
13 | resource_discovery:trade_resources(),
14 | timer:sleep(?WAIT_FOR_RESOURCES),
15 | sc_store:init(),
16 | case sc_sup:start_link() of
17 | {ok, Pid} ->
18 | sc_event_logger:add_handler(),
19 | {ok, Pid};
20 | Other ->
21 | {error, Other}
22 | end.
23 |
24 | stop(_State) ->
25 | ok.
26 |
27 | ensure_contact() ->
28 | DefaultNodes = ['contact1@localhost', 'contact2@localhost'],
29 | case get_env(simple_cache, contact_nodes, DefaultNodes) of
30 | [] ->
31 | {error, no_contact_nodes};
32 | ContactNodes ->
33 | ensure_contact(ContactNodes)
34 | end.
35 |
36 | ensure_contact(ContactNodes) ->
37 | Answering = [N || N <- ContactNodes, net_adm:ping(N) =:= pong],
38 | case Answering of
39 | [] ->
40 | {error, no_contact_nodes_reachable};
41 | _ ->
42 | DefaultTime = 6000,
43 | WaitTime = get_env(simple_cache, wait_time, DefaultTime),
44 | wait_for_nodes(length(Answering), WaitTime)
45 | end.
46 |
47 | wait_for_nodes(MinNodes, WaitTime) ->
48 | Slices = 10,
49 | SliceTime = round(WaitTime/Slices),
50 | wait_for_nodes(MinNodes, SliceTime, Slices).
51 |
52 | wait_for_nodes(_MinNodes, _SliceTime, 0) ->
53 | ok;
54 | wait_for_nodes(MinNodes, SliceTime, Iterations) ->
55 | case length(nodes()) > MinNodes of
56 | true ->
57 | ok;
58 | false ->
59 | timer:sleep(SliceTime),
60 | wait_for_nodes(MinNodes, SliceTime, Iterations - 1)
61 | end.
62 |
63 | get_env(AppName, Key, Default) ->
64 | case application:get_env(AppName, Key) of
65 | undefined -> Default;
66 | {ok, Value} -> Value
67 | end.
68 |
--------------------------------------------------------------------------------
/chapter_10/simple_cache/src/sc_element.erl:
--------------------------------------------------------------------------------
1 | -module(sc_element).
2 |
3 | -behaviour(gen_server).
4 |
5 | -export([
6 | start_link/2,
7 | create/1,
8 | create/2,
9 | fetch/1,
10 | replace/2,
11 | delete/1
12 | ]).
13 |
14 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
15 | terminate/2, code_change/3]).
16 |
17 | -define(SERVER, ?MODULE).
18 | -define(DEFAULT_LEASE_TIME, (60 * 60 * 24)).
19 |
20 | -record(state, {value, lease_time, start_time}).
21 |
22 | start_link(Value, LeaseTime) ->
23 | gen_server:start_link(?MODULE, [Value, LeaseTime], []).
24 |
25 | create(Value, LeaseTime) ->
26 | sc_element_sup:start_child(Value, LeaseTime).
27 |
28 | create(Value) ->
29 | create(Value, ?DEFAULT_LEASE_TIME).
30 |
31 | fetch(Pid) ->
32 | gen_server:call(Pid, fetch).
33 |
34 | replace(Pid, Value) ->
35 | gen_server:cast(Pid, {replace, Value}).
36 |
37 | delete(Pid) ->
38 | gen_server:cast(Pid, delete).
39 |
40 | init([Value, LeaseTime]) ->
41 | Now = calendar:local_time(),
42 | StartTime = calendar:datetime_to_gregorian_seconds(Now),
43 | {ok,
44 | #state{value = Value,
45 | lease_time = LeaseTime,
46 | start_time = StartTime},
47 | time_left(StartTime, LeaseTime)}.
48 |
49 | time_left(_StartTime, infinity) ->
50 | infinity;
51 | time_left(StartTime, LeaseTime) ->
52 | Now = calendar:local_time(),
53 | CurrentTime = calendar:datetime_to_gregorian_seconds(Now),
54 | TimeElapsed = CurrentTime - StartTime,
55 | case LeaseTime - TimeElapsed of
56 | Time when Time =< 0 -> 0;
57 | Time -> Time * 1000
58 | end.
59 |
60 | handle_call(fetch, _From, State) ->
61 | #state{value = Value,
62 | lease_time = LeaseTime,
63 | start_time = StartTime} = State,
64 | TimeLeft = time_left(StartTime, LeaseTime),
65 | {reply, {ok, Value}, State, TimeLeft}.
66 |
67 | handle_cast({replace, Value}, State) ->
68 | #state{lease_time = LeaseTime,
69 | start_time = StartTime} = State,
70 | TimeLeft = time_left(StartTime, LeaseTime),
71 | {noreply, State#state{value = Value}, TimeLeft};
72 | handle_cast(delete, State) ->
73 | {stop, normal, State}.
74 |
75 | handle_info(timeout, State) ->
76 | {stop, normal, State}.
77 |
78 | terminate(_Reason, _State) ->
79 | sc_store:delete(self()),
80 | ok.
81 |
82 | code_change(_OldVsn, State, _Extra) ->
83 | {ok, State}.
84 |
--------------------------------------------------------------------------------
/chapter_10/simple_cache/src/sc_element_sup.erl:
--------------------------------------------------------------------------------
1 | -module(sc_element_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | -export([start_link/0,
6 | start_child/2
7 | ]).
8 |
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
15 |
16 | start_child(Value, LeaseTime) ->
17 | supervisor:start_child(?SERVER, [Value, LeaseTime]).
18 |
19 | init([]) ->
20 | Element = {sc_element, {sc_element, start_link, []},
21 | temporary, brutal_kill, worker, [sc_element]},
22 | Children = [Element],
23 | RestartStrategy = {simple_one_for_one, 0, 1},
24 | {ok, {RestartStrategy, Children}}.
25 |
--------------------------------------------------------------------------------
/chapter_10/simple_cache/src/sc_event.erl:
--------------------------------------------------------------------------------
1 | -module(sc_event).
2 |
3 | -export([start_link/0,
4 | add_handler/2,
5 | delete_handler/2,
6 | lookup/1,
7 | create/2,
8 | replace/2,
9 | delete/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | gen_event:start_link({local, ?SERVER}).
15 |
16 | add_handler(Handler, Args) ->
17 | gen_event:add_handler(?SERVER, Handler, Args).
18 |
19 | delete_handler(Handler, Args) ->
20 | gen_event:delete_handler(?SERVER, Handler, Args).
21 |
22 | lookup(Key) ->
23 | gen_event:notify(?SERVER, {lookup, Key}).
24 |
25 | create(Key, Value) ->
26 | gen_event:notify(?SERVER, {create, {Key, Value}}).
27 |
28 | replace(Key, Value) ->
29 | gen_event:notify(?SERVER, {replace, {Key, Value}}).
30 |
31 | delete(Key) ->
32 | gen_event:notify(?SERVER, {delete, Key}).
33 |
--------------------------------------------------------------------------------
/chapter_10/simple_cache/src/sc_event_logger.erl:
--------------------------------------------------------------------------------
1 | -module(sc_event_logger).
2 |
3 | -behaviour(gen_event).
4 |
5 | -export([add_handler/0, delete_handler/0]).
6 |
7 | -export([init/1, handle_event/2, handle_call/2,
8 | handle_info/2, code_change/3, terminate/2]).
9 |
10 | -record(state, {}).
11 |
12 | add_handler() ->
13 | sc_event:add_handler(?MODULE, []).
14 |
15 | delete_handler() ->
16 | sc_event:delete_handler(?MODULE, []).
17 |
18 | init([]) ->
19 | {ok, #state{}}.
20 |
21 | handle_event({create, {Key, Value}}, State) ->
22 | error_logger:info_msg("create(~w, ~w)~n", [Key, Value]),
23 | {ok, State};
24 | handle_event({lookup, Key}, State) ->
25 | error_logger:info_msg("lookup(~w)~n", [Key]),
26 | {ok, State};
27 | handle_event({delete, Key}, State) ->
28 | error_logger:info_msg("delete(~w)~n", [Key]),
29 | {ok, State};
30 | handle_event({replace, {Key, Value}}, State) ->
31 | error_logger:info_msg("replace(~w, ~w)~n", [Key, Value]),
32 | {ok, State}.
33 |
34 | handle_call(_Request, State) ->
35 | Reply = ok,
36 | {ok, Reply, State}.
37 |
38 | handle_info(_Info, State) ->
39 | {ok, State}.
40 |
41 | terminate(_Reason, _State) ->
42 | ok.
43 |
44 | code_change(_OldVsn, State, _Extra) ->
45 | {ok, State}.
46 |
--------------------------------------------------------------------------------
/chapter_10/simple_cache/src/sc_store.erl:
--------------------------------------------------------------------------------
1 | -module(sc_store).
2 |
3 | -export([
4 | init/0,
5 | insert/2,
6 | delete/1,
7 | lookup/1
8 | ]).
9 |
10 | -define(TABLE_ID, ?MODULE).
11 | -define(WAIT_FOR_TABLES, 5000).
12 |
13 | -record(key_to_pid, {key, pid}).
14 |
15 | init() ->
16 | mnesia:stop(),
17 | mnesia:delete_schema([node()]),
18 | mnesia:start(),
19 | {ok, CacheNodes} = resource_discovery:fetch_resources(simple_cache),
20 | dynamic_db_init(lists:delete(node(), CacheNodes)).
21 |
22 | insert(Key, Pid) ->
23 | mnesia:dirty_write(#key_to_pid{key = Key, pid = Pid}).
24 |
25 | lookup(Key) ->
26 | case mnesia:dirty_read(key_to_pid, Key) of
27 | [{key_to_pid, Key, Pid}] ->
28 | case is_pid_alive(Pid) of
29 | true -> {ok, Pid};
30 | false -> {error, not_found}
31 | end;
32 | [] ->
33 | {error, not_found}
34 | end.
35 |
36 | delete(Pid) ->
37 | case mnesia:dirty_index_read(key_to_pid, Pid, #key_to_pid.pid) of
38 | [#key_to_pid{} = Record] ->
39 | mnesia:dirty_delete_object(Record);
40 | _ ->
41 | ok
42 | end.
43 |
44 |
45 | %% Internal Functions
46 |
47 | dynamic_db_init([]) ->
48 | mnesia:create_table(key_to_pid,
49 | [{index, [pid]},
50 | {attributes, record_info(fields, key_to_pid)}
51 | ]);
52 | dynamic_db_init(CacheNodes) ->
53 | add_extra_nodes(CacheNodes).
54 |
55 | add_extra_nodes([Node|T]) ->
56 | case mnesia:change_config(extra_db_nodes, [Node]) of
57 | {ok, [Node]} ->
58 | mnesia:add_table_copy(key_to_pid, node(), ram_copies),
59 |
60 | Tables = mnesia:system_info(tables),
61 | mnesia:wait_for_tables(Tables, ?WAIT_FOR_TABLES);
62 | _ ->
63 | add_extra_nodes(T)
64 | end.
65 |
66 | is_pid_alive(Pid) when node(Pid) =:= node() ->
67 | is_process_alive(Pid);
68 | is_pid_alive(Pid) ->
69 | case lists:member(node(Pid), nodes()) of
70 | false ->
71 | false;
72 | true ->
73 | case rpc:call(node(Pid), erlang, is_process_alive, [Pid]) of
74 | true ->
75 | true;
76 | false ->
77 | false;
78 | {badrpc, _Reason} ->
79 | false
80 | end
81 | end.
82 |
83 |
--------------------------------------------------------------------------------
/chapter_10/simple_cache/src/sc_sup.erl:
--------------------------------------------------------------------------------
1 | -module(sc_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | %% API
6 | -export([start_link/0]).
7 |
8 | %% Supervisor callbacks
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
15 |
16 | init([]) ->
17 | ElementSup = {sc_element_sup, {sc_element_sup, start_link, []},
18 | permanent, 2000, supervisor, [sc_element]},
19 |
20 | EventManager = {sc_event, {sc_event, start_link, []},
21 | permanent, 2000, worker, [sc_event]},
22 |
23 | Children = [ElementSup, EventManager],
24 | RestartStrategy = {one_for_one, 4, 3600},
25 | {ok, {RestartStrategy, Children}}.
26 |
--------------------------------------------------------------------------------
/chapter_10/simple_cache/src/simple_cache.erl:
--------------------------------------------------------------------------------
1 | -module(simple_cache).
2 |
3 | -export([insert/2, lookup/1, delete/1]).
4 |
5 | insert(Key, Value) ->
6 | case sc_store:lookup(Key) of
7 | {ok, Pid} ->
8 | sc_event:replace(Key, Value),
9 | sc_element:replace(Pid, Value);
10 | {error, _} ->
11 | {ok, Pid} = sc_element:create(Value),
12 | sc_store:insert(Key, Pid),
13 | sc_event:create(Key, Value)
14 | end.
15 |
16 | lookup(Key) ->
17 | sc_event:lookup(Key),
18 | try
19 | {ok, Pid} = sc_store:lookup(Key),
20 | {ok, Value} = sc_element:fetch(Pid),
21 | {ok, Value}
22 | catch
23 | _Class:_Exception ->
24 | {error, not_found}
25 | end.
26 |
27 | delete(Key) ->
28 | sc_event:delete(Key),
29 | case sc_store:lookup(Key) of
30 | {ok, Pid} ->
31 | sc_element:delete(Pid);
32 | {error, _Reason} ->
33 | ok
34 | end.
35 |
--------------------------------------------------------------------------------
/chapter_10/sys.config:
--------------------------------------------------------------------------------
1 | [
2 | %% write log files to sasl_dir
3 | {sasl,
4 | [
5 | {sasl_error_logger, {file, "/tmp/simple_cache.sasl_log"}}
6 | ]},
7 | {simple_cache,
8 | [
9 | %% Contact nodes for use in joining a cloud
10 | {contact_nodes, ['contact1@localhost', 'contact2@localhost']}
11 | ]}
12 | ].
13 |
--------------------------------------------------------------------------------
/chapter_11/.gitignore:
--------------------------------------------------------------------------------
1 | *.boot
2 | *.script
3 | *.tar.gz
4 |
--------------------------------------------------------------------------------
/chapter_11/README:
--------------------------------------------------------------------------------
1 | To build this code, run the following commands (or run compile.sh):
2 |
3 | erlc -o ./tcp_interface/ebin ./tcp_interface/src/*.erl
4 | erlc -o ./gen_web_server/ebin ./gen_web_server/src/*.erl
5 | erlc -pa ./gen_web_server/ebin -o ./http_interface/ebin ./http_interface/src/*.erl
6 | erlc -o ./simple_cache/ebin ./simple_cache/src/*.erl
7 | erlc -o ./resource_discovery/ebin ./resource_discovery/src/*.erl
8 |
9 | Then run the following (all on a single line) to generate the .boot file:
10 |
11 | erl -noshell -pa ./simple_cache/ebin -pa ./resource_discovery/ebin -eval 'systools:make_script("simple_cache", [local])' -s init stop
12 |
13 | Start the simple_cache system as in chapter 10 (including a contact node),
14 | but also including the paths to the ebin directories of tcp_interface,
15 | gen_web_server, and http_interface:
16 |
17 | erl -sname contact1 -detached
18 | erl -sname mynode -pa ./tcp_interface/ebin -pa ./gen_web_server/ebin -pa ./http_interface/ebin -pa ./simple_cache/ebin -pa ./resource_discovery/ebin/ -boot ./simple_cache -config sys
19 |
20 | (The node discovery waits a few seconds at startup.)
21 |
22 | Then, from the Erlang shell, run:
23 |
24 | application:start(tcp_interface).
25 | application:start(http_interface).
26 |
27 | You can now e.g. run appmon:start() to inspect the system and check that
28 | tcp_interface is running alongside the other applications.
29 |
30 | After that, open another terminal window and use telnet
31 | to connect to the TCP interface, like this:
32 |
33 | $ telnet localhost 1155
34 | Trying 127.0.0.1...
35 | Connected to localhost.localdomain.
36 | Escape character is '^]'.
37 | lookup[foo]
38 | OK:{error,not_found}.
39 | insert[foo,bar]
40 | OK:ok.
41 | lookup[foo]
42 | OK:{ok,bar}.
43 | delete[foo]
44 | OK:ok.
45 | lookup[foo]
46 | OK:{error,not_found}.
47 |
48 | Or, use a command-line HTTP client like 'curl' to connect
49 | to the HTTP interface:
50 |
51 | $ curl -T put.txt http://localhost:1156/xyzzy
52 | $ curl http://localhost:1156/xyzzy
53 | Erlang
54 |
55 | (if the file put.txt contains the text "Erlang"). You can
56 | also use a normal web browser and enter a URL such as
57 | http://localhost:1156/xyzzy in the address field. Note that
58 | the HTTP interface stores text as binaries in the cache,
59 | so for example, to look up the key xyzzy from the TCP
60 | interface, you have to enter the query like this:
61 |
62 | lookup[<<"xyzzy">>]
63 | OK:{ok,<<"Erlang\n">>}.
64 |
--------------------------------------------------------------------------------
/chapter_11/active_once.erl:
--------------------------------------------------------------------------------
1 | -module(active_once).
2 |
3 | -export([start/0]).
4 |
5 | start() ->
6 | {ok, LSock} = gen_tcp:listen(1055, [binary, {active, false}]),
7 | {ok, Socket} = gen_tcp:accept(LSock),
8 | loop(Socket).
9 |
10 | loop(Socket) ->
11 | inet:setopts(Socket, [{active,once}]),
12 | receive
13 | {tcp, Socket, Data} ->
14 | io:format("got ~p~n", [Data]),
15 | loop(Socket);
16 | {tcp_closed, _Socket} ->
17 | ok
18 | end.
19 |
--------------------------------------------------------------------------------
/chapter_11/compile.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | erlc -o ./tcp_interface/ebin ./tcp_interface/src/*.erl
3 | erlc -o ./gen_web_server/ebin ./gen_web_server/src/*.erl
4 | erlc -pa ./gen_web_server/ebin -o ./http_interface/ebin ./http_interface/src/*.erl
5 | erlc -o ./simple_cache/ebin ./simple_cache/src/*.erl
6 | erlc -o ./resource_discovery/ebin ./resource_discovery/src/*.erl
7 |
--------------------------------------------------------------------------------
/chapter_11/gen_web_server/ebin/.gitignore:
--------------------------------------------------------------------------------
1 | *.beam
2 |
--------------------------------------------------------------------------------
/chapter_11/gen_web_server/ebin/gen_web_server.app:
--------------------------------------------------------------------------------
1 | {application, gen_web_server,
2 | [{description, "A generic web server behaviour"},
3 | {vsn, "0.1.0"},
4 | {modules, [gen_web_server,
5 | gws_connection_sup,
6 | gws_server]},
7 | {registered, []},
8 | {applications, [kernel, stdlib]}
9 | ]}.
10 |
--------------------------------------------------------------------------------
/chapter_11/gen_web_server/src/gen_web_server.erl:
--------------------------------------------------------------------------------
1 | -module(gen_web_server).
2 |
3 | %% API
4 | -export([start_link/3, start_link/4,
5 | http_reply/1, http_reply/2, http_reply/3]).
6 |
7 | -export([behaviour_info/1]).
8 |
9 | behaviour_info(callbacks) ->
10 | [{init,1},
11 | {head, 3},
12 | {get, 3},
13 | {delete, 3},
14 | {options, 4},
15 | {post, 4},
16 | {put, 4},
17 | {trace, 4},
18 | {other_methods, 4}];
19 | behaviour_info(_Other) ->
20 | undefined.
21 |
22 | %%%===================================================================
23 | %%% API
24 |
25 | start_link(Callback, Port, UserArgs) ->
26 | start_link(Callback, undefined, Port, UserArgs).
27 |
28 | start_link(Callback, IP, Port, UserArgs) ->
29 | gws_connection_sup:start_link(Callback, IP, Port, UserArgs).
30 |
31 | http_reply(Code, Headers, Body) ->
32 | ContentBytes = iolist_to_binary(Body),
33 | Length = byte_size(ContentBytes),
34 | [io_lib:format("HTTP/1.1 ~s\r\n~sContent-Length: ~w\r\n\r\n",
35 | [response(Code), headers(Headers), Length]),
36 | ContentBytes].
37 |
38 | http_reply(Code) ->
39 | http_reply(Code, <<>>).
40 |
41 | http_reply(Code, Body) ->
42 | http_reply(Code, [{"Content-Type", "text/html"}], Body).
43 |
44 | %%%===================================================================
45 | %%% Internal functions
46 |
47 | headers([{Header, Text} | Hs]) ->
48 | [io_lib:format("~s: ~s\r\n", [Header, Text]) | headers(Hs)];
49 | headers([]) ->
50 | [].
51 |
52 | %% Fill in the missing status codes below if you want:
53 | response(100) -> "100 Continue";
54 | response(200) -> "200 OK";
55 | response(404) -> "404 Not Found";
56 | response(501) -> "501 Not Implemented";
57 | response(Code) -> integer_to_list(Code).
58 |
--------------------------------------------------------------------------------
/chapter_11/gen_web_server/src/gws_connection_sup.erl:
--------------------------------------------------------------------------------
1 | -module(gws_connection_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | %% API
6 | -export([start_link/4, start_child/1]).
7 |
8 | %% Supervisor callbacks
9 | -export([init/1]).
10 |
11 | %%%===================================================================
12 | %%% API functions
13 |
14 | start_link(Callback, IP, Port, UserArgs) ->
15 | {ok, Pid} = supervisor:start_link(?MODULE, [Callback, IP,
16 | Port, UserArgs]),
17 | start_child(Pid),
18 | {ok, Pid}.
19 |
20 | start_child(Server) ->
21 | supervisor:start_child(Server, []).
22 |
23 | %%%===================================================================
24 | %%% Supervisor callbacks
25 |
26 | init([Callback, IP, Port, UserArgs]) ->
27 | BasicSockOpts = [binary,
28 | {active, false},
29 | {packet, http_bin},
30 | {reuseaddr, true}],
31 | SockOpts = case IP of
32 | undefined -> BasicSockOpts;
33 | _ -> [{ip,IP} | BasicSockOpts]
34 | end,
35 | {ok, LSock} = gen_tcp:listen(Port, SockOpts),
36 | Server = {gws_server, {gws_server, start_link,
37 | [Callback, LSock, UserArgs]},
38 | temporary, brutal_kill, worker, [gws_server]},
39 | RestartStrategy = {simple_one_for_one, 1000, 3600},
40 | {ok, {RestartStrategy, [Server]}}.
41 |
--------------------------------------------------------------------------------
/chapter_11/http_interface/ebin/http_interface.app:
--------------------------------------------------------------------------------
1 | {application, http_interface,
2 | [{description, "A RESTful HTTP interface to simple_cache"},
3 | {vsn, "0.1.0"},
4 | {modules, [hi_app,
5 | hi_sup,
6 | hi_server]},
7 | {registered, [hi_sup]},
8 | {applications, [kernel, stdlib]},
9 | {mod, {hi_app, []}}
10 | ]}.
11 |
--------------------------------------------------------------------------------
/chapter_11/http_interface/src/hi_app.erl:
--------------------------------------------------------------------------------
1 | -module(hi_app).
2 |
3 | -behaviour(application).
4 |
5 | -export([start/2, stop/1]).
6 |
7 | -define(DEFAULT_PORT, 1156).
8 |
9 | start(_StartType, _StartArgs) ->
10 | Port = case application:get_env(http_interface, port) of
11 | {ok, P} -> P;
12 | undefined -> ?DEFAULT_PORT
13 | end,
14 | case hi_sup:start_link(Port) of
15 | {ok, Pid} ->
16 | {ok, Pid};
17 | Other ->
18 | {error, Other}
19 | end.
20 |
21 | stop(_State) ->
22 | ok.
23 |
--------------------------------------------------------------------------------
/chapter_11/http_interface/src/hi_server.erl:
--------------------------------------------------------------------------------
1 | -module(hi_server).
2 |
3 | -behaviour(gen_web_server).
4 |
5 | %% API
6 | -export([start_link/1, start_link/2]).
7 |
8 | %% gen_web_server callbacks
9 | -export([init/1, get/3, delete/3, put/4, post/4,
10 | head/3, options/4, trace/4, other_methods/4]).
11 |
12 | %%%===================================================================
13 | %%% API
14 |
15 | start_link(Port) ->
16 | gen_web_server:start_link(?MODULE, Port, []).
17 |
18 | start_link(IP, Port) ->
19 | gen_web_server:start_link(?MODULE, IP, Port, []).
20 | %%%===================================================================
21 | %%% gen_web_server callbacks
22 |
23 | init([]) ->
24 | {ok, []}.
25 |
26 | get({http_request, 'GET', {abs_path, <<"/",Key/bytes>>}, _},
27 | _Head, _UserData) ->
28 | case simple_cache:lookup(Key) of
29 | {ok, Value} ->
30 | gen_web_server:http_reply(200, [], Value);
31 | {error, not_found} ->
32 | gen_web_server:http_reply(404, "Sorry, no such key.")
33 | end.
34 |
35 | delete({http_request, 'DELETE', {abs_path, <<"/",Key/bytes>>}, _},
36 | _Head, _UserData) ->
37 | simple_cache:delete(Key),
38 | gen_web_server:http_reply(200).
39 |
40 | put({http_request, 'PUT', {abs_path, <<"/",Key/bytes>>}, _},
41 | _Head, Body, _UserData) ->
42 | simple_cache:insert(Key, Body),
43 | gen_web_server:http_reply(200).
44 |
45 | post(_Request, _Head, _Body, _UserData) ->
46 | gen_web_server:http_reply(501).
47 |
48 | head(_Request, _Head, _UserData) ->
49 | gen_web_server:http_reply(501).
50 |
51 | options(_Request, _Head, _Body, _UserData) ->
52 | gen_web_server:http_reply(501).
53 |
54 | trace(_Request, _Head, _Body, _UserData) ->
55 | gen_web_server:http_reply(501).
56 |
57 | other_methods(_Request, _Head, _Body, _UserData) ->
58 | gen_web_server:http_reply(501).
59 |
--------------------------------------------------------------------------------
/chapter_11/http_interface/src/hi_sup.erl:
--------------------------------------------------------------------------------
1 | -module(hi_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | %% API
6 | -export([start_link/1, start_child/0]).
7 |
8 | %% Supervisor callbacks
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link(Port) ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, [Port]).
15 |
16 | start_child() ->
17 | supervisor:start_child(?SERVER, []).
18 |
19 | init([Port]) ->
20 | Server = {hi_server, {hi_server, start_link, [Port]},
21 | permanent, 2000, worker, [hi_server]},
22 | Children = [Server],
23 | RestartStrategy = {one_for_one, 0, 1},
24 | {ok, {RestartStrategy, Children}}.
25 |
--------------------------------------------------------------------------------
/chapter_11/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # rename this file to "install" and set the executable flag,
3 | # then copy it into the bin directory of your release package
4 | # (update the erts version number below to match your release)
5 | ROOT=`pwd`
6 | DIR=./erts-5.7.4/bin
7 | sed s:%FINAL_ROOTDIR%:$ROOT: $DIR/erl.src > $DIR/erl
8 |
--------------------------------------------------------------------------------
/chapter_11/old/gen_web_server/ebin/gen_web_server.app:
--------------------------------------------------------------------------------
1 | %% This is the application resource file (.app file) for the gen_web_server,
2 | %% application.
3 | {application, gen_web_server,
4 | [{description, "An application containing the gen_web_server behaviour container and interface"},
5 | {vsn, "0.2.0.2"},
6 | {modules, [gen_web_server,
7 | gws_connection_sup,
8 | gws_server]},
9 | {registered,[]},
10 | {applications, [kernel, stdlib, sasl, inets]},
11 | {start_phases, []}]}.
12 |
13 |
--------------------------------------------------------------------------------
/chapter_11/old/restful_interface/ebin/restful_interface.app:
--------------------------------------------------------------------------------
1 | %% This is the application resource file (.app file) for the application.
2 | {application, restful_interface,
3 | [{description, "An interface mechanism using REST"},
4 | {vsn, "0.1.0"},
5 | {modules, [ri_app,
6 | ri_sup,
7 | ri_gws_impl
8 | ]},
9 | {registered,[pi_sup]},
10 | {applications, [kernel, stdlib]},
11 | {mod, {ri_app,[]}},
12 | {start_phases, []}]}.
13 |
--------------------------------------------------------------------------------
/chapter_11/old/restful_interface/src/ri_app.erl:
--------------------------------------------------------------------------------
1 | %%%----------------------------------------------------------------
2 | %%% @author Martin Logan
3 | %%% @doc
4 | %%% Example test calls:
5 | %%%
6 | %%% put: curl -T SomeFile http://localhost:1156/key
7 | %%% get: curl http://localhost:1156/key
8 | %%% delete: curl -X DELETE http://localhost:1156/key
9 | %%%
10 | %%% @end
11 | %%% @copyright 2008 Martin Logan
12 | %%%----------------------------------------------------------------,
13 | -module(ri_app).
14 |
15 | -behaviour(application).
16 |
17 | %% Application callbacks
18 | -export([start/2, stop/1]).
19 |
20 | %%%===================================================================
21 | %%% Application callbacks
22 | %%%===================================================================
23 |
24 | %%--------------------------------------------------------------------
25 | %% @private
26 | %% @doc
27 | %% This function is called whenever an application is started using
28 | %% application:start/[1,2], and should start the processes of the
29 | %% application. If the application is structured according to the OTP
30 | %% design principles as a supervision tree, this means starting the
31 | %% top supervisor of the tree.
32 | %%
33 | %% @spec start(StartType, StartArgs) -> {ok, Pid} |
34 | %% {ok, Pid, State} |
35 | %% {error, Reason}
36 | %% StartType = normal | {takeover, Node} | {failover, Node}
37 | %% StartArgs = term()
38 | %% @end
39 | %%--------------------------------------------------------------------
40 | start(_StartType, _StartArgs) ->
41 | case ri_sup:start_link() of
42 | {ok, Pid} ->
43 | {ok, Pid};
44 | Error ->
45 | Error
46 | end.
47 |
48 | %%--------------------------------------------------------------------
49 | %% @private
50 | %% @doc
51 | %% This function is called whenever an application has stopped. It
52 | %% is intended to be the opposite of Module:start/2 and should do
53 | %% any necessary cleaning up. The return value is ignored.
54 | %%
55 | %% @spec stop(State) -> void()
56 | %% @end
57 | %%--------------------------------------------------------------------
58 | stop(_State) ->
59 | ok.
60 |
61 | %%%===================================================================
62 | %%% Internal functions
63 | %%%===================================================================->
64 |
--------------------------------------------------------------------------------
/chapter_11/old/restful_interface/src/ri_gws_impl.erl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% @doc
3 | %%% The callback module that handles all incoming requests at the
4 | %%% application level.
5 | %%% @end
6 | %%%-------------------------------------------------------------------
7 | -module(ri_gws_impl).
8 |
9 | -behavour(gen_web_server).
10 |
11 | %% API
12 | -export([start_link/1]).
13 |
14 | %% Callbacks
15 | -export([init/1, get/3, delete/3, put/4, post/4]).
16 |
17 | %%%===================================================================
18 | %%% API
19 | %%%===================================================================
20 |
21 | %%--------------------------------------------------------------------
22 | %% @doc
23 | %% Creates a gen_web_server process
24 | %%
25 | %% @spec start_link(SocketManager::pid()) -> {ok, Pid} | ignore | {error, Error}
26 | %% @end
27 | %%--------------------------------------------------------------------
28 | start_link(Port) ->
29 | error_logger:info_msg("startlink on the gws impl~n"),
30 | gen_web_server:start_link(?MODULE, Port, []).
31 |
32 | %%%===================================================================
33 | %%% gen_web_server callbacks
34 | %%%===================================================================
35 | %%--------------------------------------------------------------------
36 | %% @doc Initialize the web serving process
37 | %% @spec (UserArgs) -> {ok, State}
38 | %% @end
39 | %%--------------------------------------------------------------------
40 | init(_UserArgs) ->
41 | {ok, []}.
42 |
43 | %%--------------------------------------------------------------------
44 | %% @doc Handles http GET requests
45 | %% @spec (InitialRequestLine, Head, Body) -> Response
46 | %% @end
47 | %%--------------------------------------------------------------------
48 | get({_, _, {abs_path, <<$/,Key/binary>>}, _}, _Head, _UserState) ->
49 | error_logger:info_msg("Key ~p~n", [Key]),
50 | case simple_cache:lookup(Key) of
51 | {ok, Value} ->
52 | Headers = [{"Content-Type", "text/html"}],
53 | gen_web_server:http_reply(200, Headers, Value);
54 | {error, not_found} ->
55 | Headers = [{"Content-Type", "text/html"}],
56 | gen_web_server:http_reply(404, Headers, "Content Not Found")
57 | end.
58 |
59 | %%--------------------------------------------------------------------
60 | %% @doc Handles http PUT requests
61 | %% @spec (InitialRequestLine, Head, Body, UserArgs) -> Response
62 | %% @end
63 | %%--------------------------------------------------------------------
64 | put({_, _, {abs_path, <<$/,Key/binary>>}, _}, _Head, Body, _UserState) ->
65 | simple_cache:insert(Key, Body),
66 | gen_web_server:http_reply(200).
67 |
68 | %%--------------------------------------------------------------------
69 | %% @doc Handles http DELETE requests
70 | %% @spec (InitialRequestLine, Head, Body) -> Response
71 | %% @end
72 | %%--------------------------------------------------------------------
73 | delete({_, _, {abs_path, <<$/,Key/binary>>}, _}, _Head, _UserState) ->
74 | simple_cache:delete(Key),
75 | gen_web_server:http_reply(200).
76 |
77 | %%--------------------------------------------------------------------
78 | %% @doc Handles http POST requests
79 | %% @spec (InitialRequestLine, Head, Body, UserArgs) -> Response
80 | %% @end
81 | %%--------------------------------------------------------------------
82 | post({_, _, _, _}, _Head, _Body, _UserState) ->
83 | gen_web_server:http_reply(501).
84 |
85 | %%%===================================================================
86 | %%% Internal functions
87 | %%%===================================================================
88 |
--------------------------------------------------------------------------------
/chapter_11/old/restful_interface/src/ri_sup.erl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% @author Martin Logan %%% @copyright (C) 2009, Martin Logan
3 | %%% @doc
4 | %%% simple one for one supervisor for handling the tcp server.
5 | %%% @end
6 | %%% Created : 13 May 2009 by Martin Logan
7 | %%%-------------------------------------------------------------------
8 | -module(ri_sup).
9 |
10 | -behaviour(supervisor).
11 |
12 | %% API
13 | -export([start_link/0]).
14 |
15 | %% Supervisor callbacks
16 | -export([init/1]).
17 |
18 | -define(SERVER, ?MODULE).
19 | -define(DEFAULT_PORT, 1156).
20 |
21 | %%%===================================================================
22 | %%% API functions
23 | %%%===================================================================
24 |
25 | %%--------------------------------------------------------------------
26 | %% @doc
27 | %% Starts the supervisor
28 | %%
29 | %% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
30 | %% @end
31 | %%--------------------------------------------------------------------
32 | start_link() ->
33 | Port =
34 | case application:get_env(restful_interface, port) of
35 | {ok, Port_} ->
36 | Port_;
37 | undefined ->
38 | ?DEFAULT_PORT
39 | end,
40 | supervisor:start_link({local, ?SERVER}, ?MODULE, [Port]).
41 |
42 | %%%===================================================================
43 | %%% Supervisor callbacks
44 | %%%===================================================================
45 |
46 | %%--------------------------------------------------------------------
47 | %% @private
48 | %% @doc
49 | %% Whenever a supervisor is started using supervisor:start_link/[2,3],
50 | %% this function is called by the new process to find out about
51 | %% restart strategy, maximum restart frequency and child
52 | %% specifications.
53 | %%
54 | %% @spec init(Args) -> {ok, {SupFlags, [ChildSpec]}} |
55 | %% ignore |
56 | %% {error, Reason}
57 | %% @end
58 | %%--------------------------------------------------------------------
59 | init([Port]) ->
60 | RestartStrategy = one_for_one,
61 | MaxRestarts = 0,
62 | MaxSecondsBetweenRestarts = 1,
63 |
64 | SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts},
65 |
66 | Restart = temporary,
67 | Shutdown = brutal_kill,
68 | Type = worker,
69 |
70 | WebSocket = {ri_gws_impl, {ri_gws_impl, start_link, [Port]},
71 | Restart, Shutdown, Type, [ri_gws_impl]},
72 |
73 | {ok, {SupFlags, [WebSocket]}}.
74 |
75 | %%%===================================================================
76 | %%% Internal functions
77 | %%%===================================================================
78 |
--------------------------------------------------------------------------------
/chapter_11/old/tcp_interface/ebin/tcp_interface.app:
--------------------------------------------------------------------------------
1 | {application, tcp_interface,
2 | [{description, "An interface mechanism over TCP"},
3 | {vsn, "0.1.0"},
4 | {modules, [ti_app,
5 | ti_sup,
6 | ti_server]},
7 | {registered,[ti_sup]},
8 | {applications, [kernel, stdlib]},
9 | {mod, {ti_app,[]}},
10 | {start_phases, []}]}.
11 |
12 |
--------------------------------------------------------------------------------
/chapter_11/old/tcp_interface/src/ti_app.erl:
--------------------------------------------------------------------------------
1 | %%%----------------------------------------------------------------
2 | %%% @author Martin Logan
3 | %%% @doc
4 | %%%
5 | %%% @end
6 | %%% @copyright 2008 Martin Logan
7 | %%%----------------------------------------------------------------,
8 | -module(ti_app).
9 |
10 | -behaviour(application).
11 |
12 | %% Application callbacks
13 | -export([start/2, stop/1]).
14 |
15 | -define(DEFAULT_PORT, 1055).
16 |
17 | %%%===================================================================
18 | %%% Application callbacks
19 | %%%===================================================================
20 |
21 | %%--------------------------------------------------------------------
22 | %% @private
23 | %% @doc
24 | %% This function is called whenever an application is started using
25 | %% application:start/[1,2], and should start the processes of the
26 | %% application. If the application is structured according to the OTP
27 | %% design principles as a supervision tree, this means starting the
28 | %% top supervisor of the tree.
29 | %%
30 | %% @spec start(StartType, StartArgs) -> {ok, Pid} |
31 | %% {ok, Pid, State} |
32 | %% {error, Reason}
33 | %% StartType = normal | {takeover, Node} | {failover, Node}
34 | %% StartArgs = term()
35 | %% @end
36 | %%--------------------------------------------------------------------
37 | start(_StartType, _StartArgs) ->
38 | Port =
39 | case application:get_env(tcp_interface, port) of
40 | {ok, Port_} -> Port_;
41 | undefined -> ?DEFAULT_PORT
42 | end,
43 |
44 | {ok, LSock} = gen_tcp:listen(Port, [{active, true}, {reuseaddr, true}]),
45 |
46 | case ti_sup:start_link(LSock) of
47 | {ok, Pid} ->
48 | ti_sup:start_child(),
49 | {ok, Pid};
50 | Error ->
51 | Error
52 | end.
53 |
54 | %%--------------------------------------------------------------------
55 | %% @private
56 | %% @doc
57 | %% This function is called whenever an application has stopped. It
58 | %% is intended to be the opposite of Module:start/2 and should do
59 | %% any necessary cleaning up. The return value is ignored.
60 | %%
61 | %% @spec stop(State) -> void()
62 | %% @end
63 | %%--------------------------------------------------------------------
64 | stop(_State) ->
65 | ok.
66 |
67 | %%%===================================================================
68 | %%% Internal functions
69 | %%%===================================================================
70 |
71 |
72 |
--------------------------------------------------------------------------------
/chapter_11/old/tcp_interface/src/ti_sup.erl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% @author Martin Logan
3 | %%% @copyright (C) 2009, Martin Logan
4 | %%% @doc
5 | %%% simple one for one supervisor for handling the tcp server.
6 | %%% @end
7 | %%% Created : 13 May 2009 by Martin Logan
8 | %%%-------------------------------------------------------------------
9 | -module(ti_sup).
10 |
11 | -behaviour(supervisor).
12 |
13 | %% API
14 | -export([start_link/1, start_child/0]).
15 |
16 | %% Supervisor callbacks
17 | -export([init/1]).
18 |
19 | -define(SERVER, ?MODULE).
20 |
21 | %%%===================================================================
22 | %%% API functions
23 | %%%===================================================================
24 |
25 | %%--------------------------------------------------------------------
26 | %% @doc
27 | %% Starts the supervisor
28 | %%
29 | %% @spec start_link(LSock) -> {ok, Pid} | ignore | {error, Error}
30 | %% @end
31 | %%--------------------------------------------------------------------
32 | start_link(LSock) ->
33 | supervisor:start_link({local, ?SERVER}, ?MODULE, [LSock]).
34 |
35 | %%--------------------------------------------------------------------
36 | %% @doc
37 | %% Start a child process, an sc_connection.
38 | %%
39 | %% @spec start_child() -> void()
40 | %% @end
41 | %%--------------------------------------------------------------------
42 | start_child() ->
43 | supervisor:start_child(?SERVER, []).
44 |
45 | %%%===================================================================
46 | %%% Supervisor callbacks
47 | %%%===================================================================
48 |
49 | %%--------------------------------------------------------------------
50 | %% @private
51 | %% @doc
52 | %% Whenever a supervisor is started using supervisor:start_link/[2,3],
53 | %% this function is called by the new process to find out about
54 | %% restart strategy, maximum restart frequency and child
55 | %% specifications.
56 | %%
57 | %% @spec init(Args) -> {ok, {SupFlags, [ChildSpec]}} |
58 | %% ignore |
59 | %% {error, Reason}
60 | %% @end
61 | %%--------------------------------------------------------------------
62 | init([LSock]) ->
63 | RestartStrategy = simple_one_for_one,
64 | MaxRestarts = 0,
65 | MaxSecondsBetweenRestarts = 1,
66 |
67 | SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts},
68 |
69 | Restart = temporary,
70 | Shutdown = brutal_kill,
71 | Type = worker,
72 |
73 | AChild = {ti_server, {ti_server, start_link, [LSock]},
74 | Restart, Shutdown, Type, [ti_server]},
75 |
76 | {ok, {SupFlags, [AChild]}}.
77 |
78 | %%%===================================================================
79 | %%% Internal functions
80 | %%%===================================================================
81 |
--------------------------------------------------------------------------------
/chapter_11/put.txt:
--------------------------------------------------------------------------------
1 | Erlang
2 |
--------------------------------------------------------------------------------
/chapter_11/resource_discovery/doc/.gitignore:
--------------------------------------------------------------------------------
1 | edoc-info
2 | stylesheet.css
3 | erlang.png
4 | *.html
5 |
--------------------------------------------------------------------------------
/chapter_11/resource_discovery/ebin/.gitignore:
--------------------------------------------------------------------------------
1 | *.beam
2 |
--------------------------------------------------------------------------------
/chapter_11/resource_discovery/ebin/resource_discovery.app:
--------------------------------------------------------------------------------
1 | {application, resource_discovery,
2 | [{description, "A simple resource discovery system"},
3 | {vsn, "0.1.0"},
4 | {modules, [resource_discovery,
5 | rd_app,
6 | rd_sup,
7 | rd_server]},
8 | {registered, [rd_sup, rd_server]},
9 | {applications, [kernel, stdlib]},
10 | {mod, {rd_app, []}}
11 | ]}.
12 |
--------------------------------------------------------------------------------
/chapter_11/resource_discovery/include/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_11/resource_discovery/include/.gitignore
--------------------------------------------------------------------------------
/chapter_11/resource_discovery/priv/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_11/resource_discovery/priv/.gitignore
--------------------------------------------------------------------------------
/chapter_11/resource_discovery/src/rd_app.erl:
--------------------------------------------------------------------------------
1 | -module(rd_app).
2 |
3 | -behaviour(application).
4 |
5 | -export([start/2, stop/1]).
6 |
7 | start(_StartType, _StartArgs) ->
8 | case rd_sup:start_link() of
9 | {ok, Pid} ->
10 | {ok, Pid};
11 | Other ->
12 | {error, Other}
13 | end.
14 |
15 | stop(_State) ->
16 | ok.
17 |
--------------------------------------------------------------------------------
/chapter_11/resource_discovery/src/rd_sup.erl:
--------------------------------------------------------------------------------
1 | -module(rd_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | %% API
6 | -export([start_link/0]).
7 |
8 | %% Supervisor callbacks
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
15 |
16 | init([]) ->
17 | Server = {rd_server, {rd_server, start_link, []},
18 | permanent, 2000, worker, [rd_server]},
19 | Children = [Server],
20 | RestartStrategy = {one_for_one, 0, 1},
21 | {ok, {RestartStrategy, Children}}.
22 |
--------------------------------------------------------------------------------
/chapter_11/resource_discovery/src/resource_discovery.erl:
--------------------------------------------------------------------------------
1 | -module(resource_discovery).
2 |
3 | -export([
4 | add_target_resource_type/1,
5 | add_local_resource/2,
6 | fetch_resources/1,
7 | trade_resources/0
8 | ]).
9 |
10 | add_target_resource_type(Type) ->
11 | rd_server:add_target_resource_type(Type).
12 |
13 | add_local_resource(Type, Resource) ->
14 | rd_server:add_local_resource(Type, Resource).
15 |
16 | fetch_resources(Type) ->
17 | rd_server:fetch_resources(Type).
18 |
19 | trade_resources() ->
20 | rd_server:trade_resources().
21 |
--------------------------------------------------------------------------------
/chapter_11/simple_cache.rel:
--------------------------------------------------------------------------------
1 | {release,
2 | {"simple_cache", "0.1.0"},
3 | {erts, "5.7.3"},
4 | [{kernel, "2.13.3"},
5 | {stdlib, "1.16.3"},
6 | {sasl, "2.1.7"},
7 | {mnesia, "4.4.11"},
8 | {resource_discovery, "0.1.0"},
9 | {simple_cache, "0.3.0"}
10 | ]}.
11 |
--------------------------------------------------------------------------------
/chapter_11/simple_cache.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # rename this file to "simple_cache" and set the executable flag,
3 | # then copy it into the bin directory of your release package
4 | # (update the erts version number below to match your release)
5 | ./erts-5.7.4/bin/erl \
6 | -sname cache \
7 | -boot ./releases/0.1.0/start \
8 | -config ./releases/0.1.0/sys \
9 | -detached
10 |
--------------------------------------------------------------------------------
/chapter_11/simple_cache/doc/.gitignore:
--------------------------------------------------------------------------------
1 | edoc-info
2 | stylesheet.css
3 | erlang.png
4 | *.html
5 |
--------------------------------------------------------------------------------
/chapter_11/simple_cache/ebin/.gitignore:
--------------------------------------------------------------------------------
1 | *.beam
2 |
--------------------------------------------------------------------------------
/chapter_11/simple_cache/ebin/simple_cache.app:
--------------------------------------------------------------------------------
1 | {application, simple_cache,
2 | [{description, "A simple caching system"},
3 | {vsn, "0.3.0"},
4 | {modules, [simple_cache,
5 | sc_app,
6 | sc_sup,
7 | sc_element_sup,
8 | sc_store,
9 | sc_element,
10 | sc_event,
11 | sc_event_logger]},
12 | {registered, [sc_sup, sc_element_sup, sc_event]},
13 | {applications, [kernel, sasl, stdlib, mnesia, resource_discovery]},
14 | {mod, {sc_app, []}}
15 | ]}.
16 |
--------------------------------------------------------------------------------
/chapter_11/simple_cache/include/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_11/simple_cache/include/.gitignore
--------------------------------------------------------------------------------
/chapter_11/simple_cache/priv/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_11/simple_cache/priv/.gitignore
--------------------------------------------------------------------------------
/chapter_11/simple_cache/src/sc_app.erl:
--------------------------------------------------------------------------------
1 | -module(sc_app).
2 |
3 | -behaviour(application).
4 |
5 | -export([start/2, stop/1]).
6 |
7 | -define(WAIT_FOR_RESOURCES, 2500).
8 |
9 | start(_StartType, _StartArgs) ->
10 | ok = ensure_contact(),
11 | resource_discovery:add_local_resource(simple_cache, node()),
12 | resource_discovery:add_target_resource_type(simple_cache),
13 | resource_discovery:trade_resources(),
14 | timer:sleep(?WAIT_FOR_RESOURCES),
15 | sc_store:init(),
16 | case sc_sup:start_link() of
17 | {ok, Pid} ->
18 | sc_event_logger:add_handler(),
19 | {ok, Pid};
20 | Other ->
21 | {error, Other}
22 | end.
23 |
24 | stop(_State) ->
25 | ok.
26 |
27 | ensure_contact() ->
28 | DefaultNodes = ['contact1@localhost', 'contact2@localhost'],
29 | case get_env(simple_cache, contact_nodes, DefaultNodes) of
30 | [] ->
31 | {error, no_contact_nodes};
32 | ContactNodes ->
33 | ensure_contact(ContactNodes)
34 | end.
35 |
36 | ensure_contact(ContactNodes) ->
37 | Answering = [N || N <- ContactNodes, net_adm:ping(N) =:= pong],
38 | case Answering of
39 | [] ->
40 | {error, no_contact_nodes_reachable};
41 | _ ->
42 | DefaultTime = 6000,
43 | WaitTime = get_env(simple_cache, wait_time, DefaultTime),
44 | wait_for_nodes(length(Answering), WaitTime)
45 | end.
46 |
47 | wait_for_nodes(MinNodes, WaitTime) ->
48 | Slices = 10,
49 | SliceTime = round(WaitTime/Slices),
50 | wait_for_nodes(MinNodes, SliceTime, Slices).
51 |
52 | wait_for_nodes(_MinNodes, _SliceTime, 0) ->
53 | ok;
54 | wait_for_nodes(MinNodes, SliceTime, Iterations) ->
55 | case length(nodes()) > MinNodes of
56 | true ->
57 | ok;
58 | false ->
59 | timer:sleep(SliceTime),
60 | wait_for_nodes(MinNodes, SliceTime, Iterations - 1)
61 | end.
62 |
63 | get_env(AppName, Key, Default) ->
64 | case application:get_env(AppName, Key) of
65 | undefined -> Default;
66 | {ok, Value} -> Value
67 | end.
68 |
--------------------------------------------------------------------------------
/chapter_11/simple_cache/src/sc_element.erl:
--------------------------------------------------------------------------------
1 | -module(sc_element).
2 |
3 | -behaviour(gen_server).
4 |
5 | -export([
6 | start_link/2,
7 | create/1,
8 | create/2,
9 | fetch/1,
10 | replace/2,
11 | delete/1
12 | ]).
13 |
14 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
15 | terminate/2, code_change/3]).
16 |
17 | -define(SERVER, ?MODULE).
18 | -define(DEFAULT_LEASE_TIME, (60 * 60 * 24)).
19 |
20 | -record(state, {value, lease_time, start_time}).
21 |
22 | start_link(Value, LeaseTime) ->
23 | gen_server:start_link(?MODULE, [Value, LeaseTime], []).
24 |
25 | create(Value, LeaseTime) ->
26 | sc_element_sup:start_child(Value, LeaseTime).
27 |
28 | create(Value) ->
29 | create(Value, ?DEFAULT_LEASE_TIME).
30 |
31 | fetch(Pid) ->
32 | gen_server:call(Pid, fetch).
33 |
34 | replace(Pid, Value) ->
35 | gen_server:cast(Pid, {replace, Value}).
36 |
37 | delete(Pid) ->
38 | gen_server:cast(Pid, delete).
39 |
40 | init([Value, LeaseTime]) ->
41 | Now = calendar:local_time(),
42 | StartTime = calendar:datetime_to_gregorian_seconds(Now),
43 | {ok,
44 | #state{value = Value,
45 | lease_time = LeaseTime,
46 | start_time = StartTime},
47 | time_left(StartTime, LeaseTime)}.
48 |
49 | time_left(_StartTime, infinity) ->
50 | infinity;
51 | time_left(StartTime, LeaseTime) ->
52 | Now = calendar:local_time(),
53 | CurrentTime = calendar:datetime_to_gregorian_seconds(Now),
54 | TimeElapsed = CurrentTime - StartTime,
55 | case LeaseTime - TimeElapsed of
56 | Time when Time =< 0 -> 0;
57 | Time -> Time * 1000
58 | end.
59 |
60 | handle_call(fetch, _From, State) ->
61 | #state{value = Value,
62 | lease_time = LeaseTime,
63 | start_time = StartTime} = State,
64 | TimeLeft = time_left(StartTime, LeaseTime),
65 | {reply, {ok, Value}, State, TimeLeft}.
66 |
67 | handle_cast({replace, Value}, State) ->
68 | #state{lease_time = LeaseTime,
69 | start_time = StartTime} = State,
70 | TimeLeft = time_left(StartTime, LeaseTime),
71 | {noreply, State#state{value = Value}, TimeLeft};
72 | handle_cast(delete, State) ->
73 | {stop, normal, State}.
74 |
75 | handle_info(timeout, State) ->
76 | {stop, normal, State}.
77 |
78 | terminate(_Reason, _State) ->
79 | sc_store:delete(self()),
80 | ok.
81 |
82 | code_change(_OldVsn, State, _Extra) ->
83 | {ok, State}.
84 |
--------------------------------------------------------------------------------
/chapter_11/simple_cache/src/sc_element_sup.erl:
--------------------------------------------------------------------------------
1 | -module(sc_element_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | -export([start_link/0,
6 | start_child/2
7 | ]).
8 |
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
15 |
16 | start_child(Value, LeaseTime) ->
17 | supervisor:start_child(?SERVER, [Value, LeaseTime]).
18 |
19 | init([]) ->
20 | Element = {sc_element, {sc_element, start_link, []},
21 | temporary, brutal_kill, worker, [sc_element]},
22 | Children = [Element],
23 | RestartStrategy = {simple_one_for_one, 0, 1},
24 | {ok, {RestartStrategy, Children}}.
25 |
--------------------------------------------------------------------------------
/chapter_11/simple_cache/src/sc_event.erl:
--------------------------------------------------------------------------------
1 | -module(sc_event).
2 |
3 | -export([start_link/0,
4 | add_handler/2,
5 | delete_handler/2,
6 | lookup/1,
7 | create/2,
8 | replace/2,
9 | delete/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | gen_event:start_link({local, ?SERVER}).
15 |
16 | add_handler(Handler, Args) ->
17 | gen_event:add_handler(?SERVER, Handler, Args).
18 |
19 | delete_handler(Handler, Args) ->
20 | gen_event:delete_handler(?SERVER, Handler, Args).
21 |
22 | lookup(Key) ->
23 | gen_event:notify(?SERVER, {lookup, Key}).
24 |
25 | create(Key, Value) ->
26 | gen_event:notify(?SERVER, {create, {Key, Value}}).
27 |
28 | replace(Key, Value) ->
29 | gen_event:notify(?SERVER, {replace, {Key, Value}}).
30 |
31 | delete(Key) ->
32 | gen_event:notify(?SERVER, {delete, Key}).
33 |
--------------------------------------------------------------------------------
/chapter_11/simple_cache/src/sc_event_logger.erl:
--------------------------------------------------------------------------------
1 | -module(sc_event_logger).
2 |
3 | -behaviour(gen_event).
4 |
5 | -export([add_handler/0, delete_handler/0]).
6 |
7 | -export([init/1, handle_event/2, handle_call/2,
8 | handle_info/2, code_change/3, terminate/2]).
9 |
10 | -record(state, {}).
11 |
12 | add_handler() ->
13 | sc_event:add_handler(?MODULE, []).
14 |
15 | delete_handler() ->
16 | sc_event:delete_handler(?MODULE, []).
17 |
18 | init([]) ->
19 | {ok, #state{}}.
20 |
21 | handle_event({create, {Key, Value}}, State) ->
22 | error_logger:info_msg("create(~w, ~w)~n", [Key, Value]),
23 | {ok, State};
24 | handle_event({lookup, Key}, State) ->
25 | error_logger:info_msg("lookup(~w)~n", [Key]),
26 | {ok, State};
27 | handle_event({delete, Key}, State) ->
28 | error_logger:info_msg("delete(~w)~n", [Key]),
29 | {ok, State};
30 | handle_event({replace, {Key, Value}}, State) ->
31 | error_logger:info_msg("replace(~w, ~w)~n", [Key, Value]),
32 | {ok, State}.
33 |
34 | handle_call(_Request, State) ->
35 | Reply = ok,
36 | {ok, Reply, State}.
37 |
38 | handle_info(_Info, State) ->
39 | {ok, State}.
40 |
41 | terminate(_Reason, _State) ->
42 | ok.
43 |
44 | code_change(_OldVsn, State, _Extra) ->
45 | {ok, State}.
46 |
--------------------------------------------------------------------------------
/chapter_11/simple_cache/src/sc_store.erl:
--------------------------------------------------------------------------------
1 | -module(sc_store).
2 |
3 | -export([
4 | init/0,
5 | insert/2,
6 | delete/1,
7 | lookup/1
8 | ]).
9 |
10 | -define(TABLE_ID, ?MODULE).
11 | -define(WAIT_FOR_TABLES, 5000).
12 |
13 | -record(key_to_pid, {key, pid}).
14 |
15 | init() ->
16 | mnesia:stop(),
17 | mnesia:delete_schema([node()]),
18 | mnesia:start(),
19 | {ok, CacheNodes} = resource_discovery:fetch_resources(simple_cache),
20 | dynamic_db_init(lists:delete(node(), CacheNodes)).
21 |
22 | insert(Key, Pid) ->
23 | mnesia:dirty_write(#key_to_pid{key = Key, pid = Pid}).
24 |
25 | lookup(Key) ->
26 | case mnesia:dirty_read(key_to_pid, Key) of
27 | [{key_to_pid, Key, Pid}] ->
28 | case is_pid_alive(Pid) of
29 | true -> {ok, Pid};
30 | false -> {error, not_found}
31 | end;
32 | [] ->
33 | {error, not_found}
34 | end.
35 |
36 | delete(Pid) ->
37 | case mnesia:dirty_index_read(key_to_pid, Pid, #key_to_pid.pid) of
38 | [#key_to_pid{} = Record] ->
39 | mnesia:dirty_delete_object(Record);
40 | _ ->
41 | ok
42 | end.
43 |
44 |
45 | %% Internal Functions
46 |
47 | dynamic_db_init([]) ->
48 | mnesia:create_table(key_to_pid,
49 | [{index, [pid]},
50 | {attributes, record_info(fields, key_to_pid)}
51 | ]);
52 | dynamic_db_init(CacheNodes) ->
53 | add_extra_nodes(CacheNodes).
54 |
55 | add_extra_nodes([Node|T]) ->
56 | case mnesia:change_config(extra_db_nodes, [Node]) of
57 | {ok, [Node]} ->
58 | mnesia:add_table_copy(key_to_pid, node(), ram_copies),
59 |
60 | Tables = mnesia:system_info(tables),
61 | mnesia:wait_for_tables(Tables, ?WAIT_FOR_TABLES);
62 | _ ->
63 | add_extra_nodes(T)
64 | end.
65 |
66 | is_pid_alive(Pid) when node(Pid) =:= node() ->
67 | is_process_alive(Pid);
68 | is_pid_alive(Pid) ->
69 | case lists:member(node(Pid), nodes()) of
70 | false ->
71 | false;
72 | true ->
73 | case rpc:call(node(Pid), erlang, is_process_alive, [Pid]) of
74 | true ->
75 | true;
76 | false ->
77 | false;
78 | {badrpc, _Reason} ->
79 | false
80 | end
81 | end.
82 |
83 |
--------------------------------------------------------------------------------
/chapter_11/simple_cache/src/sc_sup.erl:
--------------------------------------------------------------------------------
1 | -module(sc_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | %% API
6 | -export([start_link/0]).
7 |
8 | %% Supervisor callbacks
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
15 |
16 | init([]) ->
17 | ElementSup = {sc_element_sup, {sc_element_sup, start_link, []},
18 | permanent, 2000, supervisor, [sc_element]},
19 |
20 | EventManager = {sc_event, {sc_event, start_link, []},
21 | permanent, 2000, worker, [sc_event]},
22 |
23 | Children = [ElementSup, EventManager],
24 | RestartStrategy = {one_for_one, 4, 3600},
25 | {ok, {RestartStrategy, Children}}.
26 |
--------------------------------------------------------------------------------
/chapter_11/simple_cache/src/simple_cache.erl:
--------------------------------------------------------------------------------
1 | -module(simple_cache).
2 |
3 | -export([insert/2, lookup/1, delete/1]).
4 |
5 | insert(Key, Value) ->
6 | case sc_store:lookup(Key) of
7 | {ok, Pid} ->
8 | sc_event:replace(Key, Value),
9 | sc_element:replace(Pid, Value);
10 | {error, _} ->
11 | {ok, Pid} = sc_element:create(Value),
12 | sc_store:insert(Key, Pid),
13 | sc_event:create(Key, Value)
14 | end.
15 |
16 | lookup(Key) ->
17 | sc_event:lookup(Key),
18 | try
19 | {ok, Pid} = sc_store:lookup(Key),
20 | {ok, Value} = sc_element:fetch(Pid),
21 | {ok, Value}
22 | catch
23 | _Class:_Exception ->
24 | {error, not_found}
25 | end.
26 |
27 | delete(Key) ->
28 | sc_event:delete(Key),
29 | case sc_store:lookup(Key) of
30 | {ok, Pid} ->
31 | sc_element:delete(Pid);
32 | {error, _Reason} ->
33 | ok
34 | end.
35 |
--------------------------------------------------------------------------------
/chapter_11/sys.config:
--------------------------------------------------------------------------------
1 | [
2 | %% write log files to sasl_dir
3 | {sasl,
4 | [
5 | {sasl_error_logger, {file, "/tmp/simple_cache.sasl_log"}}
6 | ]},
7 | {simple_cache,
8 | [
9 | %% Contact nodes for use in joining a cloud
10 | {contact_nodes, ['contact1@localhost', 'contact2@localhost']}
11 | ]}
12 | ].
13 |
--------------------------------------------------------------------------------
/chapter_11/tcp_interface/ebin/.gitignore:
--------------------------------------------------------------------------------
1 | *.beam
2 |
--------------------------------------------------------------------------------
/chapter_11/tcp_interface/ebin/tcp_interface.app:
--------------------------------------------------------------------------------
1 | {application, tcp_interface,
2 | [{description, "A simple text-based TCP interface to simple_cache"},
3 | {vsn, "0.1.0"},
4 | {modules, [ti_app,
5 | ti_sup,
6 | ti_server]},
7 | {registered, [ti_sup]},
8 | {applications, [kernel, stdlib]},
9 | {mod, {ti_app, []}}
10 | ]}.
11 |
--------------------------------------------------------------------------------
/chapter_11/tcp_interface/src/ti_app.erl:
--------------------------------------------------------------------------------
1 | -module(ti_app).
2 |
3 | -behaviour(application).
4 |
5 | -export([start/2, stop/1]).
6 |
7 | -define(DEFAULT_PORT, 1155).
8 |
9 | start(_StartType, _StartArgs) ->
10 | Port = case application:get_env(tcp_interface, port) of
11 | {ok, P} -> P;
12 | undefined -> ?DEFAULT_PORT
13 | end,
14 | {ok, LSock} = gen_tcp:listen(Port, [{active, true}]),
15 | case ti_sup:start_link(LSock) of
16 | {ok, Pid} ->
17 | ti_sup:start_child(),
18 | {ok, Pid};
19 | Other ->
20 | {error, Other}
21 | end.
22 |
23 | stop(_State) ->
24 | ok.
25 |
--------------------------------------------------------------------------------
/chapter_11/tcp_interface/src/ti_server.erl:
--------------------------------------------------------------------------------
1 | -module(ti_server).
2 |
3 | -behaviour(gen_server).
4 |
5 | -export([start_link/1]).
6 |
7 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
8 | terminate/2, code_change/3]).
9 |
10 | -record(state, {lsock}).
11 |
12 | start_link(LSock) ->
13 | gen_server:start_link(?MODULE, [LSock], []).
14 |
15 | init([LSock]) ->
16 | {ok, #state{lsock = LSock}, 0}.
17 |
18 | handle_call(Msg, _From, State) ->
19 | {reply, {ok, Msg}, State}.
20 |
21 | handle_cast(stop, State) ->
22 | {stop, normal, State}.
23 |
24 | handle_info({tcp, Socket, RawData}, State) ->
25 | NewState = handle_data(Socket, RawData, State),
26 | {noreply, NewState};
27 | handle_info({tcp_closed, _Socket}, State) ->
28 | {stop, normal, State};
29 | handle_info(timeout, #state{lsock = LSock} = State) ->
30 | {ok, _Sock} = gen_tcp:accept(LSock),
31 | ti_sup:start_child(),
32 | {noreply, State}.
33 |
34 | terminate(_Reason, _State) ->
35 | ok.
36 |
37 | code_change(_OldVsn, State, _Extra) ->
38 | {ok, State}.
39 |
40 | %% Internal functions
41 | handle_data(Socket, RawData, State) ->
42 | try
43 | {Function, RawArgList} =
44 | lists:splitwith(fun (C) -> C =/= $[ end, RawData),
45 | {ok, Toks, _Line} = erl_scan:string(RawArgList ++ ".", 1),
46 | {ok, Args} = erl_parse:parse_term(Toks),
47 | Result = apply(simple_cache, list_to_atom(Function), Args),
48 | gen_tcp:send(Socket, io_lib:fwrite("OK:~p.~n", [Result]))
49 | catch
50 | _Class:Err ->
51 | gen_tcp:send(Socket, io_lib:fwrite("ERROR:~p.~n", [Err]))
52 | end,
53 | State.
54 |
--------------------------------------------------------------------------------
/chapter_11/tcp_interface/src/ti_sup.erl:
--------------------------------------------------------------------------------
1 | -module(ti_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | %% API
6 | -export([start_link/1, start_child/0]).
7 |
8 | %% Supervisor callbacks
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link(LSock) ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, [LSock]).
15 |
16 | start_child() ->
17 | supervisor:start_child(?SERVER, []).
18 |
19 | init([LSock]) ->
20 | Server = {ti_server, {ti_server, start_link, [LSock]},
21 | temporary, brutal_kill, worker, [ti_server]},
22 | Children = [Server],
23 | RestartStrategy = {simple_one_for_one, 0, 1},
24 | {ok, {RestartStrategy, Children}}.
25 |
--------------------------------------------------------------------------------
/chapter_12/lloyd-yajl-1.0.9-0-g9c15d72.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_12/lloyd-yajl-1.0.9-0-g9c15d72.tar.gz
--------------------------------------------------------------------------------
/chapter_12/nifs/json_parser/README:
--------------------------------------------------------------------------------
1 | To build the Erlang code, run the following command:
2 |
3 | erlc -o ./ebin ./src/*.erl
4 |
5 | To build the C code using gcc, run the following:
6 |
7 | gcc -o ./priv/jp_nifs.so -fpic -shared -I${OTPROOT}/erts-5.7.5/include -I${YAJLROOT}/include -L${YAJLROOT}/lib ./c_src/jp_nifs_R14.c -lyajl
8 |
9 | NOTE: Use the source file jp_nifs_R13.c instead if you're running
10 | Erlang/OTP version R13.
11 |
12 | OTPROOT is typically /usr/lib/erlang. Check what your versions of erts and
13 | erl_interface are and that the include and lib directories exist and contain
14 | the expected header files and library files - not all Erlang distributions
15 | ship these development files in the basic installation package. YAJLROOT is
16 | wherever your YAJL installation is. If you built YAJL but did not do 'make
17 | install', this will be something like lloyd-yajl-1.0.9-0/build/yajl-1.0.9/.
18 |
19 | For the YAJL shared library to be loaded at runtime, you may need to set the
20 | environment variable LD_LIBRARY_PATH to ${YAJLROOT}/lib if you did not
21 | install YAJL it in a standard location.
22 |
23 | To run the program, first start Erlang like this:
24 |
25 | erl -pa ../json_parser/ebin
26 |
27 | Then, run the following in the Erlang shell:
28 |
29 | 1> json_parser:parse_document(<<"[null,true,{\"int\":42,\"float\":3.14}]">>).
30 | {ok,{undefined,true,[{<<"int">>,42},{<<"float">>,3.14}]}}
31 | 2>
32 |
--------------------------------------------------------------------------------
/chapter_12/nifs/json_parser/doc/.gitignore:
--------------------------------------------------------------------------------
1 | edoc-info
2 | stylesheet.css
3 | erlang.png
4 | *.html
5 |
--------------------------------------------------------------------------------
/chapter_12/nifs/json_parser/ebin/.gitignore:
--------------------------------------------------------------------------------
1 | *.beam
2 |
--------------------------------------------------------------------------------
/chapter_12/nifs/json_parser/ebin/json_parser.app:
--------------------------------------------------------------------------------
1 | %% -*- mode: Erlang; fill-column: 75; comment-column: 50; -*-
2 |
3 | {application, json_parser,
4 | [{description, "JSON parser (using NIFs)"},
5 | {vsn, "0.1.0"},
6 | {modules, [json_parser]},
7 | {applications, [kernel, stdlib]}
8 | ]}.
9 |
--------------------------------------------------------------------------------
/chapter_12/nifs/json_parser/include/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_12/nifs/json_parser/include/.gitignore
--------------------------------------------------------------------------------
/chapter_12/nifs/json_parser/priv/.gitignore:
--------------------------------------------------------------------------------
1 | parser
--------------------------------------------------------------------------------
/chapter_12/nifs/json_parser/src/json_parser.erl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% @doc JSON parser user API.
3 | %%% @end
4 | %%%-------------------------------------------------------------------
5 |
6 | -module(json_parser).
7 |
8 | -export([init/0, parse_document/1]).
9 |
10 | -on_load(init/0).
11 |
12 | -define(APPNAME, json_parser).
13 |
14 | init() ->
15 | case code:priv_dir(?APPNAME) of
16 | {error, _} ->
17 | error_logger:format("~w priv dir not found~n", [?APPNAME]),
18 | exit(error);
19 | PrivDir ->
20 | erlang:load_nif(filename:join([PrivDir, "jp_nifs"]), 0)
21 | end.
22 |
23 | %% @doc Parses a document given as a binary
24 | parse_document(_Data) ->
25 | erlang:nif_error(nif_not_loaded). % help Dialyzer and friends
26 |
--------------------------------------------------------------------------------
/chapter_12/plain_port/json_parser/README:
--------------------------------------------------------------------------------
1 | To build the Erlang code, run the following command:
2 |
3 | erlc -o ./ebin ./src/*.erl
4 |
5 | To build the C code using gcc, run the following:
6 |
7 | gcc -o ./priv/jp_prog -I${OTPROOT}/lib/erl_interface-3.6.5/include -I${YAJLROOT}/include -L${OTPROOT}/lib/erl_interface-3.6.5/lib -L${YAJLROOT}/lib ./c_src/jp_prog.c -lei_st -lyajl
8 |
9 | OTPROOT is typically /usr/lib/erlang. Check what your version of
10 | erl_interface is and that the include and lib directories exist and contain
11 | the expected header files and library files - not all Erlang distributions
12 | ship these development files in the basic installation package. YAJLROOT is
13 | wherever your YAJL installation is. If you built YAJL but did not do 'make
14 | install', this will be something like lloyd-yajl-1.0.9-0/build/yajl-1.0.9/.
15 |
16 | For the YAJL shared library to be loaded at runtime, you may need to set the
17 | environment variable LD_LIBRARY_PATH to ${YAJLROOT}/lib if you did not
18 | install YAJL it in a standard location.
19 |
20 | To run the program, first start Erlang like this:
21 |
22 | erl -pa ../json_parser/ebin
23 |
24 | Then, run the following in the Erlang shell:
25 |
26 | 1> application:start(json_parser).
27 | ok
28 | 2> json_parser:parse_document(<<"[null,true,{\"int\":42,\"float\":3.14}]">>).
29 | {ok,{undefined,true,[{<<"int">>,42},{<<"float">>,3.14}]}}
30 | 3>
31 |
--------------------------------------------------------------------------------
/chapter_12/plain_port/json_parser/doc/.gitignore:
--------------------------------------------------------------------------------
1 | edoc-info
2 | stylesheet.css
3 | erlang.png
4 | *.html
5 |
--------------------------------------------------------------------------------
/chapter_12/plain_port/json_parser/ebin/.gitignore:
--------------------------------------------------------------------------------
1 | *.beam
2 |
--------------------------------------------------------------------------------
/chapter_12/plain_port/json_parser/ebin/json_parser.app:
--------------------------------------------------------------------------------
1 | %% -*- mode: Erlang; fill-column: 75; comment-column: 50; -*-
2 |
3 | {application, json_parser,
4 | [{description, "JSON parser (using plain ports)"},
5 | {vsn, "0.1.0"},
6 | {modules, [jp_app,
7 | jp_sup,
8 | jp_server,
9 | json_parser]},
10 | {registered, [jp_sup, jp_server]},
11 | {applications, [kernel, stdlib]},
12 | {mod, {jp_app, []}}
13 | ]}.
14 |
--------------------------------------------------------------------------------
/chapter_12/plain_port/json_parser/include/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_12/plain_port/json_parser/include/.gitignore
--------------------------------------------------------------------------------
/chapter_12/plain_port/json_parser/priv/.gitignore:
--------------------------------------------------------------------------------
1 | jp_prog
2 |
--------------------------------------------------------------------------------
/chapter_12/plain_port/json_parser/src/jp_app.erl:
--------------------------------------------------------------------------------
1 | -module(jp_app).
2 |
3 | -behaviour(application).
4 |
5 | -export([
6 | start/2,
7 | stop/1
8 | ]).
9 |
10 | start(_Type, _StartArgs) ->
11 | case jp_sup:start_link() of
12 | {ok, Pid} ->
13 | {ok, Pid};
14 | Other ->
15 | {error, Other}
16 | end.
17 |
18 | stop(_State) ->
19 | ok.
20 |
--------------------------------------------------------------------------------
/chapter_12/plain_port/json_parser/src/jp_server.erl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% @doc Server for the JSON parser integration.
3 | %%% @end
4 | %%%-------------------------------------------------------------------
5 |
6 | -module(jp_server).
7 |
8 | -behaviour(gen_server).
9 |
10 | %% API
11 | -export([
12 | start_link/0,
13 | parse_document/1,
14 | stop/0
15 | ]).
16 |
17 | %% gen_server callbacks
18 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
19 | terminate/2, code_change/3]).
20 |
21 | -define(SERVER, ?MODULE).
22 | -define(APPNAME, json_parser).
23 |
24 | -record(state, {port}).
25 |
26 |
27 | %%%===================================================================
28 | %%% API
29 | %%%===================================================================
30 |
31 |
32 | %%--------------------------------------------------------------------
33 | %% @doc Starts the server.
34 | %%
35 | %% @spec start_link() -> {ok, Pid}
36 | %% where
37 | %% Pid = pid()
38 | %% @end
39 | %%--------------------------------------------------------------------
40 | start_link() ->
41 | gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
42 |
43 |
44 | %%--------------------------------------------------------------------
45 | %% @doc Parses a document.
46 | %% @spec parse_document(Data) -> ok
47 | %% where
48 | %% Data = binary()
49 | %% @end
50 | %%--------------------------------------------------------------------
51 | parse_document(Data) when is_binary(Data) ->
52 | gen_server:call(?SERVER, {parse_document, Data}).
53 |
54 | %%--------------------------------------------------------------------
55 | %% @doc Stops the server.
56 | %% @spec stop() -> ok
57 | %% @end
58 | %%--------------------------------------------------------------------
59 | stop() ->
60 | gen_server:cast(?SERVER, stop).
61 |
62 |
63 | %%%===================================================================
64 | %%% gen_server callbacks
65 | %%%===================================================================
66 |
67 | init([]) ->
68 | Port = create_port(),
69 | {ok, #state{port=Port}}.
70 |
71 | handle_call({parse_document, Msg}, _From, #state{port=Port}=State) ->
72 | Port ! {self(),{command, term_to_binary(Msg)}},
73 | receive
74 | {Port, {data, Data}} ->
75 | {reply, binary_to_term(Data), State}
76 | end.
77 |
78 | handle_cast(stop, State) ->
79 | {stop, normal, State}.
80 |
81 | handle_info({Port, {exit_status, Status}}, #state{port=Port}=State) ->
82 | error_logger:format("port exited with status ~p; restarting~n",
83 | [Status]),
84 | NewPort = create_port(),
85 | {noreply, State#state{port=NewPort}}.
86 |
87 | terminate(_Reason, _State) ->
88 | ok.
89 |
90 | code_change(_OldVsn, State, _Extra) ->
91 | {ok, State}.
92 |
93 | %%%===================================================================
94 | %%% Internal functions
95 | %%%===================================================================
96 |
97 | create_port() ->
98 | case code:priv_dir(?APPNAME) of
99 | {error, _} ->
100 | error_logger:format("~w priv dir not found~n", [?APPNAME]),
101 | exit(error);
102 | PrivDir ->
103 | open_port({spawn, filename:join([PrivDir, "jp_prog"])},
104 | [binary, {packet, 4}, exit_status])
105 | end.
106 |
--------------------------------------------------------------------------------
/chapter_12/plain_port/json_parser/src/jp_sup.erl:
--------------------------------------------------------------------------------
1 | -module(jp_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | %% API
6 | -export([start_link/0]).
7 |
8 | %% Supervisor callbacks
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
15 |
16 | init([]) ->
17 | Server = {jp_server, {jp_server, start_link, []},
18 | permanent, 2000, worker, [jp_server]},
19 | Children = [Server],
20 | RestartStrategy = {one_for_one, 0, 1},
21 | {ok, {RestartStrategy, Children}}.
22 |
--------------------------------------------------------------------------------
/chapter_12/plain_port/json_parser/src/json_parser.erl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% @doc JSON parser user API.
3 | %%% @end
4 | %%%-------------------------------------------------------------------
5 |
6 | -module(json_parser).
7 |
8 | -export([parse_document/1]).
9 |
10 | %% @doc Parses a document given as a binary
11 | parse_document(Data) ->
12 | jp_server:parse_document(Data).
13 |
--------------------------------------------------------------------------------
/chapter_12/port_driver/json_parser/README:
--------------------------------------------------------------------------------
1 | To build the Erlang code, run the following command:
2 |
3 | erlc -o ./ebin ./src/*.erl
4 |
5 | To build the C code using gcc, run the following:
6 |
7 | gcc -o ./priv/jp_driver.so -fpic -shared -I${OTPROOT}/erts-5.7.5/include -I${OTPROOT}/lib/erl_interface-3.6.5/include -I${YAJLROOT}/include -L${OTPROOT}/lib/erl_interface-3.6.5/lib -L${YAJLROOT}/lib ./c_src/jp_driver.c -lei_st -lyajl
8 |
9 | OTPROOT is typically /usr/lib/erlang. Check what your versions of erts and
10 | erl_interface are and that the include and lib directories exist and contain
11 | the expected header files and library files - not all Erlang distributions
12 | ship these development files in the basic installation package. YAJLROOT is
13 | wherever your YAJL installation is. If you built YAJL but did not do 'make
14 | install', this will be something like lloyd-yajl-1.0.9-0/build/yajl-1.0.9/.
15 |
16 | For the YAJL shared library to be loaded at runtime, you may need to set the
17 | environment variable LD_LIBRARY_PATH to ${YAJLROOT}/lib if you did not
18 | install YAJL it in a standard location.
19 |
20 | To run the program, first start Erlang like this:
21 |
22 | erl -pa ../json_parser/ebin
23 |
24 | Then, run the following in the Erlang shell:
25 |
26 | 1> application:start(json_parser).
27 | ok
28 | 2> json_parser:parse_document(<<"[null,true,{\"int\":42,\"float\":3.14}]">>).
29 | {ok,{undefined,true,[{<<"int">>,42},{<<"float">>,3.14}]}}
30 | 3>
31 |
--------------------------------------------------------------------------------
/chapter_12/port_driver/json_parser/doc/.gitignore:
--------------------------------------------------------------------------------
1 | edoc-info
2 | stylesheet.css
3 | erlang.png
4 | *.html
5 |
--------------------------------------------------------------------------------
/chapter_12/port_driver/json_parser/ebin/.gitignore:
--------------------------------------------------------------------------------
1 | *.beam
2 |
--------------------------------------------------------------------------------
/chapter_12/port_driver/json_parser/ebin/json_parser.app:
--------------------------------------------------------------------------------
1 | %% -*- mode: Erlang; fill-column: 75; comment-column: 50; -*-
2 |
3 | {application, json_parser,
4 | [{description, "JSON parser (using a port driver)"},
5 | {vsn, "0.1.0"},
6 | {modules, [jp_app,
7 | jp_sup,
8 | jp_server,
9 | json_parser]},
10 | {registered, [jp_sup, jp_server]},
11 | {applications, [kernel, stdlib]},
12 | {mod, {jp_app, []}}
13 | ]}.
14 |
--------------------------------------------------------------------------------
/chapter_12/port_driver/json_parser/include/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_12/port_driver/json_parser/include/.gitignore
--------------------------------------------------------------------------------
/chapter_12/port_driver/json_parser/priv/.gitignore:
--------------------------------------------------------------------------------
1 | jp_driver.so
2 |
--------------------------------------------------------------------------------
/chapter_12/port_driver/json_parser/src/jp_app.erl:
--------------------------------------------------------------------------------
1 | -module(jp_app).
2 |
3 | -behaviour(application).
4 |
5 | -export([
6 | start/2,
7 | stop/1
8 | ]).
9 |
10 | start(_Type, _StartArgs) ->
11 | case jp_sup:start_link() of
12 | {ok, Pid} ->
13 | {ok, Pid};
14 | Other ->
15 | {error, Other}
16 | end.
17 |
18 | stop(_State) ->
19 | ok.
20 |
--------------------------------------------------------------------------------
/chapter_12/port_driver/json_parser/src/jp_server.erl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% @doc Server for the JSON parser integration.
3 | %%% @end
4 | %%%-------------------------------------------------------------------
5 |
6 | -module(jp_server).
7 |
8 | -behaviour(gen_server).
9 |
10 | %% API
11 | -export([
12 | start_link/0,
13 | parse_document/1,
14 | stop/0
15 | ]).
16 |
17 | %% gen_server callbacks
18 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
19 | terminate/2, code_change/3]).
20 |
21 | -define(SERVER, ?MODULE).
22 | -define(APPNAME, json_parser).
23 |
24 | -record(state, {port}).
25 |
26 |
27 | %%%===================================================================
28 | %%% API
29 | %%%===================================================================
30 |
31 |
32 | %%--------------------------------------------------------------------
33 | %% @doc Starts the server.
34 | %%
35 | %% @spec start_link() -> {ok, Pid}
36 | %% where
37 | %% Pid = pid()
38 | %% @end
39 | %%--------------------------------------------------------------------
40 | start_link() ->
41 | gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
42 |
43 |
44 | %%--------------------------------------------------------------------
45 | %% @doc Parses a document.
46 | %% @spec parse_document(Data) -> ok
47 | %% where
48 | %% Data = binary()
49 | %% @end
50 | %%--------------------------------------------------------------------
51 | parse_document(Data) when is_binary(Data) ->
52 | gen_server:call(?SERVER, {parse_document, Data}).
53 |
54 | %%--------------------------------------------------------------------
55 | %% @doc Stops the server.
56 | %% @spec stop() -> ok
57 | %% @end
58 | %%--------------------------------------------------------------------
59 | stop() ->
60 | gen_server:cast(?SERVER, stop).
61 |
62 |
63 | %%%===================================================================
64 | %%% gen_server callbacks
65 | %%%===================================================================
66 |
67 | init([]) ->
68 | Port = create_port(),
69 | {ok, #state{port=Port}}.
70 |
71 | handle_call({parse_document, Msg}, _From, #state{port=Port}=State) ->
72 | Port ! {self(),{command, term_to_binary(Msg)}},
73 | receive
74 | {Port, {data, Data}} ->
75 | {reply, binary_to_term(Data), State}
76 | end.
77 |
78 | handle_cast(stop, State) ->
79 | {stop, normal, State}.
80 |
81 | handle_info({Port, {exit_status, Status}}, #state{port=Port}=State) ->
82 | error_logger:format("port exited with status ~p; restarting~n",
83 | [Status]),
84 | NewPort = create_port(),
85 | {noreply, State#state{port=NewPort}}.
86 |
87 | terminate(_Reason, _State) ->
88 | ok.
89 |
90 | code_change(_OldVsn, State, _Extra) ->
91 | {ok, State}.
92 |
93 | %%%===================================================================
94 | %%% Internal functions
95 | %%%===================================================================
96 |
97 | create_port() ->
98 | case code:priv_dir(?APPNAME) of
99 | {error, _} ->
100 | error_logger:format("~w priv dir not found~n", [?APPNAME]),
101 | exit(error);
102 | PrivDir ->
103 | case erl_ddll:load(PrivDir, "jp_driver") of
104 | ok -> ok;
105 | Other -> exit(Other)
106 | end,
107 | open_port({spawn, "jp_driver"}, [binary])
108 | end.
109 |
--------------------------------------------------------------------------------
/chapter_12/port_driver/json_parser/src/jp_sup.erl:
--------------------------------------------------------------------------------
1 | -module(jp_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | %% API
6 | -export([start_link/0]).
7 |
8 | %% Supervisor callbacks
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
15 |
16 | init([]) ->
17 | Server = {jp_server, {jp_server, start_link, []},
18 | permanent, 2000, worker, [jp_server]},
19 | Children = [Server],
20 | RestartStrategy = {one_for_one, 0, 1},
21 | {ok, {RestartStrategy, Children}}.
22 |
--------------------------------------------------------------------------------
/chapter_12/port_driver/json_parser/src/json_parser.erl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% @doc JSON parser user API.
3 | %%% @end
4 | %%%-------------------------------------------------------------------
5 |
6 | -module(json_parser).
7 |
8 | -export([parse_document/1]).
9 |
10 | %% @doc Parses a document given as a binary
11 | parse_document(Data) ->
12 | jp_server:parse_document(Data).
13 |
--------------------------------------------------------------------------------
/chapter_13/.gitignore:
--------------------------------------------------------------------------------
1 | *.boot
2 | *.script
3 | *.tar.gz
4 | *.class
5 | apache-*
6 | commons-logging-*
7 | hadoop-*
8 | hbase-*
9 | zookeeper-*
10 |
--------------------------------------------------------------------------------
/chapter_13/JInterfaceExample.java:
--------------------------------------------------------------------------------
1 |
2 | import com.ericsson.otp.erlang.*;
3 |
4 | public class JInterfaceExample {
5 | public static void main(String[] args) throws Exception {
6 | if (args.length != 3) {
7 | System.out.println("wrong number of arguments");
8 | System.out.println("expected: nodeName mailboxName cookie");
9 | return;
10 | }
11 | JInterfaceExample ex = new JInterfaceExample(args[0],args[1],args[2]);
12 | ex.process();
13 | }
14 |
15 | private OtpNode node;
16 | private OtpMbox mbox;
17 |
18 | public JInterfaceExample(String nodeName, String mboxName, String cookie)
19 | throws Exception {
20 | super();
21 | node = new OtpNode(nodeName, cookie);
22 | mbox = node.createMbox(mboxName);
23 | }
24 |
25 | private void process() {
26 | while (true) {
27 | try {
28 | OtpErlangObject msg = mbox.receive();
29 | OtpErlangTuple t = (OtpErlangTuple) msg;
30 | OtpErlangPid from = (OtpErlangPid) t.elementAt(0);
31 | String name = ((OtpErlangString) t.elementAt(1)).stringValue();
32 | String greeting = "Greetings from Java, " + name + "!";
33 | OtpErlangString replystr = new OtpErlangString(greeting);
34 | OtpErlangTuple outMsg =
35 | new OtpErlangTuple(new OtpErlangObject[]{mbox.self(),
36 | replystr});
37 | mbox.send(from, outMsg);
38 | } catch (Exception e) {
39 | System.out.println("caught error: " + e);
40 | }
41 | }
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/chapter_13/Test.java:
--------------------------------------------------------------------------------
1 | import com.ericsson.otp.erlang.*;
2 |
3 | public class Test {
4 | public void main(String []args) throws Exception {
5 |
6 | OtpNode node = new OtpNode("myJavaNode");
7 | //OtpNode node = new OtpNode("myJavaNode", "secretcookie");
8 |
9 | OtpMbox named_mbox = node.createMbox("myNamedMbox");
10 | OtpMbox anon_mbox = node.createMbox();
11 |
12 | OtpErlangAtom anAtom = new OtpErlangAtom("some_atom");
13 | OtpErlangString aString = new OtpErlangString("Some string");
14 | OtpErlangInt anInt = new OtpErlangInt(22);
15 |
16 | OtpErlangTuple aTuple =
17 | new OtpErlangTuple(new OtpErlangObject[]{anAtom, aString, anInt});
18 |
19 | anon_mbox.send("myNamedMbox", aTuple);
20 |
21 | OtpErlangObject msg = named_mbox.receive();
22 |
23 | OtpErlangTuple t = (OtpErlangTuple) msg;
24 |
25 | String theAtom = ((OtpErlangAtom) t.elementAt(0)).atomValue();
26 | String theString = ((OtpErlangString) t.elementAt(1)).stringValue();
27 | int theInt = ((OtpErlangInt) t.elementAt(2)).intValue();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/chapter_13/compile.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | erlc -o ./simple_cache/ebin ./simple_cache/src/*.erl
3 | erlc -o ./resource_discovery/ebin ./resource_discovery/src/*.erl
4 | javac -cp /usr/local/lib/erlang/lib/jinterface-1.5.1/priv/OtpErlang.jar Test.java
5 | javac -cp /usr/local/lib/erlang/lib/jinterface-1.5.1/priv/OtpErlang.jar JInterfaceExample.java
6 | javac -cp hadoop-0.20.2/hadoop-0.20.2-core.jar:hbase-0.20.3/hbase-0.20.3.jar:/usr/local/lib/erlang/lib/jinterface-1.5.1/priv/OtpErlang.jar:./simple_cache/priv/java -d ./simple_cache/priv/java ./simple_cache/java_src/*.java
7 |
--------------------------------------------------------------------------------
/chapter_13/create-table.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | ./hbase-0.20.3/bin/hbase shell < 'value'}
4 | exit
5 | EOF
6 |
--------------------------------------------------------------------------------
/chapter_13/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # rename this file to "install" and set the executable flag,
3 | # then copy it into the bin directory of your release package
4 | # (update the erts version number below to match your release)
5 | ROOT=`pwd`
6 | DIR=./erts-5.7.4/bin
7 | sed s:%FINAL_ROOTDIR%:$ROOT: $DIR/erl.src > $DIR/erl
8 |
--------------------------------------------------------------------------------
/chapter_13/resource_discovery/doc/.gitignore:
--------------------------------------------------------------------------------
1 | edoc-info
2 | stylesheet.css
3 | erlang.png
4 | *.html
5 |
--------------------------------------------------------------------------------
/chapter_13/resource_discovery/ebin/.gitignore:
--------------------------------------------------------------------------------
1 | *.beam
2 |
--------------------------------------------------------------------------------
/chapter_13/resource_discovery/ebin/resource_discovery.app:
--------------------------------------------------------------------------------
1 | {application, resource_discovery,
2 | [{description, "A simple resource discovery system"},
3 | {vsn, "0.1.0"},
4 | {modules, [resource_discovery,
5 | rd_app,
6 | rd_sup,
7 | rd_server]},
8 | {registered, [rd_sup, rd_server]},
9 | {applications, [kernel, stdlib]},
10 | {mod, {rd_app, []}}
11 | ]}.
12 |
--------------------------------------------------------------------------------
/chapter_13/resource_discovery/include/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_13/resource_discovery/include/.gitignore
--------------------------------------------------------------------------------
/chapter_13/resource_discovery/priv/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_13/resource_discovery/priv/.gitignore
--------------------------------------------------------------------------------
/chapter_13/resource_discovery/src/rd_app.erl:
--------------------------------------------------------------------------------
1 | -module(rd_app).
2 |
3 | -behaviour(application).
4 |
5 | -export([start/2, stop/1]).
6 |
7 | start(_StartType, _StartArgs) ->
8 | case rd_sup:start_link() of
9 | {ok, Pid} ->
10 | {ok, Pid};
11 | Other ->
12 | {error, Other}
13 | end.
14 |
15 | stop(_State) ->
16 | ok.
17 |
--------------------------------------------------------------------------------
/chapter_13/resource_discovery/src/rd_sup.erl:
--------------------------------------------------------------------------------
1 | -module(rd_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | %% API
6 | -export([start_link/0]).
7 |
8 | %% Supervisor callbacks
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
15 |
16 | init([]) ->
17 | Server = {rd_server, {rd_server, start_link, []},
18 | permanent, 2000, worker, [rd_server]},
19 | Children = [Server],
20 | RestartStrategy = {one_for_one, 0, 1},
21 | {ok, {RestartStrategy, Children}}.
22 |
--------------------------------------------------------------------------------
/chapter_13/resource_discovery/src/resource_discovery.erl:
--------------------------------------------------------------------------------
1 | -module(resource_discovery).
2 |
3 | -export([
4 | add_target_resource_type/1,
5 | add_local_resource/2,
6 | fetch_resources/1,
7 | trade_resources/0
8 | ]).
9 |
10 | add_target_resource_type(Type) ->
11 | rd_server:add_target_resource_type(Type).
12 |
13 | add_local_resource(Type, Resource) ->
14 | rd_server:add_local_resource(Type, Resource).
15 |
16 | fetch_resources(Type) ->
17 | rd_server:fetch_resources(Type).
18 |
19 | trade_resources() ->
20 | rd_server:trade_resources().
21 |
--------------------------------------------------------------------------------
/chapter_13/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # Make sure to start HBase and create the 'cache' table before you run this
3 |
4 | # start a contact node (this also ensures that EPMD is running)
5 | erl -sname contact1 -detached -setcookie secret
6 |
7 | # start the Java node (in the background - note the '&' at the end)
8 | java -cp apache-log4j-1.2.16/log4j-1.2.16.jar:zookeeper-3.2.2/zookeeper-3.2.2.jar:commons-logging-1.1.1/commons-logging-1.1.1.jar:hadoop-0.20.2/hadoop-0.20.2-core.jar:hbase-0.20.3/hbase-0.20.3.jar:/usr/local/lib/erlang/lib/jinterface-1.5.1/priv/OtpErlang.jar:./simple_cache/priv/java HBaseNode hbase secret &
9 |
10 | # start the simple_cache system
11 | erl -sname mynode -pa ./simple_cache/ebin -pa ./resource_discovery/ebin/ -boot ./simple_cache -config sys -setcookie secret
12 |
--------------------------------------------------------------------------------
/chapter_13/simple_cache.rel:
--------------------------------------------------------------------------------
1 | {release,
2 | {"simple_cache", "0.2.0"},
3 | {erts, "5.7.3"},
4 | [{kernel, "2.13.3"},
5 | {stdlib, "1.16.3"},
6 | {sasl, "2.1.7"},
7 | {mnesia, "4.4.11"},
8 | {resource_discovery, "0.1.0"},
9 | {simple_cache, "0.4.0"}
10 | ]}.
11 |
--------------------------------------------------------------------------------
/chapter_13/simple_cache.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # rename this file to "simple_cache" and set the executable flag,
3 | # then copy it into the bin directory of your release package
4 | # (update the erts version number below to match your release)
5 | ./erts-5.7.4/bin/erl \
6 | -sname cache \
7 | -boot ./releases/0.2.0/start \
8 | -config ./releases/0.2.0/sys \
9 | -detached
10 |
--------------------------------------------------------------------------------
/chapter_13/simple_cache/doc/.gitignore:
--------------------------------------------------------------------------------
1 | edoc-info
2 | stylesheet.css
3 | erlang.png
4 | *.html
5 |
--------------------------------------------------------------------------------
/chapter_13/simple_cache/ebin/.gitignore:
--------------------------------------------------------------------------------
1 | *.beam
2 |
--------------------------------------------------------------------------------
/chapter_13/simple_cache/ebin/simple_cache.app:
--------------------------------------------------------------------------------
1 | {application, simple_cache,
2 | [{description, "A simple caching system"},
3 | {vsn, "0.4.0"},
4 | {modules, [simple_cache,
5 | sc_app,
6 | sc_sup,
7 | sc_element_sup,
8 | sc_store,
9 | sc_element,
10 | sc_event,
11 | sc_event_logger,
12 | sc_hbase]},
13 | {registered, [sc_sup, sc_element_sup, sc_event]},
14 | {applications, [kernel, sasl, stdlib, mnesia, resource_discovery]},
15 | {mod, {sc_app, []}}
16 | ]}.
17 |
--------------------------------------------------------------------------------
/chapter_13/simple_cache/include/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_13/simple_cache/include/.gitignore
--------------------------------------------------------------------------------
/chapter_13/simple_cache/java_src/HBaseConnector.java:
--------------------------------------------------------------------------------
1 |
2 | import org.apache.hadoop.hbase.HBaseConfiguration;
3 | import org.apache.hadoop.hbase.client.*;
4 | import java.util.NavigableMap;
5 |
6 | public class HBaseConnector {
7 | private HTable table;
8 |
9 | public HBaseConnector() throws Exception {
10 | super();
11 | table = new HTable(new HBaseConfiguration(), "cache");
12 | }
13 |
14 | public byte[] get(byte[] key) throws Exception {
15 | // Throws null pointer exception if key is not found
16 | Result result = table.get(new Get(key));
17 | NavigableMap> map =
18 | result.getNoVersionMap();
19 | return map.get("value".getBytes()).get("".getBytes());
20 | }
21 |
22 | public void put(byte[] key, byte[] value) throws Exception {
23 | Put put = new Put(key);
24 | put.add("value".getBytes(), "".getBytes(), value);
25 | table.put(put);
26 | }
27 |
28 | public void delete(byte[] key) throws Exception {
29 | Delete del = new Delete(key);
30 | table.delete(del);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/chapter_13/simple_cache/java_src/HBaseNode.java:
--------------------------------------------------------------------------------
1 | import com.ericsson.otp.erlang.*;
2 | import java.util.concurrent.*;
3 |
4 | public class HBaseNode {
5 | private HBaseConnector conn;
6 | private ExecutorService exec;
7 | private OtpNode node;
8 | private OtpMbox mbox;
9 |
10 | public HBaseNode(String nodeName, String cookie)
11 | throws Exception {
12 | super();
13 | conn = new HBaseConnector();
14 | exec = Executors.newFixedThreadPool(10);
15 | node = new OtpNode(nodeName, cookie);
16 | mbox = node.createMbox("hbase_server");
17 | }
18 |
19 | public static void main(String[] args) throws Exception {
20 | if (args.length != 2) {
21 | System.out.println("wrong number of arguments");
22 | System.out.println("expected: nodeName cookie");
23 | return;
24 | }
25 | HBaseNode main = new HBaseNode(args[0],args[1]);
26 | main.process();
27 | }
28 |
29 | // message format: { Action, FromPID, UniqueRef, Key [, Value] }
30 | private void process() {
31 | while (true) {
32 | try {
33 | OtpErlangObject msg = mbox.receive();
34 | OtpErlangTuple t = (OtpErlangTuple) msg;
35 | String action = ((OtpErlangAtom) t.elementAt(0)).atomValue();
36 | OtpErlangPid from = (OtpErlangPid) t.elementAt(1);
37 | OtpErlangRef ref = (OtpErlangRef) t.elementAt(2);
38 | byte[] key = ((OtpErlangBinary) t.elementAt(3)).binaryValue();
39 | byte[] value;
40 | HBaseTask task = null;
41 | if (t.arity() == 5 && action.equals("put")) {
42 | value = ((OtpErlangBinary) t.elementAt(4)).binaryValue();
43 | task = new HBaseTask(mbox, conn, from, ref, action, key, value);
44 | } else if (t.arity() == 4 && action.equals("get")) {
45 | task = new HBaseTask(mbox, conn, from, ref, action, key, null);
46 | } else if (t.arity() == 4 && action.equals("delete")) {
47 | task = new HBaseTask(mbox, conn, from, ref, action, key, null);
48 | } else {
49 | System.out.println("invalid request: " + t);
50 | continue;
51 | }
52 | exec.submit(task);
53 | } catch (Exception e) {
54 | System.out.println("caught error: " + e);
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/chapter_13/simple_cache/java_src/HBaseTask.java:
--------------------------------------------------------------------------------
1 | import com.ericsson.otp.erlang.*;
2 |
3 | public class HBaseTask implements Runnable {
4 | private OtpMbox mbox;
5 | private HBaseConnector conn;
6 | private OtpErlangPid from;
7 | private OtpErlangRef ref;
8 | private String action;
9 | private byte[] key;
10 | private byte[] value;
11 |
12 | public HBaseTask(OtpMbox mbox, HBaseConnector conn,
13 | OtpErlangPid from, OtpErlangRef ref,
14 | String action, byte[] key, byte[] value) {
15 | super();
16 | this.mbox = mbox;
17 | this.conn = conn;
18 | this.from = from;
19 | this.ref = ref;
20 | this.action = action;
21 | this.key = key;
22 | this.value = value;
23 | }
24 |
25 | public void run() {
26 | try {
27 | if (action.equals("get")) {
28 | doGet();
29 | } else if (action.equals("put")) {
30 | doPut();
31 | } else if (action.equals("delete")) {
32 | doDelete();
33 | } else {
34 | System.out.println("invalid action: " + action);
35 | }
36 | } catch (Exception e) {
37 | System.out.println("caught error: " + e);
38 | }
39 | }
40 |
41 | private void doGet() throws Exception {
42 | OtpErlangObject result;
43 | try {
44 | result = new OtpErlangBinary(conn.get(key));
45 | } catch (NullPointerException e) {
46 | result = new OtpErlangAtom("not_found");
47 | }
48 | OtpErlangTuple reply = new OtpErlangTuple(new OtpErlangObject[] {
49 | new OtpErlangAtom("reply"), ref,
50 | result
51 | });
52 | mbox.send(from, reply);
53 | }
54 |
55 | private void doPut() throws Exception {
56 | conn.put(key, value);
57 | OtpErlangTuple reply = new OtpErlangTuple(new OtpErlangObject[] {
58 | new OtpErlangAtom("reply"), ref,
59 | new OtpErlangAtom("ok")
60 | });
61 | mbox.send(from, reply);
62 | }
63 |
64 | private void doDelete() throws Exception {
65 | conn.delete(key);
66 | OtpErlangTuple reply = new OtpErlangTuple(new OtpErlangObject[] {
67 | new OtpErlangAtom("reply"), ref,
68 | new OtpErlangAtom("ok")
69 | });
70 | mbox.send(from, reply);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/chapter_13/simple_cache/priv/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/chapter_13/simple_cache/priv/.gitignore
--------------------------------------------------------------------------------
/chapter_13/simple_cache/priv/java/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 |
--------------------------------------------------------------------------------
/chapter_13/simple_cache/src/sc_app.erl:
--------------------------------------------------------------------------------
1 | -module(sc_app).
2 |
3 | -behaviour(application).
4 |
5 | -export([start/2, stop/1]).
6 |
7 | -define(WAIT_FOR_RESOURCES, 2500).
8 |
9 | start(_StartType, _StartArgs) ->
10 | ok = ensure_contact(),
11 | resource_discovery:add_local_resource(simple_cache, node()),
12 | resource_discovery:add_target_resource_type(simple_cache),
13 | resource_discovery:trade_resources(),
14 | timer:sleep(?WAIT_FOR_RESOURCES),
15 | sc_store:init(),
16 | case sc_sup:start_link() of
17 | {ok, Pid} ->
18 | sc_event_logger:add_handler(),
19 | {ok, Pid};
20 | Other ->
21 | {error, Other}
22 | end.
23 |
24 | stop(_State) ->
25 | ok.
26 |
27 | ensure_contact() ->
28 | DefaultNodes = ['contact1@localhost', 'contact2@localhost'],
29 | case get_env(simple_cache, contact_nodes, DefaultNodes) of
30 | [] ->
31 | {error, no_contact_nodes};
32 | ContactNodes ->
33 | ensure_contact(ContactNodes)
34 | end.
35 |
36 | ensure_contact(ContactNodes) ->
37 | Answering = [N || N <- ContactNodes, net_adm:ping(N) =:= pong],
38 | case Answering of
39 | [] ->
40 | {error, no_contact_nodes_reachable};
41 | _ ->
42 | DefaultTime = 6000,
43 | WaitTime = get_env(simple_cache, wait_time, DefaultTime),
44 | wait_for_nodes(length(Answering), WaitTime)
45 | end.
46 |
47 | wait_for_nodes(MinNodes, WaitTime) ->
48 | Slices = 10,
49 | SliceTime = round(WaitTime/Slices),
50 | wait_for_nodes(MinNodes, SliceTime, Slices).
51 |
52 | wait_for_nodes(_MinNodes, _SliceTime, 0) ->
53 | ok;
54 | wait_for_nodes(MinNodes, SliceTime, Iterations) ->
55 | case length(nodes()) > MinNodes of
56 | true ->
57 | ok;
58 | false ->
59 | timer:sleep(SliceTime),
60 | wait_for_nodes(MinNodes, SliceTime, Iterations - 1)
61 | end.
62 |
63 | get_env(AppName, Key, Default) ->
64 | case application:get_env(AppName, Key) of
65 | undefined -> Default;
66 | {ok, Value} -> Value
67 | end.
68 |
--------------------------------------------------------------------------------
/chapter_13/simple_cache/src/sc_element.erl:
--------------------------------------------------------------------------------
1 | -module(sc_element).
2 |
3 | -behaviour(gen_server).
4 |
5 | -export([
6 | start_link/2,
7 | create/1,
8 | create/2,
9 | fetch/1,
10 | replace/2,
11 | delete/1
12 | ]).
13 |
14 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
15 | terminate/2, code_change/3]).
16 |
17 | -define(SERVER, ?MODULE).
18 | -define(DEFAULT_LEASE_TIME, (60 * 60 * 24)).
19 |
20 | -record(state, {value, lease_time, start_time}).
21 |
22 | start_link(Value, LeaseTime) ->
23 | gen_server:start_link(?MODULE, [Value, LeaseTime], []).
24 |
25 | create(Value, LeaseTime) ->
26 | sc_element_sup:start_child(Value, LeaseTime).
27 |
28 | create(Value) ->
29 | create(Value, ?DEFAULT_LEASE_TIME).
30 |
31 | fetch(Pid) ->
32 | gen_server:call(Pid, fetch).
33 |
34 | replace(Pid, Value) ->
35 | gen_server:cast(Pid, {replace, Value}).
36 |
37 | delete(Pid) ->
38 | gen_server:cast(Pid, delete).
39 |
40 | init([Value, LeaseTime]) ->
41 | Now = calendar:local_time(),
42 | StartTime = calendar:datetime_to_gregorian_seconds(Now),
43 | {ok,
44 | #state{value = Value,
45 | lease_time = LeaseTime,
46 | start_time = StartTime},
47 | time_left(StartTime, LeaseTime)}.
48 |
49 | time_left(_StartTime, infinity) ->
50 | infinity;
51 | time_left(StartTime, LeaseTime) ->
52 | Now = calendar:local_time(),
53 | CurrentTime = calendar:datetime_to_gregorian_seconds(Now),
54 | TimeElapsed = CurrentTime - StartTime,
55 | case LeaseTime - TimeElapsed of
56 | Time when Time =< 0 -> 0;
57 | Time -> Time * 1000
58 | end.
59 |
60 | handle_call(fetch, _From, State) ->
61 | #state{value = Value,
62 | lease_time = LeaseTime,
63 | start_time = StartTime} = State,
64 | TimeLeft = time_left(StartTime, LeaseTime),
65 | {reply, {ok, Value}, State, TimeLeft}.
66 |
67 | handle_cast({replace, Value}, State) ->
68 | #state{lease_time = LeaseTime,
69 | start_time = StartTime} = State,
70 | TimeLeft = time_left(StartTime, LeaseTime),
71 | {noreply, State#state{value = Value}, TimeLeft};
72 | handle_cast(delete, State) ->
73 | {stop, normal, State}.
74 |
75 | handle_info(timeout, State) ->
76 | {stop, normal, State}.
77 |
78 | terminate(_Reason, _State) ->
79 | sc_store:delete(self()),
80 | ok.
81 |
82 | code_change(_OldVsn, State, _Extra) ->
83 | {ok, State}.
84 |
--------------------------------------------------------------------------------
/chapter_13/simple_cache/src/sc_element_sup.erl:
--------------------------------------------------------------------------------
1 | -module(sc_element_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | -export([start_link/0,
6 | start_child/2
7 | ]).
8 |
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
15 |
16 | start_child(Value, LeaseTime) ->
17 | supervisor:start_child(?SERVER, [Value, LeaseTime]).
18 |
19 | init([]) ->
20 | Element = {sc_element, {sc_element, start_link, []},
21 | temporary, brutal_kill, worker, [sc_element]},
22 | Children = [Element],
23 | RestartStrategy = {simple_one_for_one, 0, 1},
24 | {ok, {RestartStrategy, Children}}.
25 |
--------------------------------------------------------------------------------
/chapter_13/simple_cache/src/sc_event.erl:
--------------------------------------------------------------------------------
1 | -module(sc_event).
2 |
3 | -export([start_link/0,
4 | add_handler/2,
5 | delete_handler/2,
6 | lookup/1,
7 | create/2,
8 | replace/2,
9 | delete/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | gen_event:start_link({local, ?SERVER}).
15 |
16 | add_handler(Handler, Args) ->
17 | gen_event:add_handler(?SERVER, Handler, Args).
18 |
19 | delete_handler(Handler, Args) ->
20 | gen_event:delete_handler(?SERVER, Handler, Args).
21 |
22 | lookup(Key) ->
23 | gen_event:notify(?SERVER, {lookup, Key}).
24 |
25 | create(Key, Value) ->
26 | gen_event:notify(?SERVER, {create, {Key, Value}}).
27 |
28 | replace(Key, Value) ->
29 | gen_event:notify(?SERVER, {replace, {Key, Value}}).
30 |
31 | delete(Key) ->
32 | gen_event:notify(?SERVER, {delete, Key}).
33 |
--------------------------------------------------------------------------------
/chapter_13/simple_cache/src/sc_event_logger.erl:
--------------------------------------------------------------------------------
1 | -module(sc_event_logger).
2 |
3 | -behaviour(gen_event).
4 |
5 | -export([add_handler/0, delete_handler/0]).
6 |
7 | -export([init/1, handle_event/2, handle_call/2,
8 | handle_info/2, code_change/3, terminate/2]).
9 |
10 | -record(state, {}).
11 |
12 | add_handler() ->
13 | sc_event:add_handler(?MODULE, []).
14 |
15 | delete_handler() ->
16 | sc_event:delete_handler(?MODULE, []).
17 |
18 | init([]) ->
19 | {ok, #state{}}.
20 |
21 | handle_event({create, {Key, Value}}, State) ->
22 | error_logger:info_msg("create(~w, ~w)~n", [Key, Value]),
23 | {ok, State};
24 | handle_event({lookup, Key}, State) ->
25 | error_logger:info_msg("lookup(~w)~n", [Key]),
26 | {ok, State};
27 | handle_event({delete, Key}, State) ->
28 | error_logger:info_msg("delete(~w)~n", [Key]),
29 | {ok, State};
30 | handle_event({replace, {Key, Value}}, State) ->
31 | error_logger:info_msg("replace(~w, ~w)~n", [Key, Value]),
32 | {ok, State}.
33 |
34 | handle_call(_Request, State) ->
35 | Reply = ok,
36 | {ok, Reply, State}.
37 |
38 | handle_info(_Info, State) ->
39 | {ok, State}.
40 |
41 | terminate(_Reason, _State) ->
42 | ok.
43 |
44 | code_change(_OldVsn, State, _Extra) ->
45 | {ok, State}.
46 |
--------------------------------------------------------------------------------
/chapter_13/simple_cache/src/sc_hbase.erl:
--------------------------------------------------------------------------------
1 | -module(sc_hbase).
2 |
3 | -export([put/3, get/2, delete/2]).
4 |
5 | put(Node, Key, Value) ->
6 | Ref = make_ref(),
7 | {hbase_server, Node} ! {put, self(), Ref, term_to_binary(Key),
8 | term_to_binary(Value)},
9 | receive
10 | {reply, Ref, ok} ->
11 | ok
12 | after 3000 ->
13 | {error, timeout}
14 | end.
15 |
16 | get(Node, Key) ->
17 | Ref = make_ref(),
18 | {hbase_server, Node} ! {get, self(), Ref, term_to_binary(Key)},
19 | receive
20 | {reply, Ref, not_found} ->
21 | {error, not_found};
22 | {reply, Ref, Binary} ->
23 | {ok, binary_to_term(Binary)}
24 | after 3000 ->
25 | {error, timeout}
26 | end.
27 |
28 | delete(Node, Key) ->
29 | Ref = make_ref(),
30 | {hbase_server, Node} ! {delete, self(), Ref, term_to_binary(Key)},
31 | receive
32 | {reply, Ref, ok} ->
33 | ok
34 | after 3000 ->
35 | {error, timeout}
36 | end.
37 |
--------------------------------------------------------------------------------
/chapter_13/simple_cache/src/sc_store.erl:
--------------------------------------------------------------------------------
1 | -module(sc_store).
2 |
3 | -export([
4 | init/0,
5 | insert/2,
6 | delete/1,
7 | lookup/1
8 | ]).
9 |
10 | -define(TABLE_ID, ?MODULE).
11 | -define(WAIT_FOR_TABLES, 5000).
12 |
13 | -record(key_to_pid, {key, pid}).
14 |
15 | init() ->
16 | mnesia:stop(),
17 | mnesia:delete_schema([node()]),
18 | mnesia:start(),
19 | {ok, CacheNodes} = resource_discovery:fetch_resources(simple_cache),
20 | dynamic_db_init(lists:delete(node(), CacheNodes)).
21 |
22 | insert(Key, Pid) ->
23 | mnesia:dirty_write(#key_to_pid{key = Key, pid = Pid}).
24 |
25 | lookup(Key) ->
26 | case mnesia:dirty_read(key_to_pid, Key) of
27 | [{key_to_pid, Key, Pid}] ->
28 | case is_pid_alive(Pid) of
29 | true -> {ok, Pid};
30 | false -> {error, not_found}
31 | end;
32 | [] ->
33 | {error, not_found}
34 | end.
35 |
36 | delete(Pid) ->
37 | case mnesia:dirty_index_read(key_to_pid, Pid, #key_to_pid.pid) of
38 | [#key_to_pid{} = Record] ->
39 | mnesia:dirty_delete_object(Record);
40 | _ ->
41 | ok
42 | end.
43 |
44 |
45 | %% Internal Functions
46 |
47 | dynamic_db_init([]) ->
48 | mnesia:create_table(key_to_pid,
49 | [{index, [pid]},
50 | {attributes, record_info(fields, key_to_pid)}
51 | ]);
52 | dynamic_db_init(CacheNodes) ->
53 | add_extra_nodes(CacheNodes).
54 |
55 | add_extra_nodes([Node|T]) ->
56 | case mnesia:change_config(extra_db_nodes, [Node]) of
57 | {ok, [Node]} ->
58 | mnesia:add_table_copy(key_to_pid, node(), ram_copies),
59 |
60 | Tables = mnesia:system_info(tables),
61 | mnesia:wait_for_tables(Tables, ?WAIT_FOR_TABLES);
62 | _ ->
63 | add_extra_nodes(T)
64 | end.
65 |
66 | is_pid_alive(Pid) when node(Pid) =:= node() ->
67 | is_process_alive(Pid);
68 | is_pid_alive(Pid) ->
69 | case lists:member(node(Pid), nodes()) of
70 | false ->
71 | false;
72 | true ->
73 | case rpc:call(node(Pid), erlang, is_process_alive, [Pid]) of
74 | true ->
75 | true;
76 | false ->
77 | false;
78 | {badrpc, _Reason} ->
79 | false
80 | end
81 | end.
82 |
83 |
--------------------------------------------------------------------------------
/chapter_13/simple_cache/src/sc_sup.erl:
--------------------------------------------------------------------------------
1 | -module(sc_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | %% API
6 | -export([start_link/0]).
7 |
8 | %% Supervisor callbacks
9 | -export([init/1]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | start_link() ->
14 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
15 |
16 | init([]) ->
17 | ElementSup = {sc_element_sup, {sc_element_sup, start_link, []},
18 | permanent, 2000, supervisor, [sc_element]},
19 |
20 | EventManager = {sc_event, {sc_event, start_link, []},
21 | permanent, 2000, worker, [sc_event]},
22 |
23 | Children = [ElementSup, EventManager],
24 | RestartStrategy = {one_for_one, 4, 3600},
25 | {ok, {RestartStrategy, Children}}.
26 |
--------------------------------------------------------------------------------
/chapter_13/simple_cache/src/simple_cache.erl:
--------------------------------------------------------------------------------
1 | -module(simple_cache).
2 |
3 | -export([insert/2, lookup/1, delete/1]).
4 |
5 | -define(HBASE_NODE, 'hbase@localhost').
6 |
7 | insert(Key, Value) ->
8 | case sc_store:lookup(Key) of
9 | {ok, Pid} ->
10 | sc_event:replace(Key, Value),
11 | sc_element:replace(Pid, Value);
12 | {error, _} ->
13 | {ok, Pid} = sc_element:create(Value),
14 | sc_store:insert(Key, Pid),
15 | sc_event:create(Key, Value)
16 | end,
17 | sc_hbase:put(?HBASE_NODE, Key, Value).
18 |
19 | lookup(Key) ->
20 | sc_event:lookup(Key),
21 | try
22 | case sc_store:lookup(Key) of
23 | {ok, Pid} ->
24 | {ok, Value} = sc_element:fetch(Pid),
25 | {ok, Value};
26 | {error, _} ->
27 | {ok, Value} = sc_hbase:get(?HBASE_NODE, Key),
28 | insert(Key, Value),
29 | {ok, Value}
30 | end
31 | catch
32 | _Class:_Exception ->
33 | {error, not_found}
34 | end.
35 |
36 | delete(Key) ->
37 | sc_event:delete(Key),
38 | case sc_store:lookup(Key) of
39 | {ok, Pid} ->
40 | sc_hbase:delete(?HBASE_NODE, Key),
41 | sc_element:delete(Pid);
42 | {error, _Reason} ->
43 | ok
44 | end.
45 |
--------------------------------------------------------------------------------
/chapter_13/start-hbase.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | ./hbase-0.20.3/bin/start-hbase.sh
3 |
4 |
--------------------------------------------------------------------------------
/chapter_13/stop-hbase.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | ./hbase-0.20.3/bin/stop-hbase.sh
3 |
4 |
--------------------------------------------------------------------------------
/chapter_13/sys.config:
--------------------------------------------------------------------------------
1 | [
2 | %% write log files to sasl_dir
3 | {sasl,
4 | [
5 | {sasl_error_logger, {file, "/tmp/simple_cache.sasl_log"}}
6 | ]},
7 | {simple_cache,
8 | [
9 | %% Contact nodes for use in joining a cloud
10 | {contact_nodes, ['contact1@localhost', 'contact2@localhost']}
11 | ]}
12 | ].
13 |
--------------------------------------------------------------------------------
/chapter_14/profile_ex.erl:
--------------------------------------------------------------------------------
1 | -module(profile_ex).
2 |
3 | %% API
4 | -export([run/0]).
5 |
6 | run() ->
7 | spawn(fun() -> looper(1000) end),
8 | spawn(fun() -> funner(1000) end).
9 |
10 | looper(0) ->
11 | ok;
12 | looper(N) ->
13 | _ = integer_to_list(N),
14 | looper(N - 1).
15 |
16 | funner(N) ->
17 | funner(fun(X) -> integer_to_list(X) end, N).
18 |
19 | funner(_Fun, 0) ->
20 | ok;
21 | funner(Fun, N) ->
22 | Fun(N),
23 | funner(Fun, N - 1).
24 |
--------------------------------------------------------------------------------
/hello_erlware/_build.cfg:
--------------------------------------------------------------------------------
1 | project : {
2 | name : hello_erlware
3 | vsn : "0.1.0.0"
4 | },
5 |
6 | build_dir : _build,
7 |
8 | ignore_dirs : ["_",
9 | "."],
10 |
11 | ignore_apps : [],
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/hello_erlware/bin/erlware_release_start_helper:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if [ $# -lt 3 ];then
4 | echo "usage $0 [extra-args]"
5 | exit 1
6 | fi
7 |
8 | REL_NAME=$1; shift
9 | REL_VSN=$1; shift
10 | ERTS_VSN=$1; shift
11 | CONFIG_FILE_NAME=$1
12 |
13 | ERTS_DIR=$ROOTDIR/erts-$ERTS_VSN
14 | export BINDIR=$ERTS_DIR/bin
15 | export EMU=beam
16 | export PROGNAME=erl
17 | export LD_LIBRARY_PATH=$ERTS_DIR/lib
18 |
19 | export REL_DIR=$ROOTDIR/releases/$REL_NAME-$REL_VSN
20 |
21 | if [ "$CONFIG_FILE_NAME" = "no_config" ];then
22 | $BINDIR/erlexec -boot $REL_DIR/$REL_NAME $@
23 | else
24 | shift
25 | $BINDIR/erlexec -config $REL_DIR/$CONFIG_FILE_NAME -boot $REL_DIR/$REL_NAME $@
26 | fi
27 |
--------------------------------------------------------------------------------
/hello_erlware/bin/hello_erlware:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | PROG=$0
4 | PROG_DIR=$(cd `dirname $0`; pwd)
5 | test -h $0 && PROG=$(readlink $0)
6 | export ROOTDIR=$(dirname $PROG_DIR)
7 |
8 | #### Fill in values for these variables ####
9 | REL_NAME=hello_erlware
10 | REL_VSN=0.1.0.0
11 | ERTS_VSN=5.7.4
12 | INVOCATION_SUFFIX="$@"
13 | ###########################################
14 |
15 | $ROOTDIR/bin/erlware_release_start_helper $REL_NAME $REL_VSN $ERTS_VSN sys.config $INVOCATION_SUFFIX
16 |
--------------------------------------------------------------------------------
/hello_erlware/config/sys.config:
--------------------------------------------------------------------------------
1 | [
2 | {kernel, [
3 | {error_logger, {file, "/tmp/hello_erlware.error_log"}}
4 | ]},
5 |
6 | {sasl, [
7 | {sasl_error_logger, {file, "/tmp/hello_erlware.sasl_log"}}
8 | ]},
9 |
10 | {my_app, [
11 | {hello_msg, "Hello again!"}
12 | ]}
13 | ].
14 |
15 |
--------------------------------------------------------------------------------
/hello_erlware/lib/my_app/doc/overview.edoc:
--------------------------------------------------------------------------------
1 | @author Martin Logan
2 | @copyright 2010 Martin Logan
3 | @version {@vsn}
4 |
5 |
--------------------------------------------------------------------------------
/hello_erlware/lib/my_app/ebin/my_app.app:
--------------------------------------------------------------------------------
1 | %% This is the application resource file (.app file) for the my_app,
2 | %% application.
3 | {application, my_app,
4 | [{description, "Your Desc HERE"},
5 | {vsn, "0.1.0"},
6 | {modules, [my_app_app,
7 | my_app_sup,
8 | ma_hello_server]},
9 | {registered,[my_app_sup]},
10 | {applications, [kernel, stdlib, sasl]},
11 | {mod, {my_app_app,[]}},
12 | {start_phases, []}]}.
13 |
14 |
--------------------------------------------------------------------------------
/hello_erlware/lib/my_app/src/ma_hello_server.erl:
--------------------------------------------------------------------------------
1 | -module(ma_hello_server).
2 | -behaviour(gen_server).
3 |
4 | -include("eunit.hrl").
5 |
6 | %% API
7 | -export([start_link/0]).
8 |
9 | %% gen_server callbacks
10 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
11 | terminate/2, code_change/3]).
12 |
13 | -define(SERVER, ?MODULE).
14 |
15 | -record(state, {}).
16 |
17 | %%%===================================================================
18 | %%% API
19 | %%%===================================================================
20 |
21 | start_link() ->
22 | gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
23 |
24 | %%%===================================================================
25 | %%% gen_server callbacks
26 | %%%===================================================================
27 |
28 | init([]) ->
29 | error_logger:info_msg("~p init~n", [?MODULE]),
30 | {ok, #state{}, 0}.
31 |
32 | handle_call(_Request, _From, State) -> {reply, ok, State}.
33 |
34 | handle_cast(_Msg, State) -> {noreply, State}.
35 |
36 | handle_info(_Info, State) -> {stop, normal, State}.
37 |
38 | terminate(_Reason, _State) ->
39 | Msg = get_conf_value(my_app, hello_msg, "Hello Erlware and OTP"),
40 | error_logger:info_msg(Msg).
41 |
42 | code_change(_OldVsn, State, _Extra) -> {ok, State}.
43 |
44 | %%%===================================================================
45 | %%% Internal functions
46 | %%%===================================================================
47 |
48 | get_conf_value(App, Key, DefaultValue) ->
49 | case application:get_env(App, Key) of
50 | {ok, Value} -> Value;
51 | undefined -> DefaultValue
52 | end.
53 |
54 | %%%===================================================================
55 | %%% Test functions
56 | %%%===================================================================
57 |
58 | get_conf_value_test() ->
59 | ?assertMatch(default_value, get_conf_value(test, '$testval$', bad_default)).
60 |
--------------------------------------------------------------------------------
/hello_erlware/lib/my_app/src/my_app_app.erl:
--------------------------------------------------------------------------------
1 | %%%----------------------------------------------------------------
2 | %%% @author Martin Logan
3 | %%% @doc Application behaviour implementation for tcp_rpc.
4 | %%%
5 | %%% @copyright 2008 Martin Logan
6 | %%% @end
7 | %%%----------------------------------------------------------------,
8 | -module(my_app_app).
9 |
10 | -behaviour(application).
11 |
12 | %% Application callbacks
13 | -export([
14 | start/2,
15 | stop/1
16 | ]).
17 |
18 |
19 | %%%===================================================================
20 | %%% Application callbacks
21 | %%%===================================================================
22 |
23 | %%--------------------------------------------------------------------
24 | %% @private
25 | %% @doc
26 | %% This function is called whenever an application is started using
27 | %% application:start/[1,2], and should start the processes of the
28 | %% application. If the application is structured according to the OTP
29 | %% design principles as a supervision tree, this means starting the
30 | %% top supervisor of the tree.
31 | %%
32 | %% @spec start(StartType, StartArgs) -> {ok, Pid} |
33 | %% {ok, Pid, State} |
34 | %% {error, Reason}
35 | %% StartType = normal | {takeover, Node} | {failover, Node}
36 | %% StartArgs = term()
37 | %% @end
38 | %%--------------------------------------------------------------------
39 | start(_StartType, _StartArgs) ->
40 | case my_app_sup:start_link() of
41 | {ok, Pid} ->
42 | {ok, Pid};
43 | Other ->
44 | {error, Other}
45 | end.
46 |
47 | %%--------------------------------------------------------------------
48 | %% @private
49 | %% @doc
50 | %% This function is called whenever an application has stopped. It
51 | %% is intended to be the opposite of Module:start/2 and should do
52 | %% any necessary cleaning up. The return value is ignored.
53 | %%
54 | %% @spec stop(State) -> void()
55 | %% @end
56 | %%--------------------------------------------------------------------
57 | stop(_State) ->
58 | ok.
59 |
60 | %%%===================================================================
61 | %%% Internal functions
62 | %%%===================================================================
63 |
--------------------------------------------------------------------------------
/hello_erlware/lib/my_app/src/my_app_sup.erl:
--------------------------------------------------------------------------------
1 | -module(my_app_sup).
2 |
3 | -behaviour(supervisor).
4 |
5 | -export([start_link/0]).
6 | -export([init/1]).
7 |
8 | -define(SERVER, ?MODULE).
9 |
10 | %%%===================================================================
11 | %%% API functions
12 | %%%===================================================================
13 |
14 | start_link() ->
15 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
16 |
17 | %%%===================================================================
18 | %%% Supervisor callbacks
19 | %%%===================================================================
20 |
21 | init([]) ->
22 | RestartStrategy = one_for_one,
23 | MaxRestarts = 0,
24 | MaxSecondsBetweenRestarts = 3600,
25 |
26 | SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts},
27 |
28 | Restart = temporary,
29 | Shutdown = 2000,
30 | Type = worker,
31 |
32 | AChild = {ma_hello_server, {ma_hello_server, start_link, []},
33 | Restart, Shutdown, Type, [ma_hello_server]},
34 |
35 | {ok, {SupFlags, [AChild]}}.
36 |
37 | %%%===================================================================
38 | %%% Internal functions
39 | %%%===================================================================
40 |
41 |
42 |
--------------------------------------------------------------------------------
/link_ex.erl:
--------------------------------------------------------------------------------
1 | -module(link_ex).
2 |
3 | -behaviour(gen_server).
4 |
5 | -export([start_link/0, ping/0]).
6 |
7 | %% gen_server callbacks
8 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
9 | terminate/2, code_change/3]).
10 |
11 | -define(SERVER, ?MODULE).
12 |
13 | -record(state, {}).
14 |
15 | start_link() ->
16 | gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
17 |
18 | ping() ->
19 | gen_server:cast(?SERVER, ping).
20 |
21 | init([]) ->
22 | {ok, #state{}}.
23 |
24 | handle_call(_Request, _From, State) ->
25 | Reply = ok,
26 | {reply, Reply, State}.
27 |
28 | handle_cast(ping, State) ->
29 | io:format("Got It!~n"),
30 | {noreply, State}.
31 |
32 | handle_info(timeout, State) ->
33 | {noreply, State}.
34 |
35 | terminate(_Reason, _State) ->
36 | ok.
37 |
38 | code_change(_OldVsn, State, _Extra) ->
39 | {ok, State}.
40 |
--------------------------------------------------------------------------------
/link_ex2.erl:
--------------------------------------------------------------------------------
1 | -module(link_ex2).
2 |
3 | -behaviour(gen_server).
4 |
5 | -export([start_link/0, ping/0, ping_error/0]).
6 |
7 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
8 | terminate/2, code_change/3]).
9 |
10 | -define(SERVER, ?MODULE).
11 |
12 | -record(state, {}).
13 |
14 | start_link() ->
15 | gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
16 |
17 | ping_error() ->
18 | gen_server:cast(?SERVER, ping_error).
19 |
20 | ping() ->
21 | gen_server:cast(?SERVER, ping).
22 |
23 | init([]) ->
24 | process_flag(trap_exit, true),
25 | link_ex:start_link(),
26 | {ok, #state{}}.
27 |
28 | handle_call(_Request, _From, State) ->
29 | Reply = ok,
30 | {reply, Reply, State}.
31 |
32 | handle_cast(ping, State) ->
33 | link_ex:ping(),
34 | {noreply, State};
35 | handle_cast(ping_error, State) ->
36 | link_ex ! a_message_i_dont_understand,
37 | {noreply, State}.
38 |
39 | handle_info({'EXIT', _Pid, _Reason}, State) ->
40 | io:format("Restarting link_ex~n"),
41 | link_ex:start_link(),
42 | {noreply, State}.
43 |
44 | terminate(_Reason, _State) ->
45 | ok.
46 |
47 | code_change(_OldVsn, State, _Extra) ->
48 | {ok, State}.
49 |
--------------------------------------------------------------------------------
/profile_ex.erl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% @author Martin Logan
3 | %%% @copyright (C) 2009, Martin Logan
4 | %%% @doc simple demonstration of profiling functionalty
5 | %%% @end
6 | %%%-------------------------------------------------------------------
7 | -module(profile_ex).
8 |
9 | %% API
10 | -export([run/0, run_with_fprof/0]).
11 |
12 | %%%===================================================================
13 | %%% API
14 | %%%===================================================================
15 |
16 | %%--------------------------------------------------------------------
17 | %% @doc start the system and ouput fprof stats to a file
18 | %% @spec () -> ok
19 | %% @end
20 | %%--------------------------------------------------------------------
21 | run_with_fprof() ->
22 | fprof:trace(start),
23 | run(),
24 | timer:sleep(5000),
25 | fprof:trace(stop),
26 | fprof:profile(),
27 | fprof:analyse({dest, atom_to_list(?MODULE)}).
28 |
29 |
30 | %%--------------------------------------------------------------------
31 | %% @doc start two nearly identical running processes
32 | %% @spec () -> ok
33 | %% @end
34 | %%--------------------------------------------------------------------
35 | run() ->
36 | spawn(fun() -> looper(1000) end),
37 | spawn(fun() -> funner(1000) end).
38 |
39 | %%%===================================================================
40 | %%% Internal functions
41 | %%%===================================================================
42 | looper(0) ->
43 | ok;
44 | looper(N) ->
45 | integer_to_list(N),
46 | looper(N - 1).
47 |
48 | funner(N) ->
49 | funner(fun(N_) -> integer_to_list(N_) end, N).
50 |
51 | funner(_Fun, 0) ->
52 | ok;
53 | funner(Fun, N) ->
54 | Fun(N),
55 | funner(Fun, N - 1).
56 |
--------------------------------------------------------------------------------
/simple_cache/_build.cfg:
--------------------------------------------------------------------------------
1 | project : {
2 | name : simple_cache
3 | vsn : "0.1.0.4"
4 | },
5 |
6 |
--------------------------------------------------------------------------------
/simple_cache/bin/simple_cache:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | PROG=$0
4 | PROG_DIR=$(cd `dirname $0`; pwd)
5 | test -h $0 && PROG=$(readlink $0)
6 | PREFIX=$(dirname $PROG_DIR)
7 |
8 | #### Fill in values for these variables ####
9 | REL_NAME=simple_cache
10 | REL_VSN=0.1.0.4
11 | ERTS_VSN=5.7.4
12 | INVOCATION_SUFFIX="-sname $@"
13 | ###########################################
14 |
15 |
16 | export ROOTDIR=$PREFIX
17 | ERTS_DIR=$ROOTDIR/erts-$ERTS_VSN
18 | export BINDIR=$ERTS_DIR/bin
19 | export EMU=beam
20 | export PROGNAME=erl
21 | export LD_LIBRARY_PATH=$ERTS_DIR/lib
22 |
23 | REL_DIR=$PREFIX/releases/$REL_NAME-$REL_VSN
24 |
25 | $BINDIR/erlexec -config $REL_DIR/sys.config -boot $REL_DIR/$REL_NAME $INVOCATION_SUFFIX
26 |
--------------------------------------------------------------------------------
/simple_cache/config/sys.config:
--------------------------------------------------------------------------------
1 | %%% -*- mode:erlang -*-
2 | %%% Warning - this config file *must* end with
3 |
4 | %% write log files to sasl_dir
5 | [
6 | {sasl,
7 | [
8 | {sasl_error_logger, {file, "/tmp/simple_cache.sasl_log"}}
9 | ]},
10 |
11 |
12 | %% (G)eneric (A)pplication (S)services config below here. This default config provides
13 | %% the release with log rotaion and trunctation.
14 | {gas,
15 | [
16 | {mod_specs, [{elwrap, {fs_elwrap_h, start_link}}]},
17 |
18 | % elwrap config.
19 | {err_log, "/tmp/simple_cache.err_log"},
20 | {err_log_wrap_info, {{err,5000000,10},{sasl,5000000,10}}},
21 | {err_log_tty, false}, % Log to the screen
22 |
23 | {home_file_path, ".simple_cache.config"}
24 | ]},
25 |
26 | %% Epkg provides package management on the local file system
27 | {epkg,
28 | [
29 | {low_erts_vsn, "5.5.5"},
30 | {high_erts_vsn, "5.6.4"},
31 | {preferred_erts_vsn, "5.6.3"},
32 | {is_local_boot, false}
33 | ]
34 | },
35 |
36 | {simple_cache,
37 | [
38 | ]
39 | }
40 | ].
41 |
--------------------------------------------------------------------------------
/simple_cache/lib/resource_discovery/ebin/resource_discovery.app:
--------------------------------------------------------------------------------
1 | %% This is the application resource file (.app file) for the resource_discovery,
2 | %% application.
3 | {application, resource_discovery,
4 | [{description, "A simple resource discovery system"},
5 | {vsn, "0.1.0"},
6 | {modules, [resource_discovery,
7 | rd_app,
8 | rd_sup,
9 | rd_server]},
10 | {registered,[rd_sup]},
11 | {applications, [kernel, stdlib]},
12 | {mod, {rd_app,[]}},
13 | {start_phases, []}]}.
14 |
15 |
--------------------------------------------------------------------------------
/simple_cache/lib/resource_discovery/src/rd_app.erl:
--------------------------------------------------------------------------------
1 | %%%----------------------------------------------------------------
2 | %%% @author Martin Logan & Eric Merritt
3 | %%% @doc
4 | %%%
5 | %%% @end
6 | %%% @copyright 2008 Martin Logan & Eric Merritt
7 | %%%----------------------------------------------------------------,
8 | -module(rd_app).
9 |
10 | -behaviour(application).
11 |
12 | %% Application callbacks
13 | -export([start/2, stop/1]).
14 |
15 | -define(WAIT_FOR_RESOURCES, 5000).
16 |
17 | %%%===================================================================
18 | %%% Application callbacks
19 | %%%===================================================================
20 |
21 | %%--------------------------------------------------------------------
22 | %% @private
23 | %% @doc
24 | %% This function is called whenever an application is started using
25 | %% application:start/[1,2], and should start the processes of the
26 | %% application. If the application is structured according to the OTP
27 | %% design principles as a supervision tree, this means starting the
28 | %% top supervisor of the tree.
29 | %%
30 | %% @spec start(StartType, StartArgs) -> {ok, Pid} |
31 | %% {ok, Pid, State} |
32 | %% {error, Reason}
33 | %% StartType = normal | {takeover, Node} | {failover, Node}
34 | %% StartArgs = term()
35 | %% @end
36 | %%--------------------------------------------------------------------
37 | start(_StartType, _StartArgs) ->
38 | case rd_sup:start_link() of
39 | {ok, Pid} ->
40 | {ok, Pid};
41 | Error ->
42 | Error
43 | end.
44 |
45 | %%--------------------------------------------------------------------
46 | %% @private
47 | %% @doc
48 | %% This function is called whenever an application has stopped. It
49 | %% is intended to be the opposite of Module:start/2 and should do
50 | %% any necessary cleaning up. The return value is ignored.
51 | %%
52 | %% @spec stop(State) -> void()
53 | %% @end
54 | %%--------------------------------------------------------------------
55 | stop(_State) ->
56 | ok.
57 |
58 | %%%===================================================================
59 | %%% Internal functions
60 | %%%===================================================================
61 |
--------------------------------------------------------------------------------
/simple_cache/lib/resource_discovery/src/rd_sup.erl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% @author Martin Logan
3 | %%% @copyright (C) 2009, Martin Logan
4 | %%% @doc
5 | %%% @end
6 | %%% Created : 11 Jan 2009 by Martin Logan
7 | %%%-------------------------------------------------------------------
8 | -module(rd_sup).
9 |
10 | -behaviour(supervisor).
11 |
12 | %% API
13 | -export([
14 | start_link/0
15 | ]).
16 |
17 | %% Supervisor callbacks
18 | -export([init/1]).
19 |
20 | -define(SERVER, ?MODULE).
21 |
22 | %%%===================================================================
23 | %%% API functions
24 | %%%===================================================================
25 |
26 | %%--------------------------------------------------------------------
27 | %% @doc
28 | %% Starts the supervisor
29 | %%
30 | %% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
31 | %% @end
32 | %%--------------------------------------------------------------------
33 | start_link() ->
34 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
35 |
36 | %%%===================================================================
37 | %%% Supervisor callbacks
38 | %%%===================================================================
39 |
40 | %%--------------------------------------------------------------------
41 | %% @private
42 | %% @doc
43 | %% Whenever a supervisor is started using supervisor:start_link/[2,3],
44 | %% this function is called by the new process to find out about
45 | %% restart strategy, maximum restart frequency and child
46 | %% specifications.
47 | %%
48 | %% @spec init(Args) -> {ok, {SupFlags, [ChildSpec]}} |
49 | %% ignore |
50 | %% {error, Reason}
51 | %% @end
52 | %%--------------------------------------------------------------------
53 | init([]) ->
54 | RestartStrategy = one_for_one,
55 | MaxRestarts = 1000,
56 | MaxSecondsBetweenRestarts = 3600,
57 |
58 | SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts},
59 |
60 | Restart = permanent,
61 | Shutdown = brutal_kill,
62 | Type = worker,
63 |
64 | ResourceDiscovery = {rd_server,
65 | {rd_server, start_link, []},
66 | Restart, Shutdown, Type, [rd_server]},
67 |
68 | {ok, {SupFlags, [ResourceDiscovery]}}.
69 |
70 | %%%===================================================================
71 | %%% Internal functions
72 | %%%===================================================================
73 |
--------------------------------------------------------------------------------
/simple_cache/lib/resource_discovery/src/resource_discovery.erl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% @author Martin Logan
3 | %%% @copyright (C) 2009, Martin Logan
4 | %%% @doc
5 | %%% The api for the resource discovery system.
6 | %%% @end
7 | %%% Created : 23 Apr 2009 by Martin Logan
8 | %%%-------------------------------------------------------------------
9 | -module(resource_discovery).
10 |
11 | -export([
12 | add_local_resource/2,
13 | add_target_resource_type/1,
14 | fetch_resources/1,
15 | delete_resource/2,
16 | trade_resources/0
17 | ]).
18 |
19 | %%%===================================================================
20 | %%% API
21 | %%%===================================================================
22 |
23 | %%-------------------------------------------------------------------
24 | %% @doc Add a resource that is present on the local node that a
25 | %% remote service will want to consume.
26 | %% @spec (Type::resource_type(), Resource::resource_instance()) -> ok
27 | %% @end
28 | %%-------------------------------------------------------------------
29 | add_local_resource(Type, Resource) ->
30 | rd_server:add_local_resource(Type, Resource).
31 |
32 | %%-------------------------------------------------------------------
33 | %% @doc Add a type of resource that you wish to cache any remote
34 | %% instances of.
35 | %% @spec (Type::resource_type()) -> ok
36 | %% @end
37 | %%-------------------------------------------------------------------
38 | add_target_resource_type(Type) ->
39 | rd_server:add_target_resource_type(Type).
40 |
41 | %%-------------------------------------------------------------------
42 | %% @doc Fetch all the resources for a particular resource instance
43 | %% type.
44 | %% @spec (Type::resource_type()) -> {ok, [Resource::resource_instance]} | error
45 | %% @end
46 | %%-------------------------------------------------------------------
47 | fetch_resources(Type) ->
48 | rd_server:fetch_resources(Type).
49 |
50 | %%-------------------------------------------------------------------
51 | %% @doc Delete a particular resource instance for a particular
52 | %% resource type.
53 | %% @spec (Type::resource_type(), Resource::resource_instance()) -> ok
54 | %% @end
55 | %%-------------------------------------------------------------------
56 | delete_resource(Type, Resource) ->
57 | rd_server:delete_resource(Type, Resource).
58 |
59 | %%-------------------------------------------------------------------
60 | %% @doc trade resources with all remote nodes
61 | %% @spec () -> ok
62 | %% @end
63 | %%-------------------------------------------------------------------
64 | trade_resources() ->
65 | rd_server:trade_resources().
66 |
--------------------------------------------------------------------------------
/simple_cache/lib/simple_cache/doc/overview.edoc:
--------------------------------------------------------------------------------
1 | @author Martin Logan & Eric Merritt
2 | @copyright 2008 Martin Logan & Eric Merritt
3 | @version {@vsn}
4 |
5 |
--------------------------------------------------------------------------------
/simple_cache/lib/simple_cache/ebin/simple_cache.app:
--------------------------------------------------------------------------------
1 | %% -*- mode: Erlang; fill-column: 75; comment-column: 50; -*-
2 | %% This is the application resource file for simple_cache
3 |
4 | {application, simple_cache,
5 | [{description, "A simple caching system"},
6 | {vsn, "0.1.0.3"},
7 | {modules, [simple_cache,
8 | sc_app,
9 | sc_sup,
10 | sc_element_sup,
11 | sc_store,
12 | sc_element,
13 | sc_event,
14 | sc_event_logger]},
15 | {registered, [sc_sup]},
16 | {applications, [kernel, sasl, stdlib]},
17 | {mod, {sc_app, []}}
18 | ]}.
19 |
--------------------------------------------------------------------------------
/simple_cache/lib/simple_cache/src/sc_element_sup.erl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% @author Martin Logan
3 | %%% @copyright (C) 2009, Martin Logan
4 | %%% @doc
5 | %%% A simple supervisor for the elements in the cache.
6 | %%% @end
7 | %%% Created : 11 Jan 2009 by Martin Logan
8 | %%%-------------------------------------------------------------------
9 | -module(sc_element_sup).
10 |
11 | -behaviour(supervisor).
12 |
13 | %% API
14 | -export([
15 | start_link/0,
16 | start_child/2
17 | ]).
18 |
19 | %% Supervisor callbacks
20 | -export([init/1]).
21 |
22 | -define(SERVER, ?MODULE).
23 |
24 | %%%===================================================================
25 | %%% API functions
26 | %%%===================================================================
27 |
28 | %%--------------------------------------------------------------------
29 | %% @doc
30 | %% Starts the supervisor
31 | %%
32 | %% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
33 | %% @end
34 | %%--------------------------------------------------------------------
35 | start_link() ->
36 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
37 |
38 | %%--------------------------------------------------------------------
39 | %% @doc
40 | %% Start a child process, an sc_element.
41 | %%
42 | %% @spec start_child(Value, LeaseTime) -> void()
43 | %% @end
44 | %%--------------------------------------------------------------------
45 | start_child(Value, LeaseTime) ->
46 | supervisor:start_child(?SERVER, [Value, LeaseTime]).
47 |
48 | %%%===================================================================
49 | %%% Supervisor callbacks
50 | %%%===================================================================
51 |
52 | %%--------------------------------------------------------------------
53 | %% @private
54 | %% @doc
55 | %% Whenever a supervisor is started using supervisor:start_link/[2,3],
56 | %% this function is called by the new process to find out about
57 | %% restart strategy, maximum restart frequency and child
58 | %% specifications.
59 | %%
60 | %% @spec init(Args) -> {ok, {SupFlags, [ChildSpec]}} |
61 | %% ignore |
62 | %% {error, Reason}
63 | %% @end
64 | %%--------------------------------------------------------------------
65 | init([]) ->
66 | RestartStrategy = simple_one_for_one,
67 | MaxRestarts = 0,
68 | MaxSecondsBetweenRestarts = 1,
69 |
70 | SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts},
71 |
72 | Restart = temporary,
73 | Shutdown = brutal_kill,
74 | Type = worker,
75 |
76 | AChild = {sc_element, {sc_element, start_link, []},
77 | Restart, Shutdown, Type, [sc_element]},
78 |
79 | {ok, {SupFlags, [AChild]}}.
80 |
81 | %%%===================================================================
82 | %%% Internal functions
83 | %%%===================================================================
84 |
--------------------------------------------------------------------------------
/simple_cache/lib/simple_cache/src/sc_event.erl:
--------------------------------------------------------------------------------
1 | %% -*- mode: Erlang; fill-column: 132; comment-column: 118; -*-
2 | %%%---------------------------------------------------------------------------
3 | %%% @author Eric Merritt
4 | %%% @doc
5 | %%% Provides a nice interface to the eventing system.
6 | %%% @end
7 | %%% @copyright (C) 2009, Erlware
8 | %%%-------------------------------------------------------------------
9 | -module(sc_event).
10 |
11 | %% API
12 | -export([start_link/0,
13 | lookup/1,
14 | create/2,
15 | replace/2,
16 | delete/1,
17 | delete_handler/2,
18 | add_handler/2]).
19 |
20 | -define(SERVER, ?MODULE).
21 |
22 |
23 | %%====================================================================
24 | %% API
25 | %%====================================================================
26 | %%--------------------------------------------------------------------
27 | %% @doc
28 | %% Starts the gen_event with the sc_event name.
29 | %% @spec () -> ok
30 | %% @end
31 | %%--------------------------------------------------------------------
32 | start_link() ->
33 | gen_event:start_link({local, ?SERVER}).
34 |
35 | %%--------------------------------------------------------------------
36 | %% @doc
37 | %% Event fired when a new element is inserted
38 | %% @spec (Key, Value) -> ok
39 | %% @end
40 | %%--------------------------------------------------------------------
41 | create(Key, Value) ->
42 | gen_event:notify(?SERVER, {create, {Key, Value}}).
43 |
44 | %%--------------------------------------------------------------------
45 | %% @doc
46 | %% Event fired when a key is looked up
47 | %% @spec (Key) -> ok
48 | %% @end
49 | %%--------------------------------------------------------------------
50 | lookup(Key) ->
51 | gen_event:notify(?SERVER, {lookup, Key}).
52 |
53 | %%--------------------------------------------------------------------
54 | %% @doc
55 | %% Event fired when a key is deleted
56 | %% @spec (Key) -> ok
57 | %% @end
58 | %%--------------------------------------------------------------------
59 | delete(Key) ->
60 | gen_event:notify(?SERVER, {delete, Key}).
61 |
62 | %%--------------------------------------------------------------------
63 | %% @doc
64 | %% Event fired when a key is replaced
65 | %% @spec (Key, Value) -> ok
66 | %% @end
67 | %%--------------------------------------------------------------------
68 | replace(Key, Value) ->
69 | gen_event:notify(?SERVER, {replace, {Key, Value}}).
70 |
71 | %%--------------------------------------------------------------------
72 | %% @doc
73 | %% Add a handler for this event system.
74 | %% @spec (Handler, Args) -> ok
75 | %% @end
76 | %%--------------------------------------------------------------------
77 | add_handler(Handler, Args) ->
78 | gen_event:add_handler(?SERVER, Handler, Args).
79 |
80 | %%--------------------------------------------------------------------
81 | %% @doc
82 | %% Delete a handler for this event system.
83 | %% @spec (Handler, Args) -> ok
84 | %% @end
85 | %%--------------------------------------------------------------------
86 | delete_handler(Handler, Args) ->
87 | gen_event:delete_handler(?SERVER, Handler, Args).
88 |
--------------------------------------------------------------------------------
/simple_cache/lib/simple_cache/src/sc_sup.erl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% @author Martin Logan
3 | %%% @copyright (C) 2009, Martin Logan
4 | %%% @doc
5 | %%% A simple supervisor for the elements in the cache.
6 | %%% @end
7 | %%% Created : 11 Jan 2009 by Martin Logan
8 | %%%-------------------------------------------------------------------
9 | -module(sc_sup).
10 |
11 | -behaviour(supervisor).
12 |
13 | %% API
14 | -export([start_link/0]).
15 |
16 | %% Supervisor callbacks
17 | -export([init/1]).
18 |
19 | -define(SERVER, ?MODULE).
20 |
21 | %%%===================================================================
22 | %%% API functions
23 | %%%===================================================================
24 |
25 | %%--------------------------------------------------------------------
26 | %% @doc
27 | %% Starts the supervisor
28 | %%
29 | %% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
30 | %% @end
31 | %%--------------------------------------------------------------------
32 | start_link() ->
33 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
34 |
35 | %%%===================================================================
36 | %%% Supervisor callbacks
37 | %%%===================================================================
38 |
39 | %%--------------------------------------------------------------------
40 | %% @private
41 | %% @doc
42 | %% Whenever a supervisor is started using supervisor:start_link/[2,3],
43 | %% this function is called by the new process to find out about
44 | %% restart strategy, maximum restart frequency and child
45 | %% specifications.
46 | %%
47 | %% @spec init(Args) -> {ok, {SupFlags, [ChildSpec]}} |
48 | %% ignore |
49 | %% {error, Reason}
50 | %% @end
51 | %%--------------------------------------------------------------------
52 | init([]) ->
53 | ElementSup = {sc_element_sup, {sc_element_sup, start_link, []},
54 | permanent, 2000, supervisor, [sc_element]},
55 |
56 | EventManager = {sc_event, {sc_event, start_link, []},
57 | permanent, 2000, worker, [sc_event]},
58 |
59 | Children = [ElementSup, EventManager],
60 | RestartStrategy = {one_for_one, 4, 3600},
61 | {ok, {RestartStrategy, Children}}.
62 |
63 | %%%===================================================================
64 | %%% Internal functions
65 | %%%===================================================================
66 |
--------------------------------------------------------------------------------
/simple_cache/lib/simple_cache/src/simple_cache.erl:
--------------------------------------------------------------------------------
1 | %%%-------------------------------------------------------------------
2 | %%% @author Martin Logan
3 | %%% @copyright (C) 2009, Martin Logan
4 | %%% @doc
5 | %%% The main programmers' API to the simple cache.
6 | %%% @end
7 | %%% Created : 11 Jan 2009 by Martin Logan
8 | %%%-------------------------------------------------------------------
9 | -module(simple_cache).
10 |
11 | %% API
12 | -export([
13 | insert/2,
14 | delete/1,
15 | lookup/1
16 | ]).
17 |
18 | -define(NODE, "simple_cache_hbase").
19 |
20 | %%%===================================================================
21 | %%% API
22 | %%%===================================================================
23 |
24 | %%--------------------------------------------------------------------
25 | %% @doc insert an element into the cache.
26 | %% @spec insert(Key, Value) -> ok
27 | %% where
28 | %% Key = term()
29 | %% Value = term()
30 | %% @end
31 | %%--------------------------------------------------------------------
32 | insert(Key, Value) ->
33 | case sc_store:lookup(Key) of
34 | {ok, Pid} ->
35 | simple_cache_hbase:put(?NODE, Key, Value),
36 | sc_event:replace(Key, Value),
37 | sc_element:replace(Pid, Value);
38 | {error, _Reason} ->
39 | {ok, Pid} = sc_element:create(Value),
40 | sc_store:insert(Key, Pid),
41 | sc_event:create(Key, Value)
42 | end.
43 |
44 | %%--------------------------------------------------------------------
45 | %% @doc lookup an element in the cache.
46 | %% @spec lookup(Key) -> {ok, Value} | {error, not_found}
47 | %% where
48 | %% Key = term()
49 | %% Value = term()
50 | %% @end
51 | %%--------------------------------------------------------------------
52 | lookup(Key) ->
53 | sc_event:lookup(Key),
54 | try
55 | case sc_store:lookup(Key) of
56 | {ok, Pid} -> {ok, Value} sc_element:fetch(Pid),
57 | {ok, Value};
58 | _ ->
59 | {ok, Value} = simple_cache_hbase:get(?NODE, Key),
60 | insert(Key, Value),
61 | {ok, Value}
62 | catch
63 | _Class:_Exception ->
64 | {error, not_found}
65 | end.
66 |
67 | %%--------------------------------------------------------------------
68 | %% @doc delete an element into the cache.
69 | %% @spec delete(Key) -> ok
70 | %% where
71 | %% Key = term()
72 | %% @end
73 | %%--------------------------------------------------------------------
74 | delete(Key) ->
75 | sc_event:delete(Key),
76 | case sc_store:lookup(Key) of
77 | {ok, Pid} ->
78 | simple_cache_hbase:delete(?NODE, Key),
79 | sc_element:delete(Pid);
80 | {error, _Reason} ->
81 | ok
82 | end.
83 |
--------------------------------------------------------------------------------
/simple_cache/release/simple_cache.boot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/simple_cache/release/simple_cache.boot
--------------------------------------------------------------------------------
/simple_cache/release/simple_cache.rel:
--------------------------------------------------------------------------------
1 | {release,{"simple_cache","0.1.0.0"},
2 | {erts,"5.7"},
3 | [{simple_cache,"0.1.0"},
4 | {kernel,"2.13"},
5 | {stdlib,"1.16"}]}.
6 |
--------------------------------------------------------------------------------
/tcp_rpc/README:
--------------------------------------------------------------------------------
1 | There are two ways to build this code. The first is to cd into each of the application directories in the lib directory and run the following command:
2 |
3 | erlc -o ./ebin -I ./include ./src/*.erl
4 |
5 | The second way to build this code is to install the Sinan build system. To install it go to Erlware.org and download the Faxien package management system. Once installed run
6 |
7 | /bin/faxien install-release sinan
8 |
9 | When that is finished just type sinan. This will build all the applications correctly and place them at _build/development/apps/-. More on Sinan and Faxien can be found in appendix C of this book.
10 |
--------------------------------------------------------------------------------
/tcp_rpc/lib/tcp_rpc/doc/.gitignore:
--------------------------------------------------------------------------------
1 | edoc-info
2 | stylesheet.css
3 | erlang.png
4 | *.html
5 |
--------------------------------------------------------------------------------
/tcp_rpc/lib/tcp_rpc/doc/overview.edoc:
--------------------------------------------------------------------------------
1 | @author Martin Logan
2 | @copyright 2008 Martin Logan
3 | @version {@vsn}
4 |
5 |
--------------------------------------------------------------------------------
/tcp_rpc/lib/tcp_rpc/ebin/.gitignore:
--------------------------------------------------------------------------------
1 | *.beam
2 |
--------------------------------------------------------------------------------
/tcp_rpc/lib/tcp_rpc/ebin/tcp_rpc.app:
--------------------------------------------------------------------------------
1 | %% -*- mode: Erlang; fill-column: 75; comment-column: 50; -*-
2 | %% This is the application resource file for tcp_rpc
3 |
4 | {application, tcp_rpc,
5 | [{description, "RPC server for Erlang and OTP in action"},
6 | {vsn, "0.1.0"},
7 | {modules, [tr_app,
8 | tr_sup,
9 | tr_server]},
10 | {registered, [tr_sup]},
11 | {applications, [kernel, stdlib]},
12 | {mod, {tr_app, []}}
13 | ]}.
14 |
--------------------------------------------------------------------------------
/tcp_rpc/lib/tcp_rpc/include/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/tcp_rpc/lib/tcp_rpc/include/.gitignore
--------------------------------------------------------------------------------
/tcp_rpc/lib/tcp_rpc/priv/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erlware/Erlang-and-OTP-in-Action-Source/3836eb0e44ce5126617cd06541346950bb31677c/tcp_rpc/lib/tcp_rpc/priv/.gitignore
--------------------------------------------------------------------------------
/tcp_rpc/lib/tcp_rpc/src/tr_app.erl:
--------------------------------------------------------------------------------
1 | %%%----------------------------------------------------------------
2 | %%% @author Martin Logan
3 | %%% @doc Application behaviour implementation for tcp_rpc.
4 | %%%
5 | %%% @copyright 2008 Martin Logan
6 | %%% @end
7 | %%%----------------------------------------------------------------,
8 | -module(tr_app).
9 |
10 | -behaviour(application).
11 |
12 | %% Application callbacks
13 | -export([
14 | start/2,
15 | stop/1
16 | ]).
17 |
18 | %%%===================================================================
19 | %%% Application callbacks
20 | %%%===================================================================
21 |
22 | %%--------------------------------------------------------------------
23 | %% @private
24 | %% @doc
25 | %% This function is called whenever an application is started using
26 | %% application:start/[1,2], and should start the processes of the
27 | %% application. If the application is structured according to the OTP
28 | %% design principles as a supervision tree, this means starting the
29 | %% top supervisor of the tree.
30 | %%
31 | %% @spec start(StartType, StartArgs) -> {ok, Pid} |
32 | %% {ok, Pid, State} |
33 | %% {error, Reason}
34 | %% StartType = normal | {takeover, Node} | {failover, Node}
35 | %% StartArgs = term()
36 | %% @end
37 | %%--------------------------------------------------------------------
38 | start(_StartType, _StartArgs) ->
39 | case tr_sup:start_link() of
40 | {ok, Pid} ->
41 | {ok, Pid};
42 | Other ->
43 | {error, Other}
44 | end.
45 |
46 | %%--------------------------------------------------------------------
47 | %% @private
48 | %% @doc
49 | %% This function is called whenever an application has stopped. It
50 | %% is intended to be the opposite of Module:start/2 and should do
51 | %% any necessary cleaning up. The return value is ignored.
52 | %%
53 | %% @spec stop(State) -> void()
54 | %% @end
55 | %%--------------------------------------------------------------------
56 | stop(_State) ->
57 | ok.
58 |
59 | %%%===================================================================
60 | %%% Internal functions
61 | %%%===================================================================
62 |
--------------------------------------------------------------------------------
/tcp_rpc/lib/tcp_rpc/src/tr_sup.erl:
--------------------------------------------------------------------------------
1 | %%%----------------------------------------------------------------
2 | %%% @author Martin Logan
3 | %%% @doc Root supervisor for `tcp_rpc'.
4 | %%% @copyright 2008 Martin Logan
5 | %%% @end
6 | %%%----------------------------------------------------------------
7 | -module(tr_sup).
8 |
9 | -behaviour(supervisor).
10 |
11 | %% API
12 | -export([start_link/0]).
13 |
14 | %% Supervisor callbacks
15 | -export([init/1]).
16 |
17 | -define(SERVER, ?MODULE).
18 |
19 | %%%===================================================================
20 | %%% API functions
21 | %%%===================================================================
22 |
23 | %%--------------------------------------------------------------------
24 | %% @doc
25 | %% Starts the supervisor
26 | %%
27 | %% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
28 | %% @end
29 | %%--------------------------------------------------------------------
30 | start_link() ->
31 | supervisor:start_link({local, ?SERVER}, ?MODULE, []).
32 |
33 | %%%===================================================================
34 | %%% Supervisor callbacks
35 | %%%===================================================================
36 |
37 | %%--------------------------------------------------------------------
38 | %% @private
39 | %% @doc
40 | %% Whenever a supervisor is started using supervisor:start_link/[2,3],
41 | %% this function is called by the new process to find out about
42 | %% restart strategy, maximum restart frequency and child
43 | %% specifications.
44 | %%
45 | %% @spec init(Args) -> {ok, {SupFlags, [ChildSpec]}} |
46 | %% ignore |
47 | %% {error, Reason}
48 | %% @end
49 | %%--------------------------------------------------------------------
50 | init([]) ->
51 | Server = {tr_server, {tr_server, start_link, []},
52 | permanent, 2000, worker, [tr_server]},
53 | Children = [Server],
54 | RestartStrategy = {one_for_one, 0, 1},
55 | {ok, {RestartStrategy, Children}}.
56 |
57 | %%%===================================================================
58 | %%% Internal functions
59 | %%%===================================================================
60 |
--------------------------------------------------------------------------------
/tcp_rpc/sinan.cfg:
--------------------------------------------------------------------------------
1 | project : {
2 | name : tcp_rpc
3 | vsn : "0.1.1.1"
4 | },
5 |
--------------------------------------------------------------------------------