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])).