diff options
Diffstat (limited to 'src/dynomite_app.erl')
-rw-r--r-- | src/dynomite_app.erl | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/src/dynomite_app.erl b/src/dynomite_app.erl new file mode 100644 index 00000000..6ee0b978 --- /dev/null +++ b/src/dynomite_app.erl @@ -0,0 +1,145 @@ +%%%------------------------------------------------------------------- +%%% File: dynomite.erl +%%% @author Cliff Moon <cliff@powerset.com> [] +%%% @copyright 2008 Cliff Moon +%%% @doc +%%% +%%% @end +%%% +%%% @since 2008-06-27 by Cliff Moon +%%%------------------------------------------------------------------- +-module(dynomite_app). +-author('cliff@powerset.com'). +-author('brad@cloudant.com'). + +-behaviour(application). + +-include("../include/config.hrl"). +-include("../../couch/src/couch_db.hrl"). + +%% Application callbacks +-export([start/2, stop/1]). + +-define(APPS, [crypto,sasl,mochiweb]). +-define(DEFAULT_CLUSTER_URL, "http://localhost:5984/_cluster"). + +%%==================================================================== +%% Application callbacks +%%==================================================================== +%%-------------------------------------------------------------------- +%% @spec start(Type, StartArgs) -> {ok, Pid} | +%% {ok, Pid, State} | +%% {error, Reason} +%% @doc This function is called whenever an application +%% is started using application:start/1,2, and should start the processes +%% of the application. If the application is structured according to the +%% OTP design principles as a supervision tree, this means starting the +%% top supervisor of the tree. +%% @end +%%-------------------------------------------------------------------- + + +%% @doc start required apps, join cluster, start dynomite supervisor +start(_Type, _StartArgs) -> + % get process_dict hack for startargs (i.e. not from .app file) + PdStartArgs = case erase(startargs) of + undefined -> + []; + Args -> + Args + end, + + % start required apps + State = start_apps(), + + % start dynomite supervisor + ok = start_node(), + case dynomite_sup:start_link(PdStartArgs) of + {ok, Supervisor} -> + {ok, Supervisor, State}; + Error -> + Error + end. + + +%%-------------------------------------------------------------------- +%% @spec stop(State) -> void() +%% @doc This function is called whenever an application +%% has stopped. It is intended to be the opposite of Module:start/2 and +%% should do any necessary cleaning up. The return value is ignored. +%% @end +%%-------------------------------------------------------------------- +stop({_, Sup}) -> + showroom_log:message(alert, "dynomite application stopped", []), + exit(Sup, normal), + ok. + + +%%==================================================================== +%% Internal functions +%%==================================================================== + +start_apps() -> + Fun = fun(App, AccIn) -> + Result = case application:start(App) of + ok -> + App; + {error, {already_started, App}} -> + nil; + _Error -> + exit(app_start_fail) + end, + if + Result =/= nil -> [App|AccIn]; + true -> AccIn + end + end, + lists:foldl(Fun, [], ?APPS). + + +%% @spec start_node() -> ok | {error, Reason} +%% @doc start this node (join to dist. erlang cluster) +start_node() -> + PingUrl = couch_config:get("cluster","ping", ?DEFAULT_CLUSTER_URL), + ?LOG_DEBUG("PingUrl: ~p", [PingUrl]), + Result = case get_pingnode(PingUrl, 1) of + {ok, PingNode} -> + join(PingNode); + _ -> + ?LOG_INFO("No pingnode found. Becoming single-node cluster", []) + end, + couch_api:create_db(<<"users">>, []), % all nodes have local 'users' db + Result. + + +%% @spec get_pingnode(Url::string(), Retries::int()) -> node() | +%% {error, Reason} +%% @doc make a http get call to Url to get cluster information +get_pingnode(Url, Retries) -> + try couch_rep_httpc:request(#http_db{url=Url, retries=Retries}) of + {[{<<"ping_node">>, Node}]} -> + {ok, list_to_atom(binary_to_list(Node))}; + _ -> + {error, pingnode_not_found} + catch + _:_ -> + {error, pingnode_not_found} + end. + + +join(PingNode) -> + if + node() =:= PingNode -> + ok; % we must be brain, so we'll take over the world + true -> + case net_adm:ping(PingNode) of + pong -> + % there is a cluster, we just joined it + ?LOG_DEBUG("ping successful, we're in.", []), + timer:sleep(1000); %% grr, what a hack, erlang. rly? + pang -> + ?LOG_ERROR("ping not successful.", []), + throw({cluster_error, ?l2b("cluster ping not successful")}) + end + end, + ok. |