From 7fd4808cdea6d22cd520bbfd1d0e12344fea7092 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Wed, 13 Apr 2016 09:59:39 +0300 Subject: [PATCH] Clean mod_last.erl from DB specific code --- include/mod_last.hrl | 3 + src/mod_last.erl | 152 +++++++--------------------------------- src/mod_last_mnesia.erl | 72 +++++++++++++++++++ src/mod_last_riak.erl | 53 ++++++++++++++ src/mod_last_sql.erl | 75 ++++++++++++++++++++ 5 files changed, 227 insertions(+), 128 deletions(-) create mode 100644 include/mod_last.hrl create mode 100644 src/mod_last_mnesia.erl create mode 100644 src/mod_last_riak.erl create mode 100644 src/mod_last_sql.erl diff --git a/include/mod_last.hrl b/include/mod_last.hrl new file mode 100644 index 000000000..494bf7b0c --- /dev/null +++ b/include/mod_last.hrl @@ -0,0 +1,3 @@ +-record(last_activity, {us = {<<"">>, <<"">>} :: {binary(), binary()}, + timestamp = 0 :: non_neg_integer(), + status = <<"">> :: binary()}). diff --git a/src/mod_last.erl b/src/mod_last.erl index 009a1cb06..1af1847b3 100644 --- a/src/mod_last.erl +++ b/src/mod_last.erl @@ -45,23 +45,21 @@ -include("jlib.hrl"). -include("mod_privacy.hrl"). +-include("mod_last.hrl"). --record(last_activity, {us = {<<"">>, <<"">>} :: {binary(), binary()}, - timestamp = 0 :: non_neg_integer(), - status = <<"">> :: binary()}). +-callback init(binary(), gen_mod:opts()) -> any(). +-callback import(binary(), #last_activity{}) -> ok | pass. +-callback get_last(binary(), binary()) -> + {ok, non_neg_integer(), binary()} | not_found | {error, any()}. +-callback store_last_info(binary(), binary(), non_neg_integer(), binary()) -> + {atomic, any()}. +-callback remove_user(binary(), binary()) -> {atomic, any()}. start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1, one_queue), - case gen_mod:db_type(Host, Opts) of - mnesia -> - mnesia:create_table(last_activity, - [{disc_copies, [node()]}, - {attributes, - record_info(fields, last_activity)}]), - update_table(); - _ -> ok - end, + Mod = gen_mod:db_mod(Host, Opts, ?MODULE), + Mod:init(Host, Opts), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_LAST, ?MODULE, process_local_iq, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, @@ -163,38 +161,8 @@ process_sm_iq(From, To, %% @spec (LUser::string(), LServer::string()) -> %% {ok, TimeStamp::integer(), Status::string()} | not_found | {error, Reason} get_last(LUser, LServer) -> - get_last(LUser, LServer, - gen_mod:db_type(LServer, ?MODULE)). - -get_last(LUser, LServer, mnesia) -> - case catch mnesia:dirty_read(last_activity, - {LUser, LServer}) - of - {'EXIT', Reason} -> {error, Reason}; - [] -> not_found; - [#last_activity{timestamp = TimeStamp, - status = Status}] -> - {ok, TimeStamp, Status} - end; -get_last(LUser, LServer, riak) -> - case ejabberd_riak:get(last_activity, last_activity_schema(), - {LUser, LServer}) of - {ok, #last_activity{timestamp = TimeStamp, - status = Status}} -> - {ok, TimeStamp, Status}; - {error, notfound} -> - not_found; - Err -> - Err - end; -get_last(LUser, LServer, odbc) -> - case catch odbc_queries:get_last(LServer, LUser) of - {selected, []} -> - not_found; - {selected, [{TimeStamp, Status}]} -> - {ok, TimeStamp, Status}; - Reason -> {error, {invalid_result, Reason}} - end. + Mod = gen_mod:db_mod(LServer, ?MODULE), + Mod:get_last(LUser, LServer). get_last_iq(#iq{lang = Lang} = IQ, SubEl, LUser, LServer) -> case ejabberd_sm:get_user_resources(LUser, LServer) of @@ -237,29 +205,8 @@ on_presence_update(User, Server, _Resource, Status) -> store_last_info(User, Server, TimeStamp, Status) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), - DBType = gen_mod:db_type(LServer, ?MODULE), - store_last_info(LUser, LServer, TimeStamp, Status, - DBType). - -store_last_info(LUser, LServer, TimeStamp, Status, - mnesia) -> - US = {LUser, LServer}, - F = fun () -> - mnesia:write(#last_activity{us = US, - timestamp = TimeStamp, - status = Status}) - end, - mnesia:transaction(F); -store_last_info(LUser, LServer, TimeStamp, Status, - riak) -> - US = {LUser, LServer}, - {atomic, ejabberd_riak:put(#last_activity{us = US, - timestamp = TimeStamp, - status = Status}, - last_activity_schema())}; -store_last_info(LUser, LServer, TimeStamp, Status, - odbc) -> - odbc_queries:set_last_t(LServer, LUser, TimeStamp, Status). + Mod = gen_mod:db_mod(LServer, ?MODULE), + Mod:store_last_info(LUser, LServer, TimeStamp, Status). %% @spec (LUser::string(), LServer::string()) -> %% {ok, TimeStamp::integer(), Status::string()} | not_found @@ -272,71 +219,20 @@ get_last_info(LUser, LServer) -> remove_user(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), - DBType = gen_mod:db_type(LServer, ?MODULE), - remove_user(LUser, LServer, DBType). + Mod = gen_mod:db_mod(LServer, ?MODULE), + Mod:remove_user(LUser, LServer). -remove_user(LUser, LServer, mnesia) -> - US = {LUser, LServer}, - F = fun () -> mnesia:delete({last_activity, US}) end, - mnesia:transaction(F); -remove_user(LUser, LServer, odbc) -> - odbc_queries:del_last(LServer, LUser); -remove_user(LUser, LServer, riak) -> - {atomic, ejabberd_riak:delete(last_activity, {LUser, LServer})}. - -update_table() -> - Fields = record_info(fields, last_activity), - case mnesia:table_info(last_activity, attributes) of - Fields -> - ejabberd_config:convert_table_to_binary( - last_activity, Fields, set, - fun(#last_activity{us = {U, _}}) -> U end, - fun(#last_activity{us = {U, S}, status = Status} = R) -> - R#last_activity{us = {iolist_to_binary(U), - iolist_to_binary(S)}, - status = iolist_to_binary(Status)} - end); - _ -> - ?INFO_MSG("Recreating last_activity table", []), - mnesia:transform_table(last_activity, ignore, Fields) - end. - -last_activity_schema() -> - {record_info(fields, last_activity), #last_activity{}}. - -export(_Server) -> - [{last_activity, - fun(Host, #last_activity{us = {LUser, LServer}, - timestamp = TimeStamp, status = Status}) - when LServer == Host -> - Username = ejabberd_odbc:escape(LUser), - Seconds = - ejabberd_odbc:escape(jlib:integer_to_binary(TimeStamp)), - State = ejabberd_odbc:escape(Status), - [[<<"delete from last where username='">>, Username, <<"';">>], - [<<"insert into last(username, seconds, " - "state) values ('">>, - Username, <<"', '">>, Seconds, <<"', '">>, State, - <<"');">>]]; - (_Host, _R) -> - [] - end}]. +export(LServer) -> + Mod = gen_mod:db_mod(LServer, ?MODULE), + Mod:export(LServer). import(LServer) -> - [{<<"select username, seconds, state from last">>, - fun([LUser, TimeStamp, State]) -> - #last_activity{us = {LUser, LServer}, - timestamp = jlib:binary_to_integer( - TimeStamp), - status = State} - end}]. + Mod = gen_mod:db_mod(LServer, ?MODULE), + Mod:import(LServer). -import(_LServer, mnesia, #last_activity{} = LA) -> - mnesia:dirty_write(LA); -import(_LServer, riak, #last_activity{} = LA) -> - ejabberd_riak:put(LA, last_activity_schema()); -import(_, _, _) -> - pass. +import(LServer, DBType, LA) -> + Mod = gen_mod:db_mod(DBType, ?MODULE), + Mod:import(LServer, LA). transform_options(Opts) -> lists:foldl(fun transform_options/2, [], Opts). diff --git a/src/mod_last_mnesia.erl b/src/mod_last_mnesia.erl new file mode 100644 index 000000000..7a1610abb --- /dev/null +++ b/src/mod_last_mnesia.erl @@ -0,0 +1,72 @@ +%%%------------------------------------------------------------------- +%%% @author Evgeny Khramtsov +%%% @copyright (C) 2016, Evgeny Khramtsov +%%% @doc +%%% +%%% @end +%%% Created : 13 Apr 2016 by Evgeny Khramtsov +%%%------------------------------------------------------------------- +-module(mod_last_mnesia). +-behaviour(mod_last). + +%% API +-export([init/2, import/2, get_last/2, store_last_info/4, remove_user/2]). + +-include("mod_last.hrl"). +-include("logger.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +init(_Host, _Opts) -> + mnesia:create_table(last_activity, + [{disc_copies, [node()]}, + {attributes, + record_info(fields, last_activity)}]), + update_table(). + +get_last(LUser, LServer) -> + case mnesia:dirty_read(last_activity, {LUser, LServer}) of + [] -> + not_found; + [#last_activity{timestamp = TimeStamp, + status = Status}] -> + {ok, TimeStamp, Status} + end. + +store_last_info(LUser, LServer, TimeStamp, Status) -> + US = {LUser, LServer}, + F = fun () -> + mnesia:write(#last_activity{us = US, + timestamp = TimeStamp, + status = Status}) + end, + mnesia:transaction(F). + +remove_user(LUser, LServer) -> + US = {LUser, LServer}, + F = fun () -> mnesia:delete({last_activity, US}) end, + mnesia:transaction(F). + +import(_LServer, #last_activity{} = LA) -> + mnesia:dirty_write(LA). + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +update_table() -> + Fields = record_info(fields, last_activity), + case mnesia:table_info(last_activity, attributes) of + Fields -> + ejabberd_config:convert_table_to_binary( + last_activity, Fields, set, + fun(#last_activity{us = {U, _}}) -> U end, + fun(#last_activity{us = {U, S}, status = Status} = R) -> + R#last_activity{us = {iolist_to_binary(U), + iolist_to_binary(S)}, + status = iolist_to_binary(Status)} + end); + _ -> + ?INFO_MSG("Recreating last_activity table", []), + mnesia:transform_table(last_activity, ignore, Fields) + end. diff --git a/src/mod_last_riak.erl b/src/mod_last_riak.erl new file mode 100644 index 000000000..d25a3a156 --- /dev/null +++ b/src/mod_last_riak.erl @@ -0,0 +1,53 @@ +%%%------------------------------------------------------------------- +%%% @author Evgeny Khramtsov +%%% @copyright (C) 2016, Evgeny Khramtsov +%%% @doc +%%% +%%% @end +%%% Created : 13 Apr 2016 by Evgeny Khramtsov +%%%------------------------------------------------------------------- +-module(mod_last_riak). +-behaviour(mod_last). + +%% API +-export([init/2, import/2, get_last/2, store_last_info/4, remove_user/2]). + +-include("mod_last.hrl"). +-include("logger.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +init(_Host, _Opts) -> + ok. + +get_last(LUser, LServer) -> + case ejabberd_riak:get(last_activity, last_activity_schema(), + {LUser, LServer}) of + {ok, #last_activity{timestamp = TimeStamp, + status = Status}} -> + {ok, TimeStamp, Status}; + {error, notfound} -> + not_found; + Err -> + Err + end. + +store_last_info(LUser, LServer, TimeStamp, Status) -> + US = {LUser, LServer}, + {atomic, ejabberd_riak:put(#last_activity{us = US, + timestamp = TimeStamp, + status = Status}, + last_activity_schema())}. + +remove_user(LUser, LServer) -> + {atomic, ejabberd_riak:delete(last_activity, {LUser, LServer})}. + +import(_LServer, #last_activity{} = LA) -> + ejabberd_riak:put(LA, last_activity_schema()). + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +last_activity_schema() -> + {record_info(fields, last_activity), #last_activity{}}. diff --git a/src/mod_last_sql.erl b/src/mod_last_sql.erl new file mode 100644 index 000000000..edfa37639 --- /dev/null +++ b/src/mod_last_sql.erl @@ -0,0 +1,75 @@ +%%%------------------------------------------------------------------- +%%% @author Evgeny Khramtsov +%%% @copyright (C) 2016, Evgeny Khramtsov +%%% @doc +%%% +%%% @end +%%% Created : 13 Apr 2016 by Evgeny Khramtsov +%%%------------------------------------------------------------------- +-module(mod_last_sql). +-behaviour(mod_last). + +%% API +-export([init/2, get_last/2, store_last_info/4, remove_user/2, + import/1, import/2, export/1]). + +-include("mod_last.hrl"). +-include("logger.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +init(_Host, _Opts) -> + ok. + +get_last(LUser, LServer) -> + case catch odbc_queries:get_last(LServer, LUser) of + {selected, []} -> + not_found; + {selected, [{TimeStamp, Status}]} -> + {ok, TimeStamp, Status}; + Reason -> + ?ERROR_MSG("failed to get last for user ~s@~s: ~p", + [LUser, LServer, Reason]), + {error, {invalid_result, Reason}} + end. + +store_last_info(LUser, LServer, TimeStamp, Status) -> + odbc_queries:set_last_t(LServer, LUser, TimeStamp, Status). + +remove_user(LUser, LServer) -> + odbc_queries:del_last(LServer, LUser). + +import(_LServer, _LA) -> + pass. + +export(_Server) -> + [{last_activity, + fun(Host, #last_activity{us = {LUser, LServer}, + timestamp = TimeStamp, status = Status}) + when LServer == Host -> + Username = ejabberd_odbc:escape(LUser), + Seconds = + ejabberd_odbc:escape(jlib:integer_to_binary(TimeStamp)), + State = ejabberd_odbc:escape(Status), + [[<<"delete from last where username='">>, Username, <<"';">>], + [<<"insert into last(username, seconds, " + "state) values ('">>, + Username, <<"', '">>, Seconds, <<"', '">>, State, + <<"');">>]]; + (_Host, _R) -> + [] + end}]. + +import(LServer) -> + [{<<"select username, seconds, state from last">>, + fun([LUser, TimeStamp, State]) -> + #last_activity{us = {LUser, LServer}, + timestamp = jlib:binary_to_integer( + TimeStamp), + status = State} + end}]. + +%%%=================================================================== +%%% Internal functions +%%%===================================================================