| %%------------------------------------------------------------------------------ |
| %% Licensed to the Apache Software Foundation (ASF) under one or more |
| %% contributor license agreements. See the NOTICE file distributed with |
| %% this work for additional information regarding copyright ownership. |
| %% The ASF licenses this file to You under the Apache License, Version 2.0 |
| %% (the "License"); you may not use this file e34rg 56yujxcept 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(dubbo_extension). |
| -behaviour(gen_server). |
| |
| %% API |
| -export([run/3, run_fold/4, run_fold/5, register/3, unregister/3, invoke/5, invoke_foldr/4]). |
| |
| |
| -export([start_link/0]). |
| |
| -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). |
| |
| |
| -define(TAB, ?MODULE). |
| |
| -record(state, {}). |
| |
| |
| -spec register(HookName :: atom(), Module :: atom(), Priority :: integer()) -> ok | {error, term()}. |
| register(HookName, Module, Priority) -> |
| gen_server:call(?MODULE, {register, HookName, {Priority, Module}}). |
| |
| -spec unregister(HookName :: atom(), Module :: atom(), Priority :: integer()) -> ok. |
| unregister(HookName, Module, Priority) -> |
| gen_server:call(?MODULE, {unregister, HookName, {Priority, Module}}). |
| |
| -spec run(HookName :: atom(), Fun :: atom(), Args :: list()) -> ok. |
| run(HookName, Fun, Args) -> |
| case find_hooks(HookName) of |
| no_hook -> ok; |
| Hooks -> |
| run1(Hooks, HookName, Fun, Args) |
| end. |
| |
| run1([], _HookName, _Fun, _Args) -> |
| ok; |
| run1([M | Rest], HookName, Fun, Args) -> |
| Ret = (catch apply(M, Fun, Args)), |
| case Ret of |
| {'EXIT', Reason} -> |
| logger:error("~p~n error running extension: ~p~n", [HookName, Reason]), |
| run1(Rest, HookName, Fun, Args); |
| stop -> |
| ok; |
| _ -> |
| run1(Rest, HookName, Fun, Args) |
| end. |
| |
| -spec run_fold(HookName :: atom(), Fun :: atom(), Args :: list(), Acc :: any()) -> Acc2 :: any(). |
| run_fold(HookName, Fun, Args, Acc) -> |
| case find_hooks(HookName) of |
| no_hook -> Acc; |
| Hooks -> run_fold1(Hooks, HookName, Fun, Args, Acc) |
| end. |
| |
| run_fold(HookName, Fun, Args, Acc, AppendExtension) -> |
| case find_hooks(HookName) of |
| no_hook -> Acc; |
| Hooks -> |
| run_fold1(Hooks ++ AppendExtension, HookName, Fun, Args, Acc) |
| end. |
| |
| run_fold1([], _HookName, _Fun, _Args, Acc) -> |
| Acc; |
| run_fold1([M | Rest], HookName, Fun, Args0, Acc) -> |
| Args = Args0 ++ [Acc], |
| Ret = (catch apply(M, Fun, Args)), |
| case Ret of |
| {'EXIT', Reason} -> |
| logger:error("~p~n error running hook: ~p~n", [HookName, Reason]), |
| run_fold1(Rest, HookName, Fun, Args0, Acc); |
| stop -> |
| Acc; |
| {stop, NewAcc} -> |
| NewAcc; |
| {ok,NewAcc2} -> |
| run_fold1(Rest, HookName, Fun, Args0, NewAcc2) |
| end. |
| |
| invoke_foldr(HookName, Fun, Args, Acc) -> |
| case find_hooks(HookName) of |
| no_hook -> Acc; |
| Hooks -> |
| do_invoke(lists:reverse(Hooks), HookName, Fun, Args, Acc) |
| end. |
| |
| invoke(HookName, Fun, Args, Acc, AppendExtension) -> |
| case find_hooks(HookName) of |
| no_hook -> Acc; |
| Hooks -> |
| do_invoke(Hooks ++ AppendExtension, HookName, Fun, Args, Acc) |
| end. |
| |
| do_invoke([], _HookName, _Fun, _Args, Acc) -> |
| Acc; |
| do_invoke([M | Rest], HookName, Fun, Args0, Acc) -> |
| Args = Args0 ++ [Acc], |
| Ret = (catch apply(M, Fun, Args)), |
| case Ret of |
| {'EXIT', Reason} -> |
| logger:error("~p~n error running hook: ~p~n", [HookName, Reason]), |
| do_invoke(Rest, HookName, Fun, Args0, Acc); |
| {stop, NewAcc} -> |
| NewAcc; |
| {ok, Args2, NewAcc2} -> |
| do_invoke(Rest, HookName, Fun, [Args2], NewAcc2) |
| end. |
| |
| %% @hidden |
| start_link() -> |
| _ = init_tabs(), |
| gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). |
| |
| |
| init_tabs() -> |
| case ets:info(?TAB, name) of |
| undefined -> |
| ets:new(?TAB, [ordered_set, public, named_table, |
| {read_concurrency, true}, |
| {write_concurrency, true}]); |
| _ -> |
| true |
| end. |
| |
| %% @hidden |
| init([]) -> |
| {ok, #state{}}. |
| |
| %% @hidden |
| handle_call({register, HookName, {Priority, Module}}, _From, State) -> |
| do_register(HookName, {Priority, Module}), |
| {reply, ok, State}; |
| handle_call({unregister, HookName, {Priority, Module}}, _From, State) -> |
| do_unregister(HookName, {Priority, Module}), |
| {reply, ok, State}; |
| handle_call(_Msg, _From, State) -> |
| {reply, badarg, State}. |
| |
| %% @hidden |
| handle_cast(_Msg, State) -> |
| {noreply, State}. |
| |
| handle_info(_Info, State) -> |
| {noreply, State}. |
| |
| %% @hidden |
| code_change(_OldVsn, State, _Extra) -> |
| {ok, State}. |
| |
| %% @hidden |
| terminate(_Reason, _Srv) -> |
| ok. |
| |
| do_register(HookName, {_Priority, ModuleName} = Hook) -> |
| check_module(ModuleName), |
| update_hooks(HookName, [Hook]). |
| |
| |
| do_unregister(HookName, Hook) -> |
| remove_hooks(HookName, [Hook]), |
| ok. |
| |
| update_hooks(HookName, HookFuns) -> |
| case ets:lookup(?TAB, HookName) of |
| [] -> |
| true = ets:insert(?TAB, {HookName, HookFuns}); |
| [{_, Funs}] -> |
| Funs2 = lists:keysort(1, Funs ++ HookFuns), |
| true = ets:insert(?TAB, {HookName, Funs2}) |
| end. |
| |
| remove_hooks(HookName, HookFuns) -> |
| case ets:lookup(?TAB, HookName) of |
| [] -> |
| ok; |
| [{_, Funs}] -> |
| Funs2 = Funs -- HookFuns, |
| case Funs2 of |
| [] -> |
| ets:delete(?TAB, HookName); |
| _ -> |
| ets:insert(?TAB, {HookName, Funs2}) |
| end |
| end. |
| |
| check_module(ModuleName) -> |
| _ = code:ensure_loaded(ModuleName), |
| ok. |
| |
| find_hooks(HookName) -> |
| case ets:lookup(?TAB, HookName) of |
| [] -> |
| no_hook; |
| [{_, Modules}] -> |
| Modules1 = [Module || {_, Module} <- Modules], |
| Modules1 |
| end. |