Using @OnReceive to Communicate via WebSockets Protocol

There is a simple, yet flexible way to communicate with a server via WebSockets protocol. The support can transfer any classes generated by @Model annotation and reuses already existing @OnReceive infrastructure - just defines detailed special behavior, which is described here.

Define JSON Class

The first step in using WebSockets is to create a model classes to encapsulate communiation with the server. For example one for sending requests and one for receiving replies:
    @Model(className = "Comm", properties={})
    final class Communication {
        @Model(className = "Request", properties = {
            @Property(name = "msg", type = MsgType.class),
            @Property(name = "username", type = String.class),
            @Property(name = "password", type = String.class),
            @Property(name = "gameId", type = String.class),
            @Property(name = "color", type = Color.class),
            @Property(name = "from", type = String.class),
            @Property(name = "to", type = String.class),
            @Property(name = "observer", type = boolean.class),
        })
        static class RequestModel {
        }

        @Model(className = "Response", properties = {
            @Property(name = "msg", type = MsgType.class),
            @Property(name = "turn", type = Color.class),
            @Property(name = "color", type = Color.class),
            @Property(name = "gameId", type = String.class),
            @Property(name = "status", type = String.class),
            @Property(name = "moves", type = String.class, array = true),
            @Property(name = "from", type = String.class),
            @Property(name = "to", type = String.class),
        })
        static class ResponseModel {
        }

        enum MsgType {
            CreateGame, QueryGames, SendMove, JoinGame, UpdateGame;
        }
        enum Color {
            W, B;
        }
    }            
    
And then it is just a matter of creating the communication end point. As usual with @OnReceive annotation one starts with defining the response handler:
    @OnReceive(
        data = Request.class, 
        url = "{url}", 
        method = "WebSocket", 
        onError = "anErrorHappened"
    ) 
    static void queryServer(Comm c, Response r) {
        if (r == null) {
            // connection stablished!
            return;
        }
        // message arrived!
        switch (r.getMsg()) {
            case CreateGame: /* do something */ break;
            case QueryGames: /* do something */ break;
            case SendMove: /* do something */ break;
            case JoinGame: /* do something */ break;
            case UpdateGame: /* do something */ break;
        }
    }
    static void anErrorHappened(Comm c, Exception t) {
        if (t == null) {
            // OK, connection has been closed
        } else {
            // handle the error t somehow
        }
    }
    
The WebSockets specification usually defines what should happen onopen, onmessage, onclose and onerror. All these messages are supported in the previous example as well: Using the @OnReceive annotation instructs its associated annotation processor to add appropriate send method into the generated Comm class. One can use it to establish communication, send messages and close the communication channel. Here are three methods showing how to do it:
    static void connect(Comm c) {
      // open a websocket channel:
      c.queryServer("ws://server/path", null);
      // the method returns immediately and starts establishing the connection
      // once that is finished either onopen or onerror type of
      // message is delivered
    }

    static void sendMessage(Comm c, Request r) {
      // sends the message to already openned websocket channel:
      c.queryServer("ws://server/path", r);
    }

    static void disconnect(Comm c) {
      // sending null again, closes the connection
      c.queryServer("ws://server/path", null);
    }
    
One cannot change the URL while a connection is open otherwise one risks IllegalStateException runtime exceptions. To connect to different web socket server, one needs to close the connection first and only later open new one with different URL.

Enjoy WebSockets via @OnReceive!