首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Erlang - Terminate函数问题

Erlang - Terminate函数问题
EN

Stack Overflow用户
提问于 2015-10-07 07:11:56
回答 3查看 420关注 0票数 0

我正在使用Erlang创建一个模块,我有三个选项,即添加、编辑和删除。

我可以在日志中看到在init方法中调用了add函数,但我找不到任何与删除消息相关的内容。我猜这是因为"terminate method“没有被调用,但我不确定我的函数是否正确,或者我是否在正确的位置调用了edit和delete函数。

这是我的代码:

代码语言:javascript
运行
复制
-module(mod_msgschedule).

-behaviour(gen_server).
-behaviour(gen_mod).

-include("ejabberd.hrl").
-include("jlib.hrl").

%% gen_mod handlers
-export([start/2,stop/1]).

%% gen_server handlers
-export([init/1,handle_info/2, handle_call/3, handle_cast/2, terminate/2, code_change/3]).

%% Hook handlers
-export([
    remove_delayed_message/2,
    add_delayed_message/7,
    edit_delayed_message/7,
    search_delayed_messages/2,
    check_packet/1,
    process_sm_iq/3]).

-export([start_link/2]).

-define(INTERVAL, timer:minutes(5)).
-define(PROCNAME, ?MODULE).
-define(NS_DELAYMSG, "delayed-msg").
-record(state,{host :: binary()}).

-record(delayed_msg, {id,from,to,server,scheduledTimestamp,packet,relativeId}).

%%--------------------------------------------------------------------
%% gen_mod callbacks
%%--------------------------------------------------------------------
start(VHost, Opts) ->
    ejabberd_loglevel:set_custom(?MODULE, 5),
    ?DEBUG("Start Module", []),
    Proc = gen_mod:get_module_proc(VHost,?PROCNAME),
    ChildSpec = {Proc, {?MODULE, start_link, [VHost,Opts]},
                 transient, 1000, worker, [?MODULE]},
    supervisor:start_child(ejabberd_sup, ChildSpec).

stop(VHost) ->
    Proc = gen_mod:get_module_proc(VHost,?PROCNAME),
    supervisor:terminate_child(ejabberd_sup,Proc),
    supervisor:delete_child(ejabberd_sup,Proc).


start_link(VHost, Opts) ->
    Proc = gen_mod:get_module_proc(VHost, ?PROCNAME),
    gen_server:start_link({local, Proc}, ?MODULE, [VHost, Opts],[]).

init([VHost, Opts]) ->
    ?DEBUG("Start Timer", []),
    process_flag(trap_exit, true),
    ejabberd_hooks:add(filter_local_packet, VHost, ?MODULE, check_packet, 10),

    timer:send_interval(?INTERVAL, self(), tick),

    IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
    gen_iq_handler:add_iq_handler(ejabberd_sm, VHost, ?NS_DELAYMSG,?MODULE, process_sm_iq, IQDisc),
    %%gen_iq_handler:add_iq_handler(ejabberd_sm, VHost, ?NS_VCARD,
    %%                              ?MODULE,process_sm_iq, IQDisc),
    %%gen_iq_handler:add_iq_handler(ejabberd_local, VHost, ?NS_VCARD,
    %%                              ?MODULE,process_local_iq, IQDisc),

    %%DirectoryHost = gen_mod:get_opt_host(VHost, Opts, "vjud.@HOST@"),
    %%Search = gen_mod:get_opt(search, Opts, true),

    %%case Search of
    %%    true ->
    %%        ejabberd_router:register_route(DirectoryHost);
    %%    _ ->
    %%        ok
    %%end,
    {ok,#state{host=VHost}}.

terminate(_Reason, State) ->
    VHost = State#state.host,
    %%case State#state.search of
    %%    true ->
    %%        ejabberd_router:unregister_route(State#state.directory_host);
    %%    _ ->
    %%        ok
    %%end,
    ejabberd_hooks:delete(filter_local_packet, VHost,?MODULE, check_packet, 10),
    gen_iq_handler:remove_iq_handler(ejabberd_local, VHost, ?NS_DELAYMSG).
    %%gen_iq_handler:remove_iq_handler(ejabberd_local, VHost, ?NS_VCARD),
    %%gen_iq_handler:remove_iq_handler(ejabberd_sm, VHost, ?NS_VCARD),
    %%ejabberd_hooks:delete(host_config_update, VHost, ?MODULE, config_change, 50),
    %%ejabberd_hooks:delete(disco_local_features, VHost, ?MODULE, get_local_features, 50).

handle_call(get_state, _From, State) ->
    {reply, {ok, State}, State};
handle_call(stop,_From,State) ->
    {stop, normal, ok, State};
handle_call(_Request, _From,State) ->
    {reply, bad_request, State}.

%% this function is called whenever gen_server receives a 'tick' message
handle_info(tick, State) ->
    State2 = send_pending_delayed_messages(State),
    {noreply, State2};

handle_info(_Info, State) ->
    {noreply, State}.

handle_cast(_Request, State) ->
    {noreply, State}.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%% this function is called by handle_info/2 when tick message is received
send_pending_delayed_messages(State) ->
    LServer = jlib:nameprep(State#state.host),
    Now = erlang:now(),
    CurrentTimestamp = now_to_microseconds(Now),
    ?DEBUG("Timer Triggered!! ~p", [CurrentTimestamp]),
    case search_delayed_messages(LServer,CurrentTimestamp) of
        {ok, DelayedMessages} ->
            lists:foreach(fun(DelayedMessage) ->
                  route_scheduled_message(LServer, DelayedMessage),
                  remove_delayed_message(LServer, DelayedMessage)
              end, DelayedMessages);
        {error, Reason} ->
            ?DEBUG("Select command: ~p", [{error, Reason}])
    end,
    State.

route_scheduled_message(Server, #delayed_msg{from=From, to=To, packet=Packet} = DelayedMessage) ->
    NewPacket = resend_scheduled_message_packet(Server,DelayedMessage),
    ejabberd_router:route(From, To, NewPacket).

resend_scheduled_message_packet(Server,
        #delayed_msg{scheduledTimestamp=TimeStamp, packet = Packet}) ->
    add_timestamp(TimeStamp, Server, Packet).

add_timestamp(undefined, _Server, Packet) ->
    Packet;
add_timestamp({_,_,Micro} = TimeStamp, Server, Packet) ->
    {D,{H,M,S}} = calendar:now_to_universal_time(TimeStamp),
    Time = {D,{H,M,S, Micro}},
    TimeStampXML = timestamp_xml(Server, Time),
    xml:append_subtags(Packet, [TimeStampXML]).

timestamp_xml(Server, Time) ->
    FromJID = jlib:make_jid(<<>>, Server, <<>>),
    jlib:timestamp_to_xml(Time, utc, FromJID, <<"Offline Storage">>).   

%%--------------------------------------------------------------------
%% Hook handler
%%--------------------------------------------------------------------

check_packet({From, To, XML} = Packet) ->
    #jid{luser = LUser, lserver = LServer} = From,
    case XML#xmlel.name of
        <<"message">> ->
            Type = xml:get_tag_attr_s(list_to_binary("type"), XML),
            case Type of
                <<"chat">> ->
                    DeltaTimeStampS = binary_to_list(xml:get_path_s(XML, [{elem, list_to_binary("scheduled_time")}, cdata])),
                    case DeltaTimeStampS of
                        "" ->
                            Packet;
                        _ ->
                            RelativeId = binary_to_list(xml:get_path_s(XML, [{elem, list_to_binary("delayed_msg_id")}, cdata])),
                            {DeltaTimeStampI, _Rest} = string:to_integer(DeltaTimeStampS),
                            case _Rest of
                                [] ->
                                    Action = binary_to_list(xml:get_path_s(XML, [{elem, list_to_binary("delayed_msg_action")}, cdata])),
                                    ScheduledTimestamp = from_now_to_microseconds(erlang:now(),DeltaTimeStampI),
                                    NewChildren = lists:delete(lists:keyfind(<<"scheduled_time">>, 2, XML#xmlel.children),XML#xmlel.children),
                                    NewXML = XML#xmlel{ children = NewChildren },
                                    case Action of
                                        "edit" ->                                           
                                            edit_delayed_message(LServer, binary_to_list(From#jid.luser), binary_to_list(To#jid.luser), binary_to_list(To#jid.lserver), ScheduledTimestamp, NewXML,RelativeId);

                                        "delete" ->
                                            remove_delayed_message(LServer,#delayed_msg{from = binary_to_list(From#jid.luser), relativeId = RelativeId});                                           
                                        _ ->
                                            add_delayed_message(LServer, binary_to_list(From#jid.luser), binary_to_list(To#jid.luser), binary_to_list(To#jid.lserver), ScheduledTimestamp, NewXML,RelativeId)
                                        end
                            end,
                            drop
                    end;
                    <<"groupchat">> ->
                    DeltaTimeStampS = binary_to_list(xml:get_path_s(XML, [{elem, list_to_binary("scheduled_time")}, cdata])),
                    case DeltaTimeStampS of
                        "" ->
                            Packet;
                        _ ->
                            RelativeId = binary_to_list(xml:get_path_s(XML, [{elem, list_to_binary("delayed_msg_id")}, cdata])),
                            {DeltaTimeStampI, _Rest} = string:to_integer(DeltaTimeStampS),
                            case _Rest of
                                [] ->
                                    Action = binary_to_list(xml:get_path_s(XML, [{elem, list_to_binary("delayed_msg_action")}, cdata])),
                                    ScheduledTimestamp = from_now_to_microseconds(erlang:now(),DeltaTimeStampI),
                                    NewChildren = lists:delete(lists:keyfind(<<"scheduled_time">>, 2, XML#xmlel.children),XML#xmlel.children),
                                    NewXML = XML#xmlel{ children = NewChildren },
                                    case Action of
                                        "edit" ->                                           
                                            edit_delayed_message(LServer, binary_to_list(From#jid.luser), binary_to_list(To#jid.luser), binary_to_list(To#jid.lserver), ScheduledTimestamp, NewXML,RelativeId);

                                        "delete" ->
                                            remove_delayed_message(LServer,#delayed_msg{from = binary_to_list(From#jid.luser), relativeId = RelativeId});                                           
                                        _ ->
                                            add_delayed_message(LServer, binary_to_list(From#jid.luser), binary_to_list(To#jid.luser), binary_to_list(To#jid.lserver), ScheduledTimestamp, NewXML,RelativeId)
                                        end
                            end,
                            drop
                    end;
                _ -> Packet
            end;
        _ -> Packet
    end.


process_sm_iq(_From, _To, #iq{type = get, xmlns = ?NS_DELAYMSG} = IQ) ->
    ?INFO_MSG("Processing IQ Get query:~n ~p", [IQ]),
    IQ#iq{type = result, sub_el = [{xmlelement, "value", [], [{xmlcdata, "Hello World of Testing."}]}]};
process_sm_iq(_From, _To, #iq{type = set} = IQ) ->
    ?INFO_MSG("Processing IQ Set: it does nothing", []),
    IQ#iq{type = result, sub_el = []};
process_sm_iq(_From, _To, #iq{sub_el = SubEl} = IQ) ->
    ?INFO_MSG("Processing IQ other type: it does nothing", []),
    IQ#iq{type = error, sub_el = [SubEl, ?ERR_FEATURE_NOT_IMPLEMENTED]}.

%%--------------------------------------------------------------------
%% ODBC Functions
%%--------------------------------------------------------------------


remove_delayed_message(LServer, #delayed_msg{from=FromUserName, relativeId = RelativeId}) ->
        QR = ejabberd_odbc:sql_query(
          LServer,
          [<<"delete from delayed_message "
                "where from_jid = '">>, ejabberd_odbc:escape(FromUserName#jid.luser),<<"' and relative_id = '">>,ejabberd_odbc:escape(list_to_binary(RelativeId)),<<"';">>]),
        ?DEBUG("DELETE ~p", [QR]).

prepare_delayed_message(SFromUserName, SToUsername, SServer, SScheduledTimestamp, SPacket,SRelativeId) ->
    [<<"('">>,   ejabberd_odbc:escape(list_to_binary(SFromUserName)),
     <<"', '">>, ejabberd_odbc:escape(list_to_binary(SToUsername)),
     <<"', '">>, ejabberd_odbc:escape(list_to_binary(SServer)),
     <<"', ">>,   integer_to_list(SScheduledTimestamp),
     <<", '">>, ejabberd_odbc:escape(xml:element_to_binary(SPacket)),
     <<"', '">>, ejabberd_odbc:escape(list_to_binary(SRelativeId)),
     <<"')">>].

add_delayed_message(LServer, SFromUserName, SToUsername, SServer, SScheduledTimestamp, SPacket, SRelativeId) ->
    Rows = prepare_delayed_message(SFromUserName, SToUsername, SServer, SScheduledTimestamp, SPacket,SRelativeId),
    QR = ejabberd_odbc:sql_query(
      LServer,
      [<<"insert into delayed_message(from_jid,to_jid,server,scheduled_time,packet,relative_id) "
       "values ">>, join(Rows, "")]),
       ?DEBUG("Delayed message inserted? ~p", [QR]).

edit_delayed_message(LServer,SFromUserName, SToUsername, SServer, SScheduledTimestamp, SPacket, SRelativeId) ->
    ejabberd_odbc:sql_query(
      LServer,
      [<<"update delayed_message set to_jid='">>,ejabberd_odbc:escape(list_to_binary(SToUsername)),
       <<"' , server='">>,ejabberd_odbc:escape(list_to_binary(SServer)),
       <<"' , scheduled_time=">>,integer_to_list(SScheduledTimestamp),
       <<", packet='">>,ejabberd_odbc:escape(xml:element_to_binary(SPacket)),
       <<"' where from_jid='">>,ejabberd_odbc:escape(list_to_binary(SFromUserName)),
       <<"' AND relative_id = '">>, ejabberd_odbc:escape(list_to_binary(SRelativeId)),<<"';">>]).

search_delayed_messages(LServer, SScheduledTimestamp) ->
    ScheduledTimestamp = encode_timestamp(SScheduledTimestamp),
    Query = [<<"select id,from_jid,to_jid,server,scheduled_time,packet,relative_id from delayed_message where ">>,
        <<"(scheduled_time < ">>,ScheduledTimestamp,<<" OR ">>,ScheduledTimestamp,<<" = 0);">>],

    case ejabberd_odbc:sql_query(LServer,Query) of
        {selected, [<<"id">>,<<"from_jid">>,<<"to_jid">>,<<"server">>,<<"scheduled_time">>,<<"packet">>,<<"relative_id">>], Rows} ->
            {ok, rows_to_records(Rows)};
        {aborted, Reason} ->
            {error, Reason};
        {error, Reason} ->
            {error, Reason}
    end.

    rows_to_records(Rows) ->
    [row_to_record(Row) || Row <- Rows].

row_to_record({SId, SFromUserName, SToUsername, SServer, SScheduledTimestamp, SPacket,SRelativeId}) ->

    Id = list_to_integer(binary_to_list(SId)),
    Server = binary_to_list(SServer),
    From = jlib:make_jid(SFromUserName,SServer,<<"fb">>),
    To = jlib:make_jid(SToUsername,SServer,<<"fb">>),
    ScheduledTimestamp = microseconds_to_now(list_to_integer(binary_to_list(SScheduledTimestamp))),
    Packet = xml_stream:parse_element(SPacket),
    RelativeId = binary_to_list(SRelativeId),
    #delayed_msg{id = Id,
             from = From,
             to = To,
             server = Server,
             scheduledTimestamp = ScheduledTimestamp,
             packet = Packet,
             relativeId = RelativeId}.


%% ------------------------------------------------------------------
%% Helpers

choose_strategy(true,  true, get) -> get;
choose_strategy(true,  true, set) -> set;
choose_strategy(false, _,    _  ) -> not_allowed;
choose_strategy(_,     _,    _  ) -> forbidden.

compare_bare_jids(#jid{luser = LUser, lserver = LServer},
                  #jid{luser = LUser, lserver = LServer}) -> true;
compare_bare_jids(_, _) -> false.

element_to_namespace(#xmlel{attrs = Attrs}) ->
    xml:get_attr_s(<<"xmlns">>, Attrs);
element_to_namespace(_) ->
    <<>>.

%% Skip invalid elements.
to_map(Elems) ->
    [{NS, Elem} || Elem <- Elems, is_valid_namespace(NS = element_to_namespace(Elem))].

is_valid_namespace(Namespace) -> Namespace =/= <<>>.

error_iq(IQ=#iq{sub_el=SubElem}, ErrorStanza) ->
    IQ#iq{type = error, sub_el = [SubElem, ErrorStanza]}.


from_now_to_microseconds({Mega, Secs, Micro}, FromNow) ->
    Mega*1000*1000*1000*1000 + Secs * 1000 * 1000 + Micro + FromNow.

now_to_microseconds({Mega, Secs, Micro}) ->
    Mega*1000*1000*1000*1000 + Secs * 1000 * 1000 + Micro.


encode_timestamp(TimeStamp) ->
    integer_to_list(TimeStamp).

maybe_encode_timestamp(never) ->
    "null";
maybe_encode_timestamp(TimeStamp) ->
    encode_timestamp(TimeStamp).

microseconds_to_now(MicroSeconds) when is_integer(MicroSeconds) ->
    Seconds = MicroSeconds div 1000000,
    {Seconds div 1000000, Seconds rem 1000000, MicroSeconds rem 1000000}.

join([], _Sep) ->
    [];
join([H|T], Sep) ->
    [H, [[Sep, X] || X <- T]].
EN

回答 3

Stack Overflow用户

发布于 2015-10-08 03:31:51

我从来没有使用过ejabberd,所以我的评论非常笼统。

您可以在动态口令中同时使用gen_mod行为(所有ejjaberd模块都必须使用)和gen_server行为。我知道您这样做是为了使用tick消息,但您也可以使用函数timer:apply_interval(Time,Module,Function,Args)来获得相同的结果。因此,您可以删除所有的gen_server行为和它的call_back,它将只保留启动和停止gen_mod回调。

在你调用process_flag(trap_exit, true),的init/1函数中,我认为一般来说,这是一个坏主意,特别是当你没有对错误消息进行任何管理的时候(在你的代码中,它应该由一个handle_info子句来处理,但这里是匹配的handle_info(_Info, State) -> {noreply, State}.子句)。

票数 0
EN

Stack Overflow用户

发布于 2015-10-12 18:52:45

通常,不应该使用terminate,除非有必要编写自定义流程或行为,它们应该插入到监管层次结构中。

我刚刚浏览了一下代码,如果我的假设有误,请纠正我。

在这里,您要处理两件事:一个ejabberd模块和一个支持其某些功能的进程,并且您将它们的初始化交织在一起有点太多了。

在ejabberd模块中,我指的是服务器的一个组件(这并不意味着当调用来自组件的代码时,来自客户端的任何节在被服务器处理时都会通过进程边界)。

您引入了一个能够测量时间节拍的流程,这很好。但是,您还可以将一些模块初始化放入进程初始化函数(init/1)中。

我发现你的IQ/钩子处理程序没有在他们的代码中调用你的进程--这很好,因为这意味着它们并不真的依赖于进程的存在或不存在(就像当它被管理程序重启时)。

我建议在start/2中设置你的模块(注册钩子处理程序,IQ处理程序,...),在stop/1中拆卸它,并假设在出现运行时错误的情况下,管理程序会重新启动进程-不要通过将处理程序(取消)注册放在init/terminate中来约束模块设置/拆卸和进程生命周期。如果监控器必须重新启动您的进程,那么它应该几乎是立即的-即使到那时,您的IQ/钩子处理程序也不依赖于那里的模块-为什么要将它们绑定在init/terminate

还有一件事-如果你希望模块只在进程运行后启动(有时可能需要,但这里并不严格要求),那么记住supervisor:start_child是同步的和阻塞的-它只会在新子进程成功启动后返回。一旦您有了这个保证(即调用返回了正确的返回值),您就可以安全地继续进行模块设置(钩子/ IQ处理程序设置)。

如果你需要你的代码具有良好的伸缩性,那么使用timer is not the best choice --使用erlang:send_after/3或者handle_*回调返回的Timeout部分。

票数 0
EN

Stack Overflow用户

发布于 2015-11-07 04:03:24

感谢您的回答。因为我是Erlang的新手,我不知道我必须编译erl文件。我编译了它,现在正在工作。

这是编译erl文件的指令:

erlc文件/路径/至/ -I / module_name.erl

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32981257

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档