summaryrefslogtreecommitdiff
path: root/src/dynomite_app.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/dynomite_app.erl')
-rw-r--r--src/dynomite_app.erl145
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.