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 Reader
s and InputStream
s
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 detailsDecoders 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

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
objectsPong
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
MessageHandler
There are two basic steps involved (common to Programmatic endpoints)
implement appropriate
MessageHandler
implementation based on data type and whole/partial messageattach that implementation using
Session#addMessageHandler
methods (multiple combinations available)
addMessageHandler
permutationsCouple of additional (overloaded)
addMessageHandler
methods were added to theSession
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
@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
Last updated
Was this helpful?