You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ii...@apache.org on 2016/08/23 22:04:25 UTC

[4/6] config commit: updated refs/heads/master to 47e5f70

Add config:subscribe_for_changes/1

Add a new gen event handler which sends plain events to the Subscriber.

COUCHDB-3102


Project: http://git-wip-us.apache.org/repos/asf/couchdb-config/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-config/commit/01c34c09
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-config/tree/01c34c09
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-config/diff/01c34c09

Branch: refs/heads/master
Commit: 01c34c09aadb57b2a4cf375b145ef084a35314c0
Parents: 686d76b
Author: ILYA Khlopotov <ii...@ca.ibm.com>
Authored: Thu Aug 18 12:58:04 2016 -0700
Committer: ILYA Khlopotov <ii...@ca.ibm.com>
Committed: Fri Aug 19 12:56:35 2016 -0700

----------------------------------------------------------------------
 src/config.erl          |  4 ++
 src/config_notifier.erl | 68 +++++++++++++++++++++++++++++++
 test/config_tests.erl   | 96 +++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 167 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-config/blob/01c34c09/src/config.erl
----------------------------------------------------------------------
diff --git a/src/config.erl b/src/config.erl
index c0d0446..9449b1e 100644
--- a/src/config.erl
+++ b/src/config.erl
@@ -32,6 +32,7 @@
 -export([get_boolean/3, set_boolean/3]).
 
 -export([listen_for_changes/2]).
+-export([subscribe_for_changes/1]).
 -export([parse_ini_file/1]).
 
 -export([init/1, terminate/2, code_change/3]).
@@ -185,6 +186,9 @@ delete(Section, Key, Persist, Reason) when is_list(Section), is_list(Key) ->
 listen_for_changes(CallbackModule, InitialState) ->
     config_listener_mon:subscribe(CallbackModule, InitialState).
 
+subscribe_for_changes(Subscription) ->
+    config_notifier:subscribe(Subscription).
+
 
 init(IniFiles) ->
     ets:new(?MODULE, [named_table, set, protected, {read_concurrency, true}]),

http://git-wip-us.apache.org/repos/asf/couchdb-config/blob/01c34c09/src/config_notifier.erl
----------------------------------------------------------------------
diff --git a/src/config_notifier.erl b/src/config_notifier.erl
new file mode 100644
index 0000000..c1844e7
--- /dev/null
+++ b/src/config_notifier.erl
@@ -0,0 +1,68 @@
+% 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(config_notifier).
+
+-behaviour(gen_event).
+-vsn(1).
+
+%% Public interface
+-export([subscribe/1]).
+-export([subscribe/2]).
+
+%% gen_event interface
+-export([
+    init/1,
+    handle_event/2,
+    handle_call/2,
+    handle_info/2,
+    terminate/2,
+    code_change/3
+]).
+
+subscribe(Subscription) ->
+    subscribe(self(), Subscription).
+
+subscribe(Subscriber, Subscription) ->
+    gen_event:add_sup_handler(
+        config_event, {?MODULE, Subscriber}, {Subscriber, Subscription}).
+
+init({Subscriber, Subscription}) ->
+    {ok, {Subscriber, Subscription}}.
+
+handle_event({config_change, _, _, _, _} = Event, {Subscriber, Subscription}) ->
+    maybe_notify(Event, Subscriber, Subscription).
+
+handle_call(_Request, St) ->
+    {ok, ignored, St}.
+
+handle_info(_Info, St) ->
+    {ok, St}.
+
+terminate(_Reason, {_Subscriber, _Subscription}) ->
+    ok.
+
+code_change(_OldVsn, St, _Extra) ->
+    {ok, St}.
+
+maybe_notify(Event, Subscriber, all) ->
+    Subscriber ! Event;
+maybe_notify({config_change, Sec, Key, _, _} = Event, Subscriber, Subscription) ->
+    case should_notify(Sec, Key, Subscription) of
+        true ->
+            Subscriber ! Event;
+        false ->
+            ok
+    end.
+
+should_notify(Sec, Key, Subscription) ->
+    lists:any(fun(S) -> S =:= Sec orelse S =:= {Sec, Key} end, Subscription).

http://git-wip-us.apache.org/repos/asf/couchdb-config/blob/01c34c09/test/config_tests.erl
----------------------------------------------------------------------
diff --git a/test/config_tests.erl b/test/config_tests.erl
index c495bf7..c4a965b 100644
--- a/test/config_tests.erl
+++ b/test/config_tests.erl
@@ -80,6 +80,10 @@ setup_config_listener() ->
     setup(),
     spawn_config_listener().
 
+setup_config_notifier(Subscription) ->
+    setup(),
+    spawn_config_notifier(Subscription).
+
 
 teardown(Pid) when is_pid(Pid) ->
     catch exit(Pid, kill),
@@ -88,7 +92,9 @@ teardown(Pid) when is_pid(Pid) ->
 teardown(_) ->
     [application:stop(App) || App <- ?DEPS].
 
-
+teardown(_, Pid) when is_pid(Pid) ->
+    catch exit(Pid, kill),
+    teardown(undefined);
 teardown(_, _) ->
     teardown(undefined).
 
@@ -251,6 +257,25 @@ config_listener_behaviour_test_() ->
         }
     }.
 
+config_notifier_behaviour_test_() ->
+    {
+        "Test config_notifier behaviour",
+        {
+            foreachx,
+            local,
+            fun setup_config_notifier/1,
+            fun teardown/2,
+            [
+                {all, fun should_notify/2},
+                {["section_foo"], fun should_notify/2},
+                {[{"section_foo", "key_bar"}], fun should_notify/2},
+                {["section_foo"], fun should_not_notify/2},
+                {[{"section_foo", "key_bar"}], fun should_not_notify/2},
+                {all, fun should_unsubscribe_when_subscriber_gone/2}
+            ]
+        }
+    }.
+
 
 should_load_all_configs() ->
     ?assert(length(config:all()) > 0).
@@ -516,6 +541,50 @@ should_stop_monitor_on_error(Pid) ->
         ?assertEqual(0, n_handlers())
     end).
 
+should_notify(Subscription, Pid) ->
+    {to_string(Subscription), ?_test(begin
+        ?assertEqual(ok, config:set("section_foo", "key_bar", "any", false)),
+        ?assertEqual({config_change,"section_foo", "key_bar", "any", false}, getmsg(Pid)),
+        ok
+    end)}.
+
+should_not_notify([{Section, _}] = Subscription, Pid) ->
+    {to_string(Subscription), ?_test(begin
+        ?assertEqual(ok, config:set(Section, "any", "any", false)),
+        ?assertError({timeout, config_msg}, getmsg(Pid)),
+        ok
+    end)};
+should_not_notify(Subscription, Pid) ->
+    {to_string(Subscription), ?_test(begin
+        ?assertEqual(ok, config:set("any", "any", "any", false)),
+        ?assertError({timeout, config_msg}, getmsg(Pid)),
+        ok
+    end)}.
+
+should_unsubscribe_when_subscriber_gone(_Subscription, Pid) ->
+    ?_test(begin
+        ?assertEqual(1, n_notifiers()),
+
+        ?assert(is_process_alive(Pid)),
+
+        % Monitor subscriber process
+        MonRef = erlang:monitor(process, Pid),
+
+        exit(Pid, kill),
+
+        % Wait for the subscriber process to exit
+        receive
+            {'DOWN', MonRef, _, _, _} -> ok
+        after ?TIMEOUT ->
+            erlang:error({timeout, config_notifier_shutdown})
+        end,
+
+        ?assertNot(is_process_alive(Pid)),
+
+        ?assertEqual(0, n_notifiers()),
+        ok
+    end).
+
 
 spawn_config_listener() ->
     Self = self(),
@@ -531,11 +600,27 @@ spawn_config_listener() ->
     end,
     Pid.
 
+spawn_config_notifier(Subscription) ->
+    Self = self(),
+    Pid = erlang:spawn(fun() ->
+        ok = config:subscribe_for_changes(Subscription),
+        Self ! registered,
+        loop(undefined)
+    end),
+    receive
+        registered -> ok
+    after ?TIMEOUT ->
+        erlang:error({timeout, config_handler_register})
+    end,
+    Pid.
+
 
 loop(undefined) ->
     receive
         {config_msg, _} = Msg ->
             loop(Msg);
+        {config_change, _, _, _, _} = Msg ->
+            loop({config_msg, Msg});
         {get_msg, _, _} = Msg ->
             loop(Msg);
         Msg ->
@@ -546,6 +631,8 @@ loop({get_msg, From, Ref}) ->
     receive
         {config_msg, _} = Msg ->
             From ! {Ref, Msg};
+        {config_change, _, _, _, _} = Msg ->
+            From ! {Ref, Msg};
         Msg ->
             erlang:error({invalid_message, Msg})
     end,
@@ -574,3 +661,10 @@ getmsg(Pid) ->
 n_handlers() ->
     Handlers = gen_event:which_handlers(config_event),
     length([Pid || {config_listener, {?MODULE, Pid}} <- Handlers]).
+
+n_notifiers() ->
+    Handlers = gen_event:which_handlers(config_event),
+    length([Pid || {config_notifier, Pid} <- Handlers]).
+
+to_string(Term) ->
+    lists:flatten(io_lib:format("~p", [Term])).