WebSocket Chat Example

    WebSocket Chat Example

    1. import io.micronaut.websocket.WebSocketSession
    2. import io.micronaut.websocket.annotation.OnClose
    3. import io.micronaut.websocket.annotation.OnMessage
    4. import io.micronaut.websocket.annotation.OnOpen
    5. import io.micronaut.websocket.annotation.ServerWebSocket
    6. import java.util.function.Predicate
    7. @ServerWebSocket("/chat/{topic}/{username}") (1)
    8. class ChatServerWebSocket {
    9. private WebSocketBroadcaster broadcaster
    10. ChatServerWebSocket(WebSocketBroadcaster broadcaster) {
    11. this.broadcaster = broadcaster
    12. }
    13. @OnOpen (2)
    14. void onOpen(String topic, String username, WebSocketSession session) {
    15. String msg = "[" + username + "] Joined!"
    16. broadcaster.broadcastSync(msg, isValid(topic, session))
    17. }
    18. @OnMessage (3)
    19. void onMessage(
    20. String topic,
    21. String username,
    22. String message,
    23. WebSocketSession session) {
    24. String msg = "[" + username + "] " + message
    25. broadcaster.broadcastSync(msg, isValid(topic, session)) (4)
    26. }
    27. @OnClose (5)
    28. void onClose(
    29. String username,
    30. WebSocketSession session) {
    31. String msg = "[" + username + "] Disconnected!"
    32. broadcaster.broadcastSync(msg, isValid(topic, session))
    33. }
    34. private Predicate<WebSocketSession> isValid(String topic, WebSocketSession session) {
    35. return { s -> s != session && topic.equalsIgnoreCase(s.getUriVariables().get("topic", String.class, null)) }
    36. }
    37. }

    WebSocket Chat Example

    1. import io.micronaut.websocket.WebSocketBroadcaster
    2. import io.micronaut.websocket.WebSocketSession
    3. import io.micronaut.websocket.annotation.OnMessage
    4. import io.micronaut.websocket.annotation.OnOpen
    5. import io.micronaut.websocket.annotation.ServerWebSocket
    6. import java.util.function.Predicate
    7. @ServerWebSocket("/chat/{topic}/{username}") (1)
    8. class ChatServerWebSocket(private val broadcaster: WebSocketBroadcaster) {
    9. @OnOpen (2)
    10. fun onOpen(topic: String, username: String, session: WebSocketSession) {
    11. val msg = "[$username] Joined!"
    12. broadcaster.broadcastSync(msg, isValid(topic, session))
    13. }
    14. @OnMessage (3)
    15. fun onMessage(
    16. topic: String,
    17. username: String,
    18. message: String,
    19. session: WebSocketSession) {
    20. val msg = "[$username] $message"
    21. broadcaster.broadcastSync(msg, isValid(topic, session)) (4)
    22. }
    23. @OnClose (5)
    24. fun onClose(
    25. topic: String,
    26. username: String,
    27. val msg = "[$username] Disconnected!"
    28. broadcaster.broadcastSync(msg, isValid(topic, session))
    29. }
    30. private fun isValid(topic: String, session: WebSocketSession): Predicate<WebSocketSession> {
    31. return Predicate<WebSocketSession>{ s -> (s !== session && topic.equals(s.getUriVariables().get("topic", String::class.java, null), ignoreCase = true)) }
    32. }
    33. }

    In terms of binding the method arguments to each WebSocket method can be:

    • A variable from the URI template (in the above example topic and username are variables in the URI template)

    • An instance of

    The @OnClose method can also optionally receive a . The @OnClose method is invoked prior to the session closing.

    The @OnMessage Method

    The method can define a parameter that is the message body. The parameter can be one of the following:

    • Any Java primitive or simple type (such as String). In fact any type that can be converted from ByteBuf (you can register additional TypeConverter beans if you wish to support a custom type).

    • A byte[], a or a Java NIO ByteBuffer.

    • A Plain Old Java Object (POJO). In the case of a POJO the POJO will be decoded by default as JSON using . You can register a custom codec if necessary and define the content type of the handler using the @Consumes annotation.

    A method annotated with can be added to implement custom error handling. The @OnError method can optionally define a parameter that receives the exception type that is to be handled. If no @OnError handling is present and a unrecoverable exception occurs the WebSocket is automatically closed.

    Non-Blocking Message Handling

    The previous example uses the broadcastSync method of the interface which blocks until the broadcast is complete. A similar sendSync method exists in WebSocketSession to send a message to a single receiver in a blocking manner. You can however implement non-blocking WebSocket servers by instead returning a or a Future from each WebSocket handler method. For example:

    WebSocket Chat Example

    WebSocket Chat Example

    1. @OnMessage
    2. Publisher<Message> onMessage(
    3. String topic,
    4. String username,
    5. Message message,
    6. WebSocketSession session) {
    7. String text = "[" + username + "] " + message.getText()
    8. Message newMessage = new Message(text)
    9. broadcaster.broadcast(newMessage, isValid(topic, session))
    10. }

    WebSocket Chat Example

    1. @OnMessage
    2. fun onMessage(
    3. topic: String,
    4. username: String,
    5. message: Message,
    6. session: WebSocketSession): Publisher<Message> {
    7. val text = "[" + username + "] " + message.text
    8. val newMessage = Message(text)
    9. return broadcaster.broadcast(newMessage, isValid(topic, session))
    10. }

    For sending messages asynchronously outside Micronaut annotated handler methods, you can use broadcastAsync and sendAsync methods in their respective and WebSocketSession interfaces. For blocking sends, the broadcastSync and sendSync methods can be used.

    By default a unique @ServerWebSocket instance is created for each WebSocket connection. This allows you to retrieve the from the @OnOpen handler and assign it to a field of the @ServerWebSocket instance.

    If you define the @ServerWebSocket as @Singleton it should be noted that extra care will need to be taken to synchronize local state to avoid thread safety issues.

    Sharing Sessions with the HTTP Session

    The is by default backed by an in-memory map. If you add the the session module you can however share sessions between the HTTP server and the WebSocket server.

    By default Micronaut will timeout idle connections that have no activity after 5 minutes. Normally this is not a problem as browsers will automatically reconnect WebSocket sessions, however you can control this behaviour by setting the micronaut.server.idle-timeout setting (a negative value will result no timeout):

    Setting the Connection Timeout for the Server

    1. micronaut:
    2. server:
    3. idle-timeout: 30m # 30 minutes

    If you are using Micronaut’s WebSocket client then you may also wish to set the timeout on the client:

    1. micronaut:
    2. http: