summaryrefslogtreecommitdiff
path: root/src/couchdb/couch_external_manager.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/couchdb/couch_external_manager.erl')
-rw-r--r--src/couchdb/couch_external_manager.erl97
1 files changed, 97 insertions, 0 deletions
diff --git a/src/couchdb/couch_external_manager.erl b/src/couchdb/couch_external_manager.erl
new file mode 100644
index 00000000..d86013eb
--- /dev/null
+++ b/src/couchdb/couch_external_manager.erl
@@ -0,0 +1,97 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couch_external_manager).
+-behaviour(gen_server).
+
+-export([start_link/0, execute/8, config_change/2]).
+-export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2]).
+
+-include("couch_db.hrl").
+
+start_link() ->
+ gen_server:start_link({local, couch_external_manager}, couch_external_manager, [], []).
+
+execute(UrlName, Db, Verb, Path, Query, Body, Post, Cookie) ->
+ Pid = gen_server:call(couch_external_manager, {get, UrlName}),
+ case Pid of
+ {error, Reason} ->
+ Reason;
+ _ ->
+ couch_external_server:execute(Pid, Db, Verb, Path, Query, Body, Post, Cookie)
+ end.
+
+config_change("external", UrlName) ->
+ gen_server:call(couch_external_manager, {config, UrlName}).
+
+% gen_server API
+
+init([]) ->
+ Handlers = ets:new(couch_external_manager_handlers, [set, private]),
+ lists:foreach(fun({UrlName, Command}) ->
+ {ok, Pid} = couch_external_server:start_link(UrlName, Command),
+ true = ets:insert(Handlers, {UrlName, Pid})
+ end, couch_config:get("external")),
+ couch_config:register(fun config_change/2),
+ {ok, Handlers}.
+
+terminate(_Reason, Handlers) ->
+ ets:foldl(fun({_UrlName, Pid}, nil) ->
+ couch_external_server:stop(Pid),
+ nil
+ end, nil, Handlers),
+ ok.
+
+handle_call({get, UrlName}, _From, Handlers) ->
+ Resp = case ets:lookup(Handlers, UrlName) of
+ [{UrlName, Pid}] ->
+ Pid;
+ [] ->
+ Mesg = lists:flatten(io_lib:format("No server configured for ~p.", [UrlName])),
+ {error, {unknown_external_server, Mesg}}
+ end,
+ {reply, Resp, Handlers};
+handle_call({config, UrlName}, _From, Handlers) ->
+ % A newly added handler and a handler that had it's command
+ % changed are treated exactly the same.
+
+ % Shutdown the old handler.
+ case ets:lookup(Handlers, UrlName) of
+ [{UrlName, Pid}] ->
+ couch_external_server:stop(Pid);
+ _ ->
+ ok
+ end,
+ case couch_config:get("external", UrlName, nil) of
+ % Handler no longer exists
+ nil ->
+ ok;
+ % New handler start up.
+ Command ->
+ {ok, NewPid} = couch_external_server:start_link(UrlName, Command),
+ true = ets:insert(Handlers, {Command, NewPid})
+ end,
+ {reply, ok, Handlers}.
+
+handle_cast(_Whatever, State) ->
+ {noreply, State}.
+
+handle_info({'EXIT', Reason, Pid}, Handlers) ->
+ ?LOG_DEBUG("External server ~p died. (reason: ~p)", [Pid, Reason]),
+ % Remove Pid from the handlers table so we don't try closing
+ % it a second time in terminate/2.
+ ets:match_delete(Handlers, {'_', Pid}),
+ {stop, Handlers}.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+