tapestry-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hls...@apache.org
Subject svn commit: r730456 [1/2] - in /tapestry/tapestry5/trunk: src/site/apt/ src/site/apt/guide/ tapestry-core/src/main/java/org/apache/tapestry5/ tapestry-core/src/main/java/org/apache/tapestry5/annotations/ tapestry-core/src/main/java/org/apache/tapestry5...
Date Wed, 31 Dec 2008 19:50:40 GMT
Author: hlship
Date: Wed Dec 31 11:50:39 2008
New Revision: 730456

URL: http://svn.apache.org/viewvc?rev=730456&view=rev
Log:
TAP5-408: Objects that persist in the session should automatically re-store themselves into the session at the end of the request

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedSessionPersistedObject.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/ImmutableSessionPersistedObject.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultSessionPersistedObjectAnalyzer.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestEventHub.java
      - copied, changed from r728733, tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestListenerHub.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestEventHubImpl.java
      - copied, changed from r728733, tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestListenerHubImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedApplicationStateObjectAnalyzer.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedSessionPersistedObjectAnalyzer.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RestoreDirtySessionObjects.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/EndOfRequestEventHubImplTest.java
      - copied, changed from r728733, tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/EndOfRequestListenerHubImplTest.java
Removed:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestListenerHub.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestListenerHubImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/EndOfRequestListenerHubImplTest.java
Modified:
    tapestry/tapestry5/trunk/src/site/apt/guide/appstate.apt
    tapestry/tapestry5/trunk/src/site/apt/guide/persist.apt
    tapestry/tapestry5/trunk/src/site/apt/upgrade.apt
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedApplicationStateObject.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/MetaDataConstants.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedApplicationStateObject.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/PersistenceConstants.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Meta.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Persist.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/events/EndOfRequestListener.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PersistentFieldManagerImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionApplicationStatePersistenceStrategy.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/PageTesterSession.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Session.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PersistentFieldManagerImplTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SessionApplicationStatePersistenceStrategyTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SessionImplTest.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/StrategyBuilderImpl.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/StrategyBuilder.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/services/StrategyBuilderImplTest.java

Modified: tapestry/tapestry5/trunk/src/site/apt/guide/appstate.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/guide/appstate.apt?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/guide/appstate.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/guide/appstate.apt Wed Dec 31 11:50:39 2008
@@ -76,22 +76,10 @@
   Each ASO is managed according to a persistence strategy. The default persistence strategy, "session", stores the ASOs inside the session.
   The session is created as needed.
 
-* Clustering Issue
+Clustering Issues
 
-  Application State Objects are, by design, mutable objects.  This means that, once read from the session, they are often modified.
-  This poses a problem <in a cluster>, because the modified version of the ASO should be propogated to the other server(s) in the cluster.
-
-  Tapestry handles this automatically; at the end of a request, every ASO that has been accessed from the session will be restored
-  into the session.  This will trigger the application server to synchronize the new state of the ASO around the the cluster.
-
-  This can be optimized: the interface
-  {{{../apidocs/org/apache/tapestry5/OptimizedApplicationStateObject.html}OptimizedApplicationStateObject}}  can be
-  implemented by   your ASO.  This provides control over whether the ASO is "dirty" and needs to be stored.  Thus, in the majority of
-  requests where the internal state of the ASO is not changed, it will not be restored into the session.
-
-  The easiest way to take advantage of this is to extend from the
-  {{{../apidocs/org/apache/tapestry5/BaseOptimizedApplicationStateObject.html}BaseOptimizedApplicationStateObject}}, and
-  invoke markDirty() from the setter methods.
+  The clustering strategy for Application State Objects in release 5.0 now applies to all session-persisted objects.
+  See the {{{persist.html}persistent page data notes}} for more details.
   
 Configuring ASOs
 

Modified: tapestry/tapestry5/trunk/src/site/apt/guide/persist.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/guide/persist.apt?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/guide/persist.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/guide/persist.apt Wed Dec 31 11:50:39 2008
@@ -10,12 +10,15 @@
   by many users.
   
   However, you often want to store some persistent data on a page, and have access
-  to it in later requests.
+  to it in later requests.  Long term storage of data should go in a database of some form, but
+  server-side state for the duration of the current request should go in the HttpSession (though Tapestyr
+  provides a few other options as well).
   
   This is accomplished with the 
-  {{{../apidocs/org/apache/tapestry5/annotations/Persist.html}Persist annotation}}.
+  {{{../apidocs/org/apache/tapestry5/annotations/Persist.html}Persist annotation}}. Again, this does <not>
+  refer to database persistence, it refers to session persistance.
   
-  This annotation is applied to private instance fields.
+  This annotation is applied to private instance fields of components.
     
 +----+
   @Persist
@@ -94,4 +97,50 @@
 
    The method <<<discardPersistentFieldChanges()>>> of ComponentResources will discard all persistent fields
    for the page, regardless of which strategy is used to store the property. This will not affect the
-   page in memory, but takes effect for subsequent requests.
\ No newline at end of file
+   page in memory, but takes effect for subsequent requests.
+
+Clustering Issues
+
+  The Servlet API was designed with the intention that there would be only a modest amount of server-side state,
+  and that the stored values would be individual numbers and strings, and thus, immutable.
+
+  Many web frameworks do not use the HttpSession this way, and store large and mutable objects in the session.
+
+  This is not an issue for single servers, but in a cluster, anything stored in the session must be serialized to
+  a bytestream and distributed to other servers within the cluster, and restored there.
+
+  Most application servers perform the serialization and distribution as part of HttpSession.setAttribute().
+
+  This creates a problem for mutable objects, because if you read a mutable session object, change its state, but <don't>
+  invoke setAttribute(), the changes will be isolated to just a single server in the cluster.
+
+  Tapestry attempts to solve this: any session persisted object that is read during a request will be re-stored back into
+  the HttpSession at the end of the request.  This ensures that changed internal state of those mutable objects
+  is properly replicated around the cluster.
+
+  This can be a problem in a cluster as all those calls to setAttribute() may impact performance, as often the internal
+  state of the mutable object don't have changed.
+
+  Tapestry has solutions to this.
+
+* Immutable Objects
+
+  Tapestry knows that Java's String, Number and Boolean classes are immutable.    Immutable objects do not require
+  a re-store into the session.
+
+  You can mark your own session objects as immutable using the
+  {{{../apidocs//org/apache/tapestry5/annotations/ImmutableSessionPersistedObject.html}ImmutableSessionPersistedObject}} annotation.
+
+* OptimizedSessionPersistedObject
+
+  The {{{../apidocs/org/apache/tapestry5/OptimizedSessionPersistedObject}OptimizedSessionPersistedObject}} interface
+  allows an object to control this behavior. An object with this interface can track when its mutable state changes. Typically,
+  you should extend from the
+  {{{../apidocs/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.html}BaseOptimizedSessionPersistedObject}} base class.
+
+* SessionPersistedObjectAnalyzer
+
+  The {{{../apidocs/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.html}SessionPersistedObjectAnalyzer}}
+  service is ultimately responsible for determining whether a session persisted object is dirty or not (dirty meaning
+  in need of a restore into the session). This is an extensible service where new strategies, for new classes,
+  can be introduced.
\ No newline at end of file

Modified: tapestry/tapestry5/trunk/src/site/apt/upgrade.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/upgrade.apt?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/upgrade.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/upgrade.apt Wed Dec 31 11:50:39 2008
@@ -24,4 +24,8 @@
   Tapestry that the render phase should be invoked}}.
 
   There have been some significant changes to the {{{tapestry-spring/}tapestry-spring}} module, to
-  support injection of Tapestry services into Springbeans.
\ No newline at end of file
+  support injection of Tapestry services into Springbeans.
+
+  Tapestry is now more aggressive about automatically re-storing any session persisted object
+  back into the session at the end of the request (this used to only apply to application state objects).  See the 
+  {{{guide/persist.html}persistent page data}} notes for more details.
\ No newline at end of file

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedApplicationStateObject.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedApplicationStateObject.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedApplicationStateObject.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedApplicationStateObject.java Wed Dec 31 11:50:39 2008
@@ -14,44 +14,16 @@
 
 package org.apache.tapestry5;
 
-import javax.servlet.http.HttpSessionBindingEvent;
-import javax.servlet.http.HttpSessionBindingListener;
-
 /**
  * Base class for creating optimized application state objects.  Works as a {@link
  * javax.servlet.http.HttpSessionBindingListener} to determine when the object is no longer dirty.
+ *
+ * @deprecated since 5.1.0.0; use {@link org.apache.tapestry5.BaseOptimizedSessionPersistedObject} instead
  */
-public abstract class BaseOptimizedApplicationStateObject implements OptimizedApplicationStateObject, HttpSessionBindingListener
+public abstract class BaseOptimizedApplicationStateObject extends BaseOptimizedSessionPersistedObject
 {
-    private transient boolean dirty;
-
     public final boolean isApplicationStateObjectDirty()
     {
-        return dirty;
-    }
-
-    /**
-     * Invoked by the servlet container when the value is stored (or re-stored) as an attribute of the session. This
-     * clears the dirty flag.
-     */
-    public void valueBound(HttpSessionBindingEvent event)
-    {
-        dirty = false;
-    }
-
-    /**
-     * Does nothing.
-     */
-    public void valueUnbound(HttpSessionBindingEvent event)
-    {
-    }
-
-    /**
-     * Invoked by the subclass whenever the internal state of the ASO changes. Typically, this is invoked from mutator
-     * methods.
-     */
-    protected final void markDirty()
-    {
-        dirty = true;
+        return isSessionPersistedObjectDirty();
     }
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.java?rev=730456&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.java Wed Dec 31 11:50:39 2008
@@ -0,0 +1,59 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// 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.
+
+package org.apache.tapestry5;
+
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+
+/**
+ * Base implementation of {@link org.apache.tapestry5.OptimizedSessionPersistedObject}.  Subclasses should invoke {@link
+ * #markDirty()} when internal state of the object changes.
+ *
+ * @since 5.1.1.0
+ */
+public abstract class BaseOptimizedSessionPersistedObject implements OptimizedSessionPersistedObject, HttpSessionBindingListener
+{
+    private transient boolean dirty;
+
+    public final boolean isSessionPersistedObjectDirty()
+    {
+        return dirty;
+    }
+
+    /**
+     * Invoked by the servlet container when the value is stored (or re-stored) as an attribute of the session. This
+     * clears the dirty flag. Subclasses may override this method, but should invoke this implementation.
+     */
+    public void valueBound(HttpSessionBindingEvent event)
+    {
+        dirty = false;
+    }
+
+    /**
+     * Does nothing.
+     */
+    public void valueUnbound(HttpSessionBindingEvent event)
+    {
+    }
+
+    /**
+     * Invoked by the subclass whenever the internal state of the object changes. Typically, this is invoked from
+     * mutator methods.
+     */
+    protected final void markDirty()
+    {
+        dirty = true;
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/MetaDataConstants.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/MetaDataConstants.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/MetaDataConstants.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/MetaDataConstants.java Wed Dec 31 11:50:39 2008
@@ -15,9 +15,12 @@
 package org.apache.tapestry5;
 
 /**
- * Meta-data keys that are applied to components and pages.
+ * Meta-data keys that are applied to components and pages.  In addition, in many cases a {@linkplain
+ * org.apache.tapestry5.SymbolConstants symbol constant key} is also a meta data key (where the symbol value is the
+ * ultimate default).
  *
  * @see org.apache.tapestry5.services.MetaDataLocator
+ * @see org.apache.tapestry5.MetaDataConstants
  */
 public class MetaDataConstants
 {

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedApplicationStateObject.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedApplicationStateObject.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedApplicationStateObject.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedApplicationStateObject.java Wed Dec 31 11:50:39 2008
@@ -19,6 +19,7 @@
  *
  * @see org.apache.tapestry5.annotations.ApplicationState
  * @see org.apache.tapestry5.services.ApplicationStateManager
+ * @deprecated since 5.1.0.0; use {@link org.apache.tapestry5.OptimizedSessionPersistedObject} instead
  */
 public interface OptimizedApplicationStateObject
 {

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedSessionPersistedObject.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedSessionPersistedObject.java?rev=730456&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedSessionPersistedObject.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedSessionPersistedObject.java Wed Dec 31 11:50:39 2008
@@ -0,0 +1,37 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// 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.
+
+package org.apache.tapestry5;
+
+/**
+ * An optional interface implemented by objects that are persisted in the {@link org.apache.tapestry5.services.Session}.
+ * At the end of each request, any objects read from the session are re-stored into the session, to ensure that
+ * in-memory changes are flushed to other servers in a cluster. Objects that implement this interface are expected to
+ * track when they are dirty (have pending changes), so that the save back into the session can be avoided when not
+ * necessary.
+ *
+ * @see org.apache.tapestry5.annotations.ImmutableSessionPersistedObject
+ * @see org.apache.tapestry5.services.SessionPersistedObjectAnalyzer
+ * @since 5.1.1.0
+ */
+public interface OptimizedSessionPersistedObject
+{
+    /**
+     * Returns true if the object has in-memory changes.  It is the object's responsibility to set its internal flag to
+     * false, typically by implementing {@link javax.servlet.http.HttpSessionBindingListener}.
+     *
+     * @return
+     */
+    boolean isSessionPersistedObjectDirty();
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/PersistenceConstants.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/PersistenceConstants.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/PersistenceConstants.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/PersistenceConstants.java Wed Dec 31 11:50:39 2008
@@ -1,3 +1,17 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// 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.
+
 package org.apache.tapestry5;
 
 /**
@@ -8,6 +22,16 @@
 public class PersistenceConstants
 {
     /**
+     * The field's value is stored in the {@link org.apache.tapestry5.services.Session}.
+     */
+    public static final String SESSION = "session";
+
+    /**
+     * The field's value is stored on the client, as a query parameter or hidden form field.
+     */
+    public static final String CLIENT = "client";
+
+    /**
      * The page field persistence strategy that stores data in the session until the next request.
      */
     public static final String FLASH = "flash";

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java Wed Dec 31 11:50:39 2008
@@ -106,4 +106,13 @@
      * @since 5.1.0.0
      */
     public static final String USE_EXTERNAL_SPRING_CONTEXT = "tapestry.use-external-spring-context";
+
+
+    /**
+     * Identifies the default persistence strategy for all pages that do not provide an override (using this value as
+     * {@link org.apache.tapestry5.annotations.Meta key}).
+     *
+     * @since 5.1.0.0
+     */
+    public static final String PERSISTENCE_STRATEGY = "tapestry.persistence-strategy";
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java Wed Dec 31 11:50:39 2008
@@ -25,7 +25,7 @@
  * Standard implmentation of {@link ValidationTracker}. Works pretty hard to ensure a minimum amount of data is stored
  * in the HttpSession.
  */
-public final class ValidationTrackerImpl implements ValidationTracker, Serializable
+public final class ValidationTrackerImpl extends BaseOptimizedSessionPersistedObject implements ValidationTracker, Serializable
 {
     private static final long serialVersionUID = -8029192726659275677L;
 
@@ -83,6 +83,8 @@
 
     private void store(FieldTracker fieldTracker)
     {
+        markDirty();
+
         if (fieldTrackers == null)
             fieldTrackers = CollectionFactory.newList();
 
@@ -99,6 +101,8 @@
 
     public void clear()
     {
+        markDirty();
+
         extraErrors = null;
         fieldTrackers = null;
         fieldToTracker = null;
@@ -156,6 +160,8 @@
 
     public void recordError(String errorMessage)
     {
+        markDirty();
+
         if (extraErrors == null)
             extraErrors = CollectionFactory.newList();
 
@@ -170,5 +176,4 @@
 
         store(ft);
     }
-
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/ImmutableSessionPersistedObject.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/ImmutableSessionPersistedObject.java?rev=730456&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/ImmutableSessionPersistedObject.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/ImmutableSessionPersistedObject.java Wed Dec 31 11:50:39 2008
@@ -0,0 +1,32 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// 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.
+
+package org.apache.tapestry5.annotations;
+
+import java.lang.annotation.*;
+
+/**
+ * Marker annotation that can be placed on a session-persisted object to indicate that the object is immutable, and
+ * therefore does not require end-of-request restoring into the session.
+ *
+ * @see org.apache.tapestry5.OptimizedSessionPersistedObject
+ * @since 5.1.1.0
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface ImmutableSessionPersistedObject
+{
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Meta.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Meta.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Meta.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Meta.java Wed Dec 31 11:50:39 2008
@@ -1,4 +1,4 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 2007, 2008 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -26,6 +26,8 @@
  * Allows for the specification of per-component meta-data. Meta data can later be accessed via {@link
  * ComponentModel#getMeta(String)}. Meta data keys are case insensitive. Meta data defined by a subclass overrides meta
  * data for the super class (where the keys conflict).
+ *
+ * @see org.apache.tapestry5.MetaDataConstants
  */
 @Target(TYPE)
 @Retention(RUNTIME)

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Persist.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Persist.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Persist.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/Persist.java Wed Dec 31 11:50:39 2008
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 2008 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -14,7 +14,6 @@
 
 package org.apache.tapestry5.annotations;
 
-import org.apache.tapestry5.services.MetaDataLocator;
 import org.apache.tapestry5.services.Session;
 
 import java.lang.annotation.Documented;
@@ -36,7 +35,8 @@
  * In this way, the session persistence strategy for a component and all of its sub-components can be controlled by the
  * containing component.
  *
- * @see MetaDataLocator
+ * @see org.apache.tapestry5.services.MetaDataLocator
+ * @see org.apache.tapestry5.PersistenceConstants
  */
 @Target(FIELD)
 @Documented

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=730456&r1=730455&r2=730456&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 Wed Dec 31 11:50:39 2008
@@ -341,9 +341,7 @@
 
         formSupport = null;
 
-        // This forces a change to the tracker, which is nice because its internal state has
-        // changed.
-        tracker = environment.pop(ValidationTracker.class);
+        environment.pop(ValidationTracker.class);
     }
 
     @SuppressWarnings({"unchecked", "InfiniteLoopStatement"})
@@ -380,15 +378,6 @@
 
             heartbeat.end();
 
-            ValidationTracker tracker = environment.peek(ValidationTracker.class);
-
-            // Let the listeners peform any final validations
-
-            // Update through the parameter because the tracker has almost certainly changed
-            // internal state.
-
-            this.tracker = tracker;
-
             formSupport.executeDeferred();
 
             fireValidateFormEvent(context, callback);
@@ -402,7 +391,8 @@
             // as well, so that the next page render will be "clean" and show
             // true persistent data, not value from the previous form submission.
 
-            if (!this.tracker.getHasErrors()) this.tracker.clear();
+            if (!tracker.getHasErrors())
+                tracker.clear();
 
             resources.triggerContextEvent(tracker.getHasErrors() ? EventConstants.FAILURE : EventConstants.SUCCESS,
                                           context, callback);
@@ -419,6 +409,12 @@
         {
             environment.pop(Heartbeat.class);
             environment.pop(FormSupport.class);
+
+            // This forces an update that feeds through the system and gets the updated
+            // state of the tracker (if using the Form's defaultTracker property, which is flash persisted)
+            // stored back into the session.
+
+            tracker = environment.pop(ValidationTracker.class);
         }
     }
 
@@ -500,20 +496,12 @@
 
     public void recordError(String errorMessage)
     {
-        ValidationTracker tracker = this.tracker;
-
         tracker.recordError(errorMessage);
-
-        this.tracker = tracker;
     }
 
     public void recordError(Field field, String errorMessage)
     {
-        ValidationTracker tracker = this.tracker;
-
         tracker.recordError(field, errorMessage);
-
-        this.tracker = tracker;
     }
 
     public boolean getHasErrors()

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/events/EndOfRequestListener.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/events/EndOfRequestListener.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/events/EndOfRequestListener.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/events/EndOfRequestListener.java Wed Dec 31 11:50:39 2008
@@ -17,7 +17,7 @@
 /**
  * Event listener interface for objects that need to know when the current request finishes.
  *
- * @see org.apache.tapestry5.internal.services.EndOfRequestListenerHub
+ * @see org.apache.tapestry5.internal.services.EndOfRequestEventHub
  */
 public interface EndOfRequestListener
 {

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultSessionPersistedObjectAnalyzer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultSessionPersistedObjectAnalyzer.java?rev=730456&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultSessionPersistedObjectAnalyzer.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultSessionPersistedObjectAnalyzer.java Wed Dec 31 11:50:39 2008
@@ -0,0 +1,43 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// 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.
+
+package org.apache.tapestry5.internal.services;
+
+import org.apache.tapestry5.annotations.ImmutableSessionPersistedObject;
+import org.apache.tapestry5.services.SessionPersistedObjectAnalyzer;
+
+/**
+ * Default catch-all implementation of {@link org.apache.tapestry5.services.SessionPersistedObjectAnalyzer}.
+ *
+ * @since 5.1.0.0
+ */
+public class DefaultSessionPersistedObjectAnalyzer implements SessionPersistedObjectAnalyzer<Object>
+{
+    /**
+     * An object is dirty <em>unless</em> it has the {@link org.apache.tapestry5.annotations.ImmutableSessionPersistedObject}
+     * annotation.
+     *
+     * @param object to analyze
+     * @return false if immutable, true otherwise
+     */
+    public boolean isDirty(Object object)
+    {
+        boolean immutable = object.getClass().getAnnotation(ImmutableSessionPersistedObject.class) != null;
+
+        // Imuutable objects are always clean, others are assumed dirty.
+        // Go implement OptimizedSessionPersistedObject if you don't like it.
+
+        return !immutable;
+    }
+}

Copied: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestEventHub.java (from r728733, tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestListenerHub.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestEventHub.java?p2=tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestEventHub.java&p1=tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestListenerHub.java&r1=728733&r2=730456&rev=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestListenerHub.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestEventHub.java Wed Dec 31 11:50:39 2008
@@ -19,7 +19,7 @@
 /**
  * Manages request notifications for the {@link org.apache.tapestry5.internal.events.EndOfRequestListener} interface.
  */
-public interface EndOfRequestListenerHub
+public interface EndOfRequestEventHub
 {
     void addEndOfRequestListener(EndOfRequestListener listener);
 

Copied: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestEventHubImpl.java (from r728733, tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestListenerHubImpl.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestEventHubImpl.java?p2=tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestEventHubImpl.java&p1=tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestListenerHubImpl.java&r1=728733&r2=730456&rev=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestListenerHubImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EndOfRequestEventHubImpl.java Wed Dec 31 11:50:39 2008
@@ -19,7 +19,7 @@
 
 import java.util.List;
 
-public class EndOfRequestListenerHubImpl implements EndOfRequestListenerHub
+public class EndOfRequestEventHubImpl implements EndOfRequestEventHub
 {
     private final List<EndOfRequestListener> listeners = CollectionFactory.newThreadSafeList();
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalModule.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/InternalModule.java Wed Dec 31 11:50:39 2008
@@ -72,7 +72,7 @@
         binder.bind(ComponentPageElementResourcesSource.class, ComponentPageElementResourcesSourceImpl.class);
         binder.bind(RequestSecurityManager.class, RequestSecurityManagerImpl.class);
         binder.bind(InternalRequestGlobals.class, InternalRequestGlobalsImpl.class);
-        binder.bind(EndOfRequestListenerHub.class);
+        binder.bind(EndOfRequestEventHub.class);
         binder.bind(PageActivationContextCollector.class);
         binder.bind(PageLoader.class, PageLoaderImpl.class);
     }

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedApplicationStateObjectAnalyzer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedApplicationStateObjectAnalyzer.java?rev=730456&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedApplicationStateObjectAnalyzer.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedApplicationStateObjectAnalyzer.java Wed Dec 31 11:50:39 2008
@@ -0,0 +1,26 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// 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.
+
+package org.apache.tapestry5.internal.services;
+
+import org.apache.tapestry5.OptimizedApplicationStateObject;
+import org.apache.tapestry5.services.SessionPersistedObjectAnalyzer;
+
+public class OptimizedApplicationStateObjectAnalyzer implements SessionPersistedObjectAnalyzer<OptimizedApplicationStateObject>
+{
+    public boolean isDirty(OptimizedApplicationStateObject object)
+    {
+        return object.isApplicationStateObjectDirty();
+    }
+}

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedSessionPersistedObjectAnalyzer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedSessionPersistedObjectAnalyzer.java?rev=730456&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedSessionPersistedObjectAnalyzer.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedSessionPersistedObjectAnalyzer.java Wed Dec 31 11:50:39 2008
@@ -0,0 +1,26 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// 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.
+
+package org.apache.tapestry5.internal.services;
+
+import org.apache.tapestry5.OptimizedSessionPersistedObject;
+import org.apache.tapestry5.services.SessionPersistedObjectAnalyzer;
+
+public class OptimizedSessionPersistedObjectAnalyzer implements SessionPersistedObjectAnalyzer<OptimizedSessionPersistedObject>
+{
+    public boolean isDirty(OptimizedSessionPersistedObject object)
+    {
+        return object.isSessionPersistedObjectDirty();
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PersistentFieldManagerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PersistentFieldManagerImpl.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PersistentFieldManagerImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PersistentFieldManagerImpl.java Wed Dec 31 11:50:39 2008
@@ -15,6 +15,7 @@
 package org.apache.tapestry5.internal.services;
 
 import org.apache.tapestry5.ComponentResources;
+import org.apache.tapestry5.SymbolConstants;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.model.ComponentModel;
@@ -28,10 +29,6 @@
 
 public class PersistentFieldManagerImpl implements PersistentFieldManager
 {
-    public static final String META_KEY = "tapestry.persistence-strategy";
-
-    public static final String DEFAULT_STRATEGY = "session";
-
     private final MetaDataLocator metaDataLocator;
 
     private final Map<String, PersistentFieldStrategy> strategies;
@@ -93,6 +90,6 @@
 
         if (InternalUtils.isNonBlank(strategy)) return strategy;
 
-        return metaDataLocator.findMeta(META_KEY, resources, String.class);
+        return metaDataLocator.findMeta(SymbolConstants.PERSISTENCE_STRATEGY, resources, String.class);
     }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestImpl.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestImpl.java Wed Dec 31 11:50:39 2008
@@ -17,6 +17,7 @@
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.services.Request;
 import org.apache.tapestry5.services.Session;
+import org.apache.tapestry5.services.SessionPersistedObjectAnalyzer;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
@@ -38,14 +39,18 @@
 
     private final String requestEncoding;
 
+    private final SessionPersistedObjectAnalyzer analyzer;
+
     private boolean encodingSet;
 
     private Session session;
 
-    public RequestImpl(HttpServletRequest request, String requestEncoding)
+    public RequestImpl(HttpServletRequest request, String requestEncoding,
+                       SessionPersistedObjectAnalyzer analyzer)
     {
         this.request = request;
         this.requestEncoding = requestEncoding;
+        this.analyzer = analyzer;
     }
 
     public List<String> getParameterNames()
@@ -102,7 +107,10 @@
         {
             HttpSession hsession = request.getSession(create);
 
-            if (hsession != null) session = new SessionImpl(hsession);
+            if (hsession != null)
+            {
+                session = new SessionImpl(hsession, analyzer);
+            }
         }
 
         return session;

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RestoreDirtySessionObjects.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RestoreDirtySessionObjects.java?rev=730456&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RestoreDirtySessionObjects.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RestoreDirtySessionObjects.java Wed Dec 31 11:50:39 2008
@@ -0,0 +1,40 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// 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.
+
+package org.apache.tapestry5.internal.services;
+
+import org.apache.tapestry5.internal.events.EndOfRequestListener;
+import org.apache.tapestry5.services.Request;
+import org.apache.tapestry5.services.Session;
+
+/**
+ * A listener that invokes {@link org.apache.tapestry5.services.Session#restoreDirtyObjects()}.
+ */
+public class RestoreDirtySessionObjects implements EndOfRequestListener
+{
+    private final Request request;
+
+    public RestoreDirtySessionObjects(Request request)
+    {
+        this.request = request;
+    }
+
+    public void requestDidComplete()
+    {
+        Session session = request.getSession(false);
+
+        if (session != null)
+            session.restoreDirtyObjects();
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionApplicationStatePersistenceStrategy.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionApplicationStatePersistenceStrategy.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionApplicationStatePersistenceStrategy.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionApplicationStatePersistenceStrategy.java Wed Dec 31 11:50:39 2008
@@ -14,26 +14,18 @@
 
 package org.apache.tapestry5.internal.services;
 
-import org.apache.tapestry5.OptimizedApplicationStateObject;
-import org.apache.tapestry5.internal.events.EndOfRequestListener;
-import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.services.ApplicationStateCreator;
 import org.apache.tapestry5.services.ApplicationStatePersistenceStrategy;
 import org.apache.tapestry5.services.Request;
 import org.apache.tapestry5.services.Session;
 
-import java.util.Map;
-
 /**
  * Stores ASOs in the {@link Session}, which will be created as necessary.
  */
-public class SessionApplicationStatePersistenceStrategy implements
-        ApplicationStatePersistenceStrategy, EndOfRequestListener
+public class SessionApplicationStatePersistenceStrategy implements ApplicationStatePersistenceStrategy
 {
     static final String PREFIX = "aso:";
 
-    static final String ASO_MAP_ATTRIBUTE = "org.apache.tapestry.application-state-object-map";
-
     private final Request request;
 
     public SessionApplicationStatePersistenceStrategy(Request request)
@@ -53,16 +45,7 @@
 
         String key = buildKey(asoClass);
 
-        Map<String, Object> asoMap = getASOMap();
-
-        T aso = (T) asoMap.get(key);
-
-        if (aso != null) return aso;
-
-        // Otherwise, get/create it in the session and record it in the
-        // aso map.
-
-        aso = (T) session.getAttribute(key);
+        T aso = (T) session.getAttribute(key);
 
         if (aso == null)
         {
@@ -70,8 +53,6 @@
             session.setAttribute(key, aso);
         }
 
-        asoMap.put(key, aso);
-
         return aso;
     }
 
@@ -85,8 +66,6 @@
         String key = buildKey(asoClass);
 
         getSession().setAttribute(key, aso);
-
-        getASOMap().put(key, aso);
     }
 
     public <T> boolean exists(Class<T> asoClass)
@@ -97,67 +76,4 @@
 
         return session != null && session.getAttribute(key) != null;
     }
-
-    private Map<String, Object> getASOMap()
-    {
-        Map<String, Object> result = (Map<String, Object>) request.getAttribute(ASO_MAP_ATTRIBUTE);
-
-        if (result == null)
-        {
-            result = CollectionFactory.newMap();
-            request.setAttribute(ASO_MAP_ATTRIBUTE, result);
-        }
-
-        return result;
-    }
-
-    public void requestDidComplete()
-    {
-        Map<String, Object> map = getASOMap();
-
-        if (map.isEmpty()) return;
-
-        Session session = request.getSession(false);
-
-        if (session != null && session.isInvalidated()) return;
-
-        for (String key : map.keySet())
-        {
-            Object aso = map.get(key);
-
-            if (aso == null) continue;
-
-            if (needsRestore(aso))
-            {
-                if (session == null)
-                    session = request.getSession(true);
-
-                // Don't need to check invalidated as a session that gets created here
-                // can't have been invalidated yet ... but then again, how did the ASO get
-                // created then?
-
-                // It is expected that the ASO implements HttpSessionBindingListener and
-                // can clear its dirty flag as it is saved.
-
-                session.setAttribute(key, aso);
-            }
-        }
-    }
-
-    private boolean needsRestore(Object aso)
-    {
-        // We could check for basic immutable types here, but those are not typically ASOs.
-        // ASOs tend to be more complex, mutable objects.
-
-        if (aso instanceof OptimizedApplicationStateObject)
-        {
-            OptimizedApplicationStateObject optimized = (OptimizedApplicationStateObject) aso;
-
-            return optimized.isApplicationStateObjectDirty();
-        }
-
-        // If not optimized, assume that it is (in fact) dirty.
-
-        return true;
-    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java Wed Dec 31 11:50:39 2008
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 2008 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -14,32 +14,47 @@
 
 package org.apache.tapestry5.internal.services;
 
-import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newList;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.services.Session;
+import org.apache.tapestry5.services.SessionPersistedObjectAnalyzer;
 
 import javax.servlet.http.HttpSession;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
+import java.util.Map;
 
 /**
  * A thin wrapper around {@link HttpSession}.
  */
 public class SessionImpl implements Session
 {
+    private final SessionPersistedObjectAnalyzer analyzer;
+
     private final HttpSession session;
 
     private boolean invalidated = false;
 
-    public SessionImpl(HttpSession session)
+    /**
+     * Cache of attribute objects read from, or written to, the real session. This is needed for end-of-request
+     * processing.
+     */
+    private final Map<String, Object> sessionAttributeCache = CollectionFactory.newMap();
+
+    public SessionImpl(HttpSession session, SessionPersistedObjectAnalyzer analyzer)
     {
         this.session = session;
+        this.analyzer = analyzer;
     }
 
     public Object getAttribute(String name)
     {
-        return session.getAttribute(name);
+        Object result = session.getAttribute(name);
+
+        sessionAttributeCache.put(name, result);
+
+        return result;
     }
 
     public List<String> getAttributeNames()
@@ -50,11 +65,13 @@
     public void setAttribute(String name, Object value)
     {
         session.setAttribute(name, value);
+
+        sessionAttributeCache.put(name, value);
     }
 
     public List<String> getAttributeNames(String prefix)
     {
-        List<String> result = newList();
+        List<String> result = CollectionFactory.newList();
 
         Enumeration e = session.getAttributeNames();
         while (e.hasMoreElements())
@@ -79,6 +96,8 @@
         invalidated = true;
 
         session.invalidate();
+
+        sessionAttributeCache.clear();
     }
 
     public boolean isInvalidated()
@@ -90,4 +109,24 @@
     {
         session.setMaxInactiveInterval(seconds);
     }
+
+    public void restoreDirtyObjects()
+    {
+        if (invalidated) return;
+
+        if (sessionAttributeCache.isEmpty()) return;
+
+        for (Map.Entry<String, Object> entry : sessionAttributeCache.entrySet())
+        {
+            String attributeName = entry.getKey();
+
+            Object attributeValue = entry.getValue();
+
+            if (attributeValue == null)
+                continue;
+
+            if (analyzer.isDirty(attributeValue))
+                session.setAttribute(attributeName, attributeValue);
+        }
+    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/PageTesterSession.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/PageTesterSession.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/PageTesterSession.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/test/PageTesterSession.java Wed Dec 31 11:50:39 2008
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 2008 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -81,6 +81,10 @@
         return false;
     }
 
+    public void restoreDirtyObjects()
+    {
+    }
+
     public void setMaxInactiveInterval(int seconds)
     {
         nyi("setMaxInactiveInterval");

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Session.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Session.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Session.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Session.java Wed Dec 31 11:50:39 2008
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2008 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -72,4 +72,18 @@
      * @since 5.1.0.0
      */
     boolean isInvalidated();
+
+    /**
+     * Re-stores dirty objects back into the session.  This is necessary to support clustering, because (in most
+     * application servers) session objects are only broadcast around the cluster from setAttribute().  If a mutable
+     * session object is read and changed, those changes will be limited to a single server in the cluster, which can
+     * cause confusing application failures in the event of a failover.      Does nothing if there are no changes, or
+     * the session has been invalidated.
+     *
+     * @see org.apache.tapestry5.OptimizedSessionPersistedObject
+     * @see org.apache.tapestry5.internal.services.OptimizedApplicationStateObjectAnalyzer
+     * @see org.apache.tapestry5.annotations.ImmutableSessionPersistedObject
+     * @since 5.1.0.0
+     */
+    void restoreDirtyObjects();
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.java?rev=730456&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.java Wed Dec 31 11:50:39 2008
@@ -0,0 +1,40 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// 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.
+
+package org.apache.tapestry5.services;
+
+import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
+
+/**
+ * Analyzes a session-persisted object, specifically to see if it is dirty or not.  The service implementation uses a
+ * mapped configuration to form a {@linkplain org.apache.tapestry5.ioc.services.StrategyBuilder strategy} based on
+ * object type. The service is injectable using the {@link org.apache.tapestry5.ioc.annotations.Primary} marker
+ * annotation.
+ *
+ * @see org.apache.tapestry5.annotations.ImmutableSessionPersistedObject
+ * @see org.apache.tapestry5.OptimizedSessionPersistedObject
+ * @since 5.1.0.0
+ */
+@UsesMappedConfiguration(key = Class.class, value = SessionPersistedObjectAnalyzer.class)
+public interface SessionPersistedObjectAnalyzer<T>
+{
+    /**
+     * Passed an object (never null) to see if it is dirty or not. Dirty objects that are stored in the session are
+     * re-stored into the session at the end of the request.
+     *
+     * @param object
+     * @return true if object needs to be re-stored into the session
+     */
+    boolean isDirty(T object);
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java Wed Dec 31 11:50:39 2008
@@ -92,6 +92,8 @@
 
     private final EnvironmentalShadowBuilder environmentalBuilder;
 
+    private final EndOfRequestEventHub endOfRequestEventHub;
+
 
     /**
      * We inject all sorts of common dependencies (including builders) into the module itself (note: even though some of
@@ -121,7 +123,9 @@
 
                           ThreadLocale threadLocale,
 
-                          EnvironmentalShadowBuilder environmentalBuilder)
+                          EnvironmentalShadowBuilder environmentalBuilder,
+
+                          EndOfRequestEventHub endOfRequestEventHub)
     {
         this.pipelineBuilder = pipelineBuilder;
         this.shadowBuilder = shadowBuilder;
@@ -135,6 +139,7 @@
         this.response = response;
         this.threadLocale = threadLocale;
         this.environmentalBuilder = environmentalBuilder;
+        this.endOfRequestEventHub = endOfRequestEventHub;
     }
 
     public static void bind(ServiceBinder binder)
@@ -184,6 +189,8 @@
         binder.bind(URLEncoder.class, URLEncoderImpl.class);
         binder.bind(ContextPathEncoder.class, ContextPathEncoderImpl.class);
         binder.bind(UpdateListenerHub.class, UpdateListenerHubImpl.class);
+        binder.bind(ApplicationStatePersistenceStrategy.class, SessionApplicationStatePersistenceStrategy.class).withId(
+                "SessionApplicationStatePersistenceStrategy");
     }
 
     // ========================================================================
@@ -585,9 +592,7 @@
 
                                          UpdateListenerHub updateListenerHub,
 
-                                         LocalizationSetter localizationSetter,
-
-                                         final EndOfRequestListenerHub endOfRequestListenerHub)
+                                         LocalizationSetter localizationSetter)
     {
         RequestFilter staticFilesFilter = new StaticFilesFilter(context);
 
@@ -611,7 +616,7 @@
                 }
                 finally
                 {
-                    endOfRequestListenerHub.fire();
+                    endOfRequestEventHub.fire();
                 }
             }
         };
@@ -974,7 +979,10 @@
                                                                     final RequestHandler handler,
 
                                                                     @Inject @Symbol(SymbolConstants.CHARSET)
-                                                                    final String applicationCharset)
+                                                                    final String applicationCharset,
+
+                                                                    @Primary
+                                                                    final SessionPersistedObjectAnalyzer analyzer)
     {
         HttpServletRequestHandler terminator = new HttpServletRequestHandler()
         {
@@ -983,7 +991,7 @@
             {
                 requestGlobals.storeServletRequestResponse(servletRequest, servletResponse);
 
-                Request request = new RequestImpl(servletRequest, applicationCharset);
+                Request request = new RequestImpl(servletRequest, applicationCharset, analyzer);
                 Response response = new ResponseImpl(servletResponse);
 
                 // TAP5-257: Make sure that the "initial guess" for request/response is available, even if
@@ -1106,9 +1114,7 @@
     @Marker(Primary.class)
     public ObjectRenderer buildObjectRenderer(Map<Class, ObjectRenderer> configuration)
     {
-        StrategyRegistry<ObjectRenderer> registry = StrategyRegistry.newInstance(ObjectRenderer.class, configuration);
-
-        return strategyBuilder.build(registry);
+        return strategyBuilder.build(ObjectRenderer.class, configuration);
     }
 
 
@@ -1697,9 +1703,9 @@
                                                  @InjectService("ClientPersistentFieldStrategy")
                                                  PersistentFieldStrategy clientStrategy)
     {
-        configuration.add("session", new SessionPersistentFieldStrategy(request));
+        configuration.add(PersistenceConstants.SESSION, new SessionPersistentFieldStrategy(request));
         configuration.add(PersistenceConstants.FLASH, new FlashPersistentFieldStrategy(request));
-        configuration.add("client", clientStrategy);
+        configuration.add(PersistenceConstants.CLIENT, clientStrategy);
     }
 
     /**
@@ -1855,7 +1861,7 @@
         configuration.add("tapestry.datepicker.path", "org/apache/tapestry5/datepicker_106");
         configuration.add("tapestry.datepicker", "classpath:${tapestry.datepicker.path}");
 
-        configuration.add(PersistentFieldManagerImpl.META_KEY, PersistentFieldManagerImpl.DEFAULT_STRATEGY);
+        configuration.add(SymbolConstants.PERSISTENCE_STRATEGY, PersistenceConstants.SESSION);
 
         configuration.add(MetaDataConstants.RESPONSE_CONTENT_TYPE, "text/html");
 
@@ -1888,7 +1894,8 @@
     public void contributeApplicationInitializer(OrderedConfiguration<ApplicationInitializerFilter> configuration,
                                                  final TypeCoercer typeCoercer,
                                                  final ComponentClassResolver componentClassResolver,
-                                                 @ComponentClasses final InvalidationEventHub hub)
+                                                 @ComponentClasses final InvalidationEventHub invalidationEventHub,
+                                                 final @Autobuild RestoreDirtySessionObjects restoreDirtySessionObjects)
     {
         final InvalidationListener listener = new InvalidationListener()
         {
@@ -1907,7 +1914,11 @@
                 // Snuck in here is the logic to clear the PropertyAccess service's cache whenever
                 // the component class loader is invalidated.
 
-                hub.addInvalidationListener(listener);
+                invalidationEventHub.addInvalidationListener(listener);
+
+                endOfRequestEventHub.addEndOfRequestListener(restoreDirtySessionObjects);
+
+                // Perform other pending initialization
 
                 initializer.initializeApplication(context);
 
@@ -1921,9 +1932,6 @@
         configuration.add("ClearCachesOnInvalidation", clearCaches);
     }
 
-    public void contributePropBindingFactory(OrderedConfiguration<BindingFactory> configuration)
-    {
-    }
 
     /**
      * Contributes filters: <dl> <dt>Ajax</dt> <dd>Determines if the request is Ajax oriented, and redirects to an
@@ -2029,14 +2037,6 @@
         return messagesSource.getInvalidatonEventHub();
     }
 
-    public ApplicationStatePersistenceStrategy buildSessionApplicationStatePersistenceStrategy(
-            @Autobuild SessionApplicationStatePersistenceStrategy service, EndOfRequestListenerHub hub)
-    {
-        hub.addEndOfRequestListener(service);
-
-        return service;
-    }
-
     @Scope(ScopeConstants.PERTHREAD)
     public Environment buildEnvironment(PerthreadManager perthreadManager)
     {
@@ -2046,4 +2046,44 @@
 
         return service;
     }
+
+    /**
+     * The master SessionPesistedObjectAnalyzer.
+     *
+     * @since 5.1.0.0
+     */
+    @Marker(Primary.class)
+    public SessionPersistedObjectAnalyzer buildSessionPersistedObjectAnalyzer(
+            Map<Class, SessionPersistedObjectAnalyzer> configuration)
+    {
+        return strategyBuilder.build(SessionPersistedObjectAnalyzer.class, configuration);
+    }
+
+    /**
+     * Identifies String, Number and Boolean as immutable objects, a catch-all handler for Object (that understands
+     * {@link org.apache.tapestry5.annotations.ImmutableSessionPersistedObject}, and handlers for {@link
+     * org.apache.tapestry5.OptimizedSessionPersistedObject} and {@link org.apache.tapestry5.OptimizedApplicationStateObject}.
+     *
+     * @since 5.1.0.0
+     */
+    public static void contributeSessionPersistedObjectAnalyzer(
+            MappedConfiguration<Class, SessionPersistedObjectAnalyzer> configuration)
+    {
+        configuration.add(Object.class, new DefaultSessionPersistedObjectAnalyzer());
+
+        SessionPersistedObjectAnalyzer<Object> immutable = new SessionPersistedObjectAnalyzer<Object>()
+        {
+            public boolean isDirty(Object object)
+            {
+                return false;
+            }
+        };
+
+        configuration.add(String.class, immutable);
+        configuration.add(Number.class, immutable);
+        configuration.add(Boolean.class, immutable);
+
+        configuration.add(OptimizedSessionPersistedObject.class, new OptimizedSessionPersistedObjectAnalyzer());
+        configuration.add(OptimizedApplicationStateObject.class, new OptimizedApplicationStateObjectAnalyzer());
+    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/IntegrationTests.java Wed Dec 31 11:50:39 2008
@@ -533,6 +533,7 @@
     public void bean_editor()
     {
         start("BeanEditor Demo", "Clear Data");
+
         clickAndWait(SUBMIT);
 
         // Part of the override for the firstName property

Copied: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/EndOfRequestEventHubImplTest.java (from r728733, tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/EndOfRequestListenerHubImplTest.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/EndOfRequestEventHubImplTest.java?p2=tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/EndOfRequestEventHubImplTest.java&p1=tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/EndOfRequestListenerHubImplTest.java&r1=728733&r2=730456&rev=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/EndOfRequestListenerHubImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/EndOfRequestEventHubImplTest.java Wed Dec 31 11:50:39 2008
@@ -18,12 +18,12 @@
 import org.apache.tapestry5.internal.test.InternalBaseTestCase;
 import org.testng.annotations.Test;
 
-public class EndOfRequestListenerHubImplTest extends InternalBaseTestCase
+public class EndOfRequestEventHubImplTest extends InternalBaseTestCase
 {
     @Test
     public void add_and_notify()
     {
-        EndOfRequestListenerHub hub = new EndOfRequestListenerHubImpl();
+        EndOfRequestEventHub hub = new EndOfRequestEventHubImpl();
 
         EndOfRequestListener listener = newMock(EndOfRequestListener.class);
 
@@ -42,7 +42,7 @@
     @Test
     public void add_remove_notify()
     {
-        EndOfRequestListenerHub hub = new EndOfRequestListenerHubImpl();
+        EndOfRequestEventHub hub = new EndOfRequestEventHubImpl();
 
         EndOfRequestListener listener = newMock(EndOfRequestListener.class);
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PersistentFieldManagerImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PersistentFieldManagerImplTest.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PersistentFieldManagerImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PersistentFieldManagerImplTest.java Wed Dec 31 11:50:39 2008
@@ -15,6 +15,8 @@
 package org.apache.tapestry5.internal.services;
 
 import org.apache.tapestry5.ComponentResources;
+import org.apache.tapestry5.PersistenceConstants;
+import org.apache.tapestry5.SymbolConstants;
 import org.apache.tapestry5.internal.test.InternalBaseTestCase;
 import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newList;
 import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newMap;
@@ -181,7 +183,7 @@
 
         train_getFieldPersistenceStrategy(model, fieldName, "");
 
-        train_findMeta(locator, PersistentFieldManagerImpl.META_KEY, resources, String.class, strategyName);
+        train_findMeta(locator, SymbolConstants.PERSISTENCE_STRATEGY, resources, String.class, strategyName);
 
         train_getNestedId(resources, nestedId);
 
@@ -211,7 +213,7 @@
         Object value = new Object();
 
         Map<String, PersistentFieldStrategy> strategies = newMap();
-        strategies.put(PersistentFieldManagerImpl.DEFAULT_STRATEGY, strat);
+        strategies.put(PersistenceConstants.SESSION, strat);
 
         train_getComponentModel(resources, model);
 
@@ -219,9 +221,9 @@
 
         train_findMeta(
                 locator,
-                PersistentFieldManagerImpl.META_KEY,
+                SymbolConstants.PERSISTENCE_STRATEGY,
                 resources, String.class,
-                PersistentFieldManagerImpl.DEFAULT_STRATEGY);
+                PersistenceConstants.SESSION);
 
         train_getNestedId(resources, nestedId);
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java?rev=730456&r1=730455&r2=730456&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java Wed Dec 31 11:50:39 2008
@@ -38,7 +38,7 @@
 
         replay();
 
-        Request request = new RequestImpl(sr, CHARSET);
+        Request request = new RequestImpl(sr, CHARSET, null);
 
         assertNull(request.getSession(false));
 
@@ -57,7 +57,7 @@
 
         replay();
 
-        Request request = new RequestImpl(sr, CHARSET);
+        Request request = new RequestImpl(sr, CHARSET, null);
         Session session = request.getSession(true);
 
         assertEquals(session.getAttribute("foo"), "bar");
@@ -78,7 +78,7 @@
 
         replay();
 
-        new RequestImpl(sr, encoding).getParameterNames();
+        new RequestImpl(sr, encoding, null).getParameterNames();
 
         verify();
     }
@@ -98,7 +98,7 @@
 
         try
         {
-            new RequestImpl(sr, encoding).getParameterNames();
+            new RequestImpl(sr, encoding, null).getParameterNames();
             unreachable();
         }
         catch (RuntimeException ex)
@@ -118,7 +118,7 @@
 
         replay();
 
-        Request request = new RequestImpl(sr, CHARSET);
+        Request request = new RequestImpl(sr, CHARSET, null);
 
         assertEquals(request.isXHR(), expected);
 
@@ -128,7 +128,7 @@
     @DataProvider(name = "xhr_inputs")
     public Object[][] xhr_inputs()
     {
-        return new Object[][]{{null, false}, {"", false}, {"some other value", false},
+        return new Object[][] {{null, false}, {"", false}, {"some other value", false},
                 {"XMLHttpRequest", true}};
     }
 
@@ -144,7 +144,7 @@
 
         replay();
 
-        Request request = new RequestImpl(sr, CHARSET);
+        Request request = new RequestImpl(sr, CHARSET, null);
 
         assertEquals(request.getPath(), path);
 
@@ -165,7 +165,7 @@
 
         replay();
 
-        Request request = new RequestImpl(sr, CHARSET);
+        Request request = new RequestImpl(sr, CHARSET, null);
 
         assertEquals(request.getPath(), path);
 
@@ -183,7 +183,7 @@
 
         replay();
 
-        Request request = new RequestImpl(sr, CHARSET);
+        Request request = new RequestImpl(sr, CHARSET, null);
 
         assertEquals(request.getPath(), "/");
 



Mime
View raw message