1%% This Source Code Form is subject to the terms of the Mozilla Public 2%% License, v. 2.0. If a copy of the MPL was not distributed with this 3%% file, You can obtain one at https://mozilla.org/MPL/2.0/. 4%% 5%% Copyright (c) 2007-2021 VMware, Inc. or its affiliates. All rights reserved. 6%% 7 8-module(app_utils). 9 10-export([load_applications/1, 11 start_applications/1, start_applications/2, start_applications/3, 12 stop_applications/1, stop_applications/2, app_dependency_order/2, 13 app_dependencies/1]). 14 15-type error_handler() :: fun((atom(), any()) -> 'ok' | no_return()). 16-type restart_type() :: 'permanent' | 'transient' | 'temporary'. 17 18-spec load_applications([atom()]) -> 'ok'. 19-spec start_applications([atom()]) -> 'ok'. 20-spec stop_applications([atom()]) -> 'ok'. 21-spec start_applications([atom()], error_handler()) -> 'ok'. 22-spec start_applications([atom()], error_handler(), #{atom() => restart_type()}) -> 'ok'. 23-spec stop_applications([atom()], error_handler()) -> 'ok'. 24-spec app_dependency_order([atom()], boolean()) -> [digraph:vertex()]. 25-spec app_dependencies(atom()) -> [atom()]. 26-spec failed_to_start_app(atom(), any()) -> no_return(). 27-spec failed_to_stop_app(atom(), any()) -> no_return(). 28 29%%--------------------------------------------------------------------------- 30%% Public API 31 32load_applications(Apps) -> 33 load_applications(queue:from_list(Apps), sets:new()), 34 ok. 35 36start_applications(Apps) -> 37 start_applications( 38 Apps, fun failed_to_start_app/2). 39 40stop_applications(Apps) -> 41 stop_applications( 42 Apps, fun failed_to_stop_app/2). 43 44failed_to_start_app(App, Reason) -> 45 throw({error, {cannot_start_application, App, Reason}}). 46 47failed_to_stop_app(App, Reason) -> 48 throw({error, {cannot_stop_application, App, Reason}}). 49 50start_applications(Apps, ErrorHandler) -> 51 start_applications(Apps, ErrorHandler, #{}). 52 53start_applications(Apps, ErrorHandler, RestartTypes) -> 54 manage_applications(fun lists:foldl/3, 55 fun(App) -> ensure_all_started(App, RestartTypes) end, 56 fun application:stop/1, 57 already_started, 58 ErrorHandler, 59 Apps). 60 61stop_applications(Apps, ErrorHandler) -> 62 manage_applications(fun lists:foldr/3, 63 fun(App) -> 64 rabbit_log:info("Stopping application '~s'", [App]), 65 application:stop(App) 66 end, 67 fun(App) -> ensure_all_started(App, #{}) end, 68 not_started, 69 ErrorHandler, 70 Apps). 71 72app_dependency_order(RootApps, StripUnreachable) -> 73 {ok, G} = rabbit_misc:build_acyclic_graph( 74 fun ({App, _Deps}) -> [{App, App}] end, 75 fun ({App, Deps}) -> [{Dep, App} || Dep <- Deps] end, 76 [{App, app_dependencies(App)} || 77 {App, _Desc, _Vsn} <- application:loaded_applications()]), 78 try 79 case StripUnreachable of 80 true -> digraph:del_vertices(G, digraph:vertices(G) -- 81 digraph_utils:reachable(RootApps, G)); 82 false -> ok 83 end, 84 digraph_utils:topsort(G) 85 after 86 true = digraph:delete(G) 87 end. 88 89%%--------------------------------------------------------------------------- 90%% Private API 91 92load_applications(Worklist, Loaded) -> 93 case queue:out(Worklist) of 94 {empty, _WorkList} -> 95 ok; 96 {{value, App}, Worklist1} -> 97 case sets:is_element(App, Loaded) of 98 true -> load_applications(Worklist1, Loaded); 99 false -> case application:load(App) of 100 ok -> ok; 101 {error, {already_loaded, App}} -> ok; 102 Error -> throw(Error) 103 end, 104 load_applications( 105 queue:join(Worklist1, 106 queue:from_list(app_dependencies(App))), 107 sets:add_element(App, Loaded)) 108 end 109 end. 110 111app_dependencies(App) -> 112 case application:get_key(App, applications) of 113 undefined -> []; 114 {ok, Lst} -> Lst 115 end. 116 117manage_applications(Iterate, Do, Undo, SkipError, ErrorHandler, Apps) -> 118 Iterate(fun (App, Acc) -> 119 case Do(App) of 120 ok -> [App | Acc]; 121 {ok, []} -> Acc; 122 {ok, [App]} -> [App | Acc]; 123 {ok, StartedApps} -> StartedApps ++ Acc; 124 {error, {SkipError, _}} -> Acc; 125 {error, Reason} -> 126 lists:foreach(Undo, Acc), 127 ErrorHandler(App, Reason) 128 end 129 end, [], Apps), 130 ok. 131 132%% Stops the Erlang VM when the rabbit application stops abnormally 133%% i.e. message store reaches its restart limit 134default_restart_type(rabbit) -> transient; 135default_restart_type(_) -> temporary. 136 137%% Copyright Ericsson AB 1996-2016. All Rights Reserved. 138%% 139%% Code originally from Erlang/OTP source lib/kernel/src/application.erl 140%% and modified to use RestartTypes map 141%% 142ensure_all_started(Application, RestartTypes) -> 143 case ensure_all_started(Application, RestartTypes, []) of 144 {ok, Started} -> 145 {ok, lists:reverse(Started)}; 146 {error, Reason, Started} -> 147 _ = [application:stop(App) || App <- Started], 148 {error, Reason} 149 end. 150 151ensure_all_started(Application, RestartTypes, Started) -> 152 RestartType = maps:get(Application, RestartTypes, default_restart_type(Application)), 153 case application:start(Application, RestartType) of 154 ok -> 155 {ok, [Application | Started]}; 156 {error, {already_started, Application}} -> 157 {ok, Started}; 158 {error, {not_started, Dependency}} -> 159 case ensure_all_started(Dependency, RestartTypes, Started) of 160 {ok, NewStarted} -> 161 ensure_all_started(Application, RestartTypes, NewStarted); 162 Error -> 163 Error 164 end; 165 {error, Reason} -> 166 {error, {Application, Reason}, Started} 167 end. 168