Enterprise Java

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.

Semika Kaluge

I am working in software engineering field for six years of time by now. Currently, I am working for Shipxpress Inc in Sri Lanka. Primarily, I love to involve with Java developments and related frameworks like Spring, Struts, Hibernate and many more, specially interested in Javascript.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

50 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Vivek Chaudhari
11 years ago

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!! :)

Semika Loku Kaluge
11 years ago

Thanks Vivek

Mithilesh Jha
11 years ago

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

Semika Loku Kaluge
11 years ago
Reply to  Mithilesh Jha

Yes, Jha. I have updated .war file by removing that semicolumn.

Rami Tayba
Rami Tayba
11 years ago

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);

Semika Loku Kaluge
11 years ago
Reply to  Rami Tayba

This is semicolum at t like 68 which is not required. I have updated .war file by removing that. Please get the new .war file and check.

krish
krish
11 years ago

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

Amlan Karmakar
11 years ago

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

pierre
pierre
7 years ago
Reply to  Amlan Karmakar

replace ‘ ‘ by ” “

Selva
Selva
7 years ago
Reply to  pierre

Thats great! Thanks Pierre

odnalsairo
odnalsairo
10 years ago

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

Markus W.
Markus W.
10 years ago

This is awesome! Thanks!

skumar
skumar
10 years ago

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?

T.vasant
T.vasant
10 years ago

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

satya
satya
10 years ago

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…

kiran
kiran
10 years ago

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

Rajesh
Rajesh
10 years ago

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!!

Drei
Drei
10 years ago

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

Thanks

Karthik
Karthik
9 years ago

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

Nandagopal
Nandagopal
9 years ago

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.

nirav
9 years ago

i want a code for fb like chat.

Ajey Kumar
Ajey Kumar
9 years ago

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

Narahari
Narahari
7 years ago
Reply to  Ajey Kumar

Hi Semika,

I am also facing same issue, When I integrate this to struts successfully, 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.

SenthilRaja
SenthilRaja
9 years ago

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

Sunny
Sunny
9 years ago

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

dugra
dugra
9 years ago

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

Back to top button