├── 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 ++ "" 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 | --------------------------------------------------------------------------------