MongooseIM simple module event not getting handled - erlang

I am new in erlang and ejabbered/mongooseIM. I am trying to write a very simple mongoose module that will add an extra child element to the packets before sending. Below is my code:
-module(mod_test).
-behavior(gen_mod).
-export([start/2, stop/1]).
-export([add_child/1]).
-include("ejabberd.hrl").
start(Host, Opts) ->
ejabberd_hooks:add(filter_packet, Host, ?MODULE, add_child, 0),
?DEBUG(" MOD_TEST Started",[]),
ok.
stop(Host) ->
ejabberd_hooks:delete(filter_packet, Host, ?MODULE, add_child, 0),
ok.
add_child({From, To, XML} = Packet) ->
Tag = {"a","b"},
NewPacket = xml:append_subtags(Packet, [Tag]),
?INFO_MSG(" To party: ~p~n",[To]),
NewPacket.
I can compile the code with few warnings
mod_test.erl:3: Warning: behaviour gen_mod undefined
mod_test.erl:11: Warning: variable 'Opts' is unused
mod_test.erl:20: Warning: variable 'From' is unused
mod_test.erl:20: Warning: variable 'XML' is unused
Then when I add the module and run it it gets started but it doesnt make any changes to the packets and doesnt generate any logs either.
Another issue is, if I add a log within my start function, it gets compiled but I see errors while starting the module
2015-03-03 16:36:34.772 [critical] <0.200.0>#gen_mod:start_module:94 Problem starting the module mod_test for host <<"localhost">>
options: []
error: undef
[{lager,info,[" mod_test starting ...",[[]]],[]},
{mod_test,start,2,[{file,"mod_test.erl"},{line,13}]},
{gen_mod,start_module,3,[{file,"src/gen_mod.erl"},{line,83}]},
{lists,foreach,2,[{file,"lists.erl"},{line,1336}]},
{ejabberd_app,start,2,[{file,"src/ejabberd_app.erl"},{line,69}]},
{application_master,start_it_old,4,
[{file,"application_master.erl"},{line,272}]}]
2015-03-03 16:36:34.773 [critical] <0.200.0>#gen_mod:start_module:99 ejabberd initialization was aborted because a module start failed.
The trace is [{lager,info,[" mod_test starting ...",[[]]],[]},{mod_test,start,2,[{file,"mod_test.erl"},{line,13}]},{gen_mod,start_module,3,[{file,"src/gen_mod.erl"},{line,83}]},{lists,foreach,2,[{file,"lists.erl"},{line,1336}]},{ejabberd_app,start,2,[{file,"src/ejabberd_app.erl"},{line,69}]},{application_master,start_it_old,4,[{file,"application_master.erl"},{line,272}]}].
Crash dump was written to: erl_crash.dump
Problem starting the module mod_test for host <<"localhost">>
options: []
error: undef
[{lager,info,[" mod_xyz starting ...",[[]]],[]},
{mod_test,start,2,[{file,"mod_timetagg
What wrong am I doing?

So your example is actually a bit tricky, because of how the filter_packet hook works. You picked the worst hook to register for on your first attempt ;)
If you look into ejabberd_router:do_route, you'll see that filter_packet is run without a Host parameter -- it is a global hook, so when you register your add_child function for a particular Host, it will essentially be ignored.
Try the following:
-module(mod_test).
-behavior(gen_mod).
-export([start/2, stop/1]).
-export([add_child/1]).
-include_lib("ejabberd/include/ejabberd.hrl").
-include_lib("exml/include/exml.hrl").
start(Host, Opts) ->
ejabberd_loglevel:set_custom(?MODULE, 5),
ejabberd_hooks:add(filter_local_packet, Host, ?MODULE, add_child, 1),
?DEBUG(" MOD_TEST Started",[]),
ok.
stop(Host) ->
ejabberd_hooks:delete(filter_local_packet, Host, ?MODULE, add_child, 1),
ok.
add_child({From, To, Element} = HookData) ->
?DEBUG("Filtering ~p~n", [HookData]),
case Element#xmlel.name of
<<"message">> ->
Tag = #xmlel{name = <<"added-tag">>, attrs = [], children = []},
NewElement = xml:append_subtags(Element, [Tag]),
?DEBUG("will return new el: ~p", [NewElement]),
{From, To, NewElement};
_ ->
?DEBUG("will pass old el: ~p", [Element]),
HookData
end.
Registering for filter_local_packet on your given Host will now work, and all incoming stanzas will be passed to your function. It is important to remember that adding spurious tags to all stanzas may break things, so the above code will only add a <added-tag> element to <message> stanzas.
Use the example above and work from there.
Good luck!

The error is undef, which means that a function was called that isn't defined/exported. The stacktrace shows that the function in question is lager:info/2.
Lager (the logging library that handles your ?INFO_MSG) has a special quirk in that your code would call non-existent functions, but the code gets transformed by a parse transform before being compiled. It looks like this didn't happen for you.
The rebar.config file in apps/ejabberd in the MongooseIM tree contains {parse_transform, lager_transform} in erl_opts, which asks the compiler to apply the parse transform. I'd suggest putting mod_test.erl into apps/ejabberd/src and build the entire MongooseIM tree; that would ensure that your file gets built with the correct options.

Related

Erlang newbie: why do I have to restart to load new code

I am trying to write a first program in Erlang that effects message communication between a client and server. In theory the server exits when it receives no message from the client, but every time I edit the client code and run the server again, it executes the old code. I have to ^G>q>erl>[re-enter command] to get it to see the new code.
-module(srvEsOne).
%%
%% export functions
%%
-export([start/0]).
%%function definition
start()->
io:format("Server: Starting at pid: ~p \n",[self()]),
case lists:member(serverEsOne, registered()) of
true ->
unregister(serverEsOne); %if the token is present, remove it
false ->
ok
end,
register(serverEsOne,self()),
Pid = spawn(esOne, start,[self()]),
loop(false, false,Pid).
%
loop(Prec, Nrec,Pd)->
io:format("Server: I am waiting to hear from: ~p \n",[Pd]),
case Prec of
true ->
case Nrec of
true ->
io:format("Server: I reply to ~p \n",[Pd]),
Pd ! {reply, self()},
io:format("Server: I quit \n",[]),
ok;
false ->
receiveLoop(Prec,Nrec,Pd)
end;
false ->
receiveLoop(Prec,Nrec,Pd)
end.
receiveLoop(Prec,Nrec,Pid) ->
receive
{onPid, Pid}->
io:format("Server: I received a message to my pid from ~p \n",[Pid]),
loop(true, Nrec,Pid);
{onName,Pid}->
io:format("Server: I received a message to name from ~p \n",[Pid]),
loop(Prec,true,Pid)
after
5000->
io:format("Server: I received no messages, i quit\n",[]),
ok
end.
And the client code reads
-module(esOne).
-export([start/1, func/1]).
start(Par) ->
io:format("Client: I am ~p, i was spawned by the server: ~p \n",[self(),Par]),
spawn(esOne, func, [self()]),
io:format("Client: Now I will try to send a message to: ~p \n",[Par]),
Par ! {self(), hotbelgo},
serverEsOne ! {self(), hotbelgo},
ok.
func(Parent)->
io:format("Child: I am ~p, i was spawned from ~p \n",[self(),Parent]).
The server is failing to receive a message from the client, but I can't sensibly begin to debug that until I can try changes to the code in a more straightforward manner.
When you make modification to a module you need to compile it.
If you do it in an erlang shell using the command c(module) or c(module,[options]), the new compiled version of the module is automatically loaded in that shell. It will be used by all the new process you launch.
For the one that are alive and already use it is is more complex to explain and I think it is not what you are asking for.
If you have several erlang shells running, only the one where you compile the module loaded it. That means that in the other shell, if the module were previously loaded, basically if you already use the module in those shell, and even if the corresponding processes are terminated, the new version is ignored.
Same thing if you use the command erlc to compile.
In all these cases, you need to explicitly load the module with the command l(module) in the shell.
Your server loop contain only local function calls. Running code is changed only if there is remote (or external) function call. So you have to export your loop function first:
-export([loop/3]).
and then you have to change all loop/3 calls in function receiveLoop/3 to
?MODULE:loop(...)
Alternatively you can do same thing with receiveLoop/3 instead. Best practice for serious applications is doing hot code swapping by demand so you change loop/3 to remote/external only after receiving some special message.

How to start multiple instances of the same module/function under the Supervisor behavior in erlang?

Having a module/function mymodule , how to start it multiple times under the supervisor behavior ?
I need for example 2 instances of the same process (mymodule) to be started concurrently. I called the children identifiers as child1 and child2. They both point to the mymodule module that I want to start. I have specified two different functions to to start each instance of the worker process "mymodule" ( start_link1 and start_link2 )
-module(my_supervisor).
-behaviour(supervisor).
-export([start_link/0, init/1]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, _Arg = []).
init([]) ->
{ok, {{one_for_one, 10, 10},
[{child1,
{mymodule, start_link1, []},
permanent,
10000,
worker,
[mymodule]}
,
{child2,
{mymodule, start_link2, []},
permanent,
10000,
worker,
[mymodule]}
]}}.
The worker has two distinguished start_link functions ( start_link1 and start_link2 ) for testing purposes:
-module(mymodule).
-behaviour(gen_server).
start_link1() ->
log_something("at link 1"),
gen_server:start_link({global, child1}, ?MODULE, [], []).
start_link2() ->
log_something("at link 2"),
gen_server:start_link({global, child2}, ?MODULE, [], []).
init([]) ->
....
With the above I can see in my log the message "at link 1" but it does reveal "at link 2" anywhere. It also does not perform anything in the instance of link1 : just dies apparently.
The only scenario that works is when the name "child1" matches the worker module name "mymodule".
As #MilleBessö asks are you trying to two processes which have the same registered name? Does mymodule:start_link register the mymodule process under a fixed name? If so then trying to start a second one will cause a clash. Ot are you trying to start multiple my_supervisor supervisors? Then you will also get a name clash. You have not included the code for my_module.
Remember you can only have one process registered under a name. This holds for both local registered processes and those registered using global.
EDIT: Does the supervisor die as well?
A gen_server, and all other behaviours, aren't considered to be properly started until the init callback has completed and returned a correct value ({ok,State}). So if there is an error in mymodule:init/1 then this will crash the child process before it has been initialised and the supervisor will give up. While a supervisor can and will restart children when they die it does require that they all start correctly. From supervisor:start_link/3
If the supervisor and its child processes are successfully created (i.e. if all child process start functions return {ok,Child}, {ok,Child,Info}, or ignore) the function returns {ok,Pid}, where Pid is the pid of the supervisor. If there already exists a process with the specified SupName the function returns {error,{already_started,Pid}}, where Pid is the pid of that process.
If Module:init/1 returns ignore, this function returns ignore as well and the supervisor terminates with reason normal. If Module:init/1 fails or returns an incorrect value, this function returns {error,Term} where Term is a term with information about the error, and the supervisor terminates with reason Term.
I don't know if this is the problem but it give the same behaviour as you get.
Check the docs for supervisor:start_link(). The first parameter you pass in here is the name it uses to register with global, which provides a global name -> pid lookup. Since this must be unique, your second process fails to start, since the name is already taken.
Edit: Here is link to the docs: http://erldocs.com/R15B/stdlib/supervisor.html?i=5&search=supervisor:start#start_link/3
Check also simple-one-for-one supervisor restart scenario. It allows to start multiple processes with same child specification in more automated way.

Hook not being called in ejabberd

The problem I have is that the hook I defined is not being called on the event "user_send_packet". I did assume that any stanza (including messages) that is being sent would trigger this event.
I have taken into account the priority of how the hook is being called by setting it to '0'. In the log I have verified that the module is started ("mod_stanza_ack starting"). The erl file did compile, it only got the warning "gen_mod" is undefined, but I have read on a ejabberd mailing list that this is harmless and also that the variable "Host" is unused in line 12. Maybe this has something to do with it, but I cannot find out if this is the case.
-module(mod_stanza_ack).
-behaviour(gen_mod).
-include("ejabberd.hrl").
-export([start/2,
stop/1]).
-export([on_user_send_packet/3]).
start(Host, _Opts) ->
?INFO_MSG("mod_stanza_ack starting", []),
ejabberd_hooks:add(user_send_packet, global, ?MODULE, on_user_send_packet, 0),
ok.
stop(Host) ->
?INFO_MSG("mod_stanza_ack stopping", []),
ejabberd_hooks:delete(user_send_packet, global, ?MODULE, on_user_send_packet, 0),
ok.
on_user_send_packet(From, To, Packet) ->
?INFO_MSG("mod_stanza_ack a package has been sent coming from: ~p", [From]),
?INFO_MSG("mod_stanza_ack a package has been sent to: ~p", [To]),
?INFO_MSG("mod_stanza_ack a package has been sent with the following packet: ~p", [Packet]),
Packet.
The syntax looks OK and you have correct assumptions regarding the hook and compilation warnings. Which version of ejabberd do you use?
It seems there is bug/confusion in 'global' handling.
ejabberd_c2s which runs user_send_packet, runs it with Host as a scope.
The implementation of scopes is pretty naive and 'global' callback is not hooked up under Host hook.
In my 2.1.6 there is still the 'buggy' behaviour and I could reproduce your case.
To fix it add hook with Host, not 'global' keyword.

Need I export all functions in Erlang shell?

I have these code
start() ->
spawn(?MODULE, init, [self()]).
init(From) ->
spawn(?MODULE, broadcast, []).
broadcast() ->
Msg = "helloworld",
timer:sleep(10000),
broadcast().
When I test these code in Erlang shell, it give me undef error, I need to export broadcast function, I am just refuse
Code
spawn(?MODULE, init, [self()]).
means you will spawn process which initial call will be ?MODULE:init(self()) or more precisely equivalent of apply(?MODULE,init,[self()]). ?MODULE is macro evaluated to current module name but anyway It is external function call so this function have to be exported even there is ?MODULE used. In contrary
spawn(fun() -> init(self()) end).
is spawn with initial call to the func fun() -> init(self()) end which calls init/1 with result of function self(). It is local call which means init/1 may not be exported. There is another issue with it when self() is performed inside new process so you have to write
Self = self(), spawn(fun() -> init(Self) end).
to achieve same effect as in spawn(?MODULE, init, [self()]) where self() is evaluated as parameter of spawn/3.
well, the function broadcast is executed in a process. This function must therefore be accessible to all processes. Even though the same piece of source code, in the same module, spawns a process which goes to execute a function from the same module, that function must be exported out of that module to make it accessible.
That brings me to the difference between spawn(fun() -> broadcast() end). and spawn(?MODULE, broadcast, []). The later is called Spawning with MFA. In this method, the function must be exported out of the module so that it can be executed. The former is however unique, its a fun.
To understand this method: spawn(fun() -> broadcast() end)., we need to compare it with this one: spawn(fun() -> ?MODULE:broadcast() end). . Now, lets talk about the later of the two
spawn(fun() -> ?MODULE:broadcast() end). Now, here, if the function broadcast IS NOT exported in the module: ?MODULE, the process will crash. Note that in this case, the function is in the very module in which this piece of source code is written. spawn(fun() -> broadcast() end).In this case, the function must be in the very module in which this piece of source code is written. However, my thinking is this, the compiler converts this to the one above so that the spawned process is told that the function you are looking for is in module ?MODULE.I am not really a guru at compilers or run time systems, but i guess you will use this answer. My Strong advice is that in most of your source code, use this spawn(fun() -> ?MODULE:broadcast() end). or spawn(fun() -> some_module:broadcast() end). even though the piece of code or the function resides in that same very module than this: spawn(fun() -> broadcast() end).. From my personal experience the code becomes manageable and understandable. I get this nice illusion that a spawned process has to go and find the function from the specified module, and then execute with the given arguments.
Look more carefully at the warnings:
test.erl:8: Warning: function init/1 is unused
test.erl:8: Warning: variable 'From' is unused
test.erl:11: Warning: function broadcast/0 is unused
test.erl:12: Warning: variable 'Msg' is unused
The compiler thinks that your other functions are unused and never compiles them. Try this instead:
start() ->
spawn(fun() -> init(self()) end).
init(From) ->
spawn(fun() -> broadcast() end).

Exception error: undefined function in Mochiweb/Erlang

After seeing this article, I have been tinkering with mochiweb. While trying to replicate what's done in the article - basically setting up a mochiweb server, having two erlang nodes, and then calling a function defined in one node in the other (after setting net_adm:ping() between the two nodes so they know each other).
I was able to follow everything till that function call part. In n1#localhost, which is the mochiweb server, I call (just as done in the article):
router:login(IdInt, self()).
And then, in n2#localhost, which is the router.erl script, I have defined the login function:
login(Id, Pid) when is_pid(Pid) ->
gen_server:call(?SERVER, {login, Id, Pid}).
handle_call({login, Id, Pid}, _From, State) when is_pid(Pid) ->
ets:insert(State#state.pid2id, {Pid, Id}),
ets:insert(State#state.id2pid, {Id, Pid}),
link(Pid), % tell us if they exit, so we can log them out
io:format("~w logged in as ~w\n",[Pid, Id]),
{reply, ok, State};
I have pasted only the relevant parts of the code. However, when I now access the webserver on the browser - I get this error report on n1#localhost:
=CRASH REPORT==== 11-Jun-2009::12:39:49 ===
crasher:
initial call: mochiweb_socket_server:acceptor_loop/1
pid: <0.62.0>
registered_name: []
exception error: undefined function router:login/2
in function mochiconntest_web:loop/2
in call from mochiweb_http:headers/5
ancestors: [mochiconntest_web,mochiconntest_sup,<0.59.0>]
messages: []
links: [<0.61.0>,#Port<0.897>]
dictionary: [{mochiweb_request_path,"/test/123"}]
trap_exit: false
status: running
heap_size: 1597
stack_size: 24
reductions: 1551
neighbours:
=ERROR REPORT==== 11-Jun-2009::12:39:49 ===
{mochiweb_socket_server,235,{child_error,undef}}
After googling around, I got a basic gist of what the error is trying to say - basically it says that the login function being called in n1#localhost is not defined - but it is defined in n2#localhost (and both the nodes know each other - I did nodes(). to check) !! Please tell me where I am going wrong!
You are right - the code for router:login is not actually available on your host n1#localhost - it is the code within that function (the gen_server:call function) which routes the call to n2#localhost (via that ?SERVER macro) and that's where the real implementation is. The top level function is simply a way of wrapping up that call to the appropriate node.
But you do need at least the implementation of login
login(Id, Pid) when is_pid(Pid) ->
gen_server:call(?SERVER, {login, Id, Pid}).
available on n1#localhost.
(Updated)
You'd need to define, replace or us a ?SERVER macro as well. In the sample code in the article this is
-define(SERVER, global:whereis_name(?MODULE)).
but this uses the ?MODULE macro which would be wrong in your case. Basically when the gen_server process (router) is started it registers itself as ?MODULE, in this case that maps to the atom 'router' which other nodes can see (using global:whereis_name(router) ). So you should be able to just write:
login(Id, Pid) when is_pid(Pid) ->
gen_server:call(global:whereis_name(router), {login, Id, Pid}).
So the effect of calling login on n1#localhost would make a gen_server call to the router:handle_call method on n2#localhost, assuming the router gen_server process is running and has registered itself. The return value of that call comes back to your process on n1#localhost.
In the examples in your question it looks like you only loaded the router module on one node. Nodes do not by default automatically load code from eachother, so just because the function is defined on n2 doesn't mean you can call it locally on n1 (n1 would need to be able to load it in the normal way).
The code given looks like it copes properly with running in a distributed system (you can start the router server on one node and calling the API functions on other nodes will send router requests to the correct place). So you just need to put a copy of the router module on n1 and it should just work. It's possible to have n1 load the router module from n2, but it's a bit of a hassle compared to just giving n1 a copy of the module in its code path.
Interestingly, in the router code it's unnecessary to do gen_server:call(global:whereis_name(?MODULE, Message)). as the gen_server:call/2 function knows how to lookup global registrations itself. -define(SERVER, {global, ?MODULE}). would work fine if you changed the start_link function to start_link() -> gen_server:start_link(?SERVER, ?MODULE, [], []).

Resources