1 |
|
-module(mongoose_modules). |
2 |
|
|
3 |
|
-include("mongoose.hrl"). |
4 |
|
|
5 |
|
%% API |
6 |
|
-export([start/0, stop/0]). |
7 |
|
|
8 |
|
%% Module management utilities for tests |
9 |
|
-export([replace_modules/3, ensure_stopped/2, ensure_started/3]). |
10 |
|
-ignore_xref([replace_modules/3, ensure_stopped/2, ensure_started/3]). |
11 |
|
|
12 |
|
-type module_opts() :: gen_mod:module_opts(). |
13 |
|
-type module_map() :: gen_mod_deps:module_map(). |
14 |
|
-type module_list() :: gen_mod_deps:module_list(). |
15 |
|
|
16 |
|
%% @doc Start all configured modules in the dependency order. |
17 |
|
-spec start() -> 'ok'. |
18 |
|
start() -> |
19 |
33 |
[gen_mod:start_module(HostType, Mod, Opts) || HostType <- ?ALL_HOST_TYPES, |
20 |
184 |
{Mod, Opts} <- sorted_modules(HostType)], |
21 |
33 |
ok. |
22 |
|
|
23 |
|
%% @doc Stop all configured modules in the reverse dependency order |
24 |
|
%% to avoid stopping modules which have other modules dependent on them. |
25 |
|
-spec stop() -> 'ok'. |
26 |
|
stop() -> |
27 |
33 |
[gen_mod:stop_module(HostType, Mod) || HostType <- ?ALL_HOST_TYPES, |
28 |
184 |
{Mod, _} <- lists:reverse(sorted_modules(HostType))], |
29 |
33 |
ok. |
30 |
|
|
31 |
|
%% @doc Replace modules at runtime - only for testing and debugging. |
32 |
|
%% Running modules from ToStop are stopped and modules from ToEnsure are (re)started when needed. |
33 |
|
%% Unused dependencies are stopped if no running modules depend on them anymore. |
34 |
|
%% To prevent an unused dependency from being stopped, you need to include it in ToEnsure. |
35 |
|
-spec replace_modules(mongooseim:host_type(), [module()], module_map()) -> ok. |
36 |
|
replace_modules(HostType, ToStop, ToEnsure) -> |
37 |
1787 |
Current = get_modules(HostType), |
38 |
1787 |
Old = maps:with(ToStop ++ maps:keys(ToEnsure), Current), |
39 |
1787 |
OldWithDeps = gen_mod_deps:resolve_deps(HostType, Old), |
40 |
1787 |
SortedOldWithDeps = gen_mod_deps:sort_deps(HostType, OldWithDeps), |
41 |
1787 |
WithoutOld = maps:without(maps:keys(OldWithDeps), Current), |
42 |
1787 |
WithNew = maps:merge(WithoutOld, ToEnsure), |
43 |
1787 |
Target = gen_mod_deps:resolve_deps(HostType, WithNew), |
44 |
|
|
45 |
|
%% Stop each affected module if it is not in Target (stop deps first) |
46 |
1787 |
[ensure_stopped(HostType, Module) || {Module, _} <- lists:reverse(SortedOldWithDeps), |
47 |
10902 |
not maps:is_key(Module, Target)], |
48 |
|
|
49 |
|
%% Ensure each module from Target |
50 |
1787 |
[ensure_started(HostType, Module, Opts) || |
51 |
1787 |
{Module, Opts} <- gen_mod_deps:sort_deps(HostType, Target)], |
52 |
1786 |
ok. |
53 |
|
|
54 |
|
%% @doc Make sure the module is stopped. |
55 |
|
-spec ensure_stopped(mongooseim:host_type(), module()) -> |
56 |
|
already_stopped | {stopped, module_opts()}. |
57 |
|
ensure_stopped(HostType, Module) -> |
58 |
1337 |
Modules = get_modules(HostType), |
59 |
1337 |
case maps:find(Module, Modules) of |
60 |
|
error -> |
61 |
155 |
already_stopped; |
62 |
|
{_, Opts} -> |
63 |
1182 |
stop_module(HostType, Module, Modules), |
64 |
1182 |
{stopped, Opts} |
65 |
|
end. |
66 |
|
|
67 |
|
%% @doc Make sure the module is running with the provided options. |
68 |
|
-spec ensure_started(mongooseim:host_type(), module(), module_opts()) -> |
69 |
|
already_started | {started, term()} | {restarted, module_opts(), term()}. |
70 |
|
ensure_started(HostType, Module, Opts) -> |
71 |
31737 |
Modules = get_modules(HostType), |
72 |
31737 |
case maps:find(Module, Modules) of |
73 |
|
error -> |
74 |
1189 |
{ok, Result} = start_module(HostType, Module, Opts, Modules), |
75 |
1188 |
{started, Result}; |
76 |
|
{_, Opts} -> |
77 |
30285 |
already_started; |
78 |
|
{_, PrevOpts} -> |
79 |
263 |
stop_module(HostType, Module, Modules), |
80 |
263 |
{ok, Result} = start_module(HostType, Module, Opts, Modules), |
81 |
263 |
{restarted, PrevOpts, Result} |
82 |
|
end. |
83 |
|
|
84 |
|
%% Helpers |
85 |
|
|
86 |
|
-spec start_module(mongooseim:host_type(), module(), module_opts(), module_map()) -> {ok, term()}. |
87 |
|
start_module(HostType, Module, Opts, Modules) -> |
88 |
1452 |
set_modules(HostType, Modules#{Module => Opts}), |
89 |
1452 |
try |
90 |
1452 |
gen_mod:start_module(HostType, Module, Opts) |
91 |
|
catch |
92 |
|
C:R:S -> |
93 |
1 |
set_modules(HostType, Modules), |
94 |
1 |
erlang:raise(C, R, S) |
95 |
|
end. |
96 |
|
|
97 |
|
-spec stop_module(mongooseim:host_type(), module(), module_map()) -> ok. |
98 |
|
stop_module(HostType, Module, Modules) -> |
99 |
1445 |
gen_mod:stop_module(HostType, Module), |
100 |
1445 |
set_modules(HostType, maps:remove(Module, Modules)). |
101 |
|
|
102 |
|
-spec sorted_modules(mongooseim:host_type()) -> module_list(). |
103 |
|
sorted_modules(HostType) -> |
104 |
368 |
gen_mod_deps:sort_deps(HostType, get_modules(HostType)). |
105 |
|
|
106 |
|
-spec get_modules(mongooseim:host_type()) -> module_map(). |
107 |
|
get_modules(HostType) -> |
108 |
35229 |
mongoose_config:get_opt({modules, HostType}). |
109 |
|
|
110 |
|
-spec set_modules(mongooseim:host_type(), module_map()) -> ok. |
111 |
|
set_modules(HostType, Modules) -> |
112 |
2898 |
mongoose_config:set_opt({modules, HostType}, Modules). |