dev@jsf-extensions.java.net

Re: [JSF-EXT] Avatar Status (Jacob, Dan and Craig, please read)

From: Adam Winer <adam.winer_at_oracle.com>
Date: Tue, 20 Jun 2006 11:03:27 -0700

Ed Burns wrote:
>>>>>> On Mon, 19 Jun 2006 12:01:01 -0700, Adam Winer <adam.winer_at_oracle.com> said:
>
> EB> The following headers are currenty supported in the AjaxLifecycle
> EB>
> EB> com.sun.faces.Async
> EB>
> EB> If this header is present, the AjaxLifecycle treats this
> EB> request as an AJAX request.
>
> AW> Could we get an extra layer of packaging under "com.sun.faces"?
>
> Well, this is just a header name, not a package name. The package
> happens to be com.sun.faces.extensions.avatar. I'd like to keep the
> header names as short as possible, while preserving disambiguation.
> However, I could live with com.sun.faces.avatar, but I don't really see
> the need. I'll leave it as com.sun.faces unless someone strongly
> opposes.

It's a header name, but it still needs disambiguation.

> AW> Also, "async" vs. "sync" isn't a relevant distinction here -
> AW> a client is free to issue a synchronous XMLHttpRequest request
> AW> (in some cases, a good idea). The distinction should be "full page"
> AW> vs. not (aka "PartialRequest").
>
> Ok, that's good. I'll make it com.sun.faces.Partial instead of
> com.sun.faces.Async.
>
> EB> com.sun.faces.Subtrees
> EB>
> EB> This is a comma separated list of clientIds against which the
> EB> lifecycle should be run. Each lifecycle phase is run on each
> EB> clientId using invokeOnComponent() to ensure proper context.
>
> AW> If absent, the entire tree should be executed. Is that how it works?
> AW> I'd prefer the name "TargetIds", but "Subtrees" is OK.
>
> I actually prefer Subtrees because it is more descriptive.
>
> EB> com.sun.faces.lifecycle.RunThru
>
> DL> Can the RunThru header indicate validation without update? I'm wondering
> DL> what will happen to queued, value change events? I'm thinking of the
> DL> scenario where I want to validate a text field value, but not update it
> DL> until the user clicks an OK button, for example.
>
> Yes, it can.
>
> EB> This header gives the name of the request processing lifecycle
> EB> phase through which the lifecycle should be run. For example, the
> EB> replacement for Jacob's javax.faces.Update parameter is to define
> EB> the RunThru header with the value of UPDATE_MODEL_VALUES. The
> EB> response generated when running a partial lifecycle will be
> EB> described below.
>
> AW> -0.5. Why is this set on the client?
>
> Basically, to support popular JavaScript widgets via jMaki. In this
> case, the widget expects to get back a mere "value" rather than, "a
> bunch of markup that renders a value".
>
> If we go through the render phase, we end up with the extra markup, and
> we have to somehow pick apart the rendered markup to extract the value
> and hand it to the widget for display. Also, it is vital that
> validation and conversion work on this case, so, the AjaxLifecycle will
> send back a any validation and conversion errors along with the value.
>
> To achieve this, I decided to allow the client to tell the server,
> "don't render the component, just give me its converted value (along
> with any validation/conversion messages)".
>
> Adam, first, do you agree with the necessity to have JSF support popular
> JavaScript widgets via jMaki?

I'm OK with supporting such widgets.

> Second, if you agree with that requirement, now that you have some
> additional perspective on the challenges involved in providing this
> support, do you still oppose my implementation choice?

Yes, I still do: I don't see why the Renderer for a jMaki widget
*must* send back markup. It should be perfectly capable of
sending back just a value when so required.

> AW> This should be determined by the renderer, or component, or event
> AW> listener IMO. More generally, it's of critical importance that
> AW> everything should be extensible programatically on the client; for
> AW> instance, you have to be able to indicate on the server that an
> AW> additional target ID needs to be processed. This lets you send
> AW> across simple requests that only a button was pressed, but on the
> AW> server decide that additional regions of the page need to be
> AW> processed.
>
> I agree we must support this story. However, I do not agree that this
> story is appropriate for the com.sun.faces.lifecycle.RunThru header.

Well, I don't want a "RunThru" header at all unless it can
be shown that it is absolutely necessary.

> I
> intend the RunThru header to be for components that only want to send an
> update (or validation, or invoke application, etc) to one or more
> components in the view. In that case, the server does not have a right
> to add additional values.
>
> If the server is making decisions about processing additional subtrees
> than what the user requested, then the full lifecycle is required, and
> the RunThru header will not be used.
>
> AW> Also, we have to separate two concepts:
>
> AW> 1. Components that need to be processed (ARV through Invoke
> AW> Application, but *not* Render Response)
> AW> 2. Components that need to be rendered (Render Response only)
>
> AW> Inclusion in #1 does not require inclusion in #2. For
> AW> example, if I have a button click that re-renders a table
> AW> via Avatar, the button gets processed (but not re-rendered),
> AW> and the table gets re-rendered (but not processed). This isn't
> AW> just an optimization - re-rendering components unnecessarily
> AW> has a major negative accessibility impact.
>
> Yes, I agree. I see that my current implementation does not handle this
> essential case. Let's fix that.
>
> Does the button know that its job is to cause the table to be
> re-rendered? Let's assume first that it does for discussion. The
> browser would send a request with the following headers and post data
>
> com.sun.faces.Partial: true
> com.sun.faces.Update: form:redisplayButton
> com.sun.faces.Render: form:table
>
> form:redisplayButton=form:redisplayButton&javax.faces.ViewState=_id1:_id2
>
> The server would run the whole ApplyRequestValues thru InvokeApplication
> on form:redisplayButton, and then would render just form:table.
>
> Now, let's say we want the server to be the one to make the decision
> that the table needs to be re-rendered when that particular button is
> pressed. The browser would send a request with the following headers
> and post data
>
> com.sun.faces.Partial: true
> com.sun.faces.Update: form:redisplayButton
>
> form:redisplayButton=form:redisplayButton&javax.faces.ViewState=_id1:_id2
>
> We leave out the RenderSubtrees header and let the server decide what
> needs to be re-rendered. This requires some server side smarts I have
> not yet implemented.

Sure; we'll need Java APIs. For the rendering side of things,
I'd suggest looking at the ADF "partialTriggers", which has served us
well and are the outcome of years of thinking about exactly this
problem (it's directionality is flipped from what most people
first come up with), and we'd also want a pure Java API.


> So, Adam, my question to you is: do we need to enable the client to tell
> the server what should be processed, vs what should be rendered. I
> think we do.

Yes, we do. I'd suggest "Process/Render" over "Update/Render",
with the following behavior:
   "Process":
    - not set at all => process the entire tree
    - set to non-null => process only selected subtrees
   "Render":
    - not set at all => nothing re-renders
    - set to non-null => re-render only selected subtrees

... where both sets can be appended to on the server. The
preferred use-case should be that from the client, Process is set,
and Render is not. (That is, that the server should be responsible
for deciding what to re-render).

The "request goes to the server but nothing is re-rendered" case
may sound odd, but it works very well for polling scenarios,
where most requests may not actually update the page content at
all.

>>>>>> On Mon, 19 Jun 2006 15:07:17 -0400, jacob_at_hookom.net said:
>
> JH> I don't think we can have a single spot for specifying the
> JH> collection ids to process-- that's why I had Update vs. Encode, you
> JH> don't want to update a chunk of the page that you want to re-render
> JH> in all cases.
>
> Adam's comment supports the inclusion of the RenderSubtrees header (this
> is essentially Jacob's Update and Encode headers).
>
> EB> I have the following header also in-mind, but not yet implemented:
>
> EB> com.sun.faces.lifecycle.<LIFECYCLE_PHASE>
>
> EB> If defined, this is a comma separated list of client ids to be
> EB> used for that specific lifecycle phase. This value overrides the
> EB> value of com.sun.faces.Subtrees for that specific phase.
>
> AW> -1. I'd like a use case, especially before letting this be
> AW> driven by the client.
>
> Ok, I can leave this un-implemented
>
> EB> 3.
> EB>
> EB> New XML application for AJAX responses from avatar server.
> EB>
> EB> If the request has the com.sun.faces.Async and
> EB> com.sun.faces.Subtrees headers, with valid values, but no
> EB> com.sun.faces.lifecycle.RunThru header, request will look
> EB> something like this:
>
> [...]
>
> EB> And the response like this:
>
> [...]
>
> EB> <async-response>
>
> AW> <partial-response> would be better, as noted above.
>
> Ok, that's fine with me.
>
> EB> <!-- additional cells omitted -->
> EB> </tr><input type="hidden" name="form:scroller_action"/>
> EB> <input type="hidden" name="form:scroller_curPage"/></table>
> EB>
> EB> <script type='text/javascript'>
> EB> document.forms[0].submit = function() {};
> EB> var a = $('form:subview2').getElementsByTagName('a');
> EB> $A(a).each(function(e) {
> EB> new Faces.Command(e, 'mousedown', { subtrees: 'form:table,form:subview2' });
> EB> });
> EB> </script>
> EB> </div>]]></render>
> EB> <state>
> EB> <![CDATA[j_id3:j_id4]]>
> EB> </state>
> EB> </async-response>
>
> DL> Avatar request? In some cases (e.g., the progress bar component), we
> DL> will need to continuously poll for data without any Javascript event. I
> DL> was thinking that we could use a Dojo subscribe/publish event here, but
> DL> I'm wondering if the API can also generate the request immediately? For
> DL> example:
>
> DL> new Faces.Command(e, null, { subtrees: 'form:table,form:subview2' });
>
> How about using setTimeout() to kick off the Faces.Command requests
> periodically?
>
> EB> To support simple updating (with validation and conversion properly
> EB> handled) of one or more values in the component tree, use the
> EB> RunThru header with a value of UPDATE_MODEL_VALUES. A request with
> EB> this header looks like this:
>
> AW> I don't much like this; it requires the client has knowledge of what's
> AW> meant by "setting the value" of a particular component on a client,
> AW> which requires deep markup knowledge once you get past basic input
> AW> components.
>
> I understand your reservations, but suspect you did not have the "smart
> JavaScript Widgets" story in mind when you formed your opinion of it. I
> ask you to reconsider in light of that story.

I think this should be embedded in a smart Renderer, not in more
complicated generic JS or lifecycle short-circuiting that can
be badly misused.

> DL> Will this solution automatically update component state on the client?
>
> Yes, it does.
>
> DL> If so:
>
> DL> 1. Does this work with client-side state saving, where the encoded state
> DL> saving string is present in the page?
>
> Yes, it does.
>
> DL> 2. Can we control when it is necessary to return client state (e.g.,
> DL> when components are updated)?
>
> That is not implemented, but it's a good idea. I'll put that on the
> idea wiki. [1]
>
> DL> 3. Any chance responses could be handled out of order and newer state is
> DL> inadvertently overridden?
>
> I assume total ordering of requests and responses.

We'd need an event queue on the client to really ensure this.

> AW> Additional feature that would be very useful: return any
> AW> FacesMessages in XML blocks, then call a user-defined callback
> AW> with an array of those messages.
>
> I already have the <message> part. I haven't implemented the client
> side. I prefer to leave it up to the client to note the presence of
> messages and decide what to do with them.

How does the client find the messages? We should at least parse
them out into JS objects, instead of asking every client to
parse the XML format.

> EB> Next Steps - Concrete Tasks
> EB>
> EB> Currently, the result-set.jsp example, in the run-time-test
> EB> module, is the only one that works. My next step is to take
> EB> Jacob's JavaOne demo and move it into our repo and make it work
> EB> with the avatar impl. This will require support for Jacob's
> EB> javax.faces.Event header, which I don't think will be too hard to
> EB> add, but we'll see.
> EB>
> EB> After that, I want to get the ajaxZones implementation updated to
> EB> use the new framework, and remove as much of my old procedural
> EB> JavaScript code from com_sun_faces_ajax.js as possible. In concert
> EB> with this task, I will make the jsf-ajax-carstore example work
> EB> again. This will prove out the fully functional ajaxZone concept.
>
> AW> Why do we need any AJAX zones? -1 on any solution that requires
> AW> explicitly marking zones. It should be sufficient to have an "id"
> AW> set on any JSF component.
>
> I'm not ready to give up on zones yet. I think there are cases, such as
> ajaxifying the cardemo app, for example, that do require zones. Of
> course, if it shows up that we don't need them, I will take them out.

How could a "zone" ever be better than <h:panelGroup id="targetedId">?

-- Adam



> [...]
>
> EB> Another idea is to make the rendering of the response pluggable so
> EB> that one could send the response back in JSON instead of hard
> EB> coded XML. Perhaps Dan Labreque can help with this.
>
> DL> The main thing I'm looking for is to bypass the mechanism which replaces
> DL> HTML. I expect that our components will need to return both XML and
> DL> JSON, but we will not always replace newly rendered HTML. I just need a
> DL> simple hook to process the data from the response myself.
>
> DL> If the response is wrapped in XML, my Javascript function could parse
> DL> out the data, but it's extra processing. With the previous Avatar
> DL> source, the custom UIViewRoot wrapped the output, of each
> DL> avatarZoneRenderer, in XML. However, this forced my component renderers
> DL> to wrap JSON in the same XML format used by the ajaxZoneRenderer.
>
> DL> I'm not really looking for a pluggable rendering solution here, I just
> DL> don't want my response to be wrapped in XML. If I want to return XML, I
> DL> can easily set the response myself in my own renderer. However, I'm just
> DL> looking to set a simple flag indicating that the custom UIViewRoot/life
> DL> cycle should not wrap the response in XML.
>
> Well, that's easy enough to do. We could have another header,
> com.sun.faces.ResponseContentType. If absent, it's XML. If present, it
> sets the content type blindly and lets the renderer do the thing.
>
> DL> That said, I'm willing to help if I can. For example, I can take your
> DL> latest source and apply it to our components. Although, I'm not certain
> DL> my last example was useful to you? Want to meet tomorrow afternoon and
> DL> discuss possible solutions?
>
> I'd like to get Adam's issues straightened out first before we talk. Is
> that ok?
>
> Ed
>
> [1] http://wiki.java.net/bin/view/Projects/AvatarIdeaWiki
>