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)