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

[jsr372-experts] Re: [jsr372-experts mirror] Re: Re: Re: improvements to f:websocket and PushContext

From: Leonardo Uribe <leonardo.uribe_at_irian.at>
Date: Mon, 26 Sep 2016 20:04:45 -0500

Hi

NOTE: This mail is going to be a bit repetitive, but it is necessary in
order to digest this topic properly. Thanks for your patience.

According to the latest javadoc I can see, h:commandScript is a component
with the same attributes as f:ajax (without 'event') with some new ones:

- action
- actionListener
- immediate
- rendered
- value
- autorun
- name

So, I guess the intention is to work like something between a UICommand and
Ajax, but it looks like h:commandScript is something that has life on its
own, just like s:jsCallbackFunction has. It is a component that renders an
script function with some jsf behavior in it but without "get your
hands dirty" with javascript code.

Let's take a look again at the syntax proposed:

<f:websocket ... onmessage="someCommandScript" />

<h:commandScript name="someCommandScript"
                 action="#{bean.pushed}"
                 render="foo" />

If "name" is the fixed javascript function name, you can't use this inside
a composite component or multiple times on the page. There is a 1-1
relationship between f:websocket and h:commandScript, because you can't use
the name more than once in a page. You can improve it using this:

<f:websocket ... onmessage="#{f:getCommandScript(component, 'script1')}" />

<h:commandScript id="script1" name="someCommandScript"
                 action="#{bean.pushed}"
                 render="foo" />

use an EL function that generate the function name using the id, it is like
a findComponent call that locate the h:commandScript component and from
its cliendId it replace all invalid characters (':') with '_' .

The script could be generated in this way:

<script type="text/javascript">
    var form_panel_script1 = function(){
        jsf.ajax.request(...);
    }
</script>

Ok, that improve things a little bit, now I can use it inside composite
components, more than once per page but there is still a 1:1 relationship
between f:websocket and h:commandScript. You can't use more than one
h:commandScript with a f:websocket. Do you have 5 or 6 regions in your page
that needs to be updated from the server? you need 5 or 6 f:websocket with
the same number of h:commandScript tags.

I understand that this approach provides flexiblity and that's ok, but it
looks way too complex, for a simple use case where the user only wants to
refresh a component from the server.

Take a look at one possible example with p:socket tag:

<p:socket channel="/channel">
    <p:ajax event="message"
            listener="#{controller.yourListenerMethod}"
            update=":form:table" />
</p:socket>

I'm not surprised somebody else has already thought the same. If the soul
of the spec is standarize common practices, this looks like something
common and easy to understand. Note in this case the "event" property
is not used as I have thought, which is something like this:

<f:websocket channel="ping" messageEvent="true">
    <f:ajax event="updateInfoBox" render="myInfoBox"/>
    <f:ajax event="updateMessageBox" render="myMessageBox"/>
    <f:ajax event="update" render="myMessageBox myInfoBox"/>
</f:websocket>

where you can use 1 websocket connection to update 1, 2 or more components
using "event" as the payload for the push. Please note a new possible
attribute called "messageEvent" that could enable the behavior wanted,
but maybe the presence of 2 or more f:ajax could enable it.

Other way to see the problem is think about f:websocket, h:commandScript
and f:ajax together:

<f:websocket id="wschannelping" channel="ping">
</f:websocket>

<h:commandScript forwebsocket="wschannelping"
actionListener="#{bean.update}">
   <f:ajax event="updateInfoBox" .../>
</h:commandScript>

In that sense, h:commandScript register itself in f:websocket in a dynamic
way, as if f:websocket could have a built-in "Command Pattern". You can
notice that h:commandScript is a component that "by default" has an
embedded f:ajax tag, because you can't call the actionListener without an
ajax request. But maybe this mode is too much, the component lookup is too
complex.

At last, in conclusion, in my personal opinion, the proposal could be:

- Allow f:websocket and f:ajax work together (f:websocket is a component
implementing ClientBehaviorHolder).
- Add a mode with a property called "messageEvent" or whatever, by default
false, that if it is set to true, binds the pushed message with f:ajax
event
property and use it to "demux" the notification and invoke the right ajax
request.
- Use a generated function name based on the clientId in h:commandScript and
an EL function to calculate it.

Suggestions are welcome.

regards,

Leonardo Uribe

2016-09-26 6:41 GMT-05:00 Josh Juneau <juneau001_at_gmail.com>:

> +1...I agree. Individuals already understand the way that <f:ajax>
> functions, so it makes sense to use it if possible.
>
> Josh Juneau
> juneau001_at_gmail.com
> http://jj-blogger.blogspot.com
> https://www.apress.com/index.php/author/author/view/id/1866
>
>
> On Mon, Sep 26, 2016 at 5:38 AM, Kito Mann <kito.mann_at_virtua.com> wrote:
>
>> +1 for adding <f:ajax> support to <f:websocket>. I believe PrimeFaces
>> does this with <p:socket>, and it's certainly more intuitive than hooking
>> it up to <f:commandScript>.
>>
>> ___
>>
>> 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 | knowesis.io
>> <http://knowesis.io/web/webcomponents> - fresh Web Components info
>> +1 203-998-0403
>>
>> * Listen to the Enterprise Java Newscast: *http://
>> <http://blogs.jsfcentral.com/JSFNewscast/>enterprisejavanews.com
>> <http://ww.enterprisejavanews.com>*
>>
>>
>> On Sun, Sep 25, 2016 at 11:55 AM, Bauke Scholtz <balusc_at_gmail.com> wrote:
>>
>>> Hi,
>>>
>>> h:commandScript is result of https://java.net/jira/browse/J
>>> AVASERVERFACES_SPEC_PUBLIC-613 See also
>>> http://arjan-tijms.omnifaces.org/p/jsf-23.html for others.
>>>
>>> I do understand the h:commandScript limitation in portlet case, but in a
>>> portlet app there's also only one JavaScript context.
>>>
>>> I have to admit that the f:websocket+f:ajax looks more natural. I will
>>> try creating a POC on this. I only don't like the idea of turning the
>>> f:websocket from TagHandler into UIComponent. It's really kind of view
>>> metadata.
>>>
>>> Cheers, B
>>>
>>>
>>>
>>> On Sat, Sep 24, 2016 at 3:53 AM, Leonardo Uribe <leonardo.uribe_at_irian.at
>>> > wrote:
>>>
>>>> Hi
>>>>
>>>> I see, so this is hidden in the spec. Thanks for mention it.
>>>>
>>>> I have to say I can see a problem with this structure, and it is
>>>> related to the "name" used on the script.
>>>>
>>>> In JSF each component has it own clientId that is calculated somehow.
>>>> So according to the location of the component inside the tree, the clientId
>>>> is generated.
>>>>
>>>> The way how clientId works ensures the component can be relocated to
>>>> different parts of the tree and it will still work.
>>>>
>>>> The problem with a hardcode name is that you lose this property in the
>>>> code. If you are in a portlet case, I can see that if the same component is
>>>> used twice on different portlets, the page will crash by a duplicate
>>>> definition.
>>>>
>>>> There is a component in tomahawk sandbox that try this and it is called
>>>> s:jsCallbackFunction . Remember the component is in sandbox, so in that
>>>> sense it is experimental.
>>>>
>>>> * This component creates a function inside an inline &lt;script&gt;
>>>> tag,
>>>> * with a function that can be referenced later using getFunctionName()
>>>> method
>>>> * inside this component instance or the EL function
>>>> #{s:jsCallbackFunctionName(UIComponent)}.
>>>> * <p>
>>>> * Inside the function, the following code is added:
>>>> * </p>
>>>> * <code>
>>>> * generatedFunctionNameUsingClientIdAndEventName = function (...
>>>> arguments ...){
>>>> * ... jsStartContent ...
>>>> * ... clientBehavior scripts ...
>>>> * ... jsEndContent ...
>>>> * }
>>>> * </code>
>>>> * <p> This is useful in situations where the context where this script
>>>> is
>>>> * rendered is important, and it is not possible to put the scripts on
>>>> static
>>>> * javascript files.</p>
>>>>
>>>> What I mean is with an EL function it is possible to generate the
>>>> function name based on the clientId of the component and avoid the problem
>>>> h:commandScript has.
>>>>
>>>> Let me be clear about this: the way how h:commandScript works right now
>>>> creates a conflict for portlet case, and I do not want to create another
>>>> one after spend a lot of time trying to solve JAVASERVERFACES_SPEC_PUBLIC-79
>>>> 0.
>>>>
>>>> The way how h:commandScript and f:websocket interact should be more
>>>> subtle. If f:websocket is a component with an id, it should be possible to
>>>> reference it so you could "declare" a h:commandScript to be called for
>>>> f:websocket. So, with one f:websocket it should be possible to call many
>>>> h:commandScript functions.
>>>>
>>>> I think we should focus our efforts in create a syntax easy to
>>>> understand, that solve the problem of update parts of the view after an
>>>> event triggered on the server.
>>>>
>>>> regards,
>>>>
>>>> Leonardo Uribe
>>>>
>>>>
>>>> 2016-09-23 16:01 GMT-05:00 Bauke Scholtz <balusc_at_gmail.com>:
>>>>
>>>>> Hi,
>>>>>
>>>>> It has already been thought about, it can be combined with
>>>>> h:commandScript, also new in JSF 2.3.
>>>>>
>>>>> <f:websocket ... onmessage="someCommandScript" />
>>>>> <h:commandScript name="someCommandScript" action="#{bean.pushed}"
>>>>> render="foo" />
>>>>>
>>>>> The message will transparently be available as request parameters in
>>>>> associated bean.
>>>>>
>>>>> Cheers, B
>>>>>
>>>>> On Fri, Sep 23, 2016 at 9:22 PM, Leonardo Uribe <
>>>>> leonardo.uribe_at_irian.at> wrote:
>>>>>
>>>>>> Hi
>>>>>>
>>>>>> I have been thinking about the way how f:websocket / PushContext
>>>>>> works, just trying to see what's missing or another way to see this
>>>>>> feature, to see if we can make it better.
>>>>>>
>>>>>> Even if f:websocket behavior is well understood and very flexible the
>>>>>> way it is, what bothers me about it is this feature is too javascript
>>>>>> specific. What I mean is you always need to write a javascript block to
>>>>>> handle the incoming processing.
>>>>>>
>>>>>> But sometimes what you really want is update a part or just an
>>>>>> specific component in the view. In other words, sometimes the web socket is
>>>>>> only used as way to notify the view that something has changed on the
>>>>>> server and the view needs to be updated somehow.
>>>>>>
>>>>>> In other words, sometimes the user doesn't want to override onmessage
>>>>>> and instead just say update component xxx or yyy.
>>>>>>
>>>>>> For example, imagine the following syntax:
>>>>>>
>>>>>> <f:websocket channel="ping">
>>>>>> <f:ajax event="update" render="myInfoBox"/>
>>>>>> </f:websocket>
>>>>>>
>>>>>> On the server the update is triggered using this:
>>>>>>
>>>>>> @Inject
>>>>>> @Push(channel="ping")
>>>>>> private PushContext push;
>>>>>>
>>>>>> ....
>>>>>>
>>>>>> push.send("update");
>>>>>>
>>>>>> Now, f:websocket looks more like a component that implements
>>>>>> ClientBehaviorHolder than a tag, and the "default" onmessage is a method
>>>>>> that takes the message and if the event match the message it triggers the
>>>>>> related f:ajax script.
>>>>>>
>>>>>> In html markup, f:websocket should create a html tag with the
>>>>>> associated custom events.
>>>>>>
>>>>>> What do you think guys about it? does it work? is it useful? is it
>>>>>> worth?
>>>>>>
>>>>>> regards,
>>>>>>
>>>>>> Leonardo Uribe
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>