Websockets: using Encoders and Decoders

This second tutorial about Websocket will illustrate how you can use Encoders/Decoders in order to transmit complex structures between your peers.

A Websocket can use a Decoder to transform a text message into a Java object and then handle it in the @OnMessage method; whenever an object is written to the Session, a Websocket will use an Encoder to convert the object to text and send it back to the client
Often, XML or JSON is used for the transmission of WebSocket messages, and the encoding/decoding then comes down to marshaling a Java object into XML or JSON and back.

The following picture depicts the standard communication between the client and the server when using encoders and decoders:

websocket tutorial jboss wildfly

Declaring Encoders/Decoders is quite simple: all you need to do is decorating the @ServerEndpoint with the encoders/decoders which will be used in the WebSocket transmission:

package com.sample.websocket;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/hello",
        decoders = {
            MessageDecoder.class,},
        encoders = {
            MessageEncoder.class
        })
public class HelloWorldEndpoint {

    @OnMessage
    public Person hello(Person person, Session session) {
        if (person.getName().equals("john")) {
            person.setName("Mr. John");
        }
        try {
            session.getBasicRemote().sendObject(person);
            System.out.println("sent ");
        } catch (Exception ex) {
            Logger.getLogger(HelloWorldEndpoint.class.getName()).log(Level.SEVERE, null, ex);
        }
        return person;

    }

    @OnOpen
    public void myOnOpen(Session session) {
    }

}

As you can see, the OnMessage method receives as parameter the Java Object person which will be a Java representation of the structure sent by the client. Since this is a basic example, we will barely check the name of the Person object and add a prefix to it. Data is returned to the client by using the session.getBasicRemote().sendObject(Object obj).

Before entering the HelloWorldEndpoint, however data is first converted to Java object using the following Encoder which converts an incoming XML message into a Person Java object:

 
package com.sample.websocket;

import java.io.StringReader;

import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
import javax.xml.bind.*;

 
public class MessageDecoder implements Decoder.Text<Person> {

    @Override
    public Person decode(String s) {
        System.out.println("Incoming XML " + s);
        Person person = null;
        JAXBContext jaxbContext;
        try {
            jaxbContext = JAXBContext.newInstance(Person.class);

            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();

            StringReader reader = new StringReader(s);
            person = (Person) unmarshaller.unmarshal(reader);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return person;
    }

    @Override
    public boolean willDecode(String s) {
         
        return (s != null);
    }

    @Override
    public void init(EndpointConfig endpointConfig) {
        // do nothing.
    }

    @Override
    public void destroy() {
        // do nothing.
    }
}

Conversion is doing JAXB so we assume that you are familiar with basic XML to Java conversion. What we need to focus is the implemented interface which can be a generic Decoder.Text<T> or Decoder.Binary<T> depending if the object transferred is text based or binary.

So data is received by the Decoder, then it's transmitted to the Endpoint (our HelloWorldEndpoint) and finally before returning to the client, it's converted again in XML by the following Encoder which does the reverse process:

package com.sample.websocket;

import java.io.StringWriter;
 
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
 
public class MessageEncoder implements Encoder.Text<Person> {

    @Override
    public String encode(Person object) throws EncodeException {

        JAXBContext jaxbContext = null;
        StringWriter st = null;
        try {
            jaxbContext = JAXBContext.newInstance(Person.class);

            Marshaller marshaller = jaxbContext.createMarshaller();
            st = new StringWriter();
            marshaller.marshal(object, st);
            System.out.println("OutGoing XML " + st.toString());

        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return st.toString();
    }

    @Override
    public void init(EndpointConfig endpointConfig) {
        // do nothing.
    }

    @Override
    public void destroy() {
        // do nothing.
    }
}

In order to test this sample application, I've slightly changed the Java script client Web page so that you can paste the XML in a text area.

 
<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
    </head>

    <body>
        <meta charset="utf-8">
        <title>HelloWorld Web sockets</title>
        <script language="javascript" type="text/javascript">
            var wsUri = getRootUri() + "/websocket-hello/hello";

            function getRootUri() {
                return "ws://" + (document.location.hostname == "" ? "localhost" : document.location.hostname) + ":" +
                        (document.location.port == "" ? "8080" : document.location.port);
            }

            function init() {
                output = document.getElementById("output");
            }

            function send_message() {

                websocket = new WebSocket(wsUri);
                websocket.onopen = function(evt) {
                    onOpen(evt)
                };
                websocket.onmessage = function(evt) {
                    onMessage(evt)
                };
                websocket.onerror = function(evt) {
                    onError(evt)
                };

            }

            function onOpen(evt) {
                writeToScreen("Connected to Endpoint!");
                doSend(textID.value);

            }

            function onMessage(evt) {
                writeToScreen("Message Received: " + evt.data);
            }

            function onError(evt) {
                writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
            }

            function doSend(message) {
                writeToScreen("Message Sent: " + message);
                websocket.send(message);
            }

            function writeToScreen(message) {
                alert(message);
               
            }

            window.addEventListener("load", init, false);

        </script>

        <h1 style="text-align: center;">Hello World WebSocket Client</h2>

        <br>

        <div style="text-align: center;">
            <form action="">
                <input onclick="send_message()" value="Send" type="button">
                <textarea id="textID" rows="4" cols="50" name="message" >
                </textarea>
            </form>
        </div>
        <div id="output"></div>
</body>
</html>

Now feed the textarea with an XML which contains the Person's Java Bean attributes (name/surname):


<person>
  <name>john</name>
  <surname>smith</surname>
</person> 


Check the Websocket first tutorial for more information about compiling the Project in order to run it on WildFly 8. Happy coding !

Follow us on Twitter