Programming, Life and everything else

Menu

Tag Archives: Xmpp4r

One of the things that I wanted to understand and build since I first learned to program was to build a chat client. Something that would allow people to communicate and I am extremely thankful to Rishav Rastogi for introducing me to XMPP.

I never really understood all the moving parts very clearly during my first interaction with the technologies but with some time on my hands now I decided to revisit the entire process of building a web chat client. While there are a few well documented resources that cover how to build a simple web chat client the information is mostly directed towards using XMPP and building a one-on-one chat.
Though the requirements for building a multiuser chat aren’t significantly different there are subtle differences that exist.

The technology was initially called Jabber and hence both Jabber and XMPP are used interchangeably on several posts. – Wikipedia

Ejabberd
Ejabberd is a Jabber/XMPP server built using Erlang and is open-source and we would be using Ejabberd in this example. Another popular alternative for Ejabberd is Openfire – Ejabberd

Xmpp4r
Since this post would be using Rails we use the xmpp4r gem which is a wrapper over the standard XML that XMPP/Jabber/Ejabberd uses, thus allowing us to work with Ruby rather than generate XML. For those using Ruby 1.9.2 the gem installation may throw up some errors while installing the Rdoc so I’d recommend you either skip the Rdoc installation or ignore the error. The online documentation for Xmpp4r is pretty good and the gem comes with some useful examples that could help you get started.

OrbitedOrbited provides a pure JavaScript/HTML socket in the browser. It is a web router and firewall that allows you to integrate web applications with arbitrary back-end systems. – Orbited

Why do we need Orbited?
With our existing arrangement (once we install Ejabberd and xmpp4r gem) we could get a basic messaging system ready. We could have users send messages and receive messages. The problem would be to receive those messages on the browser. There is no way we can display those messages without having to poll our server to fetch this information and we know polling could cause scalability issues. Orbited fills this void by acting as a web router that routes the incoming messages to the appropriate user’s browser using a technique called as long-polling. And long-polling is more scalable than polling.

Long-PollingComet is a broad term used for technologies like Long-Polling and streaming. While traditional polling requires periodic requests to be sent to the server and then return with the response, in long-polling a connection is established with the server which persists until a response is provided (or the request times out). Once the response is provided the connection is closed and a new one is step up waiting for the next response from the server. Similarly a new connection is set up on timeout. In Streaming the connection persists between the client and the server while the information is transferred.

According to HTTP 1.1 a browser is allowed to have only 2 connections to the server one of which is used here for real time communication, though I am not fully clear if this is exactly the way the connection is setup. Apparently IE 8 allows 6 connections per host so I shall look forward to any clarifications on this.

Orbited comes with support for technologies such as STOMP, IRC and XMPP so its a handy tool to get started with.

Installation

Installing Xmpp4r
This is the easiest part especially with Rails 3. The following is a snippet of my gemfile

You can download the installer from here. At the time of this tutorial the lastest version was 2.1.6.

The installer guides you on how to setup the xmpp server. Here are some of the questions you would have to provide answers to
Domain: siddharth-ravichandrans-macbook-pro.local This is simply a name (domain name) that you would want your server to be known by. In production this could be chat.example.com or jabber.example.com. For development the default is good. Its important that you note down the domain name somewhere as you will be using this a lot.

Cluster Installation: NO

Admin: siddharth This could be any name that you choose. This provides a way to access the ejabberd web administration interface
Admin password : siddharth

Thats it, you have your ejabberd server installed. Now open the folder you installed it in and navigate to the bin folder.

./ejabberdctl start

SIDDHARTH-RAVICHANDRANs-MacBook-Pro:bin SIDDHARTH$ ./ejabberdctl status
The node ejabberd@localhost is started with status: started
ejabberd 2.1.6 is running in that node

You may log in as [AdminUser]@[domain] followed by the password.
In my case ‘Siddharth@siddharth-ravichandrans-macbook-pro.local’ with the password ‘Siddharth’

You should now be able to see a web console for the administrator

Installing Orbited

The version of Orbited that I used was 0.7.10 which is available here. Ensure that you have python 2.5 or higher installed in your system. Most linux and OS X systems come with Python pre-installed. You can check by

For the reactor epoll would the one to select on Linux machines and Kqueue for OS X but I noticed that Kqueue has not been maintained and throws errors so using select is the last resort. Though select has scalability issues its okay to use it for development.

Set the user to the user that you would want orbited to run as.
The access section identifies how orbited will communicate with Ejabberd
Orbited will listen to all incoming requests at port 8000 and communicate with port 5222 with XMPP (Ejabberd uses 5269 for server to server communication)
Therefore

Once you’ve conquered the basics of user subscription and sending messages lets take a look at the Multi User Client support provided in Xmpp4r.

Registering our users to the Jabber server. Ideally this would be after a user registers to your site, so an after_create operation.

require 'xmpp4r'
require 'xmpp4r/muc'
require 'xmpp4r/roster'
require 'xmpp4r/client'
# getting done with all the requires so you can try this on the console
client = Jabber::Client.new(Jabber::JID.new('first_user@siddharth-ravichandrans-macbook-pro.local'))
client.connect
client.register('password')
# do the same for another user with full_jiid = 'second_user@siddharth-ravichandrans-macbook-pro.local'

Logging into the server

require 'xmpp4r'
require 'xmpp4r/muc'
require 'xmpp4r/roster'
require 'xmpp4r/client'
# getting done with all the requires so you can try this on the console
client = Jabber::Client.new(Jabber::JID.new('first_user@siddharth-ravichandrans-macbook-pro.local'))
client.connect
client.auth('password')
# Don't forget to log both users in

Logging into a room/ creating a room
The MUC Client is a multi User chat Client. The XMPP4R gem provides support for MUC too.

Create a new client

muc = Jabber::MUC::MUCClient.new(client)

The MUC is not to be confused with the room. Its simply a client that serves as an interface for the user in a particular room.

This lets the user join a room called chatroom and the user is logged in to the room as client.jid.node which evaluates to first_user in our case.

The domain appends the word conference by default to all multi user chat rooms and can be changed by editing the configuration file. The JID for a room can be split as ROOM_NAME + @ + conference.domain_name/user_nick

The callbacks like the one described earlier in François Lamontagne’s two part tutorial get called when a new user joins the chat room, sends a message to the room or leaves the chat room. The MUC chat is actually very similar to the one – on – one chat example described in François Lamontagne’s example except that when a message is directed to the room it relays the message to all of the members in the room. So if you look at the xml you will notice that a message directed to the room is eventually directed to each user in the chatroom. The only difference is the send method which belongs to muc object takes care of the relaying or you may query the roster (I will come to this in a moment) to identify the members in a room and post a message to each member.

Sending a message to the room

muc.send(Jabber::Message.new('chatroom@conference.siddharth-ravichandrans-macbook-pro.local', 'Pink Floyd is the greatest band ever'))

The MUC roster is extremely useful and allows you to set callbacks too.

This pretty much wraps my example using Ejabberd and XMPP4R. The next part of my post will briefly describe how we can use orbited and have this information flow through the browser.

Starting Orbited

orbited

Would get orbited up and running if you placed the orbited.cfg in the /etc folder. Once orbited is running you can log onto http://localhost:8000/static where you would be able to the see the javascript files that Orbited provides you with. You will notice Orbited.js and a static folder. Jump into the static folder -> then protocols -> Xmpp -> to find the xmpp.js file. We will be working primarily with these two files.

So first lets make these two files available to our application by putting them in a layout file.

You will notice two partials at the bottom of my layout file called _tcpsocket and xmpp_client (both poorly named).

Before we begin try running this snippet obtained from Micheal Carter’s Sockets in the Browserarticle on CometDaily.com. Add this snippet to the _tcpsocket.html.erb partial that is included in the layout.

Load a view page (which includes the layout containing this parital). It could be any scaffold generated code block.

This would be a helpful example to understand better what Orbited does. All it does is opens a tcp socket on localhost and connects to port 5222 . The onopen callback is called when the connection is opened and sends a piece of text which is read by the onread callback and the connection close callback is called.

Basically reading the data whenever something is sent by the server while waiting for it with an open socket connection is what we do.

You may ignore the document.domain = document.domain code for now. Here we include the Orbited.js code along with the Xmpp.js javascript files provided by Orbited. We also specify the port we would be listening to and the hostname.

The xmpp.js file is where all the magic (not really) happens.

The xmpp.js contains (yet another) javascript based interface to XMPP methods, thus allowing us to perform all the XMPP operations right from the browser. The existing xmpp.js file comes with partial support for MUC operations. A poorly and hastily hacked xmpp.js file to suit basic MUC operations is available on my github account.

If you notice these methods end up generating the exact same XML code (converted to utf8) and sent to the ejabberd. So no real magic there.

Our goal is to now use this API to perform the same operations on the browser. Here is a basic MUC chat javascript. Add this to the _xmpp_client.js on the layout file. The snippet contains some missing text so use the code here

Note that the xmpp.js file has been modified slightly from what Orbited provides us and the _xmpp_client.html.erb uses this modified api hence the method parameters may appear strange when compared with the original xmpp.js file.

The ROOM_NICK parameter is defined in the view using a content_for :js block and would be available inside a chat room.

I hope this is useful and please let me know of errors or misinformation in my article. In case you are interested in having a detailed write up on the installation of all the software, add a comment and I will send you the write up as soon as possible. I have tried my best to attribute most references to their original authors and sources but in case I have forgotten any I would be glad to update it anytime.