summaryrefslogtreecommitdiff
path: root/src/rabbit.erl
diff options
context:
space:
mode:
authorTim Watson <tim@rabbitmq.com>2014-03-18 20:25:11 +0000
committerTim Watson <tim@rabbitmq.com>2014-03-18 20:25:11 +0000
commit87a1e06d6834e3740ac21034ce110b24ea693e9b (patch)
tree7142853c50b8176f762cbdf2d76addf48eba6a1f /src/rabbit.erl
parentec5086953c6b6739255041b9d30d420257f2ceba (diff)
parent19767b76842b73006d6840950d572a777077eadf (diff)
downloadrabbitmq-server-87a1e06d6834e3740ac21034ce110b24ea693e9b.tar.gz
Merge default into bug24926
Diffstat (limited to 'src/rabbit.erl')
-rw-r--r--src/rabbit.erl148
1 files changed, 120 insertions, 28 deletions
diff --git a/src/rabbit.erl b/src/rabbit.erl
index c2d7e29d..16106145 100644
--- a/src/rabbit.erl
+++ b/src/rabbit.erl
@@ -22,9 +22,9 @@
stop_and_halt/0, await_startup/0, status/0, is_running/0,
is_running/1, environment/0, rotate_logs/1, force_event_refresh/1,
start_fhc/0]).
-
+-export([run_boot_steps/0, load_steps/1, run_step/3]).
-export([start/2, stop/1]).
-
+-export([start_apps/1, stop_apps/1]).
-export([log_location/1]). %% for testing
%%---------------------------------------------------------------------------
@@ -312,8 +312,7 @@ start() ->
ok = ensure_working_log_handlers(),
rabbit_node_monitor:prepare_cluster_status_files(),
rabbit_mnesia:check_cluster_consistency(),
- ok = app_utils:start_applications(
- app_startup_order(), fun handle_app_error/2),
+ start_apps(app_startup_order()),
ok = log_broker_started(rabbit_plugins:active())
end).
@@ -331,19 +330,27 @@ boot() ->
rabbit_mnesia:check_cluster_consistency(),
Plugins = rabbit_plugins:setup(),
ToBeLoaded = Plugins ++ ?APPS,
- ok = app_utils:load_applications(ToBeLoaded),
- StartupApps = app_utils:app_dependency_order(ToBeLoaded,
- false),
- ok = app_utils:start_applications(
- StartupApps, fun handle_app_error/2),
+ start_apps(ToBeLoaded),
ok = log_broker_started(Plugins)
end).
-handle_app_error(App, {bad_return, {_MFA, {'EXIT', {Reason, _}}}}) ->
- throw({could_not_start, App, Reason});
+handle_app_error(Term) ->
+ fun(App, {bad_return, {_MFA, {'EXIT', {ExitReason, _}}}}) ->
+ throw({Term, App, ExitReason});
+ (App, Reason) ->
+ throw({Term, App, Reason})
+ end.
-handle_app_error(App, Reason) ->
- throw({could_not_start, App, Reason}).
+start_apps(Apps) ->
+ app_utils:load_applications(Apps),
+ StartupApps = app_utils:app_dependency_order(Apps, false),
+ case whereis(rabbit_boot) of
+ undefined -> run_boot_steps(Apps);
+ _ -> ok
+ end,
+ ok = app_utils:start_applications(StartupApps,
+ handle_app_error(could_not_start)),
+ StartupApps.
start_it(StartFun) ->
Marker = spawn_link(fun() -> receive stop -> ok end end),
@@ -369,12 +376,13 @@ start_it(StartFun) ->
end.
stop() ->
+ Apps = app_shutdown_order(),
case whereis(rabbit_boot) of
undefined -> ok;
- _ -> await_startup()
+ _ -> app_utils:wait_for_applications(Apps)
end,
rabbit_log:info("Stopping RabbitMQ~n"),
- ok = app_utils:stop_applications(app_shutdown_order()).
+ ok = app_utils:stop_applications(Apps).
stop_and_halt() ->
try
@@ -385,6 +393,29 @@ stop_and_halt() ->
end,
ok.
+stop_apps(Apps) ->
+ try
+ ok = app_utils:stop_applications(
+ Apps, handle_app_error(error_during_shutdown))
+ after
+ run_cleanup_steps(Apps),
+ [begin
+ {ok, Mods} = application:get_key(App, modules),
+ [begin
+ code:soft_purge(Mod),
+ code:delete(Mod),
+ false = code:is_loaded(Mod)
+ end || Mod <- Mods],
+ application:unload(App)
+ end || App <- Apps]
+ end.
+
+run_cleanup_steps(Apps) ->
+ [run_step(Name, Attributes, cleanup) ||
+ {App, Name, Attributes} <- load_steps(Apps),
+ lists:member(App, Apps)],
+ ok.
+
await_startup() ->
app_utils:wait_for_applications(app_startup_order()).
@@ -468,7 +499,7 @@ start(normal, []) ->
true = register(rabbit, self()),
print_banner(),
log_banner(),
- [ok = run_boot_step(Step) || Step <- boot_steps()],
+ run_boot_steps(),
{ok, SupPid};
Error ->
Error
@@ -496,29 +527,90 @@ app_shutdown_order() ->
%%---------------------------------------------------------------------------
%% boot step logic
-run_boot_step({_StepName, Attributes}) ->
- case [MFA || {mfa, MFA} <- Attributes] of
+run_boot_steps() ->
+ run_boot_steps([App || {App, _, _} <- application:loaded_applications()]).
+
+run_boot_steps(Apps) ->
+ Steps = load_steps(Apps),
+ [ok = run_step(StepName, Attributes, mfa) ||
+ {_, StepName, Attributes} <- Steps],
+ ok.
+
+run_step(StepName, Attributes, AttributeName) ->
+ case [MFA || {Key, MFA} <- Attributes,
+ Key =:= AttributeName] of
[] ->
ok;
MFAs ->
[try
apply(M,F,A)
of
- ok -> ok;
- {error, Reason} -> boot_error(Reason, not_available)
+ ok -> ok;
+ {error, Reason} -> boot_error({boot_step, StepName, Reason},
+ not_available)
catch
- _:Reason -> boot_error(Reason, erlang:get_stacktrace())
+ _:Reason -> boot_error({boot_step, StepName, Reason},
+ erlang:get_stacktrace())
end || {M,F,A} <- MFAs],
ok
end.
-boot_steps() ->
- sort_boot_steps(rabbit_misc:all_module_attributes(rabbit_boot_step)).
+load_steps(BaseApps) ->
+ Apps = BaseApps -- [App || {App, _, _} <- rabbit_misc:which_applications()],
+ StepAttrs = rabbit_misc:all_module_attributes(rabbit_boot_step),
+ {AllSteps, StepsDict} =
+ lists:foldl(
+ fun({AppName, Mod, Steps}, {AccSteps, AccDict}) ->
+ {[{Mod, {AppName, Steps}}|AccSteps],
+ lists:foldl(
+ fun({StepName, _}, Acc) ->
+ dict:store(StepName, AppName, Acc)
+ end, AccDict, Steps)}
+ end, {[], dict:new()}, StepAttrs),
+ Steps = lists:foldl(filter_steps(Apps, StepsDict), [], AllSteps),
+ sort_boot_steps(lists:usort(Steps)).
+
+filter_steps(Apps, Dict) ->
+ fun({Mod, {AppName, Steps}}, Acc) ->
+ Steps2 = [begin
+ Filtered = lists:foldl(filter_attrs(Apps, Dict),
+ [], Attrs),
+ {Step, Filtered}
+ end || {Step, Attrs} <- Steps,
+ filter_app(Apps, Dict, Step)],
+ [{Mod, {AppName, Steps2}}|Acc]
+ end.
+
+filter_app(Apps, Dict, Step) ->
+ case dict:find(Step, Dict) of
+ {ok, App} -> lists:member(App, Apps);
+ error -> false
+ end.
+
+filter_attrs(Apps, Dict) ->
+ fun(Attr={Type, Other}, AccAttrs) when Type =:= requires orelse
+ Type =:= enables ->
+ %% If we don't know about a dependency, we allow it through,
+ %% since we don't *know* that it should be ignored. If, on
+ %% the other hand, we recognise a dependency then we _only_
+ %% include it (i.e., the requires/enables attribute itself)
+ %% if the referenced step comes from one of the Apps we're
+ %% actively working with at this point.
+ case dict:find(Other, Dict) of
+ error -> [Attr | AccAttrs];
+ {ok, App} -> case lists:member(App, Apps) of
+ true -> [Attr | AccAttrs];
+ false -> AccAttrs
+ end
+ end;
+ (Attr, AccAttrs) ->
+ [Attr | AccAttrs]
+ end.
-vertices(_Module, Steps) ->
- [{StepName, {StepName, Atts}} || {StepName, Atts} <- Steps].
+vertices(_Module, {AppName, Steps}) ->
+ [{StepName, {AppName, StepName, Atts}} || {StepName, Atts} <- Steps].
-edges(_Module, Steps) ->
+edges(_Module, {_AppName, Steps}) ->
[case Key of
requires -> {StepName, OtherStep};
enables -> {OtherStep, StepName}
@@ -541,8 +633,8 @@ sort_boot_steps(UnsortedSteps) ->
digraph:delete(G),
%% Check that all mentioned {M,F,A} triples are exported.
case [{StepName, {M,F,A}} ||
- {StepName, Attributes} <- SortedSteps,
- {mfa, {M,F,A}} <- Attributes,
+ {_App, StepName, Attributes} <- SortedSteps,
+ {mfa, {M,F,A}} <- Attributes,
not erlang:function_exported(M, F, length(A))] of
[] -> SortedSteps;
MissingFunctions -> basic_boot_error(