You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2011/12/31 02:11:14 UTC

svn commit: r1226009 - /tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java

Author: hlship
Date: Sat Dec 31 01:11:14 2011
New Revision: 1226009

URL: http://svn.apache.org/viewvc?rev=1226009&view=rev
Log:
TAP5-1808: Change Form to (by default) immediately render markup when there are validation errors, to avoid creating the session

Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java?rev=1226009&r1=1226008&r2=1226009&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java Sat Dec 31 01:11:14 2011
@@ -76,7 +76,10 @@ import java.io.ObjectInputStream;
  * </p>
  * <p>
  * While rendering, or processing a Form submission, the Form component places a {@link FormSupport} object into the {@linkplain Environment environment},
- * so that enclosed components can coordinate with the Form component.
+ * so that enclosed components can coordinate with the Form component. It also places a {@link ValidationTracker} into the environment during both render and submission.
+ * During submission it also pushes a {@link Heartbeat} into the environment, which is {@link org.apache.tapestry5.services.Heartbeat#end() ended} just before
+ * {@linkplain FormSupport#defer(Runnable) deferred FormSupport operations} are executed.
+ * </p>
  * </p>
  *
  * @tapestrydoc
@@ -119,13 +122,11 @@ public class Form implements ClientEleme
     private Object[] context;
 
     /**
-     * The object which will record user input and validation errors. The object
-     * must be persistent between requests
-     * (since the form submission and validation occurs in a component event
-     * request and the subsequent render occurs
-     * in a render request). The default is a persistent property of the Form
-     * component and this is sufficient for
-     * nearly all purposes (except when a Form is rendered inside a loop).
+     * The object which will record user input and validation errors. When not using
+     * the default behavior supplied by the Form component (an immediate re-render of the active
+     * page when there are form validation errors), it is necessary to bind this parameter
+     * to a persistent value that can be maintained until the active page is re-rendered. See
+     * <a href="https://issues.apache.org/jira/browse/TAP5-1808">TAP5-1801</a>.
      */
     @Parameter("defaultTracker")
     private ValidationTracker tracker;
@@ -215,7 +216,10 @@ public class Form implements ClientEleme
     @Symbol(InternalSymbols.PRE_SELECTED_FORM_NAMES)
     private String preselectedFormNames;
 
-    @Persist(PersistenceConstants.FLASH)
+
+    /**
+     * Starting in 5.4, this is a simple, non-persistent property, with no extra magic tricks.
+     */
     private ValidationTracker defaultTracker;
 
     @Inject
@@ -249,9 +253,8 @@ public class Form implements ClientEleme
 
     private String clientId;
 
-    // Set during rendering or submit processing to be the
-    // same as the VT pushed into the Environment
-    private ValidationTracker activeTracker;
+    @Inject
+    private ComponentSource componentSource;
 
     String defaultValidationId()
     {
@@ -264,63 +267,25 @@ public class Form implements ClientEleme
     }
 
     /**
-     * Returns a wrapped version of the tracker parameter (which is usually bound to the
-     * defaultTracker persistent field).
-     * If tracker is currently null, a new instance of {@link ValidationTrackerImpl} is created.
-     * The tracker is then wrapped, such that the tracker parameter
-     * is only updated the first time an error is recorded into the tracker (this will typically
-     * propagate to the defaultTracker
-     * persistent field and be stored into the session). This means that if no errors are recorded,
-     * the tracker parameter is not updated and (in the default case) no data is stored into the
-     * session.
+     * Returns an instance of {@link ValidationTrackerImpl}, lazily creating it as needed. This property
+     * is the default for the <strong>tracker</strong> parameter; the property (as of Tapestry 5.4) is not
+     * persistent.
      *
-     * @return a tracker ready to receive data (possibly a previously stored tracker with field
-     *         input and errors)
-     * @see <a href="https://issues.apache.org/jira/browse/TAP5-979">TAP5-979</a>
+     * @return per-request cached instance
      */
-    private ValidationTracker getWrappedTracker()
+    public ValidationTracker getDefaultTracker()
     {
-        ValidationTracker innerTracker = tracker == null ? new ValidationTrackerImpl() : tracker;
-
-        ValidationTracker wrapper = new ValidationTrackerWrapper(innerTracker)
+        if (defaultTracker == null)
         {
-            private boolean saved = false;
-
-            private void save()
-            {
-                if (!saved)
-                {
-                    tracker = getDelegate();
-
-                    saved = true;
-                }
-            }
-
-            @Override
-            public void recordError(Field field, String errorMessage)
-            {
-                super.recordError(field, errorMessage);
-
-                save();
-            }
-
-            @Override
-            public void recordError(String errorMessage)
-            {
-                super.recordError(errorMessage);
-
-                save();
-            }
-        };
-
-        return wrapper;
-    }
+            defaultTracker = new ValidationTrackerImpl();
+        }
 
-    public ValidationTracker getDefaultTracker()
-    {
         return defaultTracker;
     }
 
+    /**
+     * @deprecated In 5.4; previously used only for testing
+     */
     public void setDefaultTracker(ValidationTracker defaultTracker)
     {
         this.defaultTracker = defaultTracker;
@@ -358,15 +323,13 @@ public class Form implements ClientEleme
         if (zone != null)
             linkFormToZone(link);
 
-        activeTracker = getWrappedTracker();
-
         environment.push(FormSupport.class, formSupport);
-        environment.push(ValidationTracker.class, activeTracker);
+        environment.push(ValidationTracker.class, tracker);
 
         if (autofocus)
         {
             ValidationDecorator autofocusDecorator = new AutofocusValidationDecorator(
-                    environment.peek(ValidationDecorator.class), activeTracker, jsSupport);
+                    environment.peek(ValidationDecorator.class), tracker, jsSupport);
             environment.push(ValidationDecorator.class, autofocusDecorator);
         }
 
@@ -387,7 +350,9 @@ public class Form implements ClientEleme
         form = writer.element("form", "id", clientId, "method", "post", "action", actionURL);
 
         if ((zone != null || clientValidation != ClientValidation.NONE) && !request.isXHR())
+        {
             writer.attributes("onsubmit", MarkupConstants.WAIT_FOR_PAGE);
+        }
 
         resources.renderInformalParameters(writer);
 
@@ -472,23 +437,20 @@ public class Form implements ClientEleme
 
         environment.pop(ValidationTracker.class);
 
-        activeTracker = null;
+        tracker.clear();
 
         environment.pop(BeanValidationContext.class);
     }
 
     @SuppressWarnings(
             {"unchecked", "InfiniteLoopStatement"})
-    @Log
     Object onAction(EventContext context) throws IOException
     {
-        activeTracker = getWrappedTracker();
-
-        activeTracker.clear();
+        tracker.clear();
 
         formSupport = new FormSupportImpl(resources, validationId);
 
-        environment.push(ValidationTracker.class, activeTracker);
+        environment.push(ValidationTracker.class, tracker);
         environment.push(FormSupport.class, formSupport);
 
         Heartbeat heartbeat = new HeartbeatImpl();
@@ -541,33 +503,44 @@ public class Form implements ClientEleme
             // true persistent data, not value from the previous form
             // submission.
 
-            if (!activeTracker.getHasErrors())
+            if (!tracker.getHasErrors())
             {
-                activeTracker.clear();
+                tracker.clear();
             }
 
-            resources.triggerContextEvent(activeTracker.getHasErrors() ? EventConstants.FAILURE
-                    : EventConstants.SUCCESS, context, eventCallback);
+            String eventType = tracker.getHasErrors()
+                    ? EventConstants.FAILURE
+                    : EventConstants.SUCCESS;
 
-            // Lastly, tell anyone whose interested that the form is completely
-            // submitted.
+            resources.triggerContextEvent(eventType, context, eventCallback);
 
             if (eventCallback.isAborted())
+            {
                 return true;
+            }
+
+            // Lastly, tell anyone whose interested that the form is completely
+            // submitted.
 
             resources.triggerContextEvent(EventConstants.SUBMIT, context, eventCallback);
 
-            if (eventCallback.isAborted()) { return true; }
+            if (eventCallback.isAborted())
+            {
+                return true;
+            }
 
             // For traditional request with no validation exceptions, re-render the
             // current page immediately, as-is.  Prior to Tapestry 5.4, a redirect was
             // sent that required that the tracker be persisted across requests.
             // See https://issues.apache.org/jira/browse/TAP5-1808
 
-            if (activeTracker.getHasErrors() && !request.isXHR()) {
+            if (tracker.getHasErrors() && !request.isXHR())
+            {
                 return STREAM_ACTIVE_PAGE_CONTENT;
             }
 
+            // The event will not work its way up.
+
             return false;
 
         } finally
@@ -581,8 +554,6 @@ public class Form implements ClientEleme
             {
                 environment.pop(BeanValidationContext.class);
             }
-
-            activeTracker = null;
         }
     }
 
@@ -695,32 +666,27 @@ public class Form implements ClientEleme
 
     public void recordError(String errorMessage)
     {
-        getActiveTracker().recordError(errorMessage);
+        tracker.recordError(errorMessage);
     }
 
     public void recordError(Field field, String errorMessage)
     {
-        getActiveTracker().recordError(field, errorMessage);
+        tracker.recordError(field, errorMessage);
     }
 
     public boolean getHasErrors()
     {
-        return getActiveTracker().getHasErrors();
+        return tracker.getHasErrors();
     }
 
     public boolean isValid()
     {
-        return !getActiveTracker().getHasErrors();
-    }
-
-    private ValidationTracker getActiveTracker()
-    {
-        return activeTracker != null ? activeTracker : getWrappedTracker();
+        return !tracker.getHasErrors();
     }
 
     public void clearErrors()
     {
-        getActiveTracker().clear();
+        tracker.clear();
     }
 
     // For testing:
@@ -738,9 +704,6 @@ public class Form implements ClientEleme
         return clientId;
     }
 
-    @Inject
-    private ComponentSource componentSource;
-
     private void preallocateNames(IdAllocator idAllocator)
     {
         for (String name : TapestryInternalUtils.splitAtCommas(preselectedFormNames))