forked from erlware/Erlang-and-OTP-in-Action-Source
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathresource_discovery.erl
118 lines (95 loc) · 3.5 KB
/
resource_discovery.erl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
-module(resource_discovery).
-behaviour(gen_server).
-export([
start_link/0,
add_target_resource_type/1,
add_local_resource/2,
fetch_resources/1,
trade_resources/0
]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-record(state, {target_resource_types,
local_resource_tuples,
found_resource_tuples}).
%% API
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
add_target_resource_type(Type) ->
gen_server:cast(?SERVER, {add_target_resource_type, Type}).
add_local_resource(Type, Resource) ->
gen_server:cast(?SERVER, {add_local_resource, {Type, Resource}}).
fetch_resources(Type) ->
gen_server:call(?SERVER, {fetch_resources, Type}).
trade_resources() ->
gen_server:cast(?SERVER, trade_resources).
%% Callbacks
init([]) ->
{ok, #state{target_resource_types = [],
local_resource_tuples = dict:new(),
found_resource_tuples = dict:new()}}.
handle_call({fetch_resources, Type}, _From, State) ->
{reply, dict:find(Type, State#state.found_resource_tuples), State}.
handle_cast({add_target_resource_type, Type}, State) ->
TargetTypes = State#state.target_resource_types,
NewTargetTypes = [Type | lists:delete(Type, TargetTypes)],
{noreply, State#state{target_resource_types = NewTargetTypes}};
handle_cast({add_local_resource, {Type, Resource}}, State) ->
ResourceTuples = State#state.local_resource_tuples,
NewResourceTuples = add_resource(Type, Resource, ResourceTuples),
{noreply, State#state{local_resource_tuples = NewResourceTuples}};
handle_cast(trade_resources, State) ->
ResourceTuples = State#state.local_resource_tuples,
AllNodes = [node() | nodes()],
lists:foreach(
fun(Node) ->
gen_server:cast({?SERVER, Node},
{trade_resources, {node(), ResourceTuples}})
end,
AllNodes),
{noreply, State};
handle_cast({trade_resources, {ReplyTo, Remotes}},
#state{local_resource_tuples = Locals,
target_resource_types = TargetTypes,
found_resource_tuples = OldFound} = State) ->
FilteredRemotes = resources_for_types(TargetTypes, Remotes),
NewFound = add_resources(FilteredRemotes, OldFound),
case ReplyTo of
noreply ->
ok;
_ ->
gen_server:cast({?SERVER, ReplyTo},
{trade_resources, {noreply, Locals}})
end,
{noreply, State#state{found_resource_tuples = NewFound}}.
handle_info(ok = _Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% Utilities
add_resources([{Type, Identifier}|T], Dict) ->
add_resources(T, add_resource(Type, Identifier, Dict));
add_resources([], Dict) ->
Dict.
add_resource(Type, Resource, Dict) ->
case dict:find(Type, Dict) of
{ok, ResourceList} ->
NewList = [Resource | lists:delete(Resource, ResourceList)],
dict:store(Type, NewList, Dict);
error ->
dict:store(Type, [Resource], Dict)
end.
resources_for_types(Types, ResourceTuples) ->
Fun =
fun(Type, Acc) ->
case dict:find(Type, ResourceTuples) of
{ok, List} ->
[{Type, Resource} || Resource <- List] ++ Acc;
error ->
Acc
end
end,
lists:foldl(Fun, [], Types).