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 2013/07/23 02:20:05 UTC

svn commit: r1505866 - in /myfaces/core/branches/2.1.x/impl/src: main/java/org/apache/myfaces/application/ main/java/org/apache/myfaces/context/ main/java/org/apache/myfaces/view/facelets/ test/java/org/apache/myfaces/view/facelets/pss/acid/ test/java/...

Author: lu4242
Date: Tue Jul 23 00:20:04 2013
New Revision: 1505866

URL: http://svn.apache.org/r1505866
Log:
MYFACES-3739 @ResourceDependency annotation + JSF 1.2 state saving + c:if (dynamic section) creates components on each click (UIViewRoot grows)

Added:
    myfaces/core/branches/2.1.x/impl/src/test/resources/org/apache/myfaces/view/facelets/pss/acid/resourceDependency2.xhtml   (with props)
Modified:
    myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/application/ApplicationImpl.java
    myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/application/StateManagerImpl.java
    myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/context/RequestViewContext.java
    myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/view/facelets/DefaultFaceletsStateManagementStrategy.java
    myfaces/core/branches/2.1.x/impl/src/test/java/org/apache/myfaces/view/facelets/pss/acid/AcidMyFacesRequestTestCase.java
    myfaces/core/branches/2.1.x/impl/src/test/java/org/apache/myfaces/view/facelets/pss/acid/managed/ResourceDependencyBean.java

Modified: myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/application/ApplicationImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/application/ApplicationImpl.java?rev=1505866&r1=1505865&r2=1505866&view=diff
==============================================================================
--- myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/application/ApplicationImpl.java (original)
+++ myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/application/ApplicationImpl.java Tue Jul 23 00:20:04 2013
@@ -1785,7 +1785,7 @@ public class ApplicationImpl extends App
                 ResourceDependency dependency = dependencyList.get(i);
                 if (!rvc.isResourceDependencyAlreadyProcessed(dependency))
                 {
-                    _handleAttachedResourceDependency(context, dependency);
+                    _handleAttachedResourceDependency(context, dependency, inspectedClass);
                     rvc.setResourceDependencyAsProcessed(dependency);
                 }
             }
@@ -1830,7 +1830,8 @@ public class ApplicationImpl extends App
      * @param facesContext
      * @param component 
      */
-    private void setResourceIdOnFaceletsMode(FacesContext facesContext, UIComponent component)
+    private void setResourceIdOnFaceletsMode(FacesContext facesContext, UIComponent component,
+            Class<?> inspectedClass)
     {
         if (component.getId() == null)
         {
@@ -1848,11 +1849,31 @@ public class ApplicationImpl extends App
                 {
                     root.getAttributes().put(RESOURCE_DEPENDENCY_UNIQUE_ID_KEY, Boolean.FALSE);
                 }
+                if (!mctx.isUsingPSSOnThisView())
+                {
+                    // Now set the identifier that will help to know which classes has been already inspected.
+                    component.getAttributes().put(
+                            RequestViewContext.RESOURCE_DEPENDENCY_INSPECTED_CLASS, inspectedClass);
+                }
+                else if (mctx.isRefreshTransientBuildOnPSSPreserveState())
+                {
+                    component.getAttributes().put(
+                            RequestViewContext.RESOURCE_DEPENDENCY_INSPECTED_CLASS, inspectedClass);
+                }
+            }
+            else
+            {
+                // This happens when there is a programmatic addition, which means the user has added the
+                // components to the tree on render response phase or earlier but outside facelets control.
+                // In that case we need to save the dependency.
+                component.getAttributes().put(
+                        RequestViewContext.RESOURCE_DEPENDENCY_INSPECTED_CLASS, inspectedClass);
             }
         }
     }
     
-    private void _handleAttachedResourceDependency(FacesContext context, ResourceDependency annotation)
+    private void _handleAttachedResourceDependency(FacesContext context, ResourceDependency annotation, 
+            Class<?> inspectedClass)
     {
         // If this annotation is not present on the class in question, no action must be taken. 
         if (annotation != null)
@@ -1881,7 +1902,7 @@ public class ApplicationImpl extends App
             
             // If the @ResourceDependency was done inside facelets processing,
             // call setId() and set a proper id from facelets
-            setResourceIdOnFaceletsMode(context, output);
+            setResourceIdOnFaceletsMode(context, output, inspectedClass);
             
             // Obtain the Map of attributes from the UIOutput component by calling UIComponent.getAttributes().
             Map<String, Object> attributes = output.getAttributes();
@@ -2311,7 +2332,7 @@ public class ApplicationImpl extends App
                 ResourceDependency dependency = dependencyList.get(i);
                 if (!rvc.isResourceDependencyAlreadyProcessed(dependency))
                 {
-                    _handleResourceDependency(context, component, dependency);
+                    _handleResourceDependency(context, component, dependency, inspectedClass);
                     rvc.setResourceDependencyAsProcessed(dependency);
                 }
             }
@@ -2333,7 +2354,8 @@ public class ApplicationImpl extends App
         }
     }
     
-    private void _handleResourceDependency(FacesContext context, UIComponent component, ResourceDependency annotation)
+    private void _handleResourceDependency(FacesContext context, UIComponent component, ResourceDependency annotation,
+            Class<?> inspectedClass)
     {
         // If this annotation is not present on the class in question, no action must be taken.
         if (annotation != null)
@@ -2361,7 +2383,7 @@ public class ApplicationImpl extends App
             
             // If the @ResourceDependency was done inside facelets processing,
             // call setId() and set a proper id from facelets
-            setResourceIdOnFaceletsMode(context, output);
+            setResourceIdOnFaceletsMode(context, output, inspectedClass);
 
             // Obtain the Map of attributes from the UIOutput component by calling UIComponent.getAttributes().
             Map<String, Object> attributes = output.getAttributes();

Modified: myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/application/StateManagerImpl.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/application/StateManagerImpl.java?rev=1505866&r1=1505865&r2=1505866&view=diff
==============================================================================
--- myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/application/StateManagerImpl.java (original)
+++ myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/application/StateManagerImpl.java Tue Jul 23 00:20:04 2013
@@ -37,6 +37,7 @@ import javax.faces.view.StateManagementS
 import javax.faces.view.ViewDeclarationLanguage;
 
 import org.apache.myfaces.application.viewstate.StateCacheUtils;
+import org.apache.myfaces.context.RequestViewContext;
 
 public class StateManagerImpl extends StateManager
 {
@@ -145,6 +146,9 @@ public class StateManagerImpl extends St
                 {
                     facesContext.setViewRoot (uiViewRoot);
                     uiViewRoot.processRestoreState(facesContext, stateArray[1]);
+                    
+                    RequestViewContext.getCurrentInstance(facesContext).refreshRequestViewContext(
+                            facesContext, uiViewRoot);
                 }
             }            
         }

Modified: myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/context/RequestViewContext.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/context/RequestViewContext.java?rev=1505866&r1=1505865&r2=1505866&view=diff
==============================================================================
--- myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/context/RequestViewContext.java (original)
+++ myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/context/RequestViewContext.java Tue Jul 23 00:20:04 2013
@@ -18,11 +18,19 @@
  */
 package org.apache.myfaces.context;
 
+import java.util.Collections;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 
 import javax.faces.application.ResourceDependency;
+import javax.faces.component.UIComponent;
 import javax.faces.component.UIViewRoot;
+import javax.faces.component.visit.VisitCallback;
+import javax.faces.component.visit.VisitContext;
+import javax.faces.component.visit.VisitHint;
+import javax.faces.component.visit.VisitResult;
 import javax.faces.context.FacesContext;
 
 /**
@@ -36,6 +44,13 @@ public class RequestViewContext
 
     public static final String VIEW_CONTEXT_KEY = "oam.VIEW_CONTEXT";
     
+    public static final String RESOURCE_DEPENDENCY_INSPECTED_CLASS = "oam.RDClass";
+    
+    private static final String SKIP_ITERATION_HINT = "javax.faces.visit.SKIP_ITERATION";
+    
+    private static final Set<VisitHint> VISIT_HINTS = Collections.unmodifiableSet( 
+            EnumSet.of(VisitHint.SKIP_ITERATION));
+    
     private Map<ResourceDependency, Boolean> addedResources;
     
     // No lazy init: every view has one (UIView.class) or more classes to process   
@@ -125,4 +140,50 @@ public class RequestViewContext
         }
         renderTargetMap.put(target, value);
     }
+    
+    /**
+     * Scans UIViewRoot facets with added component resources by the effect of
+     * @ResourceDependency annotation, and register the associated inspected classes
+     * so new component resources will not be added to the component tree again and again.
+     * 
+     * @param facesContext
+     * @param root 
+     */
+    public void refreshRequestViewContext(FacesContext facesContext, UIViewRoot root)
+    {
+        for (Map.Entry<String, UIComponent> entry : root.getFacets().entrySet())
+        {
+            UIComponent facet = entry.getValue();
+            if (facet.getId() != null && facet.getId().startsWith("javax_faces_location_"))
+            {
+                try
+                {
+                    facesContext.getAttributes().put(SKIP_ITERATION_HINT, Boolean.TRUE);
+
+                    VisitContext visitContext = VisitContext.createVisitContext(facesContext, null, VISIT_HINTS);
+                    facet.visitTree(visitContext, new RefreshViewContext());
+                }
+                finally
+                {
+                    // We must remove hint in finally, because an exception can break this phase,
+                    // but lifecycle can continue, if custom exception handler swallows the exception
+                    facesContext.getAttributes().remove(SKIP_ITERATION_HINT);
+                }
+            }
+        }
+    }
+    
+    private class RefreshViewContext implements VisitCallback
+    {
+
+        public VisitResult visit(VisitContext context, UIComponent target)
+        {
+            Class<?> inspectedClass = (Class<?>)target.getAttributes().get(RESOURCE_DEPENDENCY_INSPECTED_CLASS);
+            if (inspectedClass != null)
+            {
+                setClassProcessed(inspectedClass);
+            }            
+            return VisitResult.ACCEPT;
+        }
+    }
 }

Modified: myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/view/facelets/DefaultFaceletsStateManagementStrategy.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/view/facelets/DefaultFaceletsStateManagementStrategy.java?rev=1505866&r1=1505865&r2=1505866&view=diff
==============================================================================
--- myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/view/facelets/DefaultFaceletsStateManagementStrategy.java (original)
+++ myfaces/core/branches/2.1.x/impl/src/main/java/org/apache/myfaces/view/facelets/DefaultFaceletsStateManagementStrategy.java Tue Jul 23 00:20:04 2013
@@ -59,6 +59,7 @@ 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;
 import org.apache.myfaces.shared.config.MyfacesConfig;
 import org.apache.myfaces.shared.util.ClassUtils;
 import org.apache.myfaces.shared.util.HashMapUtils;
@@ -224,6 +225,12 @@ public class DefaultFaceletsStateManagem
             {
                 context.setViewRoot (view);
                 view.processRestoreState(context, fullState[1]);
+                
+                // If the view is restored fully, it is necessary to refresh RequestViewContext, otherwise at
+                // each ajax request new components associated with @ResourceDependency annotation will be added
+                // to the tree, making the state bigger without real need.
+                RequestViewContext.getCurrentInstance(context).
+                        refreshRequestViewContext(context, view);
             }
         }
         else
@@ -449,6 +456,12 @@ public class DefaultFaceletsStateManagem
             // Reset this list, because it will be calculated later when the view is being saved
             // in the right order, preventing duplicates (see COMPONENT_ADDED_AFTER_BUILD_VIEW for details).
             clientIdsAdded.clear();
+            
+            // This call only has sense when components has been added programatically, because if facelets has control
+            // over all components in the component tree, build the initial state and apply the state will have the
+            // same effect.
+            RequestViewContext.getCurrentInstance(context).
+                    refreshRequestViewContext(context, view);
         }
     }
 

Modified: myfaces/core/branches/2.1.x/impl/src/test/java/org/apache/myfaces/view/facelets/pss/acid/AcidMyFacesRequestTestCase.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.1.x/impl/src/test/java/org/apache/myfaces/view/facelets/pss/acid/AcidMyFacesRequestTestCase.java?rev=1505866&r1=1505865&r2=1505866&view=diff
==============================================================================
--- myfaces/core/branches/2.1.x/impl/src/test/java/org/apache/myfaces/view/facelets/pss/acid/AcidMyFacesRequestTestCase.java (original)
+++ myfaces/core/branches/2.1.x/impl/src/test/java/org/apache/myfaces/view/facelets/pss/acid/AcidMyFacesRequestTestCase.java Tue Jul 23 00:20:04 2013
@@ -508,4 +508,102 @@ public class AcidMyFacesRequestTestCase 
         executeAfterRender(facesContext);
     }
 
+    @Test
+    public void testResourceDependency2() throws Exception
+    {
+        setupRequest("/resourceDependency2.xhtml");
+        processLifecycleExecute();
+
+        executeBeforeRender(facesContext);
+        executeBuildViewCycle(facesContext);
+
+        UIPanel headPanel = (UIPanel) facesContext.getViewRoot().getFacet("head");
+        Assert.assertNull(headPanel);
+        
+        String nextUniqueId = facesContext.getViewRoot().createUniqueId(facesContext, null);
+        
+        executeViewHandlerRender(facesContext);
+        executeAfterRender(facesContext);
+        
+        UICommand button = (UICommand) facesContext.getViewRoot().findComponent("mainForm:postback");
+        submit(button);
+        
+        processLifecycleExecute();
+        
+        ResourceDependencyBean bean = facesContext.getApplication().evaluateExpressionGet(
+            facesContext, "#{resourceDependencyBean}", ResourceDependencyBean.class);
+        bean.setIncludeContent(true);
+        
+        executeBeforeRender(facesContext);
+        executeBuildViewCycle(facesContext);
+        
+        headPanel = (UIPanel) facesContext.getViewRoot().getFacet("head");
+        Assert.assertNotNull(headPanel);
+        Assert.assertTrue(1 >= headPanel.getChildCount());
+        Assert.assertNotSame(nextUniqueId, headPanel.getChildren().get(0).getId());
+        
+        executeViewHandlerRender(facesContext);
+        executeAfterRender(facesContext);
+        
+        UICommand button2 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:postback");
+        submit(button2);
+        
+        processLifecycleExecute();
+        
+        bean = facesContext.getApplication().evaluateExpressionGet(
+            facesContext, "#{resourceDependencyBean}", ResourceDependencyBean.class);
+        bean.setIncludeContent(false);
+        
+        executeBeforeRender(facesContext);
+        executeBuildViewCycle(facesContext);
+        
+        headPanel = (UIPanel) facesContext.getViewRoot().getFacet("head");
+        Assert.assertNotNull(headPanel);
+        Assert.assertTrue(1 >= headPanel.getChildCount());
+        //Assert.assertNotSame(nextUniqueId, headPanel.getChildren().get(0).getId());
+        
+        executeViewHandlerRender(facesContext);
+        executeAfterRender(facesContext);    
+        
+        UICommand button3 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:postback");
+        submit(button3);
+        
+        processLifecycleExecute();
+        
+        bean = facesContext.getApplication().evaluateExpressionGet(
+            facesContext, "#{resourceDependencyBean}", ResourceDependencyBean.class);
+        bean.setIncludeContent(true);
+        
+        executeBeforeRender(facesContext);
+        executeBuildViewCycle(facesContext);
+        
+        headPanel = (UIPanel) facesContext.getViewRoot().getFacet("head");
+        Assert.assertNotNull(headPanel);
+        Assert.assertTrue(1 >= headPanel.getChildCount());
+        //Assert.assertNotSame(nextUniqueId, headPanel.getChildren().get(0).getId());
+        
+        executeViewHandlerRender(facesContext);
+        executeAfterRender(facesContext);
+        
+        UICommand button4 = (UICommand) facesContext.getViewRoot().findComponent("mainForm:postback");
+        submit(button4);
+        
+        processLifecycleExecute();
+        
+        bean = facesContext.getApplication().evaluateExpressionGet(
+            facesContext, "#{resourceDependencyBean}", ResourceDependencyBean.class);
+        bean.setIncludeContent(false);
+        
+        executeBeforeRender(facesContext);
+        executeBuildViewCycle(facesContext);
+        
+        headPanel = (UIPanel) facesContext.getViewRoot().getFacet("head");
+        Assert.assertNotNull(headPanel);
+        Assert.assertTrue(1 >= headPanel.getChildCount());
+        //Assert.assertNotSame(nextUniqueId, headPanel.getChildren().get(0).getId());
+        
+        executeViewHandlerRender(facesContext);
+        executeAfterRender(facesContext);
+    }
+
 }

Modified: myfaces/core/branches/2.1.x/impl/src/test/java/org/apache/myfaces/view/facelets/pss/acid/managed/ResourceDependencyBean.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.1.x/impl/src/test/java/org/apache/myfaces/view/facelets/pss/acid/managed/ResourceDependencyBean.java?rev=1505866&r1=1505865&r2=1505866&view=diff
==============================================================================
--- myfaces/core/branches/2.1.x/impl/src/test/java/org/apache/myfaces/view/facelets/pss/acid/managed/ResourceDependencyBean.java (original)
+++ myfaces/core/branches/2.1.x/impl/src/test/java/org/apache/myfaces/view/facelets/pss/acid/managed/ResourceDependencyBean.java Tue Jul 23 00:20:04 2013
@@ -46,4 +46,9 @@ public class ResourceDependencyBean
     {
         this.includeContent = includeContent;
     }
+    
+    public void toggleContent()
+    {
+        this.includeContent =  !this.includeContent;
+    }
 }

Added: myfaces/core/branches/2.1.x/impl/src/test/resources/org/apache/myfaces/view/facelets/pss/acid/resourceDependency2.xhtml
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.1.x/impl/src/test/resources/org/apache/myfaces/view/facelets/pss/acid/resourceDependency2.xhtml?rev=1505866&view=auto
==============================================================================
--- myfaces/core/branches/2.1.x/impl/src/test/resources/org/apache/myfaces/view/facelets/pss/acid/resourceDependency2.xhtml (added)
+++ myfaces/core/branches/2.1.x/impl/src/test/resources/org/apache/myfaces/view/facelets/pss/acid/resourceDependency2.xhtml Tue Jul 23 00:20:04 2013
@@ -0,0 +1,32 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<!--
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml"
+    xmlns:h="http://java.sun.com/jsf/html"
+    xmlns:f="http://java.sun.com/jsf/core"
+    xmlns:ui="http://java.sun.com/jsf/facelets"
+    xmlns:c="http://java.sun.com/jsp/jstl/core"
+    xmlns:test="http://testcomponent">
+<h:head>
+</h:head>
+<h:body>
+  
+  <h:form id="mainForm">
+     <c:if test="#{resourceDependencyBean.includeContent}">
+        <test:rdcomponent id="rdcomp" value="test1"/>
+     </c:if>
+     <h:commandButton id="postback" value="POSTBACK"/>
+  </h:form>
+</h:body>
+</html>

Propchange: myfaces/core/branches/2.1.x/impl/src/test/resources/org/apache/myfaces/view/facelets/pss/acid/resourceDependency2.xhtml
------------------------------------------------------------------------------
    svn:eol-style = native