dev@jsf-extensions.java.net

Re: [JSF-EXT] Jacob: Seeking Review: 7-Immediate

From: Ed Burns <ed.burns_at_sun.com>
Date: Wed, 16 Aug 2006 13:39:32 -0700

>>>>> On Wed, 16 Aug 2006 10:44:56 -0400, jacob_at_hookom.net said:

JH> I don't know if you are running into troubled waters here by
JH> temporarily changing component state.&nbsp; The point of avatar was
JH> to process the component model in the most natural and external way
JH> possible.&nbsp; Dan does bring up an interesting use case where the
JH> component event fired internally decides to change some state which
JH> you've asserted ownership over.

Yes, I too am a little skittish about it. However, I have checked it in
and it does only modify the state if it's not already immediate, and the
component is guaranteed to be reset back to its initial state due to the
finally block. I'm pretty confident this is ok.

https://jsf-extensions.dev.java.net/issues/show_bug.cgi?id=7

Here is the change-bundle.

This feature provides an "immediate" option to the associative array
that is last argument to DynaFaces.installDeferredAjaxTransaction() and
DynaFaces.fireAjaxTransaction().

This option has the effect of setting the immediate property to true for
the components affected by this ajax transaction to true for this
transaction only. If a component is already marked as immediate, its
immediate property is unchanged.

M run-time/samples/simple-partial-update/src/main/java/test/TestBean.java

- add immediateButtonIsImmediate property that tells if the button with
  id "immediate" has its immediate value to true or false. It should be
  false.

M run-time/samples/simple-partial-update/src/main/webapp/home.jsp

- Demonstrate the "immediate" option.

M run-time/common/src/main/java/com/sun/faces/extensions/common/util/Util.java

- Copy over "prefixViewTraversal" to Util from Sun Impl.

M run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/lifecycle/PartialTraversalLifecycle.java

- add finally block around call to parent.execute() that calls
  viewRoot.postExecuteCleanup().

M run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/lifecycle/AsyncResponse.java

- add immediateAjaxRequest property.

M run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/components/PartialTraversalViewRoot.java

- mark components as immediate if this transaction is marked to be
  immediate.

M run-time/avatar/src/main/resources/com_sun_faces_ajax.js

- Fix a bug where viewState values were incorrectly being concatenated.

M pom.xml

- add master-pom to samples.

SECTION: Diffs

Index: run-time/samples/simple-partial-update/src/main/java/test/TestBean.java
===================================================================
--- run-time/samples/simple-partial-update/src/main/java/test/TestBean.java (revision 190)
+++ run-time/samples/simple-partial-update/src/main/java/test/TestBean.java (working copy)
@@ -5,11 +5,9 @@
 
 package test;
 
-import com.sun.faces.extensions.avatar.lifecycle.AsyncResponse;
-import java.util.ArrayList;
-import java.util.List;
+import javax.faces.component.ActionSource;
+import javax.faces.component.ContextCallback;
 import javax.faces.component.UIComponent;
-import javax.faces.component.UIData;
 import javax.faces.context.FacesContext;
 import javax.faces.event.ActionEvent;
 
@@ -45,6 +43,24 @@
     public void setText(String text) {
         this.text = text;
     }
+
+ /**
+ * Getter for property immediateButtonImmediate.
+ * @return Value of property immediateButtonImmediate.
+ */
+ public boolean isImmediateButtonIsImmediate() {
+ FacesContext context = FacesContext.getCurrentInstance();
+ final boolean [] result = new boolean[1];
+ result[0] = false;
+
+ context.getViewRoot().invokeOnComponent(context, "immediate", new ContextCallback() {
+ public void invokeContextCallback(FacesContext facesContext, UIComponent comp) {
+ result[0] = ((ActionSource)comp).isImmediate();
+ }
+ });
+
+ return result[0];
+ }
     
    
     
Index: run-time/samples/simple-partial-update/src/main/webapp/home.jsp
===================================================================
--- run-time/samples/simple-partial-update/src/main/webapp/home.jsp (revision 190)
+++ run-time/samples/simple-partial-update/src/main/webapp/home.jsp (working copy)
@@ -42,11 +42,37 @@
 
 </tr>
 
+<tr>
+
+<td>Same as above but with <code>immediate</code> set to
+<code>true</code> <i>for this request only</i>.</td>
+
+<td><h:commandButton id="immediate" actionListener="#{testBean.changeText}"
+ value="submit immediate"/>
+</td>
+
+</tr>
+
+<tr>
+
+<td>Value of <code>immediate</code> property of "submit immediate" button.
+</td>
+
+<td><code><h:outputText value="#{testBean.immediateButtonIsImmediate}" /></code>
+</td>
+
+</tr>
+
+
 </table>
             
             </h:form>
   <script type='text/javascript'>
     DynaFaces.installDeferredAjaxTransaction($('button'), 'click');
+ DynaFaces.installDeferredAjaxTransaction($('immediate'), 'click', {
+ execute: "immediate",
+ immediate: true
+ });
   </script>
 
 
Index: run-time/common/src/main/java/com/sun/faces/extensions/common/util/Util.java
===================================================================
--- run-time/common/src/main/java/com/sun/faces/extensions/common/util/Util.java (revision 190)
+++ run-time/common/src/main/java/com/sun/faces/extensions/common/util/Util.java (working copy)
@@ -25,7 +25,9 @@
 
 package com.sun.faces.extensions.common.util;
 
+import java.util.Iterator;
 import java.util.List;
+import javax.faces.FacesException;
 import javax.faces.FactoryFinder;
 import javax.faces.application.Application;
 import javax.faces.component.UIComponent;
@@ -137,4 +139,25 @@
        RenderKit curRenderKit = fact.getRenderKit(context, renderKitId);
        return curRenderKit;
     }
+
+ public static boolean prefixViewTraversal(FacesContext context,
+ UIComponent root,
+ TreeTraversalCallback action) throws FacesException {
+ boolean keepGoing = false;
+ if (keepGoing = action.takeActionOnNode(context, root)) {
+ Iterator<UIComponent> kids = root.getFacetsAndChildren();
+ while (kids.hasNext() && keepGoing) {
+ keepGoing = prefixViewTraversal(context,
+ kids.next(),
+ action);
+ }
+ }
+ return keepGoing;
+ }
+
+ public static interface TreeTraversalCallback {
+ public boolean takeActionOnNode(FacesContext context,
+ UIComponent curNode) throws FacesException;
+ }
+
 }
Index: run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/lifecycle/PartialTraversalLifecycle.java
===================================================================
--- run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/lifecycle/PartialTraversalLifecycle.java (revision 190)
+++ run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/lifecycle/PartialTraversalLifecycle.java (working copy)
@@ -75,7 +75,12 @@
             // portion of the lifecycle.
             async.setOnOffResponseEnabled(true);
         }
- parent.execute(context);
+ try {
+ parent.execute(context);
+ }
+ finally {
+ ((PartialTraversalViewRoot)context.getViewRoot()).postExecuteCleanup(context);
+ }
     }
     
     public void render(FacesContext context) throws FacesException {
Index: run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/lifecycle/AsyncResponse.java
===================================================================
--- run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/lifecycle/AsyncResponse.java (revision 190)
+++ run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/lifecycle/AsyncResponse.java (working copy)
@@ -314,7 +314,32 @@
         }
         return result;
     }
+
+ /**
+ * <p>Return <code>true</code> if and only if the request headers include
+ * an entry for {_at_link #PARTIAL_HEADER} and the value for that header is
+ * <code>immediate</code>. Otherwise return <code>false</code>.
+ */
     
+ public static boolean isImmediateAjaxRequest() {
+ ExternalContext ext = FacesContext.getCurrentInstance().getExternalContext();
+ Map<String, Object> requestMap = ext.getRequestMap();
+ final String immediateFlag = FACES_PREFIX + "IsImmediate";
+ if (requestMap.containsKey(immediateFlag)) {
+ return true;
+ }
+
+ Map<String, String> p = ext.getRequestHeaderMap();
+ String partialValue = p.get(PARTIAL_HEADER);
+ boolean result = false;
+ if (null != partialValue) {
+ result = partialValue.equalsIgnoreCase("immediate");
+ }
+ if (result) {
+ requestMap.put(immediateFlag, Boolean.TRUE);
+ }
+ return result;
+ }
     private Object origResponse = null;
     
     /**
Index: run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/components/PartialTraversalViewRoot.java
===================================================================
--- run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/components/PartialTraversalViewRoot.java (revision 190)
+++ run-time/avatar/src/main/java/com/sun/faces/extensions/avatar/components/PartialTraversalViewRoot.java (working copy)
@@ -31,13 +31,17 @@
 
 import com.sun.faces.extensions.avatar.event.EventParser;
 import com.sun.faces.extensions.avatar.lifecycle.AsyncResponse;
+import com.sun.faces.extensions.common.util.Util;
 import java.io.IOException;
 import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import javax.faces.FacesException;
 import javax.faces.application.FacesMessage;
+import javax.faces.component.ActionSource;
 import javax.faces.component.ContextCallback;
+import javax.faces.component.EditableValueHolder;
 import javax.faces.component.UIComponent;
 import javax.faces.component.UIViewRoot;
 import javax.faces.component.UIViewRootCopy;
@@ -129,19 +133,73 @@
 public class PartialTraversalViewRoot extends UIViewRootCopy implements Serializable {
     
     public PartialTraversalViewRoot() {
+ modifiedComponents = new ArrayList<UIComponent>();
+
+ markImmediate = new Util.TreeTraversalCallback() {
+ public boolean takeActionOnNode(FacesContext context, UIComponent comp) throws FacesException {
+ if (comp instanceof ActionSource) {
+ ActionSource as = (ActionSource)comp;
+ if (!as.isImmediate()) {
+ as.setImmediate(true);
+ modifiedComponents.add(comp);
+ }
+ } else if (comp instanceof EditableValueHolder) {
+ EditableValueHolder ev = (EditableValueHolder)comp;
+ if (!ev.isImmediate()) {
+ ev.setImmediate(true);
+ modifiedComponents.add(comp);
+ }
+ }
+
+ return true;
+ }
+
+ };
     }
     
+ private transient List<UIComponent> modifiedComponents;
+
+ private transient Util.TreeTraversalCallback markImmediate;
+
+ public void postExecuteCleanup(FacesContext context) {
+ for (UIComponent comp : modifiedComponents) {
+ if (comp instanceof ActionSource) {
+ ActionSource as = (ActionSource)comp;
+ assert(as.isImmediate());
+ as.setImmediate(false);
+ } else if (comp instanceof EditableValueHolder) {
+ EditableValueHolder ev = (EditableValueHolder)comp;
+ assert(ev.isImmediate());
+ ev.setImmediate(false);
+ }
+ }
+ modifiedComponents.clear();
+ }
+
     public void processDecodes(FacesContext context) {
         boolean invokedCallback = false;
         if (!AsyncResponse.isAjaxRequest()) {
             super.processDecodes(context);
             return;
         }
+ AsyncResponse async = AsyncResponse.getInstance();
+ modifiedComponents.clear();
+
+ // If this is an immediate "render all" request and there are
+ // no explicit execute subtrees and the user didn't request
+ // execute: none...
+ if (async.isImmediateAjaxRequest() && async.isRenderAll() &&
+ async.getExecuteSubtrees().isEmpty() &&
+ !async.isExecuteNone()) {
+ // Traverse the entire view and mark every ActionSource or
+ // EditableValueHolder as immediate.
+ Util.prefixViewTraversal(context, this, markImmediate);
+ }
+
         invokedCallback = invokeContextCallbackOnSubtrees(context,
                 new PhaseAwareContextCallback(PhaseId.APPLY_REQUEST_VALUES));
         
         // Queue any events for this request in a context aware manner
- AsyncResponse async = AsyncResponse.getInstance();
         ResponseWriter writer = null;
         try {
             writer = async.getResponseWriter();
@@ -384,15 +442,18 @@
                 ConverterException converterException = null;
                 
                 if (curPhase == PhaseId.APPLY_REQUEST_VALUES) {
+ // If the user requested an immediate request
+ // Make sure to set the immediate flag.
+ if (AsyncResponse.isImmediateAjaxRequest()) {
+ PartialTraversalViewRoot.this.markImmediate.takeActionOnNode(facesContext, comp);
+ }
+
                     comp.processDecodes(facesContext);
- }
- else if (curPhase == PhaseId.PROCESS_VALIDATIONS) {
+ } else if (curPhase == PhaseId.PROCESS_VALIDATIONS) {
                     comp.processValidators(facesContext);
- }
- else if (curPhase == PhaseId.UPDATE_MODEL_VALUES) {
+ } else if (curPhase == PhaseId.UPDATE_MODEL_VALUES) {
                     comp.processUpdates(facesContext);
- }
- else if (curPhase == PhaseId.RENDER_RESPONSE) {
+ } else if (curPhase == PhaseId.RENDER_RESPONSE) {
                     ResponseWriter writer = AsyncResponse.getInstance().getResponseWriter();
 
                     writer.startElement("render", comp);
Index: run-time/avatar/src/main/resources/com_sun_faces_ajax.js
===================================================================
--- run-time/avatar/src/main/resources/com_sun_faces_ajax.js (revision 190)
+++ run-time/avatar/src/main/resources/com_sun_faces_ajax.js (working copy)
@@ -347,7 +347,8 @@
                     if (p[1].constructor != Array) {
                         p[1] = [p[1]];
                     }
- if (this[p[0]]) {
+ // Don't concatenate the viewState.
+ if (this[p[0]] && -1 == gViewState.indexOf(p[0])) {
                         this[p[0]] = this[p[0]].concat(p[1]);
                     }
                     else {
@@ -481,7 +482,12 @@
         
         // guarantee our header
         this.options.requestHeaders.push(gPartial);
- this.options.requestHeaders.push('true');
+ if (this.options.immediate) {
+ this.options.requestHeaders.push('immediate');
+ }
+ else {
+ this.options.requestHeaders.push('true');
+ }
         
         // add methodName
         if (this.options.methodName) {
Index: pom.xml
===================================================================
--- pom.xml (revision 190)
+++ pom.xml (working copy)
@@ -145,12 +145,13 @@
     <profile>
       <id>samples</id>
       <modules>
- <module>maven/master-pom</module>
+ <module>run-time/common</module>
         <module>run-time/avatar</module>
         <module>run-time/samples/j1</module>
         <module>run-time/samples/cardemo</module>
         <module>run-time/samples/jmaki</module>
         <module>run-time/samples/simple-events</module>
+ <module>run-time/samples/simple-partial-update</module>
       </modules>
     </profile>
     <profile>





-- 
| ed.burns_at_sun.com  | {home: 407 869 9587, office: 408 884 9519 OR x31640}
| homepage:         | http://purl.oclc.org/NET/edburns/
| aim: edburns0sunw | iim: ed.burns_at_sun.com