Its my please to announce that JSR 356 – Java API for WebSocket maintenance release ballot vote finished with majority of “yes” votes (actually, only one eligible voter did not vote, all other votes were “yeses”). New release is maintenance release and it addresses only one issue: WEBSOCKET_SPEC-226.
What changed in the 1.1?
Version 1.1 is fully backwards compatible with version 1.0, there are only two methods added to javax.websocket.Session:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* Register to handle to incoming messages in this conversation. A maximum of one message handler per
* native websocket message type (text, binary, pong) may be added to each Session. I.e. a maximum
* of one message handler to handle incoming text messages a maximum of one message handler for
* handling incoming binary messages, and a maximum of one for handling incoming pong
* messages. For further details of which message handlers handle which of the native websocket
* message types please see {@link MessageHandler.Whole} and {@link MessageHandler.Partial}.
* Adding more than one of any one type will result in a runtime exception.
*
* @param clazz type of the message processed by message handler to be registered.
* @param handler whole message handler to be added.
* @throws IllegalStateException if there is already a MessageHandler registered for the same native
* websocket message type as this handler.
*/
public void addMessageHandler(Class<T> clazz, MessageHandler.Whole<T> handler);
/**
* Register to handle to incoming messages in this conversation. A maximum of one message handler per
* native websocket message type (text, binary, pong) may be added to each Session. I.e. a maximum
* of one message handler to handle incoming text messages a maximum of one message handler for
* handling incoming binary messages, and a maximum of one for handling incoming pong
* messages. For further details of which message handlers handle which of the native websocket
* message types please see {@link MessageHandler.Whole} and {@link MessageHandler.Partial}.
* Adding more than one of any one type will result in a runtime exception.
*
*
* @param clazz type of the message processed by message handler to be registered.
* @param handler partial message handler to be added.
* @throws IllegalStateException if there is already a MessageHandler registered for the same native
* websocket message type as this handler.
*/
public void addMessageHandler(Class<T> clazz, MessageHandler.Partial<T> handler);
Why do we need to add those methods? Short and not precise version: to support Lambda expressions as MessageHandlers.
Longer and slightly more precise explanation: old Session#addMessageHandler method (which is still there and works as it worked till now) does rely on getting the generic parameter during the runtime, which is not (always) possible. The unfortunate part is that it works for some common cases and the expert group did not catch this issue before 1.0 release because of that. The issue is really clearly visible when Lambdas are used as message handlers:
1
2
3
session.addMessageHandler(message -> {
System.out.println("### Received: " + message);
});
There is no way for the JSR 356 implementation to get the type of the used Lambda expression, thus this call will always result in an exception. Since all modern IDEs do recommend to use Lambda expressions when possible and MessageHandler interfaces are single method interfaces, it basically just scream “use Lambdas” all over the place but when you do that, the application will fail during runtime.
Only solution we currently have is to explicitly provide the type of registered MessageHandler. (There might be another sometime in the future when generic type reification is introduced, but that is not going to happen soon enough). So the example above will then be:
1
2
3
session.addMessageHandler(String.class, message -> {
System.out.println("### Received: " + message);
});
and voila, it works.
There are some limitations – you cannot do
1
List<String>.class
, so you will need to encapsulate these types when you want to use them in MessageHandler implementation (something like “class MyType extends ArrayList<String>”). There is no better way how to solve this issue, because Java currently does not provide good way how to describe generic types.
The api itself is available on maven central, look for javax.websocket:javax.websocket-api:1.1. The reference implementation is project Tyrus, which implements WebSocket API 1.1 from version 1.8.