Thursday, January 28, 2010

gen_client behaviour for building XMPP clients in Erlang.

 The gen_client project aims to provide a structured way to write XMPP client code in Erlang.  The framework heavily relies on exmpp, but also borrows some ideas from my favorite Strophe javascript library. The objective of the project is to create a set of generic behaviours  and let a client developer to "fill in  the blanks", i.e. to implement callback methods pretty much in the same fashion the code based on OTP/Erlang behaviours is written.
Why not to just use exmpp, one might ask? Sure you can. However, going from basic examples 
to decently capable client code is not so easy in exmpp. Motivation behind gen_client is to make coding XMPP in Erlang as effortless as Strophe does for Javascript.
One example of what I mean by "efortless" is how exmpp controls handling of incoming stream. By default, sending and receiving stanzas happens in a single process. Clearly, this is not very useful unless your XMPP client is happy with "question-answer" flow (as in echo_client.erl example from exmpp distribution), as opposed to asynchronous flow.
Of course, exmpp has means to assign a separate process for handling incoming stream (exmpp_session:set_controlling_process/2). However, it would be nice to have this as default, which is what gen_client does.
Continuing with this,  sometimes you may need a synchronous handling. For instance, you'd have to search through the whole tree of pubsub nodes, do some calculations and send results elsewhere. While this kind of task can be coded using callbacks, it does make coding much harder to deal with compared to sequential style. With gen_client, you can choose between asynchronous (gen_client:send_packet) and synchronous (gen_client:send_sync_packet) requests.

And of course, each incoming stanza will be handled by gen_client in a spawned process, so your client can do many things at once - we are using Erlang for the reason, right?

To start with gen_client, write your module that implements gen_client behaviour. And off you go:
gen_client:start(Username,  Domain,  Host,  Port,  Password, Module, [ModuleArgs]).

Summary of features that are already there:

  • Simultaneous handling of multiple incoming stanzas;
  • Synchronous and asynchronous requests;
  • Attaching IQ/presence/message handlers at runtime (somewhat similar to Strophe's addHandler style);
  • Support for ad-hoc commands (XEP-0050) and service discovery (XEP-0030);
  • Compatibility with exmpp and hence ability to reuse its codebase.

Documentation and examples will follow time permitting. This is the work in progress, mostly experimental, so please use with caution. Usual disclaimers are in place. Please share your thoughts and ask questions, if any. This code is being used in real projects, so I appreciate any feedback from you as means of moving gen_client to a production quality.