The Java EE 7 Tutorial
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.
-
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. -
-
Add the names of your encoder implementations to the
encoders
optional parameter of theServerEndpoint
annotation. -
Use the
sendObject(Object data)
method of theRemoteEndpoint.Basic
orRemoteEndpoint.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.
-
Implement one of the following interfaces:
-
Decoder.Text<T>
for text messages -
Decoder.Binary<T>
for binary messages
These interfaces specify the
willDecode
anddecode
methods.
Note:
Unlike with encoders, you can specify at most one decoder for binary messages and one decoder for text messages.
-
-
Add the names of your decoder implementations to the
decoders
optional parameter of theServerEndpoint
annotation. -
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.