Jabber away with instant messaging

Add open source, XML-based instant messaging to your Java applications

Instant messaging has enjoyed phenomenal success as a person-to-person communication tool; in some instances, it has supplanted email as the preferred means of online communication. Now, developers are using this technology for application-to-person and application-to-application communication.

Until recently, only a handful of service providers controlled this technology; currently, the popular instant messaging services are communication islands based upon proprietary protocols. Implementers face a difficult decision: support multiple protocols or lock into a single one. Regardless of the choice, the implementer must depend on a server owned by the instant messaging (IM) service provider.

Open protocols can help developers break out of the proprietary trap. The advantages are various: Open protocols encourage development of competing implementations (some of them open source). They encourage widespread adoption of a common protocol, thus preventing the development of communication islands and isolationist approaches to service provisions. In many ways, open protocols made the Internet possible. In the instant messaging realm, open protocols ensure that the interoperability issues of closed systems and protocols won't stunt the growth of IM-based services.

Jabber is an open protocol for instant messaging and presence services. A primary candidate for a common protocol, Jabber has the potential to break the proprietary grip on instant messaging services.

This article will explain how to programmatically send simple Jabber messages and develop a simple notification service based upon open standards and open source APIs and products.

Why Jabber?

The Jabber standards and architecture help create a distributed IM system, reminiscent of the email systems distributed across the Internet, with users connecting to these systems locally. This approach is diametrically opposed to the monolithic system architecture provided by such current service providers as AIM (AOL Instant Messenger), ICQ, MSN (Microsoft Network), and Yahoo, where a single central server or group of centralized servers provide the messaging service. Jabber also resembles the email architecture in other ways: Jabber addresses its end-points (humans, machines, software) with an addressing scheme almost identical to the basic SMTP (Simple Mail Transfer Protocol) scheme. For example, myname@elogex.com is a valid Jabber address, or JID (Jabber ID) in Jabber parlance. For these reasons, Jabber-based systems scale better than existing proprietary systems. Additionally, the protocol allows for gateways to proprietary instant messaging services, should that become necessary.

Various Jabber server implementations, one of which we use in this article, are freely available, which means you no longer need to depend on a third-party IM service provider (third-party Jabber services are also available for those who require third-party hosted services).

Standardization

While discussing the benefits of the Jabber standards, I should mention the IETF (Internet Engineering Task Force) IM standardization efforts. At the time of this writing, the task force's IMPPWG (Instant Messaging and Presence Protocol Working Group) has various RFCs (requests for comments) available, the most important of which are:

  • RFC 2778: A Model for Presence and Instant Messaging
  • RFC 2779: Instant Messaging/Presence Protocol Requirements

The IMPPWG has also drafted an Internet standard for a protocol named CPIM (Common Presence and Instant Messaging). The Jabber protocol is also a draft Internet standard, though not part of the IMPPWG effort.

Where does Jabber fit into this standardization effort? According to the Jabber Website, Jabber is "committed to fully support any open real-time messaging protocols, including the IETF protocol"; if and when support for this IETF protocol grows, Jabber aims to position itself as a "leading open source platform" for the IETF protocol. So far, the IETF effort has concentrated mainly on gathering requirements rather than implementation. For the moment, Jabber is the only open instant messaging and presence service protocol with significant open source support. As a result, it has become the de facto standard for open instant messaging.

Another contender to watch out for is Sun Microsystems' Jxta protocol, another XML-based protocol aimed at peer-to-peer (P2P) application developers. Various Jxta implementations are available today; however, due to its relatively recent genesis, Jxta has achieved less traction than Jabber.

See Resources for links to these standards documents and the Jabber project's full statement on IETF support.

Download and install

To get started with Jabber, you first need to download the necessary tools: you need a Jabber server, a Jabber client, and an API that helps manage and hide some of the complexities of socket management, XML parsing, message creation, and so on.

The Jabber server

For the purpose of getting yourself up and running with Jabber, the Jabber server you choose doesn't matter, since they all accept standard Jabber XML and communicate with the end-point application to deliver the payload, which is also standard Jabber XML. jabberd, the original Jabber server implementation, is open source (though not Java based), simple to install and configure, and available on many platforms, including various flavors of Unix, Linux, Windows, and Mac OS X. The JabaServer open source project is also worth mentioning, though at the time of this writing, this Java-based project is not as mature as jabberd. Also, JabaServer installation is less straightforward because you must download, install, and configure a third-party database, plus create the necessary database schema.

For this article's example, I chose jabberd. While both binary and source downloads are available for jabberd, I won't describe how to build a source distribution here. Download the binary distribution, unless you really want to compile your own, from the jabberd homepage. Installation on Windows platforms is relatively easy; the distribution is an .exe—just execute and follow the installer instructions.

After installation, you should have little or nothing to configure. On Windows 2000, no configuration was required. Just double-click the binary, and the server starts up.

The user agent/client

I decided to use the Exodus client, another open source technology, for this project. I especially like Exodus's debug tab, which allows you to see exactly what XML the client sends and receives. An added bonus: You can type Jabber messages as pure XML and send them to the server. All this proves useful for testing and experimenting with the Jabber protocol and server.

Exodus installation is straightforward. Download the Exodus zip file (I used version 0.6 for this article). Extract the file's contents directly to the directory where you want to install the client. In this release, the file's contents are simply the Exodus binary and a .dll file. For Linux- and Mac-based clients, check out the list of clients at Jabber.org. You can download the Exodus binary from the project's SourceForge homepage.

Jabber API

Probably the best of the current open source Jabber APIs is Muse. Muse provides an easy-to-use high-level API that handles low-level issues such as socket connection and management, as well as conversion of message text to XML for delivery to the Jabber server. This API is not just for Jabber; Muse provides a Java API for abstract access to other services as well, including Yahoo Messenger, ICQ, Napster, AIM, Gnutella, and XML Remote Procedure Call (XML-RPC). The latest release is 0.73a1; however only Jabber, Napster, Gnutella, and XML-RPC are currently represented in the Muse codebase. Other API implementations, such as JabberBeans, are available, but basic Jabber support functions well in release 0.73a1.

Download the Muse API from the Muse homepage.

Send your first Jabber message

To send a Jabber instant message programmatically, you must initialize the Muse Jabber API. Do that by creating an instance of the JabberContext class, then use the context as a parameter to the createSession() method on the Jabber session factory class:

1  // Initialize the Jabber context
2  JabberContext jabberContext = new JabberContext("user", "pass", "localhost");
3
4  // Create an instance of the Jabber session factory
5  Jabber jabber = new Jabber();
6  // Create the new session
7  JabberSession jabberSession = jabber.createSession(jabberContext);

The above example shows the creation of a new context at line 2. The JabberContext stores specific user-related information (username, password, server address) and also contains a unique session identifier when the session is later established using the context. For illustration purposes, I have hardcoded the username, password, and server.

At line 5, a Jabber session factory is created, which we use at line 7 to create a new JabberSession, Muse's main interface into the services offered by the Jabber server. The server's main services are:

  • Connection services: For connecting to and disconnecting from the Jabber server
  • User service: For user authentication and registration
  • Presence service: For receiving presence information from other users/services and broadcasting your own presence
  • Roster service: Jabber-speak for a buddy list or address book
  • Chat service: Sends messages of various types—group chat, private messages, headlines, and so on
  • Server service: Obtains information relating to the services offered by this Jabber server
  • Client service: Obtains information about other users, such as the last time the user signed in

Now that we have an initialized Jabber session, we can use it to connect to the Jabber server using the connect() method on the JabberSession object we just created:

8  // Connect to the server
9  jabberSession.connect("localhost", 5222);

To connect to a Jabber server, we specify the address and port number of the machine on which the server is located. The standard, default Jabber port number is 5222 and should rarely change.

Now that the JabberSession has connected to the server, we can log in with the login() method on the user service:

10  // Log in to the Jabber server
11  jabberSession.getUserService().login();

At line 11, we use the JabberSession to obtain a reference to the UserService, then call the login() method on the user service. Notice that the method itself did not specify any user information. login() obtains this information from the JabberContext associated with the JabberSession when the JabberSession was created at line 7 above.

Now that we have successfully logged in to the Jabber server, we can start sending and receiving messages. The following code fragment shows how to construct a simple headline-style message:

12  // Construct test message
13  JabberChatMessage msg = new
14    JabberChatMessage(JabberChatMessage.TYPE_HEADLINE);
15  msg.setSubject("Hello world");
16  msg.setBody("Hello world");
17  msg.setTo("user2@localhost");

At line 13, we create a JabberChatMessage instance. The single parameter specifies the type of message we require: TYPE_HEADLINE. The name of the JabberChatMessage class is a little misleading because, in fact, it might be used to contain any of the four types of messages—normal, chat, headline, and error—defined in the Jabber protocol. At line 15, setSubject() and setBody() specify the subject and body texts, respectively. Finally, setTo() sets the JID of the message's receiver at line 17.

Under the covers, the JabberChatMessage converts all of this information into an internal DOM (Document Object Model) tree so the XML can generate easily when we are ready to send the message to the Jabber server.

The final step: Send the message using the sendMessage() method:

18  // Send the message
19  jabberSession.sendMessage(msg);

Under the covers

As evidenced from the above example, the Muse API effectively hides all the details related to connection management and XML parsing, thereby allowing you to concentrate on the task at hand: creating a messaging service. However, understanding some of the underlying protocol exchanges proves useful. Let's take a look at the XML exchanges that occur when we connect to the server, log in, and send the message as described in the code above. In the following XML exchange, the messages received by the client (our example code) are prefixed with RECV, and messages sent to the server are prefixed with SEND:

SEND: <?xml version="1.0" encoding="UTF-8" ?>
        <stream:stream to="localhost" 
         xmlns="jabber:client" 
         xmlns:stream="http://etherx.jabber.org/streams">
RECV: <stream:stream from="localhost" id="3D160545">

All Jabber exchanges occur in the context of an XML stream. During the lifetime of the connection between our client code and the Jabber server, two complete XML documents transfer a fragment at a time. The initial exchange shown above allows the client to start sending the XML-streamed document to the server and the server to start sending the XML-streamed document to the client. You can find the full details of XML streaming in various reference texts and, of course, the Jabber specification (see Resources).

Next, a request for authorization information is sent to the server:

SEND: <iq xmlns="jabber:client" type='get' id='id_10028'>
        <query xmlns="jabber:iq:auth">
          <username>user</username>
        </query>
      </iq>
RECV: <iq xmlns="jabber:client" type='result' id='id_10028'>
        <query xmlns="jabber:iq:auth">
          <username>user</username>
          <password />
          <digest/>
          <sequence>482</sequence>
          <token>3D15E63A</token>
          <resource />
        </query>
      </iq>

The preceding fragment shows the first exchange in the user authentication process. To begin, the client asks the server what authentication methods are available for the given user. The server fragment answers with the following authentication methods:

  • Plain text: <password /> tag
  • Zero-knowledge authentication: Uses the <sequence> and <token> tags
  • <digest/>: Like plain text, but the password is SHA-1 (Secure Hash Algorithm)-encoded with the user's plain text password

We actually use <digest/>, as illustrated in the following fragment:

SEND: <iq xmlns="jabber:client" type='set' id='id_10030'>
        <query xmlns="jabber:iq:auth">
          <username>user</username>
          <hash>425c73373237061edcc5f23ba239c6cc69556f5c</hash>
          <resource>Home</resource>
        </query>
      </iq>
RECV: <iq xmlns="jabber:client" type='result' id='id_10030'></iq>

At this point, the user is logged in to the server and can begin sending or receiving messages:

SEND: <message xmlns="jabber:client" type='headline' 
       id='id_10032' to='user2@localhost'>
        <thread xmlns="jabber:client">id_10033</thread>
        <subject xmlns="jabber:client">Hello world</subject>
        <body xmlns="jabber:client">Hello world</body>
      </message>

The above fragment shows the test headline message we send to the receiver user2@localhost.

You might have noticed one recurring element in the preceding protocol fragments: the ID tag. Because the session can consist of multiple asynchronous conversations, the ID tag matches queries to responses.

Receive messages

Since this article's main thrust is to show you how to develop an alerting system, I have focused little on message receiving. In the interests of completeness, however, I should discuss some of the features available for handling received messages in the Muse API. In addition, because the alerting service is implemented as a user that logs in to the Jabber server, you could reasonably expect some of the message receivers to try to communicate with the sender.

Muse uses a listener attached to the session to receive notification of incoming messages. The following code shows how to create a listener and attach it to the session—the listener is actually attached to the connection, but since our interface is at the session level and the JabberSession class has a convenience method to add a listener, we assume it is attached to the session:

1  jabberSession.addMessageListener(
2    new JabberMessageListener() {
3      public void messageReceived(JabberMessageEvent event) {
4        if (event.getMessageType() == JabberCode.MSG_CHAT ) {
5          JabberChatMessage msg = 
6            (JabberChatMessage)event.getMessage();
7          JabberChatMessage reply = new 
8            JabberChatMessage(JabberChatMessage.TYPE_HEADLINE);
9          reply.setTo(msg.getFrom());
10         reply.setSubject("Re: "+msg.getSubject());
11         reply.setBody(
12          "I'm just a sender: please send messages to someone else");
13
14         // Send the message
15         jabberSession.sendMessage(reply);
16       }
17     }
18   }
19 );

The client can add several message listeners, all of which would be called each time a message is received. Typically, you would add a listener for each type of message to process. Another option: Attach multiple listeners for the same message type but for each distinct action on that message. Yet another option would attach a listener that processes all message types. The choice depends on what you want to achieve.

In the example above, a single listener is attached. The listener must be an instance of a class that implements the JabberMessageListener interface. This interface requires a single method to be implemented: void messageReceived(JabberMessageEvent). This method will be called when a message is received from the server. In the sample code, the messageReceived() method implementation first checks the message type received at line 4. Only MSG_CHAT type messages are of interest, since Jabber user agents typically use that type to send messages. At line 5, the Jabber message is extracted from the JabberMessageEvent. The received message is then used to populate a reply by setting the recipient to the message's sender at line 9 and copying the received message's subject line at line 10. A default error text is set as the message's body at line 11, and, at line 15, the message transmits in the same way as in previous examples, with the JabberSession object's sendMessage() method.

Take a stab at Jabber

This article has provided a basic introduction to the Jabber protocol and a few of the open source projects that you can use to leverage this technology for your application. The applications available for this protocol are varied and are not just restricted to the realm of instant messaging in the traditional P2P sense. For example, jogger.jabber.org offers Jogger, an interesting service that allows you to update a personal Web journal by sending a message to the Weblog application (blog). Jabber could also have a big impact on customer service applications and other structured collaboration-style applications.

As shown in the examples above, you can achieve instant messaging functionality in just a few lines of Java code using an API such as Muse or JabberBeans. Bear in mind that I did not cover some of the other important aspects of Jabber, such as presence services (is the correspondent online and available?) and roster management, both of which are relatively simple to use and program. Another important aspect is the Jabber server's modularity and extensibility, which allows a developer to create new services integrated directly into the server. I'll let you experiment with those aspects on your own.

Jason Kitchen has more than 12 years' experience in software development, playing such diverse roles as software engineer, team lead, manager, tester, and freelance consultant. Born in the UK, Jason has lived in five countries and worked in a broad range of industries, including education, telecommunications, government, banking, and logistics. He is currently employed as a lead engineer for Elogex, based in Charlotte, N.C.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more