Correct xml library for ejabberd 13 module developement

I am having a bit of trouble understanding the correct library to deal with xml in a ejabberd 13 community edition module.

For instance, I would like to simply use ejabberd as a router, scoop the contents of a message from ejabberd and queue it to rabbitMQ to be parsed external to ejabberd.

This is the test module I have written trying to accomplish these goals:

-module(mod_xml_test).

-behavior(gen_server).

-behavior(gen_mod).

%%% API: gen_mod
-export([start_link/2, start/2, stop/1]).

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

-include("logger.hrl").
-include("amqp_client/include/amqp_client.hrl").

%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link(Host, Opts) ->
    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
    gen_server:start_link({local, Proc}, ?MODULE,
  [Host, Opts], []).

start(Host, Opts) ->
    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
    ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
transient, 5000, worker, [?MODULE]},
    supervisor:start_child(ejabberd_sup, ChildSpec).

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

%%====================================================================
%% gen_server callbacks
%%====================================================================

%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%%                         {ok, State, Timeout} |
%%                         ignore               |
%%                         {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([Host, Opts]) ->
    ?INFO_MSG("SEEDS: Starting seeds module", []),
    MyHost = gen_mod:get_opt_host(Host, Opts, <<"data.@HOST@">>),
    ejabberd_router:register_route(MyHost),
    {ok, #state{host = MyHost}}.
   
   
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%%                                      {reply, Reply, State, Timeout} |
%%                                      {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, Reply, State} |
%%                                      {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(stop, _From, State) ->
    {stop, normal, ok, State}.

%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) -> {noreply, State}.

%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%%                                       {noreply, State, Timeout} |
%%                                       {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({route, From, To, Packet}, State) ->
    Packet2 = case From#jid.user of
<<"">> ->
    jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST);
_ -> Packet
      end,
    ejabberd_router:route(To, From, Packet2),
    xml_parse_test(Packet2),
    {noreply, State};
handle_info(_Info, State) -> {noreply, State}.

%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, State) ->
    ejabberd_router:unregister_route(State#state.host), ok.

%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) -> {ok, State}.

%% Fails
xml_parse_test( {xmlel, <<"message">>, _Attrs, _Els} = Record) ->
StrRecord =  exmpp_xml:document_to_list(Record),
?INFO_MSG("Message Received: ~p ~n", [StrRecord]).
%% Error output:

no case clause matching {xmlel,<<"message">>,[],[{xmlel,<<"event">>,[{<<"xmlns">>,<<"http://jabber.org/protocol/pubsub#event">>}],[{xmlel,<<"items">>,[{<<"node">>,<<"http://jabber.org/protocol/mood">>}],[{xmlel,<<"item">>,[{<<"id">>,<<"0">>}],[{xmlel,<<"mood">>,[{<<"xmlns">>,<<"http://jabber.org/protocol/mood">>}],[]}]}]}]},{xmlel,<<"delay">>,[{<<"xmlns">>,<<"urn:xmpp:delay">>},{<<"from">>,<<"test@testdom/Gajim">>},{<<"stamp">>,<<"2014-01-16T00:57:24Z">>}],[{xmlcdata,<<>>}]}]} in exmpp_xml:node_to_iolist2/4 line 3643 in gen_server:terminate/6 line 744

the xmlel record seems to be in a format that exmpp_xml does not recognize. I'm am guessing that ejabberd gives is a xmlel_old and it wants the newer one with Name space info in it.

But when I call exmpp_xml:xmlelement_to_xmlel on the received record, it returns exactly the same record, example:

2014-01-16 11:38:11.982 [info] <0.348.0>@mod_seeds2:xml_parse_test:147 Preconversion:
{xmlel,<<"message">>,[{<<"xml:lang">>,<<"en">>},{<<"to">>,<<"data.testdom">>},{<<"type">>,<<"chat">>},{<<"id">>,<<"497">>}],[{xmlel,<<"body">>,[],[{xmlcdata,<<"test">>}]},{xmlel,<<"active">>,[{<<"xmlns">>,<<"http://jabber.org/protocol/chatstates">>}],[]},{xmlel,<<"x">>,[{<<"xmlns">>,<<"jabber:x:event">>}],[{xmlel,<<"composing">>,[],[]}]},{xmlel,<<"request">>,[{<<"xmlns">>,<<"urn:xmpp:receipts">>}],[]},{xmlel,<<"thread">>,[],[{xmlcdata,<<"zKfFDtuFBbGIyUaehOmNJvxScOGSJbvw">>}]}]}

%% AFTER I called exmpp_xml:xmlelement_to_xmlel
2014-01-16 11:38:11.982 [info] <0.348.0>@mod_seeds2:xml_parse_test:149 Post conversion:
{xmlel,<<"message">>,[{<<"xml:lang">>,<<"en">>},{<<"to">>,<<"data.testdom">>},{<<"type">>,<<"chat">>},{<<"id">>,<<"497">>}],[{xmlel,<<"body">>,[],[{xmlcdata,<<"test">>}]},{xmlel,<<"active">>,[{<<"xmlns">>,<<"http://jabber.org/protocol/chatstates
">>}],[]},{xmlel,<<"x">>,[{<<"xmlns">>,<<"jabber:x:event">>}],[{xmlel,<<"composing">>,[],[]}]},{xmlel,<<"request">>,[{<<"xmlns">>,<<"urn:xmpp:receipts">>}],[]},{xmlel,<<"thread">>,[],[{xmlcdata,<<"zKfFDtuFBbGIyUaehOmNJvxScOGSJbvw">>}]}]}

%% Also Fails

xml_parse_test( {xmlel, <<"message">>, _Attrs, _Els} = Record) ->
StrRecord = xml:element_to_string(record),
?INFO_MSG("Message Received: ~p ~n", [StrRecord]).
%% Error output:
...call to undefined function xml:element_to_string..

Ok I can't seem to link to the old xml: library that just working without any includes in ejabberd 2.*

%%queue_test(Payload) ->
%%  {ok, Connection} =
%%      amqp_connection:start(#amqp_params_network{host = "localhost"}),
%%  {ok, Channel} = amqp_connection:open_channel(Connection),
%%   amqp_channel:call(Channel, #'queue.declare'{queue = <<"messages">>}),
%%   
%%   case of exmpp_xml:get_attribute(Payload, <<"message">>),
%%
%%   amqp_channel:cast(Channel,
%%                     #'basic.publish'{
%%                        exchange = <<"">>,
%%                        routing_key = <<"hello">>},
%%                      #amqp_msg{payload = RawOutput}),
%%   io:format(" [x] Sent 'Hello World!'~n"),
%%   ok = amqp_channel:close(Channel),
%%   ok = amqp_connection:close(Connection),
%%   ok.

Questions:

1. What library do I use to complete xml operations in ejabberd 13 community *module development*? It seems like nothing works.
2. It *looks* like ejabberd 13 switched to exmpp_xml style binary xml representation, but the tuples are off from the xmlel spec listed in exmpp_xml docs. xmelement_to_xmlel also does not seem to convert xmlel_old() to a format that exmpp_xml likes(it seems to do nothing at all).
3. Am I supposed to be using the old xml: library from ejabberd 2.* If so, how do I successfully link to it within a ejabberd 13 module.
4. I am missing something simple here?

Thank you.

During many months, the

During many months, the development branch of ejabberd used the exmpp library. However, this got deprecated

The development 'master' branch, which produces ejabberd 13 releases, doesn't use exmpp at all. The #xml used now is very similar to the one used in exmpp, but it isn't exactly the same. The best you can do when looking for example code, is to look at the erlang modules in the ejabberd branch you are running.

Syndicate content