What are the usecases of encoder list or decoder list in the WebSocket EndPoints annotation?
Asked Answered
I

2

7

I am learning tyrus WebSocket implementation. But I don't understand why and when do we need more then one encoders or decoders in the websocket endpoints. For example:

@ServerEndpoint(value = "/subscribe", decoders = { TextStreamMessageDecoder.class }, encoders = { TextStreamMessageEncoder.class })
public class ChatServerEndPoint {
......
}

There is only one decoder and encoder in the decoders and encoders list. As these are array of decoders or encoders then I can use multiple types of encoders or decoders at a time. But in the api description then mentioned,

The websocket runtime will use the first decoder in the list able to decode a message, ignoring the remaining decoders.

If it always use the first elements of the list then what are the usecases of multiple encoder or decoder in the WebSockets API?

After Editing

public class TextStreamMessageDecoder implements Decoder.TextStream<JsonWrapper>{

    public JsonWrapper decode(Reader reader) throws DecodeException,
        IOException {
    JsonReader jsonReader = Json.createReader(reader);
    JsonObject jsonObject = jsonReader.readObject();

    return new JsonWrapper(jsonObject);
    }

}

public class TextStreamMessageEncoder implements Encoder.TextStream<JsonWrapper>{

public void encode(JsonWrapper object, Writer writer)
        throws EncodeException, IOException {
    JsonWriter jsonWriter = Json.createWriter(writer);
    JsonObject jsonObject = object.getJson();
    jsonWriter.write(jsonObject);

}

}

public class MessageDecoder implements Decoder.Text<JsonWrapper> {

    public JsonWrapper decode(String s) throws DecodeException {
    JsonObject json = Json.createReader(new StringReader(s)).readObject();
    return new JsonWrapper(json);
    }

    public boolean willDecode(String s) {
    // TODO Auto-generated method stub
    try {
        Json.createReader(new StringReader(s)).read();
        return true;
    } catch (JsonException ex) {
        ex.printStackTrace();
        return false;
    }
    }

}

public class MessageEncoder implements Encoder.Text<JsonWrapper> {

    public String encode(JsonWrapper jsonWrapper) throws EncodeException {

    return jsonWrapper.getJson().toString();
    }

}


@ClientEndpoint(decoders = { MessageDecoder.class}, encoders = { MessageEncoder.class })
public class ChatClientEndPoint {
@OnMessage
public void onMessage(String message, Session session) {
    logger.info("onMessage: " + session.getId());
    if (this.messageHandler != null)
        this.messageHandler.handleMessage(message);

}
}


@ServerEndpoint(value = "/subscribe", decoders = { TextStreamMessageDecoder.class, MessageDecoder.class}, encoders = { TextStreamMessageEncoder.class, MessageEncoder.class })
public class ChatServerEndPoint {
@OnMessage
public void onMessage(String message, Session session) {
    logger.info("onMessage: " + session.getId());
    // logger.info("onMessage: " + message.toString());
    Gson gson = new Gson();
    ClientMessage clientMessage = gson.fromJson(message,
            ClientMessage.class);
    System.out.println(clientMessage.toString());
}
}

Server sends to Client:

 try {
            Gson gson = new Gson();
            session.getBasicRemote().sendObject(
                    gson.toJson(new ServerMessage(1, "connection accepted")));
        } catch (EncodeException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
    }

Clients sends to Server:

try {
        Gson gson = new Gson();
        session.getBasicRemote().sendObject(
                gson.toJson(new ClientMessage(1, "FirstName")));
    } catch (EncodeException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
}

Clients sends message using MessageEncoder Class while Server has two encoders and decoder including TextStreamMessageEncoder or decoder and MessageEncoder or decoder. So which decoder will be used when client send message to server?

Indite answered 14/7, 2015 at 12:36 Comment(0)
V
2

Decoders do have #willDecode method, so you could have one decoding json, other one for decoding hocon etc.

Ad encoder - they are chosen based on the generic type - runtime should always prefer the "closest" one, based on the type you are trying to send (encode).

Also, you can have more than one onMessage method type - text/binary, so you might want to register test and binary decoders.

Visit answered 15/7, 2015 at 15:9 Comment(5)
if ClientEndPoint has only TextStream Message Decoder and ServerEndPoint has only Text Message Decoder, should the message be decoded properly in both side. For me, it is decoding properly. But I don't know the reason. They are decoding same JsonWrapper class but their encoding type is different. Could you please explain it?Indite
please share your decoder implementation and the code which sends the message - server and client side. I don't know why your case should not work, since you did not share required details.Visit
I have edited my question with adding all the classesIndite
you are not using encoders/decoders at all - both @OnMessage methods consume "String message", which is handled implicitly by Tyrus. You can just remove it, (significantly reduce complexity of your code) and you'll get still the same behaviour.Visit
Your answers make me clear about encoders and decoders. I wrote the full answer with examples here. Thanks.Indite
I
0

According to the first answer I would like to summarise it as bellows:

Let's create two types of encoders and decoders including Text and BinaryStream.

public class JsonMessageEncoder implements Encoder.Text<JsonWrapper> {

    public String encode(JsonWrapper jsonWrapper) throws EncodeException {
        return jsonWrapper.getJson().toString();
    }

}

public class JsonMessageDecoder implements Decoder.Text<JsonWrapper> {

    public JsonWrapper decode(String s) throws DecodeException {
        JsonObject json = Json.createReader(new StringReader(s)).readObject();
        System.out.println("JsonWrapper decode: "+json);
        return new JsonWrapper(json);
    }

    public boolean willDecode(String s) {
        // TODO Auto-generated method stub
        try {
            Json.createReader(new StringReader(s)).read();
            return true;
        } catch (JsonException ex) {
            ex.printStackTrace();
            return false;
        }
    }

}

public class BinaryStreamMessageEncoder implements Encoder.BinaryStream<BinaryMessage>{

    public void encode(BinaryMessage object, OutputStream os)
            throws EncodeException, IOException {
        ObjectOutput objectOutput = new ObjectOutputStream(os);
        objectOutput.writeObject(object);

    }

}

public class BinaryStreamMessageDecoder implements Decoder.BinaryStream<BinaryMessage>{

    public BinaryMessage decode(InputStream is) throws DecodeException,
            IOException {
        ObjectInput objectInput = new ObjectInputStream(is);
        try {
            BinaryMessage binaryMessage= (BinaryMessage) objectInput.readObject();
            return binaryMessage;
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;

    }

}

Now add these encoders and decoders in the Client and Server Endpoints.

@ClientEndpoint(decoders = { JsonMessageDecoder.class, BinaryStreamMessageDecoder.class}, encoders = { JsonMessageEncoder.class, BinaryStreamMessageEncoder.class })
public class ChatClientEndPoint {
@OnMessage
    public void onMessage(JsonWrapper message, Session session) {
        logger.info("JsonWrapper onMessage: " + session.getId());
        if (this.messageHandler != null)
            this.messageHandler.handleMessage(message);

    }

    @OnMessage
    public void onMessage(BinaryMessage message, Session session) {
        logger.info("BinaryMessage onMessage: " + session.getId());
        if (this.messageHandler != null)
            this.messageHandler.handleMessage(message);

    }
}

@ServerEndpoint(value = "/subscribe", decoders = { JsonMessageDecoder.class, BinaryStreamMessageDecoder.class}, encoders = { JsonMessageEncoder.class,BinaryStreamMessageEncoder.class })
public class ChatServerEndPoint {
@OnMessage
    public void onMessage(JsonWrapper message, Session session) {
        logger.info("JsonWrapper onMessage: " + session.getId());
        // logger.info("onMessage: " + message.toString());
        Gson gson = new Gson();
        ClientMessage clientMessage = gson.fromJson(message.getJson().toString(),
                ClientMessage.class);
        System.out.println(clientMessage.toString());
    }
    @OnMessage
    public void onMessage(BinaryMessage message, Session session) {
        logger.info("BinaryMessage onMessage: " + session.getId());
        System.out.println(message.toString());
    }
}

Clients sends message to Server:

try {
            session.getBasicRemote().sendObject(new BinaryMessage("Binary Message"));
        } catch (EncodeException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

Server Sends Message to Client:

try {
            session.getBasicRemote().sendObject(
                    gson.toJson(new ServerMessage(1, "connection accepted")));
        } catch (EncodeException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

In the example: We use two @OnMessage methods for each end points where one is for Text type and another is for BinaryStream type decoder. Client and Server sends different types of message to each other where client sends BinaryMessage which has one String attribute while server send Json Object. So runtime will detect which type of decoder and encoder will be used when communicating.

Output Client:

JsonWrapper decode: {"id":1,"message":"connection accepted"}

ServerMessage [id=1, message=connection accepted]

Output Server:

BinaryMessage [name=Binary Message]

Indite answered 21/7, 2015 at 23:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.