Skip to main content

Using SailFin as Instant Messaging (IM/Chat) server

Posted by bhavanishankar on April 13, 2008 at 11:48 PM PDT
It is very easy to use SailFin server as an Instant Messaging server (IM server). 
For that, I wrote a IM/Chat server application which is nothing but a SIMPLE SipServlet
running on top of SailFin.

Here is how you can deploy and use my application :

    * Download, install and start SailFin - http://sailfin.dev.java.net
    * Download the application (IMServer.war) from here
    * Deploy the application using 'asadmin deploy IMServer.war'
    * Go through the flash demo and see how to use the application.
    * Needless to mention, you need to have a SIP aware IM client (I used X-Lite).

The flash demo is worth thousand lines of description.

The complete source code of my application is available here.
For viewing and modifying the source code, you need to have NetBeans 6.0
and latest version of SIP Application Development plug-in

Highlights of the code:

Here are the main code snippets of the IMServer SipServlet with some basic
description. However the complete source code is available for download. I have used
the very familiar names in SIP world i.e., 'bob' & 'alice' for explaining the code snippets.

Sending and receiving PRESENCE information to/from buddies:

When 'bob' adds 'alice' to his Friend's list, 'bob' will send a SUBSCRIBE request to SailFin
server requesting to send NOTIFICATIONS for any status change of 'alice'.

When 'bob' sends a SUBSCRIBE request, doSubscribe method is called. As part of
doSubscribe, we also send a SUBSCRIBE request to 'bob' so that SailFin gets notified of
any status change of 'bob'.

Here is the code for doSubscribe:

 
/**
     * This method is called when the Person adds a FRIEND to his      * friend's list, Also, this method is called for every FRIEND in      * Person's friend's list when the softphone starts up.
     */
    @Override
    protected void doSubscribe(SipServletRequest request) throws ServletException, IOException {
        String friendID = ((SipURI) request.getRequestURI()).getUser();
        System.out.println(className + " :: doSubscribe " + "for friend : " + friendID + ", incoming request = \n\n" + request + "\n");         /**          * Add FRIEND to the Person's friend's list. Also, store the SipSession          * of this request, so that we can send the NOTIFY using this SipSession.          */
        Person person = findPerson(request);
        if (person != null && !person.getId().equals(friendID)) {
            person.addFriendID(friendID);
            person.setSubscriptionSession(friendID, request.getSession());
        }         
        /**          * Send 200 OK.          */
        SipServletResponse resp = request.createResponse(SipServletResponse.SC_OK);
        resp.send();         
        /**          * SUBSCRIBE for the presence information of this user.          */
        subscribeForPresenceEvents(request);         
        /**          * If the friend is currently online, send his presence information          * to this person.          */
        sendPresenceInformation(PersonDB.findPersonById(friendID), person);
    }

    /**
     * Send the SUBSCRIBE request to sourceAddress of SipServletRequest.
     */
    private void subscribeForPresenceEvents(SipServletRequest request) {
        SipURI sourceAddress = getSourceAddress(request);
        try {
            SipServletRequest sReq = sf.createRequest(sf.createApplicationSession(), "SUBSCRIBE", request.getRequestURI(), sourceAddress);
            sReq.addHeader("Event", "presence");
            sReq.addHeader("Expires", "3600");
            System.out.println(className + " :: subscribeForPresenceEvents, " + "outgoing SUBSCRIBE request = \n\n" + sReq + "\n");
            sReq.send();
        } catch (Exception ex) {
            System.out.println("Unable to send SUBSCRIBE request to " + sourceAddress);
            ex.printStackTrace();
        }
    }
 
 

For the SUBSCRIBE request we sent to 'bob' in doSubscribe method, we also receive
NOTIFICATIONS from 'bob' whenever he changes his status.

Following is the method which handles the NOTIFICATIONS received from 'bob'. As part
of this method, we send bob's status to all his friends who are currently online.


    /**
     * This method is called when someone sends the presence information.
     */
    @Override
    protected void doNotify(SipServletRequest request) throws ServletException, IOException {
        System.out.println(className + " :: " + "doNotify, incoming request = \n\n" + request + "\n");          /**          * Retrieve the presence information from request and store          * it in Person object.          */
        Person person = storePresenceInformation(request);         
        /**          * Send 200 OK.          */
        SipServletResponse resp = request.createResponse(SipServletResponse.SC_OK);
        resp.send();         
        /**          * Notify the presence information to all friends          * who are currently online.          */
        sendPresenceInformationToAllFriends(person);
    }
 


Sending and receiving InstantMessage to/from buddies:

Below is the code snippet which is invoked when 'bob' sends an InstantMessage to
'alice'. This method takes care of forwarding that message to the current IP address of
'alice'.

 
/**
     * This method is called when someone types some message in his/her * Chat window. We need to direct that message to the present location * of the destination.
     */
    @Override
    protected void doMessage(SipServletRequest request) throws ServletException, IOException {
        System.out.println(className + " :: " + "doMessage, incoming request = \n\n" + request + "\n");
        String to = ((SipURI) request.getRequestURI()).getUser();
        Person p = PersonDB.findPersonById(to);
        if (p != null && p.getStatus() == Person.Status.ONLINE) {
            System.out.println("personID = " + p.getId() + ", proxying request to presentLocationURI = " + p.getPresentLocationURI());
            Proxy proxy = request.getProxy();
            proxy.proxyTo(sf.createURI(p.getPresentLocationURI()));
            /** * Send 200 OK */
            SipServletResponse resp = request.createResponse(SipServletResponse.SC_OK);
            resp.send();
        } else {
            /** * Send 404 NOT_FOUND */
            SipServletResponse resp = request.createResponse(SipServletResponse.SC_NOT_FOUND);
            resp.send();
        }