springspring-mvccometcometdreverse-ajax

Spring-MVC, Cometd : Check who is typing in chat in Comet


I am working on a Spring-MVC application in which I have implemented chat functionality using Cometd. As a feature, I would like to know if there is any way Cometd has support or some way I can show which user is typing. Ofcourse the user information I can retrieve. Here is my chat code. Thanks.

ChatServiceImpl :

@Named
@Singleton
@Service
public class ChatServiceImpl {
    @Inject
    private BayeuxServer bayeux;

    @Session
    private ServerSession serverSession;

    @Listener(value = "/service/person/{id}")
    public void privateChat(ServerSession remote, ServerMessage.Mutable message,@Param("id")String id) {
        System.out.println("wassup");
        Person sender = this.personService.getCurrentlyAuthenticatedUser();
        String senderName = sender.getFirstName();

        Map<String, Object> input = message.getDataAsMap();
        String data = (String) input.get("name");
        String timestamp = (String) input.get("timestamp");
        String temp = message.getChannel();
        String temp1 = temp;
        temp = temp.replace("/service/person/", "");
        String channelName = temp1.replace("/service","");
        final int conversationId = Integer.valueOf(temp);

        Replies replies = new Replies();
        replies.setReplyingPersonName(senderName);
        replies.setReplyText(data);
        replies.setReplyTimeStamp(timestamp);
        replies.setReplyingPersonId(sender.getId());
        replies.setRead(false);
        Long replyId = this.repliesService.addReply(replies, conversationId, sender);

        Map<String, Object> output = new HashMap<String, Object>();
        output.put("text", data);
        output.put("firstname", senderName);
        output.put("channelname", channelName);
        output.put("timestamp", timestamp);
        output.put("id",sender.getId());
        output.put("read","true");
        output.put("replyid",replyId);

        ServerChannel serverChannel = bayeux.createChannelIfAbsent("/person/" + id).getReference();
        serverChannel.setPersistent(true);
        serverChannel.publish(serverSession, output);

    }

Application.js : Please note, I am using parts of this file in other JS file.

(function($)
{
    var cometd = $.cometd;

    $(document).ready(function()
    {
        function _connectionEstablished()
        {
            $('#body').append('<div>CometD Connection Established</div>');
        }

        function _connectionBroken()
        {
            $('#body').append('<div>CometD Connection Broken</div>');
        }

        function _connectionClosed()
        {
            $('#body').append('<div>CometD Connection Closed</div>');
        }

        var _connected = false;
        function _metaConnect(message)
        {
            if (cometd.isDisconnected())
            {
                _connected = false;
                _connectionClosed();
                return;
            }

            var wasConnected = _connected;
            _connected = message.successful === true;
            if (!wasConnected && _connected)
            {
                _connectionEstablished();
            }
            else if (wasConnected && !_connected)
            {
                _connectionBroken();
            }
        }

        // Function invoked when first contacting the server and
        // when the server has lost the state of this client
        function _metaHandshake(handshake)
        {
            if (handshake.successful === true)
            {
                cometd.batch(function()
                {
                    cometd.subscribe('/chat/1306', function(message)
                    {
                        var data = message.data;
                        $('#body').append('<div>Server Says: ' + data.firstname + '/' + data.accountid + data.time1+'</div>');
                    });
                });
            }
        }

        // Disconnect when the page unloads
        $(window).unload(function()
        {
            cometd.disconnect(true);
        });

        $(document).on('click', '#sender', function()
        {
            cometd.publish('/service/chat/1306', { name: 'hello_' + Date.now() });
        });

        var cometURL = location.protocol + "//" + location.host + config.contextPath + "/cometd";
        cometd.configure({
            url: cometURL,
            logLevel: 'debug'
        });

        cometd.websocketEnabled = false;

        cometd.addListener('/meta/handshake', _metaHandshake);
        cometd.addListener('/meta/connect', _metaConnect);

        cometd.handshake();
    });
})(jQuery);

Kindly let me know how I can achieve this, as I cannot find many references for this. Thanks a lot. :-)


Solution

  • This is easily achieved by detecting on the client side the typing start/stop (in a smart way to avoid to send too many messages to the server), then send a CometD service message to the server.

    The server can then just broadcast a message to a special channel (say /chat/typing) with the nickname of the user that is typing.

    The client application will subscribe to /chat/typing and receive these messages, then display in the UI who is typing, possibly coalescing multiple users into a single UI notification.

    The CometD part is trivial, the detection of the start/stop of the typing in a smart way is probably most of the work.