dev@jsf-extensions.java.net

Re: Avatar Status (Jacob, Dan and Craig, please read)

From: Dan Labrecque <Dan.Labrecque_at_Sun.COM>
Date: Mon, 19 Jun 2006 13:10:11 -0700

Just a couple questions/comments in-line.

Thanks,
Dan

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.
>
> 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.
>
> 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.
>
> 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.
>
> 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.
>
> 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>
> <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>
>

Will Faces.Command always require a Javascript event to generate an
Avatar request? In some cases (e.g., the progress bar component), we
will need to continuously poll for data without any Javascript event. I
was thinking that we could use a Dojo subscribe/publish event here, but
I'm wondering if the API can also generate the request immediately? For
example:

        new Faces.Command(e, null, { subtrees: 'form:table,form:subview2' });


> 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:
>
>
> 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>
>
>

Will this solution automatically update component state on the client?
If so:

1. Does this work with client-side state saving, where the encoded state
saving string is present in the page?
2. Can we control when it is necessary to return client state (e.g.,
when components are updated)?
3. Any chance responses could be handled out of order and newer state is
inadvertently overridden?

> The values returned is subject to conversion and validation with
> any converters or validators that are attached to the component.
>

Can the RunThru header indicate validation without update? I'm wondering
what will happen to queued, value change events? I'm thinking of the
scenario where I want to validate a text field value, but not update it
until the user clicks an OK button, for example.

> This implementation also supports updating multiple components in
> the tree with a single transaction.
>
> 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.
>
> 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.
>

The main thing I'm looking for is to bypass the mechanism which replaces
HTML. I expect that our components will need to return both XML and
JSON, but we will not always replace newly rendered HTML. I just need a
simple hook to process the data from the response myself.

If the response is wrapped in XML, my Javascript function could parse
out the data, but it's extra processing. With the previous Avatar
source, the custom UIViewRoot wrapped the output, of each
avatarZoneRenderer, in XML. However, this forced my component renderers
to wrap JSON in the same XML format used by the ajaxZoneRenderer.

I'm not really looking for a pluggable rendering solution here, I just
don't want my response to be wrapped in XML. If I want to return XML, I
can easily set the response myself in my own renderer. However, I'm just
looking to set a simple flag indicating that the custom UIViewRoot/life
cycle should not wrap the response in XML.

That said, I'm willing to help if I can. For example, I can take your
latest source and apply it to our components. Although, I'm not certain
my last example was useful to you? Want to meet tomorrow afternoon and
discuss possible solutions?