CometD: Facebook similar chat for your Java web application

Chatting is easy just like eating a piece of cake or drinking a hot coffee. Have you ever thought about developing a chat program by yourself?. You know that, it is not easy as chatting. But, if you are a developer and if you read to the end of this article, you may put a try to develop a chatting application by your self and allow your users to chat via your web application.I had to implement a chatting application for my web application. As every one does, I started to search on internet. I found IRC. When I read and search more about IRC, I understood that finding a web base client for IRC was difficult. I wanted to have more customizable web client which is working similar to Facebook. At last and luckily, I found CometD.

Finally, I was able to implement chatting application by using CometD and more customizable chat windows opening on the browser which is exactly similar to Facebook. This works almost all the modern browsers. This article explains step by step, How to implement chatting application from the scratch and also How to integrate chatting application to your existing Java base web application. Remember, Your web application should be a Java base one.

You need to download the cometD from their official web site. It has all the dependencies required to implement the chatting application except tow java script libraries. I have written two Javascript libraries, one to create dynamic chat windows like Facebook and other to handle CometD chatting functionality in generic way. If you can manage these stuff by your self, you don’t need to use those tow Javascript libraries. Actually, CometD documentation provides good details. But, I go ahead with the tutorial by using those tow libraries. Any way, I recommend first use those tow libraries and then customize it as you need. I hope to share the sample application with you and you can deploy it in your localhost and test, how it works.

1.Adding required jar files.

If you use maven to build your project, add the following dependencies into your pom.xml file

<dependencies>
    <dependency>
        <groupId>org.cometd.java</groupId>
        <artifactId>bayeux-api</artifactId>
        <version>2.5.0</version>
    </dependency>
    <dependency>
         <groupId>org.cometd.java</groupId>
         <artifactId>cometd-java-server</artifactId>
         <version>2.5.0</version>
    </dependency>
    <dependency>
         <groupId>org.cometd.java</groupId>
         <artifactId>cometd-websocket-jetty</artifactId>
         <version>2.5.0</version>
         <exclusions>
             <exclusion>
                 <groupId>org.cometd.java</groupId>
                 <artifactId>cometd-java-client</artifactId>
             </exclusion>
         </exclusions>
   </dependency>
   <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.6.6</version>
   </dependency>
   <dependency>
        <groupId>org.cometd.java</groupId>
        <artifactId>cometd-java-annotations</artifactId>
        <version>2.5.0</version>
   </dependency>
</dependencies>

If you are not using maven to build your project, just copy the following .jar files into /WEB-INF/lib folder from your CometD download bundle. You can find these .jar files from /cometd-demo/target/cometd-demo-2.5.0.warfile.

  • bayeux-api-2.5.0.jar
  • cometd-java-annotations-2.5.0.jar
  • cometd-java-common-2.5.0.jar
  • cometd-java-server-2.5.0.jar
  • cometd-websocket-jetty-2.5.0.jar
  • javax.inject-1.jar
  • jetty-continuation-7.6.7.v20120910.jar
  • jetty-http-7.6.7.v20120910.jar
  • jetty-io-7.6.7.v20120910.jar
  • jetty-jmx-7.6.7.v20120910.jar
  • jetty-util-7.6.7.v20120910.jar
  • jetty-websocket-7.6.7.v20120910.jar
  • jsr250-api-1.0.jar
  • slf4j-api-1.6.6.jar
  • slf4j-simple-1.6.6.jar

2.Adding required Javascript files.

You need to link the following Javascript files.

  • cometd.js
  • AckExtension.js
  • ReloadExtension.js
  • jquery-1.8.2.js
  • jquery.cookie.js
  • jquery.cometd.js
  • jquery.cometd-reload.js
  • chat.window.js
  • comet.chat.js

The ‘ chat.window.js‘ and ‘ comet.chat.js‘ are my own tow Javascript libraries which does not come with CometD distribution. If you are totally following this tutorial, you have to link those tow libraries as well. Provided sample application has these tow Javascript libraries.

3.Writing chat service class.

/**
 * @author Semika siriwardana
 * CometD chat service.
 */
package com.semika.cometd;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.inject.Inject;

import org.cometd.annotation.Configure;
import org.cometd.annotation.Listener;
import org.cometd.annotation.Service;
import org.cometd.annotation.Session;
import org.cometd.bayeux.client.ClientSessionChannel;
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.ConfigurableServerChannel;
import org.cometd.bayeux.server.ServerMessage;
import org.cometd.bayeux.server.ServerSession;
import org.cometd.server.authorizer.GrantAuthorizer;
import org.cometd.server.filter.DataFilter;
import org.cometd.server.filter.DataFilterMessageListener;
import org.cometd.server.filter.JSONDataFilter;
import org.cometd.server.filter.NoMarkupFilter;

@Service('chat')
public class ChatService {
    private final ConcurrentMap<String, Map<String, String>> _members = new ConcurrentHashMap<String, Map<String, String>>();
    @Inject
    private BayeuxServer _bayeux;
    @Session
    private ServerSession _session;

    @Configure ({'/chat/**','/members/**'})
    protected void configureChatStarStar(ConfigurableServerChannel channel) {
        DataFilterMessageListener noMarkup = new DataFilterMessageListener(new NoMarkupFilter(),new BadWordFilter());
        channel.addListener(noMarkup);
        channel.addAuthorizer(GrantAuthorizer.GRANT_ALL);
    }

    @Configure ('/service/members')
    protected void configureMembers(ConfigurableServerChannel channel) {
        channel.addAuthorizer(GrantAuthorizer.GRANT_PUBLISH);
        channel.setPersistent(true);
    }

    @Listener('/service/members')
    public void handleMembership(ServerSession client, ServerMessage message) {
        Map<String, Object> data = message.getDataAsMap();
        final String room = ((String)data.get('room')).substring('/chat/'.length());
        
        Map<String, String> roomMembers = _members.get(room);
        if (roomMembers == null) {
            Map<String, String> new_room = new ConcurrentHashMap<String, String>();
            roomMembers = _members.putIfAbsent(room, new_room);
            if (roomMembers == null) roomMembers = new_room;
        }
        final Map<String, String> members = roomMembers;
        String userName = (String)data.get('user');
        members.put(userName, client.getId());
        client.addListener(new ServerSession.RemoveListener() {
            public void removed(ServerSession session, boolean timeout) {
                members.values().remove(session.getId());
                broadcastMembers(room, members.keySet());
            }
        });

        broadcastMembers(room, members.keySet());
    }

    private void broadcastMembers(String room, Set<String> members) {
        // Broadcast the new members list
        ClientSessionChannel channel = _session.getLocalSession().getChannel('/members/'+room);
        channel.publish(members);
    }

    @Configure ('/service/privatechat')
    protected void configurePrivateChat(ConfigurableServerChannel channel) {
        DataFilterMessageListener noMarkup = new DataFilterMessageListener(new NoMarkupFilter(),new BadWordFilter());
        channel.setPersistent(true);
        channel.addListener(noMarkup);
        channel.addAuthorizer(GrantAuthorizer.GRANT_PUBLISH);
    }

    @Listener('/service/privatechat')
    protected void privateChat(ServerSession client, ServerMessage message) {
        Map<String,Object> data = message.getDataAsMap();
        
        String room = ((String)data.get('room')).substring('/chat/'.length());
        Map<String, String> membersMap = _members.get(room);
        if (membersMap == null) {
            Map<String,String>new_room=new ConcurrentHashMap<String, String>();
            membersMap=_members.putIfAbsent(room,new_room);
            if (membersMap==null)
                membersMap=new_room;
        }
        
        String peerName = (String)data.get('peer');
        String peerId = membersMap.get(peerName);
        
        if (peerId != null) {
            
         ServerSession peer = _bayeux.getSession(peerId);
            
            if (peer != null) {
             Map<String, Object> chat = new HashMap<String, Object>();
                String text = (String)data.get('chat');
                chat.put('chat', text);
                chat.put('user', data.get('user'));
                chat.put('scope', 'private');
                chat.put('peer', peerName);
                ServerMessage.Mutable forward = _bayeux.newMessage();
                forward.setChannel('/chat/' + room);
                forward.setId(message.getId());
                forward.setData(chat);

                if (text.lastIndexOf('lazy') > 0) {
                    forward.setLazy(true);
                }
                if (peer != client) {
                    peer.deliver(_session, forward);
                }
                client.deliver(_session, forward);
            }
        }
    }

    class BadWordFilter extends JSONDataFilter {
        @Override
        protected Object filterString(String string) {
            if (string.indexOf('dang') >= 0) {
                throw new DataFilter.Abort();
            }
            return string;
        }
    }
}

4.Changing web.xml file.

You should add the following filter into your web.xml file.

<filter>
     <filter-name>continuation</filter-name>
     <filter-class>org.eclipse.jetty.continuation.ContinuationFilter</filter-class>
</filter>
<filter-mapping>
     <filter-name>continuation</filter-name>
     <url-pattern>/cometd/*</url-pattern>
</filter-mapping>

And also the following servlet.

<servlet>
    <servlet-name>cometd</servlet-name>
    <servlet-class>org.cometd.annotation.AnnotationCometdServlet</servlet-class>
    <init-param>
         <param-name>timeout</param-name>
         <param-value>20000</param-value>
    </init-param>
    <init-param>
         <param-name>interval</param-name>
         <param-value>0</param-value>
    </init-param>
    <init-param>
         <param-name>maxInterval</param-name>
         <param-value>10000</param-value>
    </init-param>
    <init-param>
         <param-name>maxLazyTimeout</param-name>
         <param-value>5000</param-value>
    </init-param>
    <init-param>
         <param-name>long-polling.multiSessionInterval</param-name>
         <param-value>2000</param-value>
    </init-param>
    <init-param>
         <param-name>logLevel</param-name>
         <param-value>0</param-value>
    </init-param>
    <init-param>
         <param-name>transports</param-name>
         <param-value>org.cometd.websocket.server.WebSocketTransport</param-value>
    </init-param>
    <init-param>
         <param-name>services</param-name>
         <param-value>com.semika.cometd.ChatService</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>cometd</servlet-name>
    <url-pattern>/cometd/*</url-pattern>
</servlet-mapping>

5.Implementing client side functions.

I think this section should be descriptive. If you allows your users to chat with other users, you need to show the list of online users in you web page, just like Facebook shows the online users inside the right side bar. For that, you can place a simple <span> or <div> tag inside your page. I have done it as follows.

<div id='members'></div>

All the online users will be displayed with in the above container. Once you click on a particular user name, it will open a new chat window similar to Facebook. For each pair of users, it will open a new chat window. To get this behaviour, you should use ‘ chat.window.js‘ which I mentioned before. Chatting in between particular pair of users will continue through a dedicated chat window.
Just after user is logging into your web application as usual way, we should subscribe that user to chat channels. You can do it using the following way.

$(document).ready(function(){ 
    $.cometChat.onLoad({memberListContainerID:'members'});
});

Note that, I have passed the ‘id’ of online user list container as a configuration parameter. Then, user should be joined with channel as follows.You can call the bellow method with the username.

function join(userName){
   $.cometChat.join(userName);
}

Since for each chat, there is a dedicated chat window just like Facebook, we should maintain global Javascript array to store those created chat window objects. You need to place the following Javascript code inside your page.

function getChatWindowByUserPair(loginUserName, peerUserName) {
    var chatWindow;  
    for(var i = 0; i < chatWindowArray.length; i++) {
       var windowInfo = chatWindowArray[i];
       if (windowInfo.loginUserName == loginUserName && windowInfo.peerUserName == peerUserName) {
           chatWindow =  windowInfo.windowObj;
       }
    }
    return chatWindow;
}
 
function createWindow(loginUserName, peerUserName) {  
    var chatWindow = getChatWindowByUserPair(loginUserName, peerUserName);
    if (chatWindow == null) { //Not chat window created before for this user pair.
       chatWindow = new ChatWindow(); //Create new chat window.
       chatWindow.initWindow({
             loginUserName:loginUserName, 
             peerUserName:peerUserName,
             windowArray:chatWindowArray});
   
       //collect all chat windows opended so far.
       var chatWindowInfo = { peerUserName:peerUserName, 
                              loginUserName:loginUserName,
                              windowObj:chatWindow 
                            };
   
       chatWindowArray.push(chatWindowInfo);
   }
   chatWindow.show();  
   return chatWindow;
}

As I mentioned above, declare following global Javascript variable.

var chatWindowArray = [];   
var config = {
    contextPath: '${pageContext.request.contextPath}'
};

Since I am using a JSP page, I have to get the context path via ‘ pageContext‘ variable. If you are using a HTML page, manage it by your self to declare ‘config’ Javascript global variable. Now, you almost reached to last part of the tutorial.

5.How does the sample application works?

You can download the comet.war file and deploy it in your server. Point the browser to following URL.

http://localhost:8080/comet

This will bring you to a page which has a text field and button called ‘Join’. Insert some user name as you wish and click on ‘Join’ button. Then you will be forwarded to a another page which has list of online users. Your name is highlighted in red color. To chat in your local machine, You can open another browser (IE and FF) and join to the chat channel. The peer user displays in blue color in the online users list. Once you click on a peer user, it will open a new chat window so that You can chat with him. This functions very similar to Facebook chatting.

I have tested this chatting application in IE, FF and Crome and works fine. If you want any help of integrating this with your Java base web application, just send me a mail.

Reference: Facebook similar chat for your Java web application. from our JCG partner Semika loku kaluge at the Code Box blog.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

23 Responses to "CometD: Facebook similar chat for your Java web application"

  1. Woww cool.. Thanks for that.. I am an university student doing computer science and I find this blog so much useful. Most of the time I don’t even know that such a technology exists until i find it in blogs Keep up the good work!! :)

  2. it’s cool and so easy. there are one unneccessary semicolumn is there in application.jsp i removed that and it working well.But it’s fine.Now these day video and voice chatting is also available on most sites so if you know plz post it..well thank alot for this good content

  3. Rami Tayba says:

    I got an ERROR after deploying:
    HTTP Status 500 -org.apache.jasper.JasperException: Unable to compile class for JSP:

    An error occurred at line: 68 in the jsp file: /application.jsp
    Syntax error on token “;”, delete this token
    65:
    66:
    67:
    68: var userName = ”;
    69: $(document).ready(function(){
    70: $.cometChat.onLoad({memberListContainerID:’members’});
    71: join(userName);

  4. krish says:

    hello friend you have done very good work … i need your help to implement it in seam it is not working 

    on this function i got false in if condition

    _metaHandshake: function (message) {
    if (message.successful) {
    $.cometChat._connectionInitialized();
    }

    help me if it is working with seam

  5. I am getting an error in the service class. ‘Chat’ is giving me invalid character constant

  6. odnalsairo says:

    how to deploy this to tomcat? i follow your instructions but in application.jsp appeared blank. did i missing something?
    thanks n advance. cheers!

  7. Markus W. says:

    This is awesome! Thanks!

  8. skumar says:

    Hi Semika, I tried with cometd ver 2.6.0 but facing issue. It opened chat window but while chatting causing problem. Do you have updated version?

  9. T.vasant says:

    hi Semika Loku Kaluge,

    i need your help similar to this application
    my requriement is all list online user name and checkBox side by side and next row one INVITE button, then when i checked any checkbox and cilck on INVITE button that all checked user revcieved one message like invitation message when accept this request chat will with all user those who accept request and open only one chat room like group chat…… plz help me its urgent

  10. satya says:

    i need to open window upon window like facebook means,
    if win1 is already opened then i need to open win2 on same position like fb…

  11. kiran says:

    hai friend am deploy your comet file all are work good but when i am give the user name and the click on join button it will forword to next page but it not display the user name. please help me. my mail id hemadri1989@gmail.com
    i think you can reply as early as possible. thank you

  12. Rajesh says:

    Hi,I need a help,

    Could you please let me know what difference it will make when we use tomcat Apache server for jsp pages.
    I gets script error as
    http://localhost/comet/cometd/handshake not found

    i just put all your scripts in apache folder and application.jsp and java files in Eclipse
    And i used ANT build

    Please let me know did i miss anything

    thanks in advance!!

  13. Drei says:

    Hi, could you please help me how to implement this in Netbeans? I’m a java newbie.

    Thanks

  14. Karthik says:

    Hi Semika, I need some help from you.

    I Have developed chat application with the help of your Post “CometD: Facebook similar chat for your Java web application”. It is wonderful… I am so happy with this Application.

    But, Now I want to develop this same chat application using Android. How should I? Can you please help me in this regard. Please post me the example like the above Post if u have any.

    Thanks…
    Kartheek

  15. Nandagopal says:

    Hi frnd. You have done a greater work. But when i changed the width of the window in js file. it shows nothing changed. Help me in that. Thanks in advance.

  16. nirav says:

    i want a code for fb like chat.

  17. Ajey Kumar says:

    Hi Semika,

    I must have to say, excellent post.

    I tried your code and it worked well. Now I’m trying to implement it with Struts where I’m stuck at one point. It does handshaking well and opening chat window to corresponding users as well. But does not send any text, neither I’m able to see any text in sender’s and recipient’s chat window. Also, the chat window close button does not work. Did you also have faced similar issue before? I appreciate any kind of helps or inputs.

    Regards,

    Ajey

  18. SenthilRaja says:

    Hi,
    This is very good tool. I want to know where we are getting message. I want to handle offline user also. if user is in offline, i want to send sms to sms. I planning to implement this module. Please help me out.

    Thanks in advance

  19. Sunny says:

    hi,

    At first thanks for your awesome code. your war file works perfectly in my tomcat server. but when I want to put this code to one of my academic project, it’s not working. from firefox console, it;s giving me message; “POST http://localhost:8080/ProjectX/cometd/handshake [HTTP/1.1 404 Not Found 1ms]”
    I’m stuck here for last few days. Would you please tell me what’s I’m missing? Why handshaking is happening?

    Thanks in advance.

    Regards
    Sunny

  20. dugra says:

    hi,
    thank you providing this service. but i have some doubt,if i want to add smileys in my chatting application.plz help me

    thanks for advance

Leave a Reply


three + 1 =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close