The Java EE 7 Tutorial

Previous
Next

18.7 Using Encoders and Decoders

The Java API for WebSocket provides support for converting between WebSocket messages and custom Java types using encoders and decoders. An encoder takes a Java object and produces a representation that can be transmitted as a WebSocket message; for example, encoders typically produce JSON, XML, or binary representations. A decoder performs the reverse function; it reads a WebSocket message and creates a Java object.

This mechanism simplifies WebSocket applications, because it decouples the business logic from the serialization and deserialization of objects.

18.7.1 Implementing Encoders to Convert Java Objects into WebSocket Messages

The procedure to implement and use encoders in endpoints follows.

  1. Implement one of the following interfaces:

    • Encoder.Text<T> for text messages

    • Encoder.Binary<T> for binary messages

    These interfaces specify the encode method. Implement an encoder class for each custom Java type that you want to send as a WebSocket message.

  2. Add the names of your encoder implementations to the encoders optional parameter of the ServerEndpoint annotation.

  3. Use the sendObject(Object data) method of the RemoteEndpoint.Basic or RemoteEndpoint.Async interfaces to send your objects as messages. The container looks for an encoder that matches your type and uses it to convert the object to a WebSocket message.

For example, if you have two Java types (MessageA and MessageB) that you want to send as text messages, implement the Encoder.Text<MessageA> and Encoder.Text<MessageB> interfaces as follows:

public class MessageATextEncoder implements Encoder.Text<MessageA> {
   @Override
   public void init(EndpointConfig ec) { }
   @Override
   public void destroy() { }
   @Override
   public String encode(MessageA msgA) throws EncodeException {
      // Access msgA's properties and convert to JSON text...
      return msgAJsonString;
   }
}

Implement Encoder.Text<MessageB> similarly. Then, add the encoders parameter to the ServerEndpoint annotation as follows:

@ServerEndpoint(
   value = "/myendpoint",
   encoders = { MessageATextEncoder.class, MessageBTextEncoder.class }
)
public class EncEndpoint { ... }

Now, you can send MessageA and MessageB objects as WebSocket messages using the sendObject method as follows:

MessageA msgA = new MessageA(...);
MessageB msgB = new MessageB(...);
session.getBasicRemote.sendObject(msgA);
session.getBasicRemote.sendObject(msgB);

As in this example, you can have more than one encoder for text messages and more than one encoder for binary messages. Like endpoints, encoder instances are associated with one and only one WebSocket connection and peer, so there is only one thread executing the code of an encoder instance at any given time.

18.7.2 Implementing Decoders to Convert WebSocket Messages into Java Objects

The procedure to implement and use decoders in endpoints follows.

  1. Implement one of the following interfaces:

    • Decoder.Text<T> for text messages

    • Decoder.Binary<T> for binary messages

    These interfaces specify the willDecode and decode methods.


    Note:

    Unlike with encoders, you can specify at most one decoder for binary messages and one decoder for text messages.


  2. Add the names of your decoder implementations to the decoders optional parameter of the ServerEndpoint annotation.

  3. Use the OnMessage annotation in the endpoint to designate a method that takes your custom Java type as a parameter. When the endpoint receives a message that can be decoded by one of the decoders you specified, the container calls the method annotated with @OnMessage that takes your custom Java type as a parameter if this method exists.

For example, if you have two Java types (MessageA and MessageB) that you want to send and receive as text messages, define them so that they extend a common class (Message). Because you can only define one decoder for text messages, implement a decoder for the Message class as follows:

public class MessageTextDecoder implements Decoder.Text<Message> {
   @Override
   public void init(EndpointConfig ec) { }
   @Override
   public void destroy() { }
   @Override
   public Message decode(String string) throws DecodeException {
      // Read message...
      if ( /* message is an A message */ )
         return new MessageA(...);
      else if ( /* message is a B message */ )
         return new MessageB(...);
   }
   @Override
   public boolean willDecode(String string) {
      // Determine if the message can be converted into either a
      // MessageA object or a MessageB object...
      return canDecode;
   }
}

Then, add the decoder parameter to the ServerEndpoint annotation as follows:

@ServerEndpoint(
   value = "/myendpoint",
   encoders = { MessageATextEncoder.class, MessageBTextEncoder.class },
   decoders = { MessageTextDecoder.class }
)
public class EncDecEndpoint { ... }

Now, define a method in the endpoint class that receives MessageA and MessageB objects as follows:

@OnMessage
public void message(Session session, Message msg) {
   if (msg instanceof MessageA) {
      // We received a MessageA object...
   } else if (msg instanceof MessageB) {
      // We received a MessageB object...
   }
}

Like endpoints, decoder instances are associated with one and only one WebSocket connection and peer, so there is only one thread executing the code of a decoder instance at any given time.

Previous
Next