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.
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, firstname.lastname@example.org 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).
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.
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,
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
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: <?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: