Ed Burns wrote:
> Here is a status report on Avatar. It's coming along. I hope to have
> the first milestone release on the 30th.
>
> https://jsf-extensions.dev.java.net/files/documents/4613/36314/20060617-avatar-plan.html
>
> Attached for your convenience and discussion.
>
> I have just checked in a major change to the avatar code in
> jsf-extensions. I'm using some ideas from Jacob's JavaOne demo as a
> starting point. Here's what I have.
>
> 1.
>
> AjaxLifecycle
> Description of new Avatar Implementation
>
> The AjaxLifecycle is now the main lifecycle for Avatar enabled
> apps. This replaces the custom UIViewRoot approach and decorates
> the default Lifecycle. All one need change in their web.xml is to
> add an init-param to their Faces Servlet mapping.
>
>
> <!-- Faces Servlet -->
> <servlet>
> <servlet-name>Faces Servlet</servlet-name>
> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
> <init-param>
> <param-name>javax.faces.LIFECYCLE_ID</param-name>
> <param-value>com.sun.faces.lifecycle.AJAX</param-value>
> </init-param>
> <load-on-startup>1</load-on-startup>
> </servlet>
>
> <!-- Faces Servlet Mapping -->
> <servlet-mapping>
> <servlet-name>Faces Servlet</servlet-name>
> <url-pattern>/faces/*</url-pattern>
> </servlet-mapping>
>
> This approach has the benefit of not requiring mounting any
> additional FacesServlet instances, nor filters or phaseListeners,
> not to mention it frees up the UIViewRoot for others to sub-class
> and replace if necessary.
+1 - a good approach!
> 2.
>
> Use request headers to convey ajax metadata.
>
> The following headers are currenty supported in the AjaxLifecycle
>
> com.sun.faces.Async
>
> If this header is present, the AjaxLifecycle treats this
> request as an AJAX request.
Could we get an extra layer of packaging under "com.sun.faces"?
Also, "async" vs. "sync" isn't a relevant distinction here -
a client is free to issue a synchronous XMLHttpRequest request
(in some cases, a good idea). The distinction should be "full page"
vs. not (aka "PartialRequest").
> com.sun.faces.Subtrees
>
> This is a comma separated list of clientIds against which the
> lifecycle should be run. Each lifecycle phase is run on each
> clientId using invokeOnComponent() to ensure proper context.
If absent, the entire tree should be executed. Is that how it works?
I'd prefer the name "TargetIds", but "Subtrees" is OK.
> com.sun.faces.lifecycle.RunThru
>
> This header gives the name of the request processing lifecycle
> phase through which the lifecycle should be run. For example, the
> replacement for Jacob's javax.faces.Update parameter is to define
> the RunThru header with the value of UPDATE_MODEL_VALUES. The
> response generated when running a partial lifecycle will be
> described below.
-0.5. Why is this set on the client? This should be determined by the
renderer, or component, or event listener IMO. More generally, it's
of critical importance that everything should be extensible
programatically on the client; for instance, you have to be able to
indicate on the server that an additional target ID needs to be
processed. This lets you send across simple requests that only a button
was pressed, but on the server decide that additional regions of the
page need to be processed.
Also, we have to separate two concepts:
1. Components that need to be processed (ARV through Invoke
Application, but *not* Render Response)
2. Components that need to be rendered (Render Response only)
Inclusion in #1 does not require inclusion in #2. For
example, if I have a button click that re-renders a table
via Avatar, the button gets processed (but not re-rendered),
and the table gets re-rendered (but not processed). This isn't
just an optimization - re-rendering components unnecessarily
has a major negative accessibility impact.
> I have the following header also in-mind, but not yet implemented:
>
> com.sun.faces.lifecycle.<LIFECYCLE_PHASE>
>
> If defined, this is a comma separated list of client ids to be
> used for that specific lifecycle phase. This value overrides the
> value of com.sun.faces.Subtrees for that specific phase.
-1. I'd like a use case, especially before letting this be
driven by the client.
> 3.
>
> New XML application for AJAX responses from avatar server.
>
> If the request has the com.sun.faces.Async and
> com.sun.faces.Subtrees headers, with valid values, but no
> com.sun.faces.lifecycle.RunThru header, request will look
> something like this:
>
>
> POST http://localhost:8080/jsf-extensions/faces/result-set.jsp HTTP/1.1
> Host: localhost:8080
> User-Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.0.4) Gecko/20060508 Firefox/1.5.0.4
> Accept: text/javascript, text/html, application/xml, text/xml, */*
> Accept-Language: en-us,en;q=0.5
> Accept-Encoding: gzip,deflate
> Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
> Keep-Alive: 300
> Proxy-Connection: keep-alive
> X-Requested-With: XMLHttpRequest
> X-Prototype-Version: 1.5.0_rc0
> Content-Type: application/x-www-form-urlencoded
> Connection: close
> com.sun.faces.Async: true
> com.sun.faces.Subtrees: form:table,form:subview2
> Content-Length: 277
> Cookie: com.sun.faces.extensions.flashPostbackRequest=8.0; JSESSIONID=33ab516de8b10090a6e8c9f604f4
> Pragma: no-cache
> Cache-Control: no-cache
>
> options=%5Bobject%20Object%5D&form%3Ascroller_action=10&form%3Ascroller_curPage=1&javax.faces.ViewState=j_id3%3Aj_id4&form=form&http%3A%2F%2Flocalhost%3A8080%2Fjsf-extensions%2Ffaces%2Fresult-set.jsp%23=http%3A%2F%2Flocalhost%3A8080%2Fjsf-extensions%2Ffaces%2Fresult-set.jsp%23
>
>
> And the response like this:
>
>
>
> HTTP/1.1 200 OK
> X-Powered-By: Servlet/2.5
> Cache-Control: no-cache
> Content-Type: text/xml;charset=ISO-8859-1
> Date: Sat, 17 Jun 2006 18:23:28 GMT
> Server: Sun Java System Application Server Platform Edition 9.1
> Connection: close
>
> <async-response>
<partial-response> would be better, as noted above.
> <render id="form:table">
> <![CDATA[<table id="form:table" class="list-background">
> <thead>
> <tr>
> <th class="list-header" scope="col">Account Id</th>
> <th class="list-header" scope="col">Customer Name</th>
> <th class="list-header" scope="col">Symbol</th>
> <th class="list-header" scope="col">Total Sales</th>
> </tr>
> </thead>
> <tbody>
> <tr class="list-row-even">
> <td class="list-column-center"><span id="form:table:180:accountId">180</span></td>
> <td class="list-column-center"><script type="text/javascript" src="http://localhost:8080/jsf-extensions/jmaki.js"></script>
> <script type="text/javascript">jmaki.webRoot='http://localhost:8080/jsf-extensions';</script>
> <script type="text/javascript" src="http://localhost:8080/jsf-extensions/resources/scriptaculous/prototype.js"></script>
> <script type="text/javascript" src="http://localhost:8080/jsf-extensions/resources/scriptaculous/scriptaculous.js"></script>
> <link rel="stylesheet" type="text/css" href="http://localhost:8080/jsf-extensions/inplace/component.css"></link>
> <a id="form:table:180:j_id_id52" href="#">name_180</a>
> <script type="text/javascript">
> jmaki.addWidget({service:'http://localhost:8080/jsf-extensions/faces/result-set.jsp',script:'http://localhost:8080/jsf-extensions//inplace/component.js',uuid:'form:table:180:j_id_id52',name:'inplace',id:'form:table:180:j_id_id52'});</script>
> </td>
> <td class="list-column-center"><span id="form:table:180:symbol">symbol_180</span></td>
> <td class="list-column-center"><span id="form:table:180:totalSales">180.0</span></td>
> </tr>
>
> <-- additional rows omitted -->]]></render>
> <render id="form:subview2">
> <![CDATA[<div id="form:subview2"><table border="0" cellpadding="0" align="center"><tr align="center" valign="top"><td><font size="-1">Result Page: </font></td><td>
> <a href="#" onmousedown="document.forms[0]['form:scroller_action'].value='-2'; document.forms[0]['form:scroller_curPage'].value='10'; document.forms[0].submit()">Previous<img src="/jsf-extensions/images/arrow-left.gif" alt="" /><br /></a></td><td>
> <a href="#" onmousedown="document.forms[0]['form:scroller_action'].value='1'; document.forms[0]['form:scroller_curPage'].value='10'; document.forms[0].submit()">1</a></td>
>
> <!-- additional cells omitted -->
> </tr><input type="hidden" name="form:scroller_action"/>
> <input type="hidden" name="form:scroller_curPage"/></table>
>
> <script type='text/javascript'>
> document.forms[0].submit = function() {};
> var a = $('form:subview2').getElementsByTagName('a');
> $A(a).each(function(e) {
> new Faces.Command(e, 'mousedown', { subtrees: 'form:table,form:subview2' });
> });
> </script>
> </div>]]></render>
> <state>
> <![CDATA[j_id3:j_id4]]>
> </state>
> </async-response>
>
> To support simple updating (with validation and conversion properly handled) of one or more values in the component tree, use the RunThru header with a value of UPDATE_MODEL_VALUES. A request with this header looks like this:
I don't much like this; it requires the client has knowledge of what's
meant by "setting the value" of a particular component on a client,
which requires deep markup knowledge once you get past basic input
components.
> POST http://localhost:8080/jsf-extensions/faces/result-set.jsp HTTP/1.1
> Host: localhost:8080
> User-Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.0.4) Gecko/20060508 Firefox/1.5.0.4
> Accept: text/javascript, text/html, application/xml, text/xml, */*
> Accept-Language: en-us,en;q=0.5
> Accept-Encoding: gzip,deflate
> Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
> Keep-Alive: 300
> Proxy-Connection: keep-alive
> X-Requested-With: XMLHttpRequest
> X-Prototype-Version: 1.5.0_rc0
> Content-Type: application/x-www-form-urlencoded
> Connection: close
> com.sun.faces.Async: true
> com.sun.faces.Subtrees: form:table:181:j_id_id52
> com.sun.faces.lifecycle.RunThru: UPDATE_MODEL_VALUES
> Content-Length: 163
> Cookie: com.sun.faces.extensions.flashPostbackRequest=8.0; JSESSIONID=33ab516de8b10090a6e8c9f604f4
> Pragma: no-cache
> Cache-Control: no-cache
>
> form%3Atable%3A181%3Aj_id_id52=181&form%3Atable%3A181%3Aj_id_id52-inplaceeditor=form%3Atable%3A181%3Aj_id_id52-inplaceeditor&javax.faces.ViewState=j_id3%3Aj_id4&_=
>
> And the response will look like this:
>
>
> HTTP/1.1 200 OK
> X-Powered-By: Servlet/2.5
> Cache-Control: no-cache
> Content-Type: text/xml;charset=ISO-8859-1
> Content-Length: 133
> Date: Sat, 17 Jun 2006 18:29:42 GMT
> Server: Sun Java System Application Server Platform Edition 9.1
> Connection: close
>
> <async-response><update id="form:table:181:j_id_id52"><![CDATA[181]]></update><state><![CDATA[j_id3:j_id4]]></state></async-response>
>
> The values returned is subject to conversion and validation with
> any converters or validators that are attached to the component.
>
> This implementation also supports updating multiple components in
> the tree with a single transaction.
Additional feature that would be very useful: return any
FacesMessages in XML blocks, then call a user-defined callback
with an array of those messages.
> Next Steps - Concrete Tasks
>
> Currently, the result-set.jsp example, in the run-time-test
> module, is the only one that works. My next step is to take
> Jacob's JavaOne demo and move it into our repo and make it work
> with the avatar impl. This will require support for Jacob's
> javax.faces.Event header, which I don't think will be too hard to
> add, but we'll see.
>
> After that, I want to get the ajaxZones implementation updated to
> use the new framework, and remove as much of my old procedural
> JavaScript code from com_sun_faces_ajax.js as possible. In concert
> with this task, I will make the jsf-ajax-carstore example work
> again. This will prove out the fully functional ajaxZone concept.
Why do we need any AJAX zones? -1 on any solution that requires
explicitly marking zones. It should be sufficient to have an "id"
set on any JSF component.
-- Adam
> Next Steps - Concepts
>
> You can see where I'm going with this lifecycle idea. I'd like to
> expose the JSF lifecycle easily to AJAX. One interesting idea is
> to pass up a chunk of JavaScript in the AJAX request, along with
> processing instructions about it, that will cause the JavaScript
> to be executed on the server to help serv up the response. We'd
> need Phobos to do this, but I think having the ability for the
> client to send up JavaScript to the server to be executed in the
> context of the JSF lifecycle is a powerful feature.
>
> Another idea is to make the rendering of the response pluggable so
> that one could send the response back in JSON instead of hard
> coded XML. Perhaps Dan Labreque can help with this.
>