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

[jsr372-experts] Re: [jsr372-experts mirror] Re: [JAVASERVERFACES-SPEC_PUBLIC-1396] f:socket for SSE and WebSocket PROPOSAL

From: Bauke Scholtz <balusc_at_gmail.com>
Date: Sat, 19 Dec 2015 14:09:53 +0100

Hi Ed,

As to renaming f:socket to f:websocket: I feel kind of wary on a bit longer
tag name, but if you desire that, that's OK by me :)

As to how ajax updating works: the f:socket definitely isn't an
UIComponent. It just registers a PreRenderView event listener on the
viewroot which runs every render response and re-evaluates the "enabled"
attribute (which is by the way renamed to "connected" in my current PoC).
If you carefully read the SocketEventListener source code of OmniFaces
o:socket, you can get an idea of its working:
https://github.com/omnifaces/omnifaces/blob/master/src/main/java/org/omnifaces/cdi/push/SocketEventListener.java

As to JS API: in my f:socket PoC I've so far the following functions:
- jsf.push.init(port, channel, onmessage, onclose, autoconnect);
- jsf.push.open(channel);
- jsf.push.close(channel);
The init() is automatically executed when f:socket is embedded in page. Its
autoconnect argument will be true if "connected" is absent or evaluates to
boolean true. The open() and close() will be executed during postbacks when
the "connected" attribute is an EL expression which gets changed during a
(ajax) postback on the same view.

As to getting actual WebSocket object, that's kind of complicated as I've
developed it as a reconnecting web socket which automatically
reconnectswhen it times out and thus basically creates a new WS object. So
during the client "session" there's not necessarily the same WS object all
the time. That's unfortunately the nature of web sockets and strongly
webbrowser-dependent. Best what we can offer is an abstract JS API with
functions like mentioned above.

As to dropping onerror, that's because it's not terribly useful. In WS API,
the actual error reason code is only available during onclose (it'll be
provided as 1st argument of onclose). But if you desire, I can add back
onerror and rework it as such that onerror is only called when close code
!= 1000, and otherwise only onclose is called (and thus not both
simultaneously). This however deviates kind of from WS API. Moreover, if we
want to cover all WS events, shouldn't we offer onopen too as that's one
still missing? So, onopen, onmessage, onerror and onclose whereby the
onerror+onclose is somewhat reworked that the error code ends up in onerror
and onclose is not called.

As to sending a message from client to server: I've no problem with adding
a new function for that to the JS API. We only need to make the endusers
really clear that there's no means of a FacesContext at the moment the push
message arrives, for the simple reason that there's no means of a HTTP
request/response. There's also no JSF view scope nor session scope
available. Receiving the message will only work in a request and
application scoped CDI bean. That's exactly why I personally think it's
more clear to endusers if we recommend to keep using f:ajax for that
instead.

As to changing PartialViewContext to add getEvalScripts(): in my PoC I've
already implemented it. It's rather trivial. I've created a separate issue
for it: https://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-1412

As to using javax.json: great! I will take care of writing a basic Json
encoder which can accept "everything". I only wonder about the interface.
Should it end up in public JSF API? Or should it be kept internal?

As to the setup: right now only a boolean context parameter is required to
programmatically install the WS endpoint and the security filter in JSF's
servlet context initializer and listener. We'd rather not have it enabled
by default (not all server admins would be happy with an WS endpoint open
all time, even though it's access-restricted), and installing the WS
endpoint can unfortunately not lazily be done during runtime (as it's
against JSR356 spec). Also, I unfortunately realized too late that handling
WS handshake requests can't be done in FacesServlet because the push
handshake request will never hit it anyway as it's filtered away by a
special servlet filter installed by the websocket implementation. So we
really need a servlet container initializer to install the security filter
and a servlet context listener to install the WS endpoint. I've proof of
concept already ready and it works perfectly fine in Undertow (WildFly) and
Tomcat, but I stumbled upon a lazy loading issue of Tyrus (as used in
GlassFish/Payara), see also https://java.net/jira/browse/TYRUS-420. I'm
still in discussion, but regardless of the outcome, I will eventually solve
it.

Hopefully this answers everything you need to know.


Neil, as to the portlet case, as you can read above, I realized installing
the security filter and endpoint unfortunately can't be done in
FacesServlet itself after all. Is there something like a container
initializer and context listener in portlets? Portlet has also its own
concept of a servlet filter? It should just map to /javax.faces.push/* and
check if that "/*" is allowed and otherwise return "bad request" error. In
Mojarra, FacesInitializer is the container initializer and
ConfigureListener is the context listener.


Kito, as to long polling support, I personally think it's not necessary to
support 00's technology. Moreover, WS is server side natively available
since Java EE 7 already. Even barebones servletcontainers like Tomcat and
Jetty supports it out the box. JSF 2.3 will require Java EE 8 and not be
backwards compatible with Java EE 7/6 anyway. I can however imagine that
long polling is desirable for clients which do not support WS, such as
IE6-9. At the moment, IE6-7 is definitely dead and IE8-9 has still a
significant share (~10%). But at the moment JSF 2.3 is released, I don't
think IE8-9 will still have that significant market share.


Cheers, B









On Fri, Dec 18, 2015 at 10:28 PM, Kito Mann <kito.mann_at_virtua.com> wrote:

> FWIW, I strongly believe we should support long polling as an alternative
> to WebSocket. JSF is used by lots of big companies that may not be able to
> use WebSocket, and having a reliable fallback is important.
>
> ___
>
> Kito D. Mann | @kito99 | Author, JSF in Action
> Web Components, Polymer, JSF, PrimeFaces, Java EE, and Liferay training
> and consulting
> Virtua, Inc. | virtua.tech
> JSFCentral.com | @jsfcentral
> +1 203-998-0403
>
> * Listen to the Enterprise Java Newscast: *http://
> <http://blogs.jsfcentral.com/JSFNewscast/>enterprisejavanews.com
> <http://ww.enterprisejavanews.com>*
>
>
> On Fri, Dec 18, 2015 at 1:02 PM, Edward Burns <edward.burns_at_oracle.com>
> wrote:
>
>> Thanks for this work and sorry for my inactivity.
>>
>> >>>>> On Mon, 7 Dec 2015 10:23:22 +0100, Bauke Scholtz <balusc_at_gmail.com>
>> said:
>>
>> B> Hi,
>> B> Last weekend I developed and added o:socket for OmniFaces, which could
>> be
>> B> used as base for f:socket:
>> B>
>> http://omnifaces.org/docs/javadoc/current/org/omnifaces/cdi/push/Socket.html
>> B> Source code can be found here:
>> B>
>> https://github.com/omnifaces/omnifaces/tree/master/src/main/java/org/omnifaces/cdi/push
>> B> (4 classes).
>>
>> B> Some considerations I took are however differ from f:socket:
>>
>> B> - I dropped SSE support. As per http://caniuse.com/#feat=websockets vs
>> B> http://caniuse.com/#feat=eventsource the WS enjoy broader support
>> B> (IE/Edge!) and the protocol is also much more efficient than SSE (no
>> B> persistent open connection). A single protocol also keeps server/client
>> B> side code much simpler.
>>
>> For f:socket, I agree to only support websocket. In that case, I
>> suggest we rename it to f:websocket. This lets us drop the "transport"
>> attribute also.
>>
>> B> - I dropped proposed widgetvar/autoconnect attributes and instead added
>> B> enabled attribute. It basically controls whether the underlying WS init
>> B> script will be rendered or not.
>>
>> This much sounds good.
>>
>> B> This is evaluated during view render time and can be
>> B> ajax-updated.
>>
>> What can be ajax-updated, the value of the "enabled" attribute? If
>> that's the case, there must be a server side UIComponent corresponding
>> to f:websocket. Is that the case in your design?
>>
>> B> The socket can be explicitly closed via a public script function
>> B> taking just channel name.
>>
>> Now we're getting into JS API defining territory. How about we make it
>> so when you open a WebSocket with <f:websocket id="foo"> we populate a
>> JavaScript associative array named "jsf.websocket" such that
>>
>> jsf.websockets["foo"]
>>
>> you get the actual JavaScript WebSocket object? This will have its
>> onopen, onerror, onmessage, onclose function references set as described
>> in the tag attributes.
>>
>> B> - I dropped proposed onerror attribute and instead added onclose
>> attribute.
>> B> It will also be invoked during onerror and is more useful as the
>> CloseEvent
>> B> is then available which also contains the error code. The script is
>> written
>> B> that way that it's also invoked when WS is not supported (e.g. IE6-9),
>> a
>> B> custom error code of -1 is then passed.
>>
>> How can you get away with dropping onerror when it's part of the W3C
>> WebSocket API? [1]
>>
>> B> The combination with o:commandScript (similar component is also
>> scheduled
>> B> for JSF 2.3, see spec issue 613) makes it a very nice tool to trigger
>> ajax
>> B> updates via push.
>>
>> OK, we'll take that up over there.
>>
>> >>>>> On Mon, 7 Dec 2015 19:08:52 +0100, arjan tijms <
>> arjan.tijms_at_gmail.com> said:
>>
>> AT> Would be great if a version of this can be pushed to the RI before
>> long.
>> AT> It's been a while since the last commit for a 2.3 feature, and nothing
>> AT> better to keep the community interested than some actual working code
>> they
>> AT> can play around with I guess ;)
>>
>> >>>>> On Mon, 7 Dec 2015 20:56:02 +0100, Michael Müller <
>> michael.mueller_at_mueller-bruehl.de> said:
>>
>> MM> Looks great for pushing data from server to client.
>> MM> But how to send data from client to server?
>> MM> Do we need something like
>> MM> public void receiveMessage?
>> MM> Or shall standard socket handling be used?
>>
>> Rather than Ajax, I think it's more natural to just allow the developer to
>> get access to the actual WebSocket, as I proposed above.
>>
>> >>>>> On Tue, 8 Dec 2015 20:38:10 +0100, Bauke Scholtz <balusc_at_gmail.com>
>> said:
>>
>> B> It would only require a change in PartialViewContext API. It must
>> offer a
>> B> method which triggers the partial response writer to start and end the
>> B> <eval>. Right now this is nowhere available in public API and even not
>> in
>> B> Mojarra implementation. Directly accessing the PartialResponseWriter to
>> B> achieve the <eval> job isn't possible. I'd propose adding
>> B> PartialViewContext#getEvalScripts() which returns a mutable
>> List<String> so
>> B> that the developer can add scripts to <eval> of the <partial-response>.
>> B> This allows easily executing scripts on complete of ajax response. Both
>> B> OmniFaces and PrimeFaces have an enhanced PartialViewContext
>> implementation
>> B> which offers this additional method which is ultimately used by
>> B> respectively RequestContext#execute() and Ajax#oncomplete(). The
>> <o:socket>
>> B> uses it to open/close the push channel during ajax requests depending
>> on
>> B> its "enabled" attribute.
>>
>> Yes, I like this proposal. Can you please file a separate JIRA for that
>> and open up a discussion thread here citing it?
>>
>> B> Further the o:socket encodes the push message as JSON string in and the
>> B> receiver script parses it back to JSON object before invoking the
>> onmessage
>> B> function. This JSON step is not necessary, but really nice to have.
>> Only,
>> B> it may require an internal JSON encoder or perhaps the javax.json API
>> B> (which is unfortunately not available on servletcontainers like Tomcat
>> and
>> B> Jetty on contrary to WebSockets). I could omit the JSON feature and
>> leave
>> B> it up to the developer. If JSON is not mandatory, it will be easy to
>> create
>> B> an API and impl for the RI.
>>
>> I have no problem depending on javax.json. We are an EE spec after all.
>>
>> >>>>> On Tue, 8 Dec 2015 23:09:45 +0100, arjan tijms <
>> arjan.tijms_at_gmail.com> said:
>>
>> AT> If I understood correctly, JSF 2.3 can depend on pretty much
>> everything
>> AT> from the Java EE Web Profile, so this shouldn't be a problem by
>> itself. For
>> AT> Tomcat and friends it would then be a matter of adding the JSON
>> encoder
>> AT> separately, wouldn't it? That's currently how it works for the CDI and
>> AT> BeanValidation dependencies.
>>
>> We can fully depend on EE8. That even means Servlet 4.0. This is a
>> departure from all preceding revisions of JSF.
>>
>> AT> Nevertheless, may be nice to see if some explicit pluggability
>> mechanism
>> AT> can be used here.
>>
>> No thank you. We have enough pluggability already.
>>
>> AT> For an initial version in Mojarra it may be an option to omit JSON
>> first,
>> AT> and then do the JSON support in a second iteration.
>>
>> I disagree, let's keep it in.
>>
>> B> I only wonder if both you and Ed agree on taking over the <o:socket>
>> in its
>> B> current form for JSF 2.3 <f:socket>. Among others, I omitted the SSE
>> B> support as it isn't standardized yet while WS is even backed by a Java
>> EE
>> B> API (JSR356) which is even implemented in barebones servletcontainers
>> like
>> B> Tomcat and Jetty. And, some attributes have been reworked to keep it
>> as
>> B> simple as possible.
>>
>> I'd like to get an answer to my question about whether or not there is a
>> server side UIComponent for each <f:websocket>. If so, we may have some
>> more fundamental architectural differences of opinion.
>>
>> >>>>> On Thu, 10 Dec 2015 14:49:31 +0100, Bauke Scholtz <balusc_at_gmail.com>
>> said:
>>
>> B> Yes, interpret it as ".. agree on me taking over .." :) Currently still
>> B> work in progress, programmatic CDI management is not funny.
>>
>> See preceding question.
>>
>> B> Just in case, I made some more changes to OmniFaces socket:
>> B> - Added "port" attribute which allows explicitly specifying WS server
>> port
>> B> number (as it's possible that the very same container runs the WS
>> server on
>> B> a different port than the HTTP server).
>> B> - Endpoint is now lazily programmatically registered on first usage of
>> B> o:socket instead than eagerly on startup.
>> B> - Endpoint now accepts only connections from predefined channel names
>> (the
>> B> ones specified in o:socket channel attribute), all others will just be
>> B> aborted by an exception.
>>
>> These are fine.
>>
>> >>>>> On Fri, 11 Dec 2015 12:50:35 +0000, Bauke Scholtz <balusc_at_gmail.com>
>> said:
>>
>> B> It has in the meanwhile been further improved though. For a live demo,
>> see
>> B> also http://snapshot.omnifaces.org/push/socket
>>
>> This is a lot of momentum!
>>
>> >>>>> On Wed, 16 Dec 2015 20:55:37 +0100, Bauke Scholtz <balusc_at_gmail.com>
>> said:
>>
>> B> While developing and testing the o:socket I wasn't satisfied with how
>> WS
>> B> session and HTTP session interacted with each other (among others, HTTP
>> B> session timeout doesn't trigger a re-handshake of the WS session,
>> causing
>> B> its endpoint config to still refer the old HTTP session), so I have
>> further
>> B> improved the o:socket to be less dependent on that and made even more
>> B> Portlet friendly as there are no hard javax.servlet dependencies
>> B> anymore.
>>
>> Yes, those two session objects are entirely disjoint, and necessarily
>> so. To access the HttpSession from the WebSocket session, you must save
>> it aside during the handshake. Perhaps we can make that easier so you
>> can have access to it automatically?
>>
>> B> For security, there's currently in o:socket only a servlet filter in
>> place
>> B> which should return 400 on WS handshake requests on non-existing
>> channels:
>> B>
>> https://github.com/omnifaces/omnifaces/blob/master/src/main/java/org/omnifaces/cdi/push/SocketChannelFilter.java
>> .
>>
>> B> As this isn't Portlet compatible, I'm for Mojarra's f:socket thinking
>> to
>> B> programmatically add /javax.faces.push/* to servlet mapping and then
>> do the
>> B> job in FacesServlet instead. Would this be OK for Portlets too? Neil?
>> Or
>> B> perhaps I should append the current FacesServlet mapping to the push
>> URL
>> B> and then inspect the presence of the /javax.faces.push prefix as
>> currently
>> B> also done for /javax.faces.resource requests.
>>
>> This is my biggest beef with p:socket. It's a pain to set up. I'd
>> really like to make it so you don't have to do any additional set up.
>> We can bake this functionality straight into the FacesServlet.
>>
>> Can you rework your impl to do it that way?
>>
>> Ed
>>
>> --
>> | edward.burns_at_oracle.com | office: +1 407 458 0017
>>
>>
>> [1]
>> http://www.w3.org/TR/2011/WD-websockets-20110419/#the-websocket-interface
>>
>
>