As promised, here is a short roundup of what proved helpful to me when we wrote our own mod_roster. I think this post may develop a bit over time, but now is just a quick reference of what to look at when you try to write your own mod_roster.
But first of all, why would you ever want to write your own mod_roster for ejabberd? Here is why:
We have a massive database of users, user information and relationship status already, we store this in postgresql and are quite happy about that state. When we started to build ESL Wire around an XMPP-Server (first OpenFire, now ejabberd) we were quickly facing the problem of having two databases that need to be kept in sync, but basically hold the same data. Since syncronization of _the same data_ is generally a bad idea and most of the time leads to inconsistencies we had to think up something different. Since we already planned of “opening” our infrastructure a bit, to make it easier to scale to different countries, users and customers, we decided to have a field-test with the jabber-server. By now we are talking to the ESL database using a webservice and parsing it’s JSON responses. All data is being kept in one place and we no longer have any inconsitencies. So basically what we have now is a mod_roster_json…of course this brings other issues to the table, but hey, let’s take it step by step!
So, what is important information?
Read the Jabber RFC and escpecially focus on all it’s “exceptions from the rule”, read up on gateway-interaction and be sure you know how the subscription states in jabber work.
* RFC3921
Then, look into the hooks ejabberd provides and what the functions that handle the hook need to return, this caused me a lot of headache, since I found this quite late…after a lot of reverse engineering other “mod_rosters”.
Relevant hooks for mod_roster are:
remove_user(User, Server) -> ok
resend_subscription_requests_hook(Acc, User, Server) -> [Packet]
roster_get(Acc, {User, Server}) -> [RosterItem]
roster_get_jid_info(Acc, User, Server, JID) -> {Subscription, Groups}
roster_get_subscription_lists(Acc, User, Server) -> {[FromSubscription], [ToSubscription]}
roster_in_subscription(Acc, User, Server, JID, SubscriptionType, Reason) -> bool()
roster_out_subscription(Acc, User, Server, JID, SubscriptionType, Reason) -> bool()
roster_process_item(RosterItem, Server) -> RosterItem
If you want to implement Nested Roster Groups, you will need this as well:
privacy_iq_get(Acc, From, To, IQ) -> {result, Packet} | {error, Error}
In this context the return values mean this (in examples):
ok = ok
Error = ?STANZA_ERROR/3
[Packet] = {xmlelement, ...}
[RosterItem] = #roster
Subscription = none | from | to | both | remove
Groups = [string()]
bool() = true | false
After this, implement the functions as wanted above, set the debug level to 5 in ejabberd.cfg
and put this everywhere you may need to see the current state of data:
?DEBUG("MY_MOD_ROSTER: DATA AT THIS POINT IS: ~p~n", [Data])
Now walk through your code step-by-step and have a look at the original mod_roster and/or
mod_roster_odbc to see what the hooks are supposed to do (will describe this here later).
* mod_roster.erl
* mod_roster_odbc.erl
Comparing these two should give you a good idea of what the functions are supposed to do and how you should handle the incoming data to get to what you need to return to satisfy the hook. Once I have a bit more time, I will describe in depth what the hooks are supposed to do.
Oh, and one more thing:
If you have “slow” transactions on where you store your data (webservice, for example) be sure to play around with the queing setting of your module, since this can lead to some strange results when you just changed a rosteritem but another thread was too fast and fetched the old one again. Do this by playing around whith this line:
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
See possible values here:
* no_queue: no thread is created to run the handler
* one_queue: one thread is dedicated to run the handler
* {queues, N}: N threads are created to run the handler
* parallel: one thread is created for each IQ received
Tags: ejabberd, howto, mod_roster, xmpp
Author: steam
[...] saw this very interesting post: http://dev.esl.eu/blog/2009/01/05/writing-your-own-mod_roster-in-ejabberd/ because I am now translating the zemblatapi project to erlang so that it can be integrated in [...]