jsr372-experts@javaserverfaces-spec-public.java.net

[jsr372-experts] Re: [jsr372-experts mirror] Re: websocket.

From: Leonardo Uribe <leonardo.uribe_at_irian.at>
Date: Tue, 10 Jan 2017 21:04:42 -0500

Hi


MM>> Technically, a websocket is an upgrade of the HTTP protocol which
MM>> establishes a bidirectional communication channel between two peers.
Once
MM>> the connection is established it will be kept open until one of the
peers
MM>> closes it. Thus, it can be used to send any time any information (with
MM>> respect to technical limitations) from any of both peers to the other.
MM>> Because the channel is open all the time, this connection is faster
MM>> than a series of HTTP request response cycles.

MM>> Although a websocket establishes a channel between two peers, it is
MM>> broadly used to broadcast information from one server to all clients.
MM>> An example might be a chat room, where all individual user inputs are
MM>> distributed to all (other) users. A simplified version of this
scenario
MM>> might be implemented using Java EE 7 like this:

  @OnMessage
  public void onMessage(String message, Session session) throws
IOException, EncodeException{
    for (Session peer : peers) {
        peer.getBasicRemote().sendObject(session.getId() + ": " + message);
    }
  }

MM>> Within JSF, we may send data from client to server at any time by
initiating
MM>> an ajax request.
MM>> But, before JSF 2.3 there is no standard way to push information from
MM>> server to client.

Yes, agreed.

MM>> Here <f:websocket> comes into play.
MM>> Via @Push the developer may push information from server to client.
MM>> If I understood it correctly, a send(message) will perform a
broadcast, whilst
MM>> .send(message, user) will send to a named user.

The name of the component seems to be changed at some point. According to
the latest
javadoc there is h:scriptWebsocket (but f:websocket still appears), so I
guess
that's the final name.

h:scriptWebsocket has two attributes to control that broadcast: "scope" and
"user".
The javadoc says "... All open websockets on the same channel and user will
receive the same push message from the server. ...".

So, according to the "scope" attribute set for the channel, send(message)
will
perform the broadcast of that message. If "user" is set, the default scope
is "session", not "application".

MM>> What happens, if a couple of clients use the same value for user?

Depends of the "scope" used to initialize the channel in h:scriptWebsocket.
If the
scope is "application" everyone sharing the same user token will receive
the message.
If the scope is "session", only the one sharing the same user and session
will receive
the message.

MM>> Will it be a selective broadcast, e.g. like sending data to one of a
couple
MM>> of chatrooms?

If you need an special logic, the idea is onOpen provides a point to
initialize
the channel and provide the custom logic. For a chatroom, set "user" and
specify
scope as "session" is the best option.

MM>> (due to some technical problems which I reported earlier, I could not
MM>> verify this detail yet)

Ok.

MM>> Back on client side, the data is handled by a JavaScript function.

MM>> Let's take a look at a possible usage of a websocket with JSF 2.2 /
Java EE 7:
      <div>
        Enter message:
        <h:inputText onkeypress="if (event.keyCode === 13)
{acceptValue(this);}"/>
        <br/>
      </div>
    <h:inputTextarea id="messages" style="width: 100%; min-height: 10em;"/>

MM>> A small JavaScript file is needed to establish the communication
channel and
MM>> to handle the client side sending and receiving part of the messages.

var websocket;

window.onload = function () {
  websocket = new WebSocket(obtainUri());
  websocket.onerror = function (evt) {
    onError(evt)
  };
  websocket.onmessage = function (evt) {
    onMessage(evt)
  };
}

function obtainUri(){
  return "ws://" + document.location.host + document.location.pathname +
"chat";
}

function onError(evt) {
  writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
}


function acceptValue(element) {
    websocket.send(element.value);
    element.value = "";
    return false;
}

function onMessage(evt) {
  element = document.getElementById("messages");
  if (element.value.length === 0) {
    element.value = evt.data;
  } else {
    oldTexts = element.value.split("\n").slice(-19);
    element.value = oldTexts.join("\n") + "\n" + evt.data;
    element.scrollTop = element.scrollHeight;
  }
  return;
}

MM>> It is not very much code to broadcast a message from an input field to
a
MM>> textarea of all connected clients. Since I use JavaScript, I can
implement
MM>> any scenario like broacasting admin messages etc. This solution takes
MM>> advance of the high performing open communication channel in both
directions.

MM>> Now, back to JSF 2.3 and <f:websocket>

MM>> To distribute the received data, I need to implement some JavaScript
code.
MM>> Formerly, JSF tags could be used to hide all those non-Java stuff.
MM>> Although I can use JavaScript by JSF.js to perform an ajax request,
MM>> a common way is to hide this by using <f:ajax>. With respect to
MM>> <f:websocket> such a JSF feature (hiding away JavaScript) is missing.

As previously mentioned, now you can use f:ajax inside h:scriptWebsocket.

MM>> And we can use <f:websocket> only to push data from server to client.
MM>> Thus we can not take advantage of the faster channel from client to
server.

Well, it is not that simple. It is more accurate to say that JSF as a
framework
needs to keep a consistent view state. There is a queue on top of JSF ajax
requests that serialize the requests sent to the server. If you send
information
from the client to the server using a websocket and that information changes
the view, the view state could be in a inconsistent state later. Enforce
ajax
to communicate from the client to the server ensures consistency.

What happens if you want to communicate from the server to the client and
you
don't need or dont change the view in the process? Well, you can imagine
a "simplified" lifecycle, where the message is received on the websocket
server
side listener and then some action to process the message happens, but later
you realize that the websocket channel is a view related concept. For
example,
if the view is destroyed, the associated websocket must be closed. The
"properties" of h:scriptWebsocket are things that are initialized per view.
The "listener" of the message in the server is something that should be
defined in the tag as an EL expression, so it is in the view. Well, it is
theoretically possible to do it, but it is not easy to do it.

It could be something like:

<h:scriptWebsocket channel="chat" onservermessage="#{...}"/>

But at this point it is too late to do it. The problem is the EL expression
requires a context to be evaluated properly, and the context is given
by the view.

MM>> I suggest optionally to connect the <f:websocket> to an input element
MM>> as well to an output element, e.g.

<f:websocket channel="xxx" user="yyy" input="idOfUiInputElement"
output="idOfUiElement">

MM>> input is optional whilst output is mutable exclusive with onmessage.
Both
MM>> attributes may point to the same element.

MM>> For output we may define a behavior like "adds all messages",
"replaces content
MM>> with new message", "keeps last n messages".

MM>> For the JavaScript fans, we may establish a mutual exclusive option to
MM>> send data via the open channel.

MM>> Thoughts?

It doesn't look like something to include in the standard, or at least it is
something that can be done in a third party library.

regards,

Leonardo Uribe

2017-01-04 2:04 GMT-05:00 Bauke Scholtz <balusc_at_gmail.com>:

> Hi,
>
> Since m08 you can nest f:ajax into f:websocket. See also javax.faces.Push
> javadoc.
>
> As to user, yes indeed channels with same user attribute will receive the
> same message. And no, this value cannot be controlled by the client, if
> that was your concern.
>
> Cheers, B
>
> On Tue, Jan 3, 2017, 23:56 Michael Müller <michael.mueller_at_mueller-
> bruehl.de> wrote:
>
>> Hi all,
>>
>> Here are some questions and thoughts about websocket:
>>
>>
>> Technically, a websocket is an upgrade of the HTTP protocol which
>> establishes a bidirectional communication channel between two peers. Once
>> the connection is established it will be kept open until one of the peers
>> closes it. Thus, it can be used to send any time any information (with
>> respect to technical limitations) from any of both peers to the other.
>> Because the channel is open all the time, this connection is faster than a
>> series of HTTP request response cycles.
>>
>> Although a websocket establishes a channel between two peers, it is
>> broadly used to broadcast information from one server to all clients. An
>> example might be a chat room, where all individual user inputs are
>> distributed to all (other) users. A simplified version of this scenario
>> might be implemented using Java EE 7 like this:
>>
>> @OnMessage
>> public void onMessage(String message, Session session) throws
>> IOException, EncodeException{
>> for (Session peer : peers) {
>> peer.getBasicRemote().sendObject(session.getId() + ": " +
>> message);
>> }
>> }
>>
>>
>> Within JSF, we may send data from client to server at any time by
>> initiating an ajax request.
>> But, before JSF 2.3 there is no standard way to push information from
>> server to client.
>>
>> Here <f:websocket> comes into play.
>>
>> Via @Push the developer may push information from server to client.
>> If I understood it correctly, a send(message) will perform a broadcast,
>> whilst .send(message, user) will send to a named user.
>> What happens, if a couple of clients use the same value for user? Will it
>> be a selective broadcast, e.g. like sending data to one of a couple of
>> chatrooms?
>> (due to some technical problems which I reported earlier, I could not
>> verify this detail yet)
>>
>> Back on client side, the data is handled by a JavaScript function.
>>
>>
>> Let's take a look at a possible usage of a websocket with JSF 2.2 / Java
>> EE 7:
>>
>> <div>
>> Enter message:
>> <h:inputText onkeypress="if (event.keyCode === 13)
>> {acceptValue(this);}"/>
>> <br/>
>> </div>
>> <h:inputTextarea id="messages" style="width: 100%; min-height:
>> 10em;"/>
>>
>> A small JavaScript file is needed to establish the communication channel
>> and to handle the client side sending and receiving part of the messages.
>>
>> var websocket;
>>
>> window.onload = function () {
>> websocket = new WebSocket(obtainUri());
>> websocket.onerror = function (evt) {
>> onError(evt)
>> };
>> websocket.onmessage = function (evt) {
>> onMessage(evt)
>> };
>> }
>>
>> function obtainUri(){
>> return "ws://" + document.location.host + document.location.pathname +
>> "chat";
>> }
>>
>> function onError(evt) {
>> writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
>> }
>>
>>
>> function acceptValue(element) {
>> websocket.send(element.value);
>> element.value = "";
>> return false;
>> }
>>
>> function onMessage(evt) {
>> element = document.getElementById("messages");
>> if (element.value.length === 0) {
>> element.value = evt.data;
>> } else {
>> oldTexts = element.value.split("\n").slice(-19);
>> element.value = oldTexts.join("\n") + "\n" + evt.data;
>> element.scrollTop = element.scrollHeight;
>> }
>> return;
>> }
>>
>> It is not very much code to broadcast a message from an input field to a
>> textarea of all connected clients. Since I use JavaScript, I can implement
>> any scenario like broacasting admin messages etc. This solution takes
>> advance of the high performing open communication channel in both
>> directions.
>>
>> Now, back to JSF 2.3 and <f:websocket>
>>
>> To distribute the received data, I need to implement some JavaScript
>> code. Formerly, JSF tags could be used to hide all those non-Java stuff.
>> Although I can use JavaScript by JSF.js to perform an ajax request, a
>> common way is to hide this by using <f:ajax>. With respect to <f:websocket>
>> such a JSF feature (hiding away JavaScript) is missing. And we can use
>> <f:websocket> only to push data from server to client. Thus we can not take
>> advantage of the faster channel from client to server.
>>
>> I suggest optionally to connect the <f:websocket> to an input element as
>> well to an output element, e.g.
>>
>> <f:websocket channel="xxx" user="yyy" input="idOfUiInputElement"
>> output="idOfUiElement">
>>
>> input is optional whilst output is mutable exclusive with onmessage. Both
>> attributes may point to the same element.
>>
>> For output we may define a behavior like "adds all messages", "replaces
>> content with new message", "keeps last n messages".
>>
>> For the JavaScript fans, we may establish a mutual exclusive option to
>> send data via the open channel.
>>
>> Thoughts?
>>
>> --
>>
>> Herzliche Grüße - Best Regards,
>>
>> Michael Müller
>> Brühl, Germany
>> blog.mueller-bruehl.de
>> it-rezension.de
>> @muellermi
>>
>>
>> Read my books
>> "Web Development with Java and JSF": <https://leanpub.com/jsf>
>> https://leanpub.com/jsf
>> "Java Lambdas and Parallel Streams":
>> <http://www.apress.com/de/book/9781484224861>
>> http://www.apress.com/de/book/9781484224861
>> "Visitors" a photographic image book: <https://leanpub.com/visitors>
>> https://leanpub.com/visitors
>>
>>
>>