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

[jsr372-experts] websocket.

From: Michael Müller <michael.mueller_at_mueller-bruehl.de>
Date: Tue, 3 Jan 2017 23:56:14 +0100

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 <http://blog.mueller-bruehl.de/>
it-rezension.de <http://it-rezension.de/>
@muellermi
Read my books
   "Web Development with Java and JSF": https://leanpub.com/jsf
   "Java Lambdas and Parallel Streams": 
http://www.apress.com/de/book/9781484224861
   "Visitors" a photographic image book: https://leanpub.com/visitors