You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2015/07/11 01:37:55 UTC

svn commit: r1690324 - in /myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets: DefaultFaceletsStateManagementStrategy.java tag/jsf/FaceletState.java

Author: lu4242
Date: Fri Jul 10 23:37:54 2015
New Revision: 1690324

URL: http://svn.apache.org/r1690324
Log:
MYFACES-3978 NullPointerException in FaceletStateValueExpression when ViewPooling enabled

Modified:
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/DefaultFaceletsStateManagementStrategy.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/FaceletState.java

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/DefaultFaceletsStateManagementStrategy.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/DefaultFaceletsStateManagementStrategy.java?rev=1690324&r1=1690323&r2=1690324&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/DefaultFaceletsStateManagementStrategy.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/DefaultFaceletsStateManagementStrategy.java Fri Jul 10 23:37:54 2015
@@ -27,7 +27,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-
+import javax.el.ValueExpression;
 import javax.faces.FacesException;
 import javax.faces.FactoryFinder;
 import javax.faces.application.ProjectStage;
@@ -52,7 +52,6 @@ import javax.faces.view.StateManagementS
 import javax.faces.view.ViewDeclarationLanguage;
 import javax.faces.view.ViewDeclarationLanguageFactory;
 import javax.faces.view.ViewMetadata;
-
 import org.apache.myfaces.application.StateManagerImpl;
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
 import org.apache.myfaces.context.RequestViewContext;
@@ -309,7 +308,25 @@ public class DefaultFaceletsStateManagem
                     }
                     if (faceletViewState != null)
                     {
-                        view.getAttributes().put(ComponentSupport.FACELET_STATE_INSTANCE,  faceletViewState);
+                        //if (skipBuildView)
+                        //{
+                            FaceletState newFaceletState = (FaceletState) view.getAttributes().get(
+                                    ComponentSupport.FACELET_STATE_INSTANCE);
+                            if (newFaceletState != null)
+                            {
+                                newFaceletState.restoreState(context, 
+                                        ((FaceletState)faceletViewState).saveState(context));
+                                faceletViewState = newFaceletState;
+                            }
+                            else
+                            {
+                                view.getAttributes().put(ComponentSupport.FACELET_STATE_INSTANCE,  faceletViewState);
+                            }
+                        //}
+                        //else
+                        //{
+                        //    view.getAttributes().put(ComponentSupport.FACELET_STATE_INSTANCE,  faceletViewState);
+                        //}
                     }
                     if (state.length == 3)
                     {
@@ -373,6 +390,10 @@ public class DefaultFaceletsStateManagem
                 states = (Map<String, Object>) state[1];
                 //Save the last unique id counter key in UIViewRoot
                 Integer lastUniqueIdCounter = (Integer) view.getAttributes().get(UNIQUE_ID_COUNTER_KEY);
+                // Retrieve the facelet state before restore anything. The reason is
+                // it could be necessary to restore the bindings map from here.
+                FaceletState oldFaceletState = (FaceletState) view.getAttributes().get(
+                        ComponentSupport.FACELET_STATE_INSTANCE);
                 
                 // Visit the children and restore their state.
                 boolean emptyState = false;
@@ -414,7 +435,36 @@ public class DefaultFaceletsStateManagem
                 }
                 if (faceletViewState != null)
                 {
-                    view.getAttributes().put(ComponentSupport.FACELET_STATE_INSTANCE,  faceletViewState);
+                    // Make sure binding map
+                    if (oldFaceletState != null && oldFaceletState.getBindings() != null && 
+                            !oldFaceletState.getBindings().isEmpty())
+                    {
+                        // Be sure the new facelet state has the binding map filled from the old one.
+                        // When vdl.buildView() is called by restoreView, FaceletState.bindings map is filled, but
+                        // when view pool is enabled, vdl.buildView() could restore the view, but create an alternate
+                        // FaceletState instance, different from the one restored. In this case, the restored instance
+                        // has precedence, but we need to fill bindings map using the entries from the instance that
+                        // comes from the view pool.
+                        FaceletState newFaceletState = (FaceletState) faceletViewState;
+                        for (Map.Entry<String, Map<String, ValueExpression> > entry : 
+                                oldFaceletState.getBindings().entrySet())
+                        {
+                            for (Map.Entry<String, ValueExpression> entry2 : entry.getValue().entrySet())
+                            {
+                                ValueExpression expr = newFaceletState.getBinding(entry.getKey(), entry2.getKey());
+                                if (expr == null)
+                                {
+                                    newFaceletState.putBinding(entry.getKey(), entry2.getKey(), entry2.getValue());
+                                }
+                            }
+                        }
+                        view.getAttributes().put(ComponentSupport.FACELET_STATE_INSTANCE,  newFaceletState);
+                    }
+                    else
+                    {
+                        //restore bindings
+                        view.getAttributes().put(ComponentSupport.FACELET_STATE_INSTANCE,  faceletViewState);
+                    }
                 }
                 if (lastUniqueIdCounter != null)
                 {

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/FaceletState.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/FaceletState.java?rev=1690324&r1=1690323&r2=1690324&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/FaceletState.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/view/facelets/tag/jsf/FaceletState.java Fri Jul 10 23:37:54 2015
@@ -147,6 +147,21 @@ public class FaceletState implements Sta
         bindings.put(key, expr);
     }
     
+    /**
+     * A "Facelet Binding ValueExpression" is a ValueExpression used/generated by facelets algorithm
+     * associated with an uniqueId, which usually is bound to the tagId. Components like c:forEach or
+     * ui:param uses it and the reason behind this is avoid use VariableMapper to create EL Expressions
+     * that later cannot be cached. Instead, the intention is make an indirection using 2 ValueExpression
+     * instances. In that way, all EL Expressions can be cached, because VariableMapper will use an
+     * instance that contains the uniqueId and the one stored in the map will have the real value or
+     * EL Expression that points to the managed bean. (Remember each EL expression that uses a variable
+     * stored in VariableMapper will copy the EL expression bound to the variable, so if the EL expression
+     * value changes across views, all EL Expressions that contains a reference cannot be cached).
+     * 
+     * This map is something special, because its content is related to the view structure. It does not need
+     * to be saved fully into the state, and it does not have any delta state, but it "evolves" with the initial
+     * state.
+     */
     public ValueExpression getBinding(String uniqueId, String key)
     {
         if (bindingsMap == null)