Building a web chat application using WebSocket

Sample Number

454

Level

Expert

Description

This sample demonstrates how to build a web chat application using the WebSocket transport.

Use Case

I want to build a chat application where websocket clients which belongs to certain chat rooms can communicate with each other.

454

As shown in the above diagram, websocket clients which belongs to a particular logical group can communicate with each other.

What is a logical group?

Logical group of a websocket client can be described as a logical categorization of websocket clients at the authentication period. For more information about websocket client authentication and local groups please refer WebSocket Transport Guide

Assume that websocket clients are connected to the UltraESB with the same subscriber path, but with different logical groups and these logical groups correspond to the chat rooms available.

Sample Configuration

The configuration for this use case consists of a proxy service, exposed on the WebSocket transport. The proxy service execute the code within the ChatApp class in inSequece.

The 'ws-endpoint' has the address 'ws://broadcast/groups' (which means that the messages submitted to this endpoint will broadcast that message to logical groups) and notice the address type is 'prefix'. Hence, the suffix of the address is set within the inSequence.

Proxy service configuration

 1<u:proxy id="chat">
 2    <u:transport id="ws-listener">
 3        <u:property name="ultra.transport.ws.groups"
 4                    value="sports,movies,music"/>
 5    </u:transport>
 6    <u:target>
 7        <u:inSequence>
 8            <u:class name="samples.services.websocket.ChatApp"/>
 9        </u:inSequence>
10    </u:target>
11</u:proxy>
12
13<u:endpoint id="ws-endpoint">
14    <u:address type="prefix">ws://broadcast/groups</u:address>
15</u:endpoint>
Note
For the simplicity the complete configuration is not displayed here, you may look at the complete configuration either from the binary distribution under samples/conf/ultra-sample-454.xml, or from the sample 454 configuration of the source tag.

Below is the code block within the ChatApp java class. The chatClients map is used to store the WebSocketClients with their logical groups. The message sent by the WebSocket client to the UltraESB and it will be available within the proxy service as a WebSocketMessage with the opcode TEXT. The flow of the execute() method is as follows

  1. Obtain the opcode of the WebSocketMessage (line 7)

  2. Obtain the ID of the WebSocket client who has sent the current message to the UltraESB (line 8)

  3. If the opcode is TEXT then broadcast an appropriate JSON message to all the WebSocket clients which belongs to the same chat room as the WebSocket client who has sent the current message to the UltraESB (line 10-15)

  4. If the opcode is TIMEOUT or CLOSING, broadcast and appropriate JSON message and remove that particular WebSocket client from the chatClients Map (line 16-21)

  5. If the opcode is CONNECTED, broadcast an appropriate JSON message and add that particular websocket client to the chatClientsMap with its logical group (line 23-29)

  6. SendMessage() method prepares a new WebSocketMessage with the JSON string and set the suffix of the address of the endpoint (line 39) as the logical group of the original message sender (i.e. the logical group of the WebSocket client who has sent the current message to the UltraESB)

Content of the ChatApp java class

 1public class ChatApp extends AbstractJavaSequence {
 2    /* stores all the chat clients with the chat room they have connected to */
 3    Map<String, String> chatClients = new HashMap<>();
 4
 5    @Override
 6    public void execute(Message msg, Mediation mediation) throws Exception {
 7        final WebSocketMessage.Opcode opcode = mediation.getWebSocketSupport().getOpcode(msg);
 8        final String originPeerID = mediation.getWebSocketSupport().getOriginPeerID(msg);
 9
10        if (opcode == WebSocketMessage.Opcode.TEXT) {
11            /* obtain the input message and create output json message */
12            final String payloadAsString = mediation.readFullPayloadAsString(msg);
13            final String message = "{\"state\":\"msg\"," + "\"username\":\"" + originPeerID + "\",\"message\":\"" + payloadAsString + "\"}";
14            sendMessage(msg, mediation, message, chatClients.get(originPeerID));
15
16        } else if (opcode == WebSocketMessage.Opcode.TIMEOUT || opcode == WebSocketMessage.Opcode.CLOSING) {
17            /* notify other clients in the chat room that a particular client left the chat room */
18            final String message = "{\"state\":\"close\"," + "\"username\":\"" + originPeerID + "\"}";
19            sendMessage(msg, mediation, message, chatClients.get(originPeerID));
20            /* remove disconnected chat client */
21            chatClients.remove(originPeerID);
22
23        } else if (opcode == WebSocketMessage.Opcode.CONNECTED) {
24            /* notify other clients in the chat room that a particular client joined the chat room */
25            final String message = "{\"state\":\"connect\"," + "\"username\":\"" + originPeerID + "\"}";
26            final String[] logicalGroups = mediation.getWebSocketSupport().getOriginatedPeerLogicalGroups(msg);
27            /* add the chat client */
28            chatClients.put(originPeerID, logicalGroups[0]);
29            sendMessage(msg, mediation, message, logicalGroups[0]);
30        }
31    }
32
33    private void sendMessage(Message msg, Mediation mediation, String message, String logicalGroup) throws UnsupportedEncodingException {
34        /* create new WebSocketMessage with json string as set it as current payload */
35        org.adroitlogic.ultraesb.core.format.WebSocketMessage payload =
36                new org.adroitlogic.ultraesb.core.format.WebSocketMessage(message);
37        msg.setCurrentPayload(payload);
38        /* set broadcasting logical groups as message sender's logical groups */
39        mediation.getWebSocketSupport().setRecipientLogicalGroups(msg, Arrays.asList(logicalGroup));
40        /* broadcast message to logical groups */
41        mediation.sendToEndpoint(msg, "ws-endpoint");
42    }
43}

Below is the JavaScrip code section which is responsible for parsing the JSON message received from the UltraESB. 

JavaScript within the chat.html client

 1ws.onmessage = function (e) {
 2    console.log(e.data);
 3    var json = JSON.parse(e.data);
 4    var msgType = json['state'];
 5    if(msgType == 'msg') {
 6        log(json['username'] + ' : ' + json['message'] + "\n");
 7    } else if(msgType == 'close') {
 8        log(json['username'] + ' left the ' + groupName + ' chat room.\n')
 9    } else if (msgType == 'connect') {
10        log(json['username'] + ' joined the ' + groupName + ' chat room.\n');
11    }
12};

In Action

To run the example, start the UltraESB sample configuration 454 via the ToolBox or on the command line as follows.

Running the sample from startup script

$ cd /opt/ultraesb-2.6.1/bin
$ ./ultraesb.sh -sample 454

Now open the chat.html client at samples/resources/websocket/ in three browser tabs and give Tom, Bob, and Sam as User ID, 'sports' as Chat room and click connect button.

Browser Support
browser
You must open chat.html file using a browser which has native WebSocket support such as Internet Explorer 10+, Mozilla Firefox 11+, Google Chrome 16+, Opera 12.10+ or Safari 6+.

Now send any message from one user to another. Below is an example conversation between Sam, Bob and Tom. Feel free to experiment with different users and different chat rooms.

chat sam
chat bob
In this topic
In this topic
Contact Us