Skip to main content

Instant Messaging in Java Made Easy: The Smack API

October 5, 2006

{cs.r.title}







Instant messaging (IM) needs little introduction. Just in case you've been stuck on a desert island for the last ten years, IM is a well-known and widely used technology which allows people (or software) to exchange messages over a network in real time. Using a loosely coupled client-server architecture, IM clients send their messages over the internet to a central server, which in turn forwards them to the appropriate recipient or recipients. Instant messaging services are regularly used by hundreds of millions of users worldwide.

There are many, many IM clients around. Companies such as AOL, Microsoft, and Yahoo provide IM services based on proprietary messaging protocols, and also provide popular client software using these protocols. Many third-party clients also work with these protocols. However, these protocols remain proprietary and closed, which tends to make them difficult to work with for a developer.

Jabber is an open, XML-based standard for instant messaging, providing a non-commercial alternative to these proprietary messaging services. With an estimated user base of over 21 million, Jabber is a widely used protocol, especially in the open source world. Jabber is also the protocol used by Google's new IM client, Google Talk. There are hundreds of (mostly free) Jabber clients available, as well as many open source and commercial servers.

There are many potential reasons you might want to incorporate instant messaging features into your application. IM techniques provide an excellent, lightweight way of sending notification messages, or to allow users to chat with each other from within an application. Or, of course, you may simply want to write your own killer IM client application!

In this article, we will look at the Smack API, an easy-to-use open source Jabber API that you can use to integrate instant messaging and chat features into your Java applications.

Jabbering Online: The Basics of Jabber

Jabber is based on a simple XML protocol, known as the XMPP protocol. Transmitted in XML form, Jabber messages are lightweight, human-readable, and easy to understand, which is a boon for IM application developers. There are several different types of messages available, including chat and groupchat messages used for typical IM communications, but also the email-like normal messages, as well as the ticker-tape-style headline messages.

Let's start with an example. Here is a typical XMPP message:

  [prettify]     <message to="tom@server.com" type="chat">         <thread>thread1234</thread>         <body>Hi Tom!</body>     <message>[/prettify]

As we can see, the protocol is indeed simple. An XMPP message uses (appropriately enough) the <message> root element, which takes a number of attributes and embedded elements describing the message details. The principal attributes are type, which indicates the type of the message being sent (chat, groupchat, normal, or headline), and to, which is used to designate the message recipient. The main embedded element is the <body> element, which contains the actual contents of the message. Other attributes, such as the optional id attribute and <thread> element, are sometimes used for routing purposes, such as to identify the appropriate chat session for a given message.

Introducing the Smack Jabber API

With such a simple protocol, you could very well write and transmit the Jabber/XMMP XML messages by hand. Indeed, many Jabber clients do just that. However, it would be nice if a high-level Java API existed to hide all the nitty-gritty details. In fact, one does: the Smack API.

The Smack API is a free Jabber API entirely written in Java. The API is open source under the Apache license, and is written and maintained by Jives Software, a company that specializes in open source messaging solutions. They also provide an open source IM server (Wildfire) and client (Spark), also based on Jabber and written in Java. This elegant API encapsulates the XMMP message protocol beneath a set of intuitive high-level classes such as Chat and GroupChat.

Sending Your First Jabber Message

Let's start off with a simple example: sending a message to a connected user. The first thing we need to know is the address of the Jabber server we are using. This could be one of the numerous public Jabber servers, such as Jabber.org, or it could be a local Jabber messaging server hosted within your organization. There are many free and open source Jabber messaging servers available, such as the Wildfire server we mentioned earlier.

  [prettify]     XMPPConnection connection = new XMPPConnection("jabber.org");     connection.login("user", "password");[/prettify]

Once you have opened a connection, things start getting interesting, and we begin to see how simple the Smack API really is to use. Indeed, sending a message to another user can be as simple as this:

  [prettify]     Chat chat = connection.createChat("tom@jabber.org");     chat.sendMessage("Hi Tom!");[/prettify]

Here we create a new chat session with Tom (if he is connected), and send him a short message. The Chat object is used to keep track of the chat session. The sendMessage() method is a convenient shorthand method for sending text messages in the context of a given chat session. A chat session is identified by a unique thread ID, which should be used in all messages in the context of this chat session. The sendMessage() method takes care of this sort of detail.

You can also use the Message class to gain a finer control over the messages you send. This class is pretty much a direct Java encapsulation of the XML <message> element we saw earlier. It has the advantage of providing direct access to all of the message attributes, such as message type. Here is how you might send a notification in the form of a "headline" message. Headline messages are one-off messages; no answer is expected, and they are not sent in the context of a chat.

  [prettify]     XMPPConnection connection = new XMPPConnection("jabber.org");     connection.login("user", "password");     Message message = new Message();     message.setTo("tom@jabber.org");     message.setSubject("Server down");     message.setBody("The 'jupiter' server has just gone down");     message.setType(Message.Type.HEADLINE);     connection.sendPacket(message);     connection.close();[/prettify]

Although it is the most widely used, the XMPPConnection class is not the only way to establish a Jabber connection in the Smack API. Other, more specialized connection classes also exist. The SSLXMPPConnection, for example, lets you securely connect to a Jabber server using an SSL-encrypted connection. You can even connect to Google's new Google Talk, using the special GoogleTalkConnection class. You use this connection in much the same way as you would any other Smack connection. Here is an example:

  [prettify]     GoogleTalkConnection connection = new GoogleTalkConnection();     connection.login("scott", "tiger");     connection.createChat("tom@gmail.com").sendMessage("Hi Tom!");[/prettify]

Is Anybody out There? Detecting Presence

When you want to chat with someone, it's always nice to know who's connected. When you set up an account on an IM server, you generally set up a list of people you would like to talk to. These people are known, depending on your client software, as contacts, friends, buddies, or some similar term. In the Smack API, this list is known as a roster.

You can see who is in your own roster, and who is connected, by using (yes, you guessed it!) the Roster class. A Roster contains a list of RosterEntry items, which you can obtain as follows:

  [prettify]     Roster roster = connection.getRoster();     Iteration iter = roster.getEntries()     while (iter.hasNext()) {         RosterEntry entry = (RosterEntry) iter.next();         System.out.println(entry.getName() + " (" + entry.getUser() + ")");     }[/prettify]

Running this code might produce something like the following:

  [prettify]     tom (tom@jabber.org)     dick (dick@jabber.org)     harry (harry@jabber.org)[/prettify]

If you know who you are after, you can also use the getEntry() method to find the person directly via their account ID:

  [prettify]     RosterEntry entry = roster.getEntry("tom@jabber.org");[/prettify]

Once you know who is in your list, you may want to know if they are currently online or not. You can obtain this information by using the Presence class. The following code, for example, checks the status of a given user:

  [prettify]     Presence presence = roster.getPresence("tom@jabber.org");     if (presence.getType() == Presence.Type.AVAILABLE) {        // Tom is online...     }[/prettify]

This will give you the status of a user at a given point in time, but you may need to have this information in real time; for example, to update a list of contacts in a client application. You can get this information in real time by implementing the RosterListener interface, as shown here:

  [prettify]     roster.addRosterListener(new RosterListener() {         public void entriesAdded(Collection arg0) {             // New contacts have been added             ...         }         public void entriesDeleted(Collection arg0) {             // Contacts have been removed from the roster             ...         }         public void entriesUpdated(Collection arg0) {             // Contact details have been updated             ...         }         public void presenceChanged(String arg0) {             // The presence status of this user has changed             ...         }     });[/prettify]

Roger, Roger: Receiving Incoming Messages

It's one thing to be able to send messages, but you may also need to be able to receive and analyze them, as well. Using the Smack API, you can handle incoming messages in one of two ways, depending on your application architecture and needs. The first approach is to actively poll a queue of incoming messages whenever necessary. The second possibility is to set up a listener to intercept and process incoming messages as needed.

The first approach uses a synchronous processing model with the PacketCollector class. The basic principle is to define a PacketFilter object to limit the messages to the ones you really want to process, and then to set up a PacketCollector object that can be used, when necessary, to retrieve or poll for collected messages. First, you need to set up the collector object:

  [prettify]     // Accept only messages from HQ     PacketFilter filter         = new AndFilter(new PacketTypeFilter(Message.class),                         new FromContainsFilter("headquaters@mycompany.com"));     // Collect these messages     PacketCollector collector = connection.createPacketCollector(filter);[/prettify]

Next, generally in another part of the application, you will retrieve the collected results. You can retrieve the incoming messages in two ways. Using the nextResult() method will block the application until a matching message is received:

  [prettify]     Packet packet = collector.nextResult();     if (packet instanceof Message) {         Message msg = (Message) packet;         // Process message         ...     }[/prettify]

Alternatively, if you use pollMessage() instead, the collector will simply be polled: the application is not blocked, and if no message is ready, the method will simply return null.

The second approach uses an asynchronous approach based on the PacketListener class. As in the previous example, you define a PacketFilter object to limit the messages to the ones you really want to process. Then you add a PacketListener object that handles the incoming messages. Since this approach uses a Listener pattern, the messages will be processed asynchronously, as they arrive. Here is an example of this approach in action:

  [prettify]     XMPPConnection connection = new XMPPConnection("jabber.org");     connection.login("user", "password");     // Accept only messages from HQ     PacketFilter filter         = new AndFilter(new PacketTypeFilter(Message.class),                         new FromContainsFilter("headquaters@mycompany.com"));     PacketListener myListener = new PacketListener() {         public void processPacket(Packet packet) {             if (packet instanceof Message) {                 Message msg = (Message) packet;                 // Process message                 System.out.println("Roger, Roger");                 ...             }         }     };     // Register the listener.     connection.addPacketListener(myListener, filter);[/prettify]

Conclusion

If you need to work with a Jabber IM service, the Smack API has a lot going for it. It is free and open source; the code is clean, simple, and readable; and the API is well documented. Its high-level classes let you code simple functions quickly and efficiently, without hiding the details if you need to get down to the nitty-gritty. And it's comprehensive coverage of the Jabber protocol mean that you will be able to do pretty much anything you want.

Resources

width="1" height="1" border="0" alt=" " />
John Ferguson Smart is a freelance consultant specialising in Enterprise Java, Web Development, and Open Source technologies, currently based in Wellington, New Zealand.