Technical

WebSockets in WebLogic

A new feature in WebLogic 12c is the support for WebSockets. WebSockets are a server-side tech to allow push data to multiple client browsers with minimal overhead. They are useful when you want to provide real-time updates to users or want updates or content from one client to be pushed to multiple clients simultaneously. In the past (and on older browsers) you would have to use some form of long-polling (where the browser maintains an open connection with the server and sends messages back and forth on this single connection) to be able to provide these features.  However, long-polling has a significant performance impact on your webserver as each connection must be kept open at all times (which does not scale well). With WebSockets you can avoid this performance impact and still deliver reliable instant communication with clients.

I’m not going to go into the detail of how WebSockets work (there are plenty of resources on the net for that), but in this post I will go through a simple example of WebSockets in WebLogic.

A really common example of WebSockets is building a chat application. It’s ideally suited for WebSockets as the alternative using AJAX would be to constantly poll the server for new messages. There are already some examples on the net of building a chat app in WebLogic, but most of them don’t cover how to do this in JDeveloper (they use NetBeans) and don’t go into detail on how to use JSON when communicating with the client (they just rely on simple messages being passed back and forth). All the code for this project is available on Github: https://github.com/Joelith/websocket-chat

  1. Create a new blank application in JDeveloper
  2. Create a new Web Project
  3. Right-click the project and select ‘Project Properties’.
    1. Go to ‘Libraries and Classpath’
    2. Click ‘Add Library…’ and select “WebLogic 12.1 API’. This adds the WebSocket libraries to your project
  4. Create a new Java Class and call it ‘SocketMediator’. This is going to be the class that will define our WebSocket definition
  5. Add the following annotation above the class definition. This tells WebLogic that this project uses WebSocket so that WebLogic will use the correct URI (ws:// instead of http://). The pathPatterns defines the URL fragment that this socket will listen to. This is appended to your applications context-root.
@WebSocket (
pathPatterns = {"/chat"},
timeout = 600,
maxConnections = 1000)
  1. Change the class definition so that it extends WebSocketAdapter and implements WebSocketListener.

To actually do any work with our WebSocket we need to override the methods that defined in the WebSocketListener. There are quite a few, but the main ones are:

  • onOpen: This will be run whenever a client connects to the WebSocket
  • onMessage: This will be run whenever a client sends a message
  • onClose: This will be run whenever a client terminates their connection
  • onTimeout: This will be run whenever a client’s connection is terminated by WebLogic due to a timeout (in the above example we set the timeout to 10 minutes)

Before we do that, let’s discuss the logic we want to implement in this example:

  • We want a chat page where users provide their name and then post messages
  • Each user will have their own colour so that we can easily distinguish who is saying what
  • Whenever a user enters the chat we need to tell everyone else
  • Whenever a user leaves the chat (either through timeout or because they closed their connection) we need to tell everyone else
  • We need to tell a user when they have been disconnected due to inactivity

Let’s start with registering a user with a name and colour. This will be handled by both the onOpen and onMessage methods.

@Override public void onOpen(WebSocketConnection connection) {
    int userId = connection.hashCode();
    User user = new User(userId);
    users.put(userId, user);
}

Here we use the hashCode of a connection as a unique identifier for the user. This is useful as we can access the hash code later and associate it with the user that sent the message. We create a user with this id and add it to our collection of currently connected users. The User class here is just a simple class that holds the details for our user

public class User {

    private String username;
    private String colour;
    private Integer id;

    public User(Integer id) {
        this.id = id;
        this.colour = this.getRandomHexColour();
    }

    public void setUsername (String username) {
        this.username = username;
    }

    public String getUsername () {
        return username;
    }

    public String getColour () {
        return colour;
    }

    public static String getRandomHexColour() {
        Random random = new Random();
        float hue = random.nextFloat();
        // sat between 0.1 and 0.9
        float saturation = (random.nextInt(2000) + 7000) / 10000f;
        float luminance = 0.9f;
        Color colour = Color.getHSBColor(hue, saturation, luminance);
        return '#' + Integer.toHexString((colour.getRGB() & 0xffffff) | 0x1000000).substring(1);
    }
}

You’ll notice that we don’t set the username during the onOpen, this is because the onOpen method doesn’t accept any input from the client. We will have to wait for a message from them to actually contain that information.

The next step is to handle the messages from the client. We are going to use JSON to define a message format that will allow multiple different types of operations to be performed. In this case:

  • register: A user must provide their username to register before sending messages
  • message: A message sent from the user to be pushed to all other clients

In other WebSocket implementations (like node.js) you can handle multiple operations easily by defining different endpoint handlers, but in WebLogic we have to do it within the one onMessage handler. To achieve this we parse the JSON string sent over the socket and extract the operation parameter. Parsing JSON in WebLogic is not quite seamless (12.1.3 may help here) so you have to use a third-party library like Jackson (which is provided in a default install). So our code will now look like this:

ObjectMapper mapper = new ObjectMapper();
Map<String, String> msgData = mapper.readValue(msg, Map.class);
String operation = msgData.get("operation");
switch (operation) {

As you can see we conver the JSON message (msg) into a simple string Map and extract the operation parameter. This kind of JSON parsing is rudimentary but gets the job done. Alternatively you could create a Java class that has the same structure and attributes as the JSON and have Jackson convert into that object, but for our simple use case the above is enough.

Once we know the operation we handle them in the switch. For the register function we extract the username from the JSON and add to our User object and then tell all other connected clients that the user has joined:

String username = msgData.get("username");
user.setUsername(username);
this.systemBroadcast(username + " has joined the chat", userId);

The systemBroadcast function simply gets all current connections and sends the provided message to the clients

    public void systemBroadcast (String message, Integer userId) {
        try {
            for(WebSocketConnection conn : getWebSocketContext().getWebSocketConnections()) {
                if (conn.hashCode() != userId) {
                    conn.send("{"type":"system", "message":"" + message + ""}");
                }
            }
        } catch (IOException e) {
            System.out.println("Broadcast Exception");
        }
    }

You’ll notice that we just print out the JSON directly, rather than creating a proper JSON object and then converting it to a string. Again, our use case is simple so we can get away with this, but for more complex uses you may have to do just that.

For the message operation we simply send the message to everyone (including the person who sent it, so that it can appear in their chat window). The code for that is similar to the systemBroadcast.

And that’s basically it! You’ll need to create a html interface to display the chat (see the source code) and then deploy the whole thing to WebLogic. Then navigate to the URL of the app (in my case it was http://localhost:7001/WebSocketTest-ChatWS-context-root/) and start chatting. Open multiple windows to see the broadcasting in action.

Advertisements

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