Receiving Messages
From an API perspective, sending WebSocket messages is much simpler because of it's dependency on a simple construct i.e. javax.websocket.RemoteEndpoint interface. For an understanding of how to receive messages, we need to take into account both annotated as well as programmatic styles
  • Annotated endpoints: it's all about passing the right type of parameters in the method annotated with @OnMessage
  • Programmatic endpoints: implementation of the appropriate javax.websocket.MessageHandler (child) interface encapsulates the logic for receiving messages

Receive modes: a quick primer

Here is an overview of the options available when receiving messages
Mode
Description
Complete
The message is received in its entirety
Partial
This works in conjunction with the partial send capability. If the sender sends messages in chunks, the message is received in chunks. The receiver will get a true boolean flag to notify it of the last message part
Streaming
Receive messages in form of Java Readers and InputStreams
Receving messages can also end up with a lot of permutations and combinations (just like the send APIs). So here is what we'll do in order to help tackle that
  • Pick up a message type (thankfully there are just two of them - String and Binary!)
  • For each of the endpoint styles (Annotated and Programmatic), we will look at the possible receiving modes (as mentioned above)
  • Take a look at how Java objects and Pong messages are handled (separate sections)

Receiving text messages

Annotated endpoints

Receving text messages in annotated endpoints is all about having the appropriate method parameter type and the WebSocket runtime will automatically figure out the rest
Complete
...
@OnMessage
public void handleChatMsg(String chat) {
System.out.println("Got message - " + chat);
}
...
Partial
...
@OnMessage
public void pushChunk(String partMsg, boolean last) {
String chunkSeq = last ? "intermediate" : "last" ;
System.out.println("Got " + chunkSeq + " chunk - "+ partMsg);
}
...
Streaming
...
@OnMessage
public void handleChatMsg(Reader charStream) {
System.out.println("reading char stream");
}
...

Programmatic endpoints

As mentioned earlier, Programmatic endpoints are inheritance based and thus need some more custom code to set them up as message receviers.
Complete
public class WholeTextMsgHandler extends MessageHandler.Whole<String> {
@Override
public void onMessage(String chat) {
System.out.println("Got message - " + chat);
}
}
Partial
public class PartialTextMsgHandler extends MessageHandler.Partial<String> {
@Override
public void onMessage(String partMsg, boolean last) {
String chunkSeq = last ? "intermediate" : "last" ;
System.out.println("Got " + chunkSeq + " chunk - "+ partMsg);
}
}
Streaming
public class WholeStreamingTextMsgHandler extends MessageHandler.Whole<Reader> {
@Override
public void onMessage(Reader charStream) {
System.out.println("Got stream message - " + charStream);
}
}

Receiving binary messages

When it comes to binary messages, the pattern (for both annotated and programmatic endpoints) is the same (except for the data type of course!). Binary messages support is available in the form of byte[] (array), java.nio.ByteBuffer and java.io.InputStream

Annotated endpoints

Complete
...
@OnMessage
public void handleImage(ByteBuffer img) {
System.out.println("Got message - " + chat);
}
...
Partial
...
@OnMessage
public void pushChunk(byte[] audioPart, boolean last) {
String chunkSeq = last ? "intermediate" : "last" ;
System.out.println("Got " + chunkSeq + " clip");
}
...
Streaming
...
@OnMessage
public void handleChatMsg(InputStream binaryStream) {
System.out.println("reading binary stream");
}
...

Programmatic endpoints

The concept remains the same apart from a change in the data types..
Complete
public class WholeBinaryMsgHandler extends MessageHandler.Whole<byte[]> {
@Override
public void onMessage(byte[] image) {
System.out.println("Got image - " + image.length);
}
}
Partial
public class PartialBinaryMsgHandler extends MessageHandler.Partial<ByteBuffer> {
@Override
public void onMessage(ByteBuffer clip, boolean last) {
String chunkSeq = last ? "intermediate" : "last" ;
System.out.println("Got " + chunkSeq + " chunk");
}
}
Streaming
public class WholeStreamingBinaryMsgHandler extends MessageHandler.Whole<InputStream> {
@Override
public void onMessage(InputStream binaryStream) {
System.out.println("Got stream binary message");
}
}

Receiving text, binary messages as Java objects

Text and binary messages sent by a WebSocket peer can be received as Java objects within your message handling logic (annotated or programmatic).
But how would the on-wire format (text/binary) to Java object transformation take place ?
This is where Decoders come into the picture. An implementation of a javax.websocket.Decoder provides the necessary logic to convert a message from it's on-wire format into it's Java representation.

WebSocket Decoders: the details

Decoders are complementary to Encoders (which were discussed in the Sending Messages lesson). Take a look at the diagram below to visualize how they work at runtime
Decoders in action

Transforming native data types (Text, Binary) into Java objects

A summary of Decoders and their types
Basic Decoder Type
Description
Decoder
The top level interface for different types of Decoders
Decoder.Text<T>
Defines how a custom Java object (of type T) is produced from a text payload (java.lang.String)
Decoder.Binary<T>
Defines how a custom Java object (of type T) is produced from a binary payload (java.nio.ByteBuffer)
A sample to demonstrate how a Decoder which creates a Subscription object from a String
public class StockSubscriptionDecoder implements Decoder.Text<Subscription> {
@Override
public Subscription decode(String subscription){
//client sends comma seperated list of subscription e.g. appl,goog,orcl
return new Subscription(Arrarys.asList(subscription.split(",")));
}
@Override
public void willDecode(String subscription){
return subscription!=null && subscription.split(",").length > 0;
}
}

Transforming Streaming inputs into Java objects

Native text and binary messages can be received as streams - as explained in the previous sections. Java objects can also be received in a streaming style
Streaming Decoder Type
Description
Decoder.TextStream<T>
Defines how a custom Java object (of type T) is produced from a character stream (java.io.Reader)
Decoder.BinaryStream<T>
Defines how a custom Java object (of type T) is produced from a binary stream (java.io.InputStream)
This example shows how you can handle data in a streaming form using a Reader which creates a Conversation object
public class ConversationDecoder implements Decoder.TextStream<Conversation> {
@Override
//handles new-line delimited content
public Conversation decode(Reader content) {
Conversation conversation = new Conversation();
try(LineNumberReader lineByLineReader = new LineNumberReader(content)){
String line = lineByLineReader.readLine();
while(line != null) {
conversation.add(line);
line = lineByLineReader.readLine();
}
}
return conversation;
}
}

Handling Pong objects

Pong messages were introduced in the API Overview chapter and were then discussed in the Sending Messages chapter as well. Receiving a health-check response message (a.k.a javax.websocket.Pong) is possible in both annotated and programmatic endpoints. Here are the examples
//annotated Pong handler
...
@OnMessage
public void healthCheckCallback(PongMessage pong) {
System.out.println("Pong for Ping! "+ new String(pong.getApplicationData().array());
}
...
//programmatic Pong handler
public class PongMsgHandler extends MessageHandler.Whole<PongMessage> {
@Override
public void onMessage(PongMessage pong) {
System.out.println("Pong for Ping! "+ new String(pong.getApplicationData().array());
}
}

Common notes

Using a MessageHandler

There are two basic steps involved (common to Programmatic endpoints)
  • implement appropriate MessageHandler implementation based on data type and whole/partial message
  • attach that implementation using Session#addMessageHandler methods (multiple combinations available)

addMessageHandler permutations

Couple of additional (overloaded) addMessageHandler methods were added to the Session interface as a part of WebSocket 1.1 release. In fact this was the only (minor) change in the 1.1 MR (maintenance release). For details, please check the change log
//attaching message handlers
public class ProgrammaticEndpoint extends Endpoint {
@Override
public void onOpen(Session session, EndpointConfig config) {
session.addMessageHandler(new WholeBinaryMsgHandler()); //basic
session.addMessageHandler(String.class, new WholeTextMsgHandler()); //specify class type for Whole message handler
session.addMessageHandler(ByteBuffer.class, new PartialBinaryMsgHandler()); //specify class type for Partial message handler
}
}

Other possible parameters for @OnMessage

In addition to the message itself, a method annotated with OnMessage can also receive the following information (which will be injected by the implementation at runtime)
  • zero or more String parameters annotated with @javax.websocket.PathParam (it is similar in spirit to the JAX-RS @javax.ws.rs.PathParam annotation)
  • an instance of Session
  • an instance of EndpointConfig (server or client side)
public void onMsgCallback(String theMsg, @PathParam("user") String username, Session peer, EndpointConfig condfig){
System.out.println("I have everything I could possibly receive from the WebSocket implementation !");
}

Handling Java primitives

A WebSocket implementation provides default decoders for Java primitive (int-Integer, long-Long, double-Double etc.) data types. It is possible to write a custom decoder for any of these in order to override the default ones

Up next

We'll explore the WebSocket Client API in detail
Copy link
On this page
Receive modes: a quick primer
Receiving text messages
Annotated endpoints
Programmatic endpoints
Receiving binary messages
Annotated endpoints
Programmatic endpoints
Receiving text, binary messages as Java objects
Transforming native data types (Text, Binary) into Java objects
Transforming Streaming inputs into Java objects
Handling Pong objects
Common notes
Using a MessageHandler
Other possible parameters for @OnMessage
Handling Java primitives
Up next