JetCracker

Life-time learner's blog

JSF: How to create a chat with ajax

Howdy!

In our JSF project, which I am currently working on, there was a problem to create a chat in order to enable users to communicate. Today I’ll show you how to create a simple chat using JSF, PrimeFaces and ajax.

Main Idea

The idea is quite simple. Somewhere on the server (e.g. in the database) there are messages. On your web-page there is a message box in which all chat messages are displayed. Each second the web-page requests the server for new messages, and if there are any, adds them into message box.

Problems

  1. We have to ask server for new messages every second by invoking JSF managed bean’s method. How?
  2. Managed bean (server side) has to return new messages to the web-page. How?
  3. After page is loaded (ready) the message box should be updated and contain all recent chat messaged. How?

Solutions

As it was said above, I am describing a solution for PrimeFaces library. But if you don’t (or unable to) use it, there might be a similar solution. See specifications of your particular library.

The first problem is very simple to solve. In PrimeFaces, there are different (at least two) components for invoking JSF managed bean’s method from the web page. You can use, for example, Poll component. But here I will use RemoteCommand. To solve the second problem we will use call-back parameters (the mechanism of passing data from the server to the client’s web-page). In my implementation we won’t need to solve the last problem. It will be solved automatically.

Let’s get the ball rolling!

Creating a simple ajax chat with PrimeFaces

An ideal Enterprise application should be 4-tier i.e. consist of 4 modules: Client side, Web-module, EJB-module and database. The ideal Java EE application structure is shown on the picture below.  In this tutorial we won’t use any real databases. We just emulate one using synchronized array for storing messages.

To create a chat you will need:

  1. Netbeans IDE 7 with JavaEE
  2. PrimeFaces 3

Create a Web-application project in NetBeans IDE and choose Java Server Faces for preferred web-framework. Also you have to add PrimeFaces library into your project.

Create a package simplechat.ejb. Then create a class MessageManager and an interface MessageManagerLocal. Our EJB module will emulate  a database and we will use it for obtaining and sending messages.

MessageManager.java

package ru.reshaka.labs.chat.ejb;

import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import ru.reshaka.labs.chat.Message;

/**
 * Simple chat logic
 * @author Danon
 */
@Singleton
@Startup
public class MessageManager implements MessageManagerLocal {

    private final List messages =
            Collections.synchronizedList(new LinkedList());;

    @Override
    public void sendMessage(Message msg) {
        messages.add(msg);
        msg.setDateSent(new Date());
    }

    @Override
    public Message getFirstAfter(Date after) {
        if(messages.isEmpty())
            return null;
        if(after == null)
            return messages.get(0);
        for(Message m : messages) {
            if(m.getDateSent().after(after))
                return m;
        }
        return null;
    }

}

MessageManagerLocal.java

package ru.reshaka.labs.chat.ejb;

import java.util.Date;
import javax.ejb.Local;
import ru.reshaka.labs.chat.Message;

/**
 * Local interface for chat lagic EJB
 * @author Danon
 */
@Local
public interface MessageManagerLocal {

    void sendMessage(Message msg);

    Message getFirstAfter(Date after);

}

This classes represent a singleton start-up Java bean. This will give as a global point for accessing messages. If you want to use real database, you should use a simple stateless EJB instead.

As you can see, our implementation is very simple. We just have a list of messages inside to which we can access safely using methods of our singleton. MessageManager.sendMessage(msg) – adds new message into the list. MessageManager.getFirsAfter(Date) returns the first message that was sent after specified date.

Now that we created EJB-part of our application, let’s create the web-part. Add new package simplechat.web into project and create a class Message in this package.

Message.java

package ru.reshaka.labs.chat;

import java.io.Serializable;
import java.util.Date;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

/**
 * Represents message
 * @author Danon
 */
public class Message implements Serializable {
    private Date dateSent;
    private String user;
    private String message;

    public Date getDateSent() {
        return dateSent;
    }

    public void setDateSent(Date dateSent) {
        this.dateSent = dateSent;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }
}

This is a plain Java object which represents a message. As you may notice the class implements interface Serializable. It is very important for exchanging data between all parts of our application. When Message object is transmitted it should be serialized.

The next thing we’ve got to do is to create a JSF managed bean in simplechat.web package.

MessageBean.java

package ru.reshaka.labs.chat;

import java.io.Serializable;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.event.ActionEvent;
import org.primefaces.context.RequestContext;
import ru.reshaka.labs.chat.ejb.MessageManagerLocal;

/**
 *
 * @author Anton Danshin
 */
@ManagedBean
@ViewScoped
public class MessageBean implements Serializable {

    @EJB
    MessageManagerLocal mm;

    private final List messages;
    private Date lastUpdate;
    private Message message;

    /**
     * Creates a new instance of MessageBean
     */
    public MessageBean() {
        messages = Collections.synchronizedList(new LinkedList());
        lastUpdate = new Date(0);
        message = new Message();
    }

    public Date getLastUpdate() {
        return lastUpdate;
    }

    public void setLastUpdate(Date lastUpdate) {
        this.lastUpdate = lastUpdate;
    }

    public Message getMessage() {
        return message;
    }

    public void setMessage(Message message) {
        this.message = message;
    }

    public void sendMessage(ActionEvent evt) {
        mm.sendMessage(message);
    }

    public void firstUnreadMessage(ActionEvent evt) {
       RequestContext ctx = RequestContext.getCurrentInstance();

       Message m = mm.getFirstAfter(lastUpdate);

       ctx.addCallbackParam("ok", m!=null);
       if(m==null)
           return;

       lastUpdate = m.getDateSent();

       ctx.addCallbackParam("user", m.getUser());
       ctx.addCallbackParam("dateSent", m.getDateSent().toString());
       ctx.addCallbackParam("text", m.getMessage());

    }

}

This managed bean we will use fin our web-page for sending and receiving messages. It stores the date of the last message which was received. And the client will request only new messages, which wasn’t received yet.

For passing messages to the client side (web-browser) we use the mechanism of call-back parameters. The data are serialised and transmitted in JSON format.

index.xhtml

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      >
    <h:head>
        <title>Chat test!</title>
    </h:head>
    <script type="text/javascript">
        setInterval("nextMessage()", 200); //update the chart every 200 ms               
        
        function updateMessages(xhr, status, args) {
            if(!args.ok) return;
            $('#chat').append('<div class="msg">[' +args.dateSent+ '] <strong>'+args.user+'</strong>: '+args.text+'</div>');
        }
        
    </script>
    <h:body>
        <h:form prependId="false">
            <h:panelGrid columns="2">
                Name: <p:inputText value="#{messageBean.message.user}"/>
                Text: <p:inputText value="#{messageBean.message.message}"/>
                <p:commandButton type="reset" value="Clear"/>
                <p:commandButton value="Send!" actionListener="#{messageBean.sendMessage}"/>
            </h:panelGrid>

            <p:remoteCommand name="nextMessage" actionListener="#{messageBean.firstUnreadMessage}" 
                             oncomplete="updateMessages(xhr, status, args);"/>
        </h:form>
        <hr/>
        <h3>Live chat</h3>
        <div id="chat"></div>
    </h:body>
</html>

On the web-page, there are: a form for sending messages, a message box for displaying messages and a PrimeFaces RemoteCommand component. This component invokes MessageBean.firstUnreadMessage() method using ajax request. When the request is completed, a javascript function updateMessages(xhdr, status, args) is called. The function gets call-back parameters and adds new message in the message box if there is one.

Also, web-browser should request new messages repeatedly. We can make him do that using setInterval(“nextMessage()”, 200).

Now you should get the following directory structure in your Netbeans project:

Simplechat - Netbeans

Screenshots

Simple Chat in Action [screenshot]

Improved version

Improvement

This implementation has one disadvantage. We get only one message per one call. But how to pass several messages in one request? Well, there is a trick. To pass an array or a list of objects from JSF bean to browser we should create JSONArray and pass it as string. I don’t have time to show you how to do it and prefer to leave it for your own investigation. Good luck!

Feel free to ask a question. Thank you for visiting.

Anton Danshin

P.S. Sorry for bad English. I am still learning. 🙂

Advertisements

35 responses to “JSF: How to create a chat with ajax

  1. Markus March 23, 2012 at 09:14

    Very nice! Your English is quite good so no apologies needed. 🙂

    • jetcracker March 23, 2012 at 17:21

      thank you very much)

  2. Pingback: Spring 2012: Overall stats « JetCracker

  3. subramanyam September 13, 2012 at 12:18

    Very nice!Can I get the download of it

  4. jetcracker September 13, 2012 at 23:27

    Unfortunately, no. I wrote this article when I was creating a chat in our project. Now the chat is more functional (for example, it requests all new messages at once, and it has a list of users online), and it is a part of project. That’s why I can’t upload it separately – it will not work.

    Maybe, i will write a universal solution that could be embedded in any java web-application.

    For now, I can give any clarification if you want to create chat in your project and need help.

  5. dee September 14, 2012 at 13:58

    just curious, did you able to run the entire program? i tried on my side and there was quite a few error 😦

    • jetcracker September 14, 2012 at 17:55

      Yes, the chat worked.
      I run it on glassfish with jdk 6, jsf 2 and PrimeFaces 3.1

      • Ahad January 11, 2013 at 06:49

        In my case, It run. but does not work. just web interface comes and nothing happens if I give input

  6. patricio September 16, 2012 at 06:33

    ive got a error with MessageManager.java in Message method 😦

  7. dee September 16, 2012 at 09:52

    Hi Anton Danshin. that was an impressive work. i am experiencing so many errors in the coding. was thinking if you could email me the netbeans project so i could check mine against yours? I am a student doing a project and need to include a module on the messaging. please help me. thanks.

  8. subramanyam September 27, 2012 at 09:58

    The code which you kept has some exceptions, can you send an chat example using jsf and primefaces to my mail.Plz help.

    • jetcracker September 28, 2012 at 01:27

      it’s not possible at the moment. Later I’ll try to create a small chat that can be easily integrated into java web application. I’ll let you know about it.

      • Gaurav October 11, 2012 at 08:08

        Hi Jetcracker, I have a small problem. I also need to pass the html tags (like img tag for emoticons, etc) from the bean to the UI. but when I pass it from the bean like say
        ctx.addCallbackParam(“text”, “” + m.getMessage() + ““);
        or
        ctx.addCallbackParam(“text”, “” + m.getMessage());
        the text does not get rendered. Any idea how can I do that?
        Yes the path of the images are correct. The html tags are somehow removed and anything which is enclosed within them.

      • Gaurav October 11, 2012 at 08:10

        Oops my tags got replace here as well.
        Please read them as:
        ctx.addCallbackParam(“text”, “>strong<” + m.getMessage() + “>/strong<“);
        or
        ctx.addCallbackParam(“text”, “>img=src/img.png /<” + m.getMessage());

      • jetcracker October 29, 2012 at 14:05

        I don’t know how to solve your problem, but I recommend you to do the following:

        1. Try to xml escape the text you are passing to callback params (org.apache.commons.lang.StringEscapeUtils.escapeXml(str) from Apache Commons).
        2. Also, when you get the text in javascript process it with

        // assume that 'data' contains your callback params
        var validHtml = $('div').html(data.text);
        // Now you can append 'validHtml' to any alement
        

        Let me know if it worked / didn’t work.

      • Gaurav October 11, 2012 at 08:12

        Correcting my question
        Hi Jetcracker, I have a small problem. I also need to pass the html tags (like img tag for emoticons, etc) from the bean to the UI. but when I pass it from the bean like say
        ctx.addCallbackParam(“text”, “<strong>” + m.getMessage() + “</strong>“);
        or
        ctx.addCallbackParam(“text”, “<img=’dir/path/smile.png’ / >” + m.getMessage());
        the text does not get rendered. Any idea how can I do that?
        Yes the path of the images are correct. The html tags are somehow removed and anything which is enclosed within them.

  9. subramanyam October 4, 2012 at 11:45

    I am getting so many exceptions while running, can you resolve it.I will send my code to you.

  10. siava October 9, 2012 at 21:10

    what does it mean “ugute HAXYU”? This mysterious phrase is emphasizing this tutorial i suppose, but author don’t share his vision with us…

    • jetcracker October 29, 2012 at 13:51

      this actually means “F*** you” 🙂
      But it is not referred to any one. Just to make my friends laugh.
      Just stupid Russian humor 🙂

      • jetcracker October 29, 2012 at 14:08

        foreigners won’t understand, russians will)
        but i don’t think it’s a big deal…. and unfortunately I don’t have any other screenshots 😦

  11. Gaurav October 11, 2012 at 08:03

    Hi I was getting the non-serializable exception and I solved it by scoping the bean as session 🙂

  12. Sujith October 11, 2012 at 11:40

    Hi..first of all thanks for the documentation. In the setinterval you are calling nextMessage(). Could you tell me where that function is defined..

    • jetcracker October 29, 2012 at 13:46

      Hello.
      It it is defined by PrimeFaces component RemoteCommand:

       <p:remoteCommand name="nextMessage" 
                                         actionListener="#{messageBean.firstUnreadMessage}"
      	                     oncomplete="updateMessages(xhr, status, args);"/>
      

      This component is rendered as

      var nextMessage = function() {
         // here some code which makes ajax request 
         // and invokes call-back function updateMessages()
         // on complete
      }
      
      • Cooljnr January 29, 2013 at 19:45

        it never work for me .it gives me error message “An Error Occurred:
        /index.xhtml Not Found in ExternalContext as a Resource”

  13. Cooljnr January 29, 2013 at 20:24

    l have solved the problem ,now the interface is coming but the texts are not appearing as live chat messages as shown in your snapshop

  14. wadexyz (@wadexyz) April 9, 2013 at 19:37

    I love the sample conversation.

  15. Francisco J Guzman May 19, 2013 at 21:43

    hey man im new in ejb but working in eclipse i try to do this in a database from posgrest
    my teacher teach us doing with JEE architecture mean 3 diferents proyects depends from the module..EAR project…EJB…project…and WEB…project i try to do it buuut the restrictions of the diferents projects…can u help me to adapt this project to my projecto…sorry for my english 🙂

    • jetcracker May 23, 2013 at 18:52

      I have never developed a web-application in Eclipse, I use NetBeans for that. But I can help you. If you have Skype, we can solve your problem together.
      My Skype is anton.danshin

  16. Mambo May 31, 2013 at 07:59

    hi! l have a problem , the texts are not appearing as live chat messages as shown in your snapshop

    • jetcracker August 15, 2013 at 03:56

      many people have the same probem… anyway, this article should be rewritten, because there are better ways to organize webchat

      Consider using RESTful web-services.

      Plus since JavaEE 7 release in July, there is a server API for creating Web Sockets. Perhaps, this is the best way to create a webchat,

  17. jl October 4, 2013 at 23:13

    Mowed your example, everything working, but in time to show on the screen does not work, does not execute javascript, could tell me what I can do? The args parameter is always generated as null?

    • jetcracker October 7, 2013 at 16:03

      The answer is: this is not a good exaple of chat…
      Now that I am more experienced, I would use JAX-RS web-services to create a simple chat (using polling to refresh).
      If you are interested to know how to create it using web-serivices – call me on skype. anton.danshin

  18. aysegulP May 12, 2014 at 17:05

    Message not found in ejb classes.How can you solve this problem?

    • jetcracker May 20, 2014 at 14:10

      I wrote the article when I started learning JSF… And that was several years ago!!!
      Now I realize this is not a good way to create a chat. I would recoomend using web-services (JAX-RS) or web-sockets (web-sockets are supported since Java EE7, Glassfish version 4.0).

      Maybe some time later i will rewrite this article to web-services…

  19. woldeab December 10, 2014 at 21:19

    Reblogged this on ፍሽኽታ fishikta.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: