Instant Messenging with ColdFusion, Jabber and Smack

I noticed a couple different people wanting information on instant messenging with ColdFusion and then in another place instant messenging with Jabber and ColdFusion in the last couple days (for reasons beyond me) [first on cfguru, then on my blog, then on the jive software forums]. For those that care I put together a quick and ugly web-based instant messenging interface to Jabber using an open source Java library called Smack. You can download the source code at the end of this article or follow along as I describe the process.

Setup: 1) You need to download the Smack library and place the smack.jar and smackx.jar files into a directory acccesible to ColdFusion (commonly /WEB-INF/lib/). 2) You need to setup a couple Jabber accounts. You can either download, install and run your own jabber server or create a free account on jabber.org (or one of the many other public jabber servers). 3) You need to get a Jabber client (I’m using Exodus).

There are 2 (technically 3) parts to the interface. The easiest is a standard Application.cfm that creates an application that enables us to use sessions. You need sessions because you need to be able to persist the Jabber chat and connection objects across requests.

The second part is composed of 2 documents: a) the default page that presents a form for sending a message and has an iframe to hold the conversation and then b) the iframe that holds the conversation and then initializes the Jabber connection and chat objects. I’ll show the iframe first.

The iframe is split up into 2 parts: initialization and display. The first thing it does is check to see if the connection object exists in session scope:

// if no connection exists, create one
if (NOT structKeyExists(session, "connection")) {

If the connection doesn’t exist, the following block of code is run:

// create a connection to the jabber.org server
XMPPConnection = createObject("java", "org.jivesoftware.smack.XMPPConnection").init("jabber.org");
// login to the jabber server
login = XMPPConnection.login("username", "password");
// create a chat w/ another user
chat = XMPPConnection.createChat("user@domain.com");
// store the connection and chat objects in session scope
session.connection = XMPPConnection;
session.chat = chat;
// init an empty string to hold the conversation
session.conversation = "";

The block above creates a Smack XMPPConnection to the Jabber server (in this case I’m using ‘jabber.org’, but you should change this to be whatever server you have an account on). Next I call the login(username,password) method to login to the Jabber server and then I create a Smack Chat object by called the createChat() method on the connection. Because I need the chat and the connection to persist (in order to chat with someone), I store both in session scope. Finally, I create a session variable called ‘conversation’ that will store the conversation between the 2 chatting clients.

If the connection already exists in session scope, another block of code is run:

// get the connection & chat objects from the session
connection = session.connection;
chat = session.chat;
// retrieve the message using pollMessage() (which is nonblocking)
nextMessage = chat.pollMessage();
// if no message exists 'nextMessage' will be undefined (I think 'null' technicall)
if (IsDefined("nextMessage")) {
  // message does exist, add it to the conversation
  session.conversation = session.conversation & "<br /><strong><font color='blue'>" & chat.getParticipant() & "</font></strong> : " & nextMessage.getBody();
}

I retrieve the Smack XMPPConnection and Smack Chat objects from the session, and then call the pollMessage() method on the chat object. It’s important that I use the pollMessage() method instead of the nextMessage() method because the pollMessage method is non-blocking, it won’t wait until a message shows up from the person I’m chatting with, it returns immediately if no message exists. If the pollMessage() method returns a Smack Message object (which in Java will either return a Message or null), then I add the message to the conversation and finally display the conversation:

writeoutput("<html><head><title></title>");
writeoutput("<META HTTP-EQUIV='Refresh' CONTENT='1'>");
writeoutput("</head><body>");
writeoutput(#session.conversation#);
writeoutput("</body></html>");

You’ll notice that I’m having the iframe page refresh itself every 1 second, it would probably be better to use some JavaScript and a one pixel image to do the trick, but this works for now.

The last step is to create the page that holds the iframe and that presents the form for sending a message. The iframe is simple:

<iframe src="frame.cfm" name="chatwindow" id="chatwindow" width="800" height="500" marginwidth="5" marginheight="5"></iframe>

and then some code to handle a form post, retrieve the chat and connection objects from session scope, send the message to Jabber and then add the message to the conversation:

// if we have a form post
if (IsDefined("form.formPosted")) {
  // grab the connection and chat objects from the session
  connection = session.connection;
  chat = session.chat;
  // send the message via the chat object
  chat.sendMessage(form.message);
  // append the message to the conversation
  session.conversation = session.conversation & "<br /><strong><font color='red'>" & connection.getUser() & "</font></strong> : " & form.message;
}

Finally, a short form for entering the message:

<form action="#cgi.script_name#" method="post">
<input type="text" name="message">
<input type="submit" name="newmessage" value="Send Message">
<input type="hidden" name="formPosted" value="1">
</form>

That’s all there is too it!

You can download the source code for my examples here:

source code

I’d love your feedback on the code (if you use it) and I’d love to hear how you’re using instant messenging with ColdFusion, if you’re so inclined.

Screenshots:

28 thoughts on “Instant Messenging with ColdFusion, Jabber and Smack”

  1. CFMX + IM is applicable in a variety of applications: system alert and monitoring, GPS, mobile agent, etc. Of course, using a hidden frame on the client is a possible solution and has been done on remoting applications. However, this approach is subscribe and poll service. I’m looking for subscribe and push solution on browser as an IM integration to other applications. Work is underway on the part of Mozilla to do just that (jabberzilla). CF is served as integrator in this architecture. I really appreciate your suggestion on CFMX-Jabber integration. That helps a lot! I’d try the code in a bit. Another thing, I’m not familiar with Smack, how does it compare to Muse API?

  2. Another poster talked about using Flash remoting and even using Flex JSP tag lib’s with CFMX and SMACK. Do you think you will give that a try next. Thanks again and nice work. Dan

  3. Hi
    is possible to use Jabber IM for support area in our site like php live support? What do you think about? Any tips, suggestions?

  4. The ‘nextMessage’ variable is defined right above the line that checks for it:

    // retrieve the message using pollMessage() (which is nonblocking)
    nextMessage = chat.pollMessage();
    // if no message exists ‘nextMessage’ will be undefined (I think ‘null’ technicall)
    if (IsDefined(“nextMessage”)) {

  5. I mean does it ever return other then just null which in CF I think that means undefined. I got the chat sendMessage() part to work but the other half pollMessage() and nextMessage(300) does not read
    incoming packets. Your code copies the form variable into a session variable which is both sent and saved (red font) however the other half (blue font) never seems to work. I was wondering if you could give this another look and repost because many people are looking at this with interest. Thanks Dan

  6. hey Dan,

    I’m not sure how you’re testing this… my setup was that I started a chat in a browser using the CF code and was chatting with a Windows client (Exodus). What are you using as the non browser client? So my setup looked something like this:

    IE –> CFMX –> Smack –> Jabber Server –> Exodus

    Is that what you have?

  7. Hello AJ

    Yes same set up. Does this mean you have seen data returning from a client from (exodus) which shows up in blue after a page refresh? Can you provide a screen shot?? Maybe?? I would like to started on a java client, do you have an simple example, to get started? -Dan

  8. Yes, I am seeing data returning from the client (exodus) which shows up in blue after a page refresh, screen shots are included in the text above.

  9. Your cfmx-smack code snippet works great! Thank you. I have this problem though. How do I register packetListner on cfmx since CFC can’t do interface/implement?

  10. hi Vui,

    You could write a java class like I did here that registers the packetListener, but I’m not sure you could actually do anything interesting with it.. what can you accomplish by using that?

  11. The PacketListener is added to each connection session with Jabber server. Of course it doesn’t make sense if the Listener is implemented as servlet. Coming from ColdFusion and scripting background, I tend to push the scripting envelop on ColdFusion for all applications and integrations. I intend to integrate workflow application with IM. Now, thanks to your sample, it’s easy to setup an event notification system to users of application, i.e., CFMX -> Jabber -> user. Since it’s persistent XML socket, it is very interesting to have users sending packets to CFMX for processing, i.e., users -> Jabber -> CFMX. After all, it’s more efficient than a HTTP request. As the Jabber communication protocol is so simple, even a telnet client can do IM with Jabber. How do I approach Jabber packet processing issue on CFMX?

  12. Because ColdFusion is limited to the HTTP Request/Response model I think the best you could do would be to have a scheduled task that pings a ColdFusion page every n seconds, in that page you’d check the persistent Jabber connection session for any new incoming messages… if a message exists, then take the appropriate action, ie: send information via email, send another jabber message, store information in a database, etc.

    I admire that you “tend to push the scripting envelop on ColdFusion for all applications and integrations”, but I’d suggest that you’re trying to use ColdFusion when other tools would be better suited to the job. You could run a Jabber server (there are Java versions available) and then implement a listener that ties directly into the Jabber server.

  13. Thank you for your invaluable suggestions, Mr. Johnson. I just can’t wait to have CFC being able to do interface/implement than to go with the http request. There will be performance issues if the number of incoming messages gets too big. The custom PacketListener on Jabber server is definitely an ideal solution. Thank you again.

  14. I must say that I am shocked that you got it working
    and I am very happy. Here are all my screen shots. It shows object creation, Exodus client working, Netstat port connections.

    Screen Shots:
    http://www.opticalalert.com/smack/index.cfm

    The packetLister has also been my biggest problem. I was thinking about making a class file with a packetlistener and just call it from my client. Will that work? Dan

    Now if I could get you to do a Java Server Faces example with cfmx.

  15. AJ: a question about your example. The example works fine from a jabber client (ie:Exodus) to the web based client and back. But messages sent from the web based client to the web based client are never displayed. Yet those messages show up in Exodus just fine. Is this a problem with the smack libraries, and if not, what am I missing here? It seems that chat.nextMessage() or chat.pollMessage() should retrieve those messages when called from the web based client, just as it does for messages sent from Exodus. Any insight would be greately appreciated.

  16. Hi,
    This threads over 2 years old, but I figured I would give this a shot anyway. I am using this tutorial, which is extremely helpful by the way, to create an IM client pretty much exactly as shown. My first problem was with logging into Google talk, but I got that working great with a few minor changes. Now I am having trouble sending messages from any client back to the Coldfusion one. I am using pollMessage() as shown. I can send from Coldfusion to any client with no problems. However I cannot send back to Coldfusion, except under certain circumstances. The Google Talk client, Psi, etc do not work under any circumstances. However I can get the Exodus client to send messages back to Coldfusion if I just opened Exodus and just opened the web page with the Coldfusion client. But if I close the Coldfusion client and reopen it I can send messages with no problems, but cannot receive them. I have tried many different combinations of resetting sessions in the browser, using different computers, closing the conenction with close() and nothing works. I can still send messages from Coldfusion, but cannot receive them. The only way to make it work again is to reset the sessions in Coldfusion, reopen the coldfusion page and close and reopen Exodus. Do you have any suggestions on what to try next? Also I was looking into using listeners instead of pollMessage(). Any info here on integrateing it with this tutorial would be great. Thanks.

    Paul

  17. I’m having the smae problems recieving messages, it just doesnt work … if any one can get that going please post a comment here

  18. I just discovered this thread and code samples as well. I realize CF enterprise can do this kind of stuff but we don’t have the budget for it. This code sample was just what I was looking for to do some rudimentry notification.

    Thanks for taking the time to post the example.

    -Nate

  19. @Paul – have you had any luck in solving this? I am having the exact same issue.

    @Nate – The CFEvent Gateway is great for bot style apps, but its not well suited to creating a web based im client as the CF server is what you talk to putting limits on its functionality for web based im.

    e.g. you have a sales person on exodus. client comes to webpage and start to chat. they appear to the sales guy as coldfusion@server.com, there is no way of spoofing the buddyID – for obvious reason!

  20. Same-o problem as you, Paul. Guess that it’ll only work with Exodus, since i’m using Pidgin and it doesn’t seems to receive any answer. looking forward to solve it, Dinho

Leave a Reply to AJ Cancel reply

Your email address will not be published. Required fields are marked *