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 2006/10/19 07:20:03 UTC

svn commit: r465496 [1/2] - in /tapestry/tapestry5/tapestry-core/trunk/src: main/aspect/org/apache/tapestry/internal/aspects/ main/java/org/apache/tapestry/ main/java/org/apache/tapestry/annotations/ main/java/org/apache/tapestry/corelib/components/ ma...

Author: hlship
Date: Wed Oct 18 22:20:00 2006
New Revision: 465496

URL: http://svn.apache.org/viewvc?view=rev&rev=465496
Log:
Add support for basic session persistence, and sset the default for action requests to be a redirect to the containing page.

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Persist.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistWorker.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldBundleImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldChangeImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldManagerImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/SessionPersistentFieldStrategy.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebSessionImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldBundle.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldChange.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldManager.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldStrategy.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/WebSession.java
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/event.apt
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/persist.apt
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/screencast.apt
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PersistentFieldBundleImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PersistentFieldManagerImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/SessionPersistentFieldStrategyTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/WebRequestImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/WebSessionImplTest.java
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/aspect/org/apache/tapestry/internal/aspects/InternalConcurrence.aj
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Link.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TapestryConstants.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Retain.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/ActionLink.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResources.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCUtilities.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/ModelMessages.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassTransformerImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentEventDispatcher.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactory.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebRequestImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebResponseImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/util/InternalUtils.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ObjectProvider.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/PageLifecycleListener.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TransformConstants.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/WebRequest.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/WebResponse.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/BaseTestCase.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/model/ModelStrings.properties
    tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/internal/services/ServicesStrings.properties
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/component-classes.apt
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/infrastructure.apt
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/reload.apt
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/guide/templates.apt
    tapestry/tapestry5/tapestry-core/trunk/src/site/apt/index.apt
    tapestry/tapestry5/tapestry-core/trunk/src/site/site.xml
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/integration/app1/pages/ActionPage.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/bindings/DefaultComponentLifecyle.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/IOCUtilitiesTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/model/MutableComponentModelImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/ComponentEventImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/LinkFactoryImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/OnEventWorkerTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PageLoaderImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/structure/PageImplTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/pages/EventHandlerTarget.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/transform/pages/TargetObjectSubclass.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/util/InternalUtilsTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/StaticModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/util/StrategyRegistryTest.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/resources/log4j.properties

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/aspect/org/apache/tapestry/internal/aspects/InternalConcurrence.aj
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/aspect/org/apache/tapestry/internal/aspects/InternalConcurrence.aj?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/aspect/org/apache/tapestry/internal/aspects/InternalConcurrence.aj (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/aspect/org/apache/tapestry/internal/aspects/InternalConcurrence.aj Wed Oct 18 22:20:00 2006
@@ -4,7 +4,7 @@
 // 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
+//     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,

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Link.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Link.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Link.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/Link.java Wed Oct 18 22:20:00 2006
@@ -25,8 +25,6 @@
  * <p>
  * Query parameter values are kept separate from the path portion to support encoding those values
  * into hidden form fields (where appropriate).
- * 
- * 
  */
 public interface Link
 {
@@ -46,6 +44,9 @@
 
     String getParameterValue(String name);
 
-    /** Returns the link as a URI. The URI includes any query parameters. */
+    /** Returns the link as a normal URI. The URI includes any query parameters. */
     String toURI();
+
+    /** Returns the link as a redirect URI. The URI includes any query parameters. */
+    String toRedirectURI();
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TapestryConstants.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TapestryConstants.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TapestryConstants.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/TapestryConstants.java Wed Oct 18 22:20:00 2006
@@ -25,5 +25,5 @@
 public final class TapestryConstants
 {
     /** Default client event name, used in most situations. */
-    public static final String DEFAULT_ACTION = "action";
+    public static final String DEFAULT_EVENT = "action";
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Persist.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Persist.java?view=auto&rev=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Persist.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Persist.java Wed Oct 18 22:20:00 2006
@@ -0,0 +1,41 @@
+// Copyright 2006 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.tapestry.annotations;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.apache.tapestry.services.WebSession;
+
+/**
+ * Identifies a field as persistent, meaning its value persists from one request to the next.
+ * Different strategies exist for how this is accomplished, the most common being the default,
+ * "session", which stores the field's value in the {@link WebSession}.
+ */
+@Target(FIELD)
+@Documented
+@Retention(RUNTIME)
+public @interface Persist {
+
+    /**
+     * The strategy used to persist the value; the default is "session", meaning indefintely inside
+     * the {@link WebSession}.
+     */
+    String value() default "session";
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Retain.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Retain.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Retain.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/annotations/Retain.java Wed Oct 18 22:20:00 2006
@@ -26,8 +26,13 @@
  * This is most often associated with fields that are <em>lazily loaded</em>. By marking such
  * fields with the Retain annotation, the fields will <em>not</em> be discarded at the end of the
  * request.
- * 
- * 
+ * <p>
+ * This is quite different from {@link Persist}, because the value that's allowed to be retained is
+ * not stored persistently; it is simply not cleared out. A subsequent request, even from the same
+ * user, may be processed by a different instance of the page where the value is still null.
+ * <p>
+ * This annotation should only be used with lazily-evaluated objects that contain no client-specific
+ * information.
  */
 @Target(FIELD)
 @Retention(RUNTIME)

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/ActionLink.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/ActionLink.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/ActionLink.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/corelib/components/ActionLink.java Wed Oct 18 22:20:00 2006
@@ -14,7 +14,7 @@
 
 package org.apache.tapestry.corelib.components;
 
-import static org.apache.tapestry.TapestryConstants.DEFAULT_ACTION;
+import static org.apache.tapestry.TapestryConstants.DEFAULT_EVENT;
 
 import java.util.List;
 
@@ -51,7 +51,7 @@
     {
         Object[] contextArray = _context == null ? new Object[0] : _context.toArray();
 
-        Link link = _resources.createActionLink(DEFAULT_ACTION, false, contextArray);
+        Link link = _resources.createActionLink(DEFAULT_EVENT, false, contextArray);
 
         writer.element("a", "href", link.toURI());
         

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResources.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResources.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResources.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/InternalComponentResources.java Wed Oct 18 22:20:00 2006
@@ -15,13 +15,13 @@
 package org.apache.tapestry.internal;
 
 import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.internal.structure.Page;
+import org.apache.tapestry.services.PersistentFieldManager;
 
 /**
  * An extension of {@link org.apache.tapestry.ComponentResources} that represents additional methods
  * that are private to the framework and not exposed in any public APIs. Ideally, there will not be
  * any need for this interface (we'll see as we go).
- * 
- * 
  */
 public interface InternalComponentResources extends ComponentResources
 {
@@ -66,5 +66,25 @@
      * @see org.apache.tapestry.runtime.PageLifecycleListener#containingPageDidLoad()
      */
     boolean isLoaded();
+
+    /**
+     * Posts a change to a persistent field. If the component is still loading, then this change is
+     * ignored. Otherwise, it is propogated, via the
+     * {@link Page#postFieldChange(org.apache.tapestry.internal.structure.ComponentPageElement, String, Object) page}
+     * to the {@link PersistentFieldManager}.
+     */
+    void postFieldChange(String fieldName, Object newValue);
+
+    /** Checks to see if there is a value stored for the indicated field. */
+    boolean hasFieldChange(String fieldName);
+
+    /**
+     * Get the current persisted value of the field.
+     * 
+     * @param fieldName
+     *            the name of the field to access
+     * @return the value stored for the field, or null if no value is currently stored
+     */
+    Object getFieldChange(String fieldName);
 
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCUtilities.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCUtilities.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCUtilities.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCUtilities.java Wed Oct 18 22:20:00 2006
@@ -21,7 +21,6 @@
 import java.lang.reflect.Modifier;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Enumeration;
 import java.util.List;
 import java.util.Map;
 
@@ -35,8 +34,6 @@
 
 /**
  * Contains static methods used within this package.
- * 
- * 
  */
 @Utility
 public final class IOCUtilities
@@ -178,25 +175,6 @@
     }
 
     /**
-     * Converts an enumeration (of Strings) into a sorted list of Strings.
-     */
-    public static List<String> toList(Enumeration e)
-    {
-        List<String> result = newList();
-
-        while (e.hasMoreElements())
-        {
-            String name = (String) e.nextElement();
-
-            result.add(name);
-        }
-
-        Collections.sort(result);
-
-        return result;
-    }
-
-    /**
      * Invoked by a proxy when the registry has shutdown.
      * 
      * @throws IllegalStateException
@@ -240,7 +218,7 @@
      * to strings.
      * 
      * @param map
-     *            the map to extract keys from
+     *            the map to extract keys from (may be null)
      * @return the sorted keys, or the empty set if map is null
      */
     @SuppressNullCheck

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/ModelMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/ModelMessages.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/ModelMessages.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/ModelMessages.java Wed Oct 18 22:20:00 2006
@@ -45,4 +45,9 @@
     {
         return MESSAGES.format("duplicate-component-id", id, componentClassName);
     }
+
+    static String missingPersistentField(String fieldName)
+    {
+        return MESSAGES.format("missing-persistent-field", fieldName);
+    }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/model/MutableComponentModelImpl.java Wed Oct 18 22:20:00 2006
@@ -23,7 +23,9 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.Resource;
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
 import org.apache.tapestry.internal.ioc.IOCUtilities;
+import org.apache.tapestry.model.ComponentModel;
 import org.apache.tapestry.model.EmbeddedComponentModel;
 import org.apache.tapestry.model.MutableComponentModel;
 import org.apache.tapestry.model.MutableEmbeddedComponentModel;
@@ -32,11 +34,11 @@
 
 /**
  * Internal implementation of {@link org.apache.tapestry.model.MutableComponentModel}.
- * 
- * 
  */
 public final class MutableComponentModelImpl implements MutableComponentModel
 {
+    private final ComponentModel _parentModel;
+
     private final Resource _baseResource;
 
     private final String _componentClassName;
@@ -47,11 +49,17 @@
 
     private Map<String, EmbeddedComponentModel> _embeddedComponents;
 
-    public MutableComponentModelImpl(String componentClassName, Log log, Resource baseResource)
+    /** Maps from field name to strategy. */
+    private Map<String, String> _persistentFields;
+
+    @SuppressNullCheck
+    public MutableComponentModelImpl(String componentClassName, Log log, Resource baseResource,
+            ComponentModel parentModel)
     {
         _componentClassName = componentClassName;
         _log = log;
         _baseResource = baseResource;
+        _parentModel = parentModel;
     }
 
     @Override
@@ -96,12 +104,12 @@
 
     public ParameterModel getParameterModel(String parameterName)
     {
-        // TODO: Parameters defined in base model
+        ParameterModel result = IOCUtilities.get(_parameters, parameterName);
 
-        if (_parameters == null)
-            return null;
+        if (result == null && _parentModel != null)
+            result = _parentModel.getParameterModel(parameterName);
 
-        return _parameters.get(parameterName);
+        return result;
     }
 
     public List<String> getParameterNames()
@@ -113,6 +121,9 @@
         if (_parameters != null)
             names.addAll(_parameters.keySet());
 
+        if (_parentModel != null)
+            names.addAll(_parentModel.getParameterNames());
+
         Collections.sort(names);
 
         return names;
@@ -140,16 +151,65 @@
 
     public List<String> getEmbeddedComponentIds()
     {
-        // TODO: Parent compent model?
+        List<String> result = newList();
+
+        if (_embeddedComponents != null)
+            result.addAll(_embeddedComponents.keySet());
+
+        if (_parentModel != null)
+            result.addAll(_parentModel.getEmbeddedComponentIds());
+
+        Collections.sort(result);
 
-        return IOCUtilities.sortedKeys(_embeddedComponents);
+        return result;
     }
 
     public EmbeddedComponentModel getEmbeddedComponentModel(String componentId)
     {
-        // TODO: Parent compent model?
+        EmbeddedComponentModel result = IOCUtilities.get(_embeddedComponents, componentId);
+
+        if (result == null && _parentModel != null)
+            result = _parentModel.getEmbeddedComponentModel(componentId);
+
+        return result;
+    }
+
+    public String getFieldPersistenceStrategy(String fieldName)
+    {
+        String result = IOCUtilities.get(_persistentFields, fieldName);
+
+        if (result == null && _parentModel != null)
+            result = _parentModel.getFieldPersistenceStrategy(fieldName);
+
+        if (result == null)
+            throw new IllegalArgumentException(ModelMessages.missingPersistentField(fieldName));
+
+        return result;
+    }
+
+    public List<String> getPersistentFieldNames()
+    {
+        List<String> result = newList();
+
+        if (_persistentFields != null)
+            result.addAll(_persistentFields.keySet());
+
+        if (_parentModel != null)
+            result.addAll(_parentModel.getPersistentFieldNames());
+
+        Collections.sort(result);
+
+        return result;
+    }
+
+    public void setFieldPersistenceStrategy(String fieldName, String strategy)
+    {
+        // TODO: Check for name collision here, or in parent
+
+        if (_persistentFields == null)
+            _persistentFields = newMap();
 
-        return IOCUtilities.get(_embeddedComponents, componentId);
+        _persistentFields.put(fieldName, strategy);
     }
 
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassTransformerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassTransformerImpl.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassTransformerImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentClassTransformerImpl.java Wed Oct 18 22:20:00 2006
@@ -115,7 +115,7 @@
 
         Resource baseResource = new ClasspathResource(classname.replace(".", "/") + ".class");
 
-        MutableComponentModel model = new MutableComponentModelImpl(classname, log, baseResource);
+        MutableComponentModel model = new MutableComponentModelImpl(classname, log, baseResource, null);
 
         _workerChain.transform(transformation, model);
 

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentEventDispatcher.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentEventDispatcher.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentEventDispatcher.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ComponentEventDispatcher.java Wed Oct 18 22:20:00 2006
@@ -17,6 +17,7 @@
 import java.io.IOException;
 
 import org.apache.tapestry.ComponentEventHandler;
+import org.apache.tapestry.Link;
 import org.apache.tapestry.internal.structure.ComponentPageElement;
 import org.apache.tapestry.internal.structure.Page;
 import org.apache.tapestry.services.Dispatcher;
@@ -26,19 +27,18 @@
 /**
  * Processes component action events sent as requests from the client. Action events include an
  * event type, identify a page and a component, and may provide additional context strings.
- * 
- * 
  */
 public class ComponentEventDispatcher implements Dispatcher
 {
-    private final PageResponseRenderer _renderer;
 
     private final RequestPageCache _cache;
 
-    public ComponentEventDispatcher(final PageResponseRenderer renderer, final RequestPageCache cache)
+    private final LinkFactory _linkFactory;
+
+    public ComponentEventDispatcher(final RequestPageCache cache, LinkFactory linkFactory)
     {
-        _renderer = renderer;
         _cache = cache;
+        _linkFactory = linkFactory;
     }
 
     public boolean dispatch(WebRequest request, WebResponse response) throws IOException
@@ -82,12 +82,11 @@
 
         element.triggerEvent(eventType, context, handler);
 
-        // TODO: maybe the return value from triggerEvent should be true if any handler was found,
-        // false if no handler was found. The latter feels like an error condition.
+        Link link = _linkFactory.createPageLink(page);
 
-        // Once the pieces fall into place, we won't just blindly re-render the page.
+        String URL = link.toRedirectURI();
 
-        _renderer.renderPageResponse(page, response);
+        response.sendRedirect(URL);
 
         return true;
     }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/InternalModule.java Wed Oct 18 22:20:00 2006
@@ -52,6 +52,8 @@
 import org.apache.tapestry.services.ComponentClassResolver;
 import org.apache.tapestry.services.ComponentClassTransformWorker;
 import org.apache.tapestry.services.MarkupWriterFactory;
+import org.apache.tapestry.services.PersistentFieldManager;
+import org.apache.tapestry.services.PersistentFieldStrategy;
 import org.apache.tapestry.services.RequestExceptionHandler;
 import org.apache.tapestry.services.WebContext;
 import org.apache.tapestry.services.WebRequest;
@@ -156,10 +158,11 @@
     public PageLoader buildPageLoader(@InjectService("PageElementFactory")
     PageElementFactory pageElementFactory, @InjectService("tapestry.BindingSource")
     BindingSource bindingSource, @InjectService("LinkFactory")
-    LinkFactory linkFactory)
+    LinkFactory linkFactory, @Inject("infrastructure:persistentFieldManager")
+    PersistentFieldManager persistentFieldManager)
     {
         PageLoaderImpl service = new PageLoaderImpl(_componentTemplateSource, pageElementFactory,
-                bindingSource, linkFactory);
+                bindingSource, linkFactory, persistentFieldManager);
 
         // Recieve invalidations when the class loader is discarded (due to a component class
         // change).
@@ -426,7 +429,7 @@
             }
         };
 
-        // Do be honest, order probably doesn't matter.
+        // To be honest, order probably doesn't matter.
 
         configuration.add("Keyword", keywordFactory);
         configuration.add("This", thisFactory);
@@ -444,9 +447,18 @@
     }
 
     /** Service used to create links for components and pages. */
-
     public LinkFactory buildLinkFactory()
     {
         return new LinkFactoryImpl(_request, _response, _componentClassResolver);
+    }
+
+    /**
+     * This exists as its own service just so that we can monitor it using logging.
+     * <p>
+     * TODO: Move this to TapestryModule?
+     */
+    public PersistentFieldStrategy buildSessionPersistentFieldStrategy()
+    {
+        return new SessionPersistentFieldStrategy(_request);
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactory.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactory.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactory.java Wed Oct 18 22:20:00 2006
@@ -16,11 +16,10 @@
 
 import org.apache.tapestry.Link;
 import org.apache.tapestry.internal.structure.ComponentPageElement;
+import org.apache.tapestry.internal.structure.Page;
 
 /**
  * A source for {@link Link} objects.
- * 
- * 
  */
 public interface LinkFactory
 {
@@ -41,4 +40,12 @@
      */
     Link createActionLink(ComponentPageElement component, String action, boolean forForm,
             Object... context);
+
+    /**
+     * Creates a link to a page. TODO: What about context? Additional query parameters?
+     * 
+     * @param page
+     * @return
+     */
+    Link createPageLink(Page page);
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkFactoryImpl.java Wed Oct 18 22:20:00 2006
@@ -17,6 +17,7 @@
 import org.apache.tapestry.Link;
 import org.apache.tapestry.internal.annotations.SuppressNullCheck;
 import org.apache.tapestry.internal.structure.ComponentPageElement;
+import org.apache.tapestry.internal.structure.Page;
 import org.apache.tapestry.services.ComponentClassResolver;
 import org.apache.tapestry.services.WebRequest;
 import org.apache.tapestry.services.WebResponse;
@@ -47,8 +48,9 @@
     {
         Defense.notBlank(action, "action");
 
-        String logicalPageName = _componentClassResolver.resolvePageClassNameToPageName(component
-                .getContainingPage().getName());
+        String pageName = component.getContainingPage().getName();
+
+        String logicalPageName = _componentClassResolver.resolvePageClassNameToPageName(pageName);
 
         StringBuilder builder = new StringBuilder();
 
@@ -71,6 +73,23 @@
 
         // TODO: Much more: query parameter for case where active page != component page.
         // Letting listeners add extra parameters.
+
+        return new LinkImpl(_response, builder.toString());
+    }
+
+    public Link createPageLink(Page page)
+    {
+        Defense.notNull(page, "page");
+
+        String pageName = page.getName();
+        String logicalPageName = _componentClassResolver.resolvePageClassNameToPageName(pageName);
+
+        StringBuilder builder = new StringBuilder();
+
+        builder.append(_request.getContextPath());
+        builder.append("/");
+        builder.append(logicalPageName);
+        builder.append(".html");
 
         return new LinkImpl(_response, builder.toString());
     }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/LinkImpl.java Wed Oct 18 22:20:00 2006
@@ -22,8 +22,6 @@
 
 /**
  * Starting implementation of {@link Link}. Currently does not support query parameters.
- * 
- * 
  */
 public class LinkImpl implements Link
 {
@@ -50,6 +48,11 @@
     public String toURI()
     {
         return _response.encodeURL(_path);
+    }
+
+    public String toRedirectURI()
+    {
+        return _response.encodeRedirectURL(_path);
     }
 
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PageLoaderImpl.java Wed Oct 18 22:20:00 2006
@@ -43,6 +43,7 @@
 import org.apache.tapestry.model.ComponentModel;
 import org.apache.tapestry.model.EmbeddedComponentModel;
 import org.apache.tapestry.services.BindingSource;
+import org.apache.tapestry.services.PersistentFieldManager;
 import org.apache.tapestry.util.CollectionFactory;
 import org.apache.tapestry.util.IdAllocator;
 
@@ -60,6 +61,8 @@
 
     private final LinkFactory _linkFactory;
 
+    private final PersistentFieldManager _persistentFieldManager;
+
     private Page _page;
 
     private Locale _locale;
@@ -67,12 +70,13 @@
     @SuppressNullCheck
     public PageLoaderImpl(ComponentTemplateSource templateSource,
             PageElementFactory pageElementFactory, BindingSource bindingSource,
-            LinkFactory linkFactory)
+            LinkFactory linkFactory, PersistentFieldManager persistentFieldManager)
     {
         _templateSource = templateSource;
         _pageElementFactory = pageElementFactory;
         _bindingSource = bindingSource;
         _linkFactory = linkFactory;
+        _persistentFieldManager = persistentFieldManager;
     }
 
     /**
@@ -96,7 +100,7 @@
         {
             _locale = locale;
 
-            _page = new PageImpl(pageClassName, _locale, _linkFactory);
+            _page = new PageImpl(pageClassName, _locale, _linkFactory, _persistentFieldManager);
 
             loadRootComponent(pageClassName);
 

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistWorker.java?view=auto&rev=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistWorker.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistWorker.java Wed Oct 18 22:20:00 2006
@@ -0,0 +1,134 @@
+// Copyright 2006 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.tapestry.internal.services;
+
+import static java.lang.String.format;
+
+import java.lang.reflect.Modifier;
+
+import org.apache.tapestry.annotations.Persist;
+import org.apache.tapestry.internal.util.InternalUtils;
+import org.apache.tapestry.model.MutableComponentModel;
+import org.apache.tapestry.services.ClassTransformation;
+import org.apache.tapestry.services.ComponentClassTransformWorker;
+import org.apache.tapestry.services.MethodSignature;
+import org.apache.tapestry.services.PersistentFieldBundle;
+import org.apache.tapestry.services.TransformConstants;
+import org.apache.tapestry.services.TransformUtils;
+import org.apache.tapestry.util.BodyBuilder;
+
+/**
+ * Converts fields with the {@link Persist} annotation into persistent fields.
+ */
+public class PersistWorker implements ComponentClassTransformWorker
+{
+
+    public void transform(ClassTransformation transformation, MutableComponentModel model)
+    {
+        for (String name : transformation.findFieldsWithAnnotation(Persist.class))
+        {
+            makeFieldPersistent(name, transformation, model);
+        }
+    }
+
+    /**
+     * Making a field persistent:
+     * <ul>
+     * <li>Need a secondary default field that stores the initial value</li>
+     * <li>Store the active value into the default field when the page finishes loading</li>
+     * <li>Roll the active value back to the default when the page detaches</li>
+     * <ii>On changes to the active field, post the change via the InternalComponentResources</li>
+     * <li>When the page attaches, pull the persisted value for the field out of the
+     * {@link PersistentFieldBundle}</li>
+     * </ul>
+     * 
+     * @param fieldName
+     * @param transformation
+     * @param model
+     */
+    private void makeFieldPersistent(String fieldName, ClassTransformation transformation,
+            MutableComponentModel model)
+    {
+        String fieldType = transformation.getFieldType(fieldName);
+        Persist annotation = transformation.getFieldAnnotation(fieldName, Persist.class);
+
+        // Record the type of persistence, until needed later.
+
+        model.setFieldPersistenceStrategy(fieldName, annotation.value());
+
+        String defaultFieldName = transformation.addField(Modifier.PRIVATE, fieldType, fieldName
+                + "_default");
+
+        transformation.extendMethod(TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE, format(
+                "%s = %s;",
+                defaultFieldName,
+                fieldName));
+
+        transformation.extendMethod(
+                TransformConstants.CONTAINING_PAGE_DID_DETACH_SIGNATURE,
+                format("%s = %s;", fieldName, defaultFieldName));
+
+        String resourcesFieldName = transformation.getResourcesFieldName();
+
+        String writeMethodName = transformation.newMemberName("_write_"
+                + InternalUtils.stripMemberPrefix(fieldName));
+
+        BodyBuilder builder = new BodyBuilder();
+
+        builder.begin();
+        builder.addln("%s.postFieldChange(\"%s\", ($w) $1);", resourcesFieldName, fieldName);
+        builder.addln("%s = $1;", fieldName);
+        builder.end();
+
+        transformation.addMethod(new MethodSignature(Modifier.PRIVATE, "void", writeMethodName,
+                new String[]
+                { fieldType }, null), builder.toString());
+
+        transformation.replaceWriteAccess(fieldName, writeMethodName);
+
+        builder.clear();
+        builder.begin();
+
+        // Check to see if there's a recorded change for this component, this field.
+
+        builder.addln("if (%s.hasFieldChange(\"%s\"))", resourcesFieldName, fieldName);
+
+        String wrapperType = TransformUtils.getWrapperTypeName(fieldType);
+
+        // Get the value, cast it to the correct type (or wrapper type)
+        builder.add(
+                "  %s = ((%s) %s.getFieldChange(\"%1$s\"))",
+                fieldName,
+                wrapperType,
+                resourcesFieldName);
+
+        // For primtive types, add in the method call to unwrap the wrapper type to a primitive type
+
+        String unwrapMethodName = TransformUtils.getUnwrapperMethodName(fieldType);
+
+        if (unwrapMethodName == null)
+            builder.addln(";");
+        else
+            builder.addln(".%s();", unwrapMethodName);
+
+        builder.end();
+
+        transformation.extendMethod(
+                TransformConstants.CONTAINING_PAGE_DID_ATTACH_SIGNATURE,
+                builder.toString());
+
+        transformation.claimField(fieldName, annotation);
+    }
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldBundleImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldBundleImpl.java?view=auto&rev=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldBundleImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldBundleImpl.java Wed Oct 18 22:20:00 2006
@@ -0,0 +1,69 @@
+// Copyright 2006 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.tapestry.internal.services;
+
+import static org.apache.tapestry.util.CollectionFactory.newMap;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
+import org.apache.tapestry.services.PersistentFieldBundle;
+import org.apache.tapestry.services.PersistentFieldChange;
+
+@SuppressNullCheck
+public class PersistentFieldBundleImpl implements PersistentFieldBundle
+{
+    /**
+     * Keyed on componentId + fieldName.
+     */
+    private final Map<String, Object> _values = newMap();
+
+    public PersistentFieldBundleImpl(Collection<PersistentFieldChange> changes)
+    {
+        for (PersistentFieldChange change : changes)
+        {
+            String key = buildKey(change.getComponentId(), change.getFieldName());
+
+            _values.put(key, change.getValue());
+        }
+    }
+
+    private String buildKey(String componentId, String fieldName)
+    {
+        StringBuilder builder = new StringBuilder();
+        if (componentId != null)
+            builder.append(componentId);
+        builder.append(':');
+        builder.append(fieldName);
+
+        return builder.toString();
+    }
+
+    public boolean containsValue(String componentId, String fieldName)
+    {
+        String key = buildKey(componentId, fieldName);
+
+        return _values.containsKey(key);
+    }
+
+    public Object getValue(String componentId, String fieldName)
+    {
+        String key = buildKey(componentId, fieldName);
+
+        return _values.get(key);
+    }
+
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldChangeImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldChangeImpl.java?view=auto&rev=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldChangeImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldChangeImpl.java Wed Oct 18 22:20:00 2006
@@ -0,0 +1,55 @@
+// Copyright 2006 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.tapestry.internal.services;
+
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
+import org.apache.tapestry.services.PersistentFieldChange;
+import org.apache.tapestry.util.Defense;
+
+@SuppressNullCheck
+public class PersistentFieldChangeImpl implements PersistentFieldChange
+{
+    private final String _componentId;
+
+    private final String _fieldName;
+
+    private final Object _value;
+
+    public PersistentFieldChangeImpl(final String componentId, final String fieldName,
+            final Object value)
+    {
+        Defense.notNull(componentId, "componentId");
+        Defense.notBlank(fieldName, "fieldName");
+
+        _componentId = componentId;
+        _fieldName = fieldName;
+        _value = value;
+    }
+
+    public String getComponentId()
+    {
+        return _componentId;
+    }
+
+    public String getFieldName()
+    {
+        return _fieldName;
+    }
+
+    public Object getValue()
+    {
+        return _value;
+    }
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldManagerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldManagerImpl.java?view=auto&rev=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldManagerImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldManagerImpl.java Wed Oct 18 22:20:00 2006
@@ -0,0 +1,72 @@
+// Copyright 2006 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.tapestry.internal.services;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
+import org.apache.tapestry.internal.ioc.IOCUtilities;
+import org.apache.tapestry.services.PersistentFieldBundle;
+import org.apache.tapestry.services.PersistentFieldChange;
+import org.apache.tapestry.services.PersistentFieldManager;
+import org.apache.tapestry.services.PersistentFieldStrategy;
+import org.apache.tapestry.util.CollectionFactory;
+
+@SuppressNullCheck
+public class PersistentFieldManagerImpl implements PersistentFieldManager
+{
+    private final Map<String, PersistentFieldStrategy> _strategies;
+
+    public PersistentFieldManagerImpl(final Map<String, PersistentFieldStrategy> strategies)
+    {
+        _strategies = strategies;
+    }
+
+    private PersistentFieldStrategy getStrategy(String strategyName)
+    {
+        PersistentFieldStrategy result = _strategies.get(strategyName);
+
+        if (result == null)
+        {
+            String catalog = IOCUtilities.joinSorted(_strategies.keySet());
+
+            throw new RuntimeException(ServicesMessages.unknownPersistentFieldStrategy(
+                    strategyName,
+                    catalog));
+        }
+
+        return result;
+    }
+
+    public PersistentFieldBundle gatherChanges(String pageName)
+    {
+        Collection<PersistentFieldChange> allChanges = CollectionFactory.newList();
+
+        for (PersistentFieldStrategy strategy : _strategies.values())
+        {
+            allChanges.addAll(strategy.gatherFieldChanges(pageName));
+        }
+
+        return new PersistentFieldBundleImpl(allChanges);
+    }
+
+    public void postChange(String pageName, String componentId, String fieldName, String strategy,
+            Object newValue)
+    {
+        getStrategy(strategy).postChange(pageName, componentId, fieldName, newValue);
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java Wed Oct 18 22:20:00 2006
@@ -208,4 +208,9 @@
     {
         return MESSAGES.format("component-event-is-aborted", methodDescription);
     }
+
+    static String unknownPersistentFieldStrategy(String stategyName, String catalog)
+    {
+        return MESSAGES.format("unknown-persistent-field-strategy", stategyName, catalog);
+    }
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/SessionPersistentFieldStrategy.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/SessionPersistentFieldStrategy.java?view=auto&rev=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/SessionPersistentFieldStrategy.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/SessionPersistentFieldStrategy.java Wed Oct 18 22:20:00 2006
@@ -0,0 +1,105 @@
+// Copyright 2006 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.tapestry.internal.services;
+
+import static org.apache.tapestry.util.CollectionFactory.newList;
+import static org.apache.tapestry.util.Defense.notBlank;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
+import org.apache.tapestry.services.PersistentFieldChange;
+import org.apache.tapestry.services.PersistentFieldStrategy;
+import org.apache.tapestry.services.WebRequest;
+import org.apache.tapestry.services.WebSession;
+
+/**
+ * A strategy for storing persistent page properties into the {@link WebSession session}.
+ * <p>
+ * Builds attribute names as:
+ * <code>state:<em>page-name</em>:<em>component-id</em>:<em>field-name</em></code>
+ */
+@SuppressNullCheck
+public class SessionPersistentFieldStrategy implements PersistentFieldStrategy
+{
+    /**
+     * Prefix used to identify keys stored in the session that are being used to store persistent
+     * field data.
+     */
+    public static final String PREFIX = "state:";
+
+    private final WebRequest _request;
+
+    public SessionPersistentFieldStrategy(final WebRequest request)
+    {
+        _request = request;
+    }
+
+    public Collection<PersistentFieldChange> gatherFieldChanges(String pageName)
+    {
+        WebSession session = _request.getSession(false);
+
+        if (session == null)
+            return Collections.emptyList();
+
+        List<PersistentFieldChange> result = newList();
+
+        String fullPrefix = PREFIX + pageName + ":";
+
+        for (String name : session.getAttributeNames(fullPrefix))
+        {
+            PersistentFieldChange change = buildChange(name, session.getAttribute(name));
+
+            result.add(change);
+        }
+
+        return result;
+    }
+
+    private PersistentFieldChange buildChange(String name, Object attribute)
+    {
+
+        String[] chunks = name.split(":");
+
+        // Will be empty string for the root component
+        String componentId = chunks[2];
+        String fieldName = chunks[3];
+
+        return new PersistentFieldChangeImpl(componentId, fieldName, attribute);
+    }
+
+    public void postChange(String pageName, String componentId, String fieldName, Object newValue)
+    {
+        notBlank(pageName, "pageName");
+        notBlank(fieldName, "fieldName");
+
+        StringBuilder builder = new StringBuilder(PREFIX);
+        builder.append(pageName);
+        builder.append(':');
+
+        if (componentId != null)
+            builder.append(componentId);
+
+        builder.append(':');
+        builder.append(fieldName);
+
+        WebSession session = _request.getSession(true);
+
+        session.setAttribute(builder.toString(), newValue);
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebRequestImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebRequestImpl.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebRequestImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebRequestImpl.java Wed Oct 18 22:20:00 2006
@@ -17,15 +17,15 @@
 import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
 
-import org.apache.tapestry.internal.ioc.IOCUtilities;
+import org.apache.tapestry.internal.util.InternalUtils;
 import org.apache.tapestry.services.WebRequest;
+import org.apache.tapestry.services.WebSession;
 
 /**
  * Basic implementation of {@link org.apache.tapestry.services.WebRequest} that wraps around an
  * {@link javax.servlet.http.HttpServletRequest}.
- * 
- * 
  */
 public class WebRequestImpl implements WebRequest
 {
@@ -38,7 +38,7 @@
 
     public List<String> getParameterNames()
     {
-        return IOCUtilities.toList(_request.getParameterNames());
+        return InternalUtils.toList(_request.getParameterNames());
     }
 
     public String getParameter(String name)
@@ -59,6 +59,13 @@
     public String getContextPath()
     {
         return _request.getContextPath();
+    }
+
+    public WebSession getSession(boolean create)
+    {
+        HttpSession session = _request.getSession(create);
+
+        return session == null ? null : new WebSessionImpl(session);
     }
 
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebResponseImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebResponseImpl.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebResponseImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebResponseImpl.java Wed Oct 18 22:20:00 2006
@@ -42,4 +42,15 @@
     {
         return _response.encodeURL(URL);
     }
+
+    public String encodeRedirectURL(String URL)
+    {
+        return _response.encodeRedirectURL(URL);
+    }
+
+    public void sendRedirect(String URL) throws IOException
+    {
+        _response.sendRedirect(URL);
+    }
+
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebSessionImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebSessionImpl.java?view=auto&rev=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebSessionImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/WebSessionImpl.java Wed Oct 18 22:20:00 2006
@@ -0,0 +1,73 @@
+// Copyright 2006 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.tapestry.internal.services;
+
+import static org.apache.tapestry.util.CollectionFactory.newList;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+
+import javax.servlet.http.HttpSession;
+
+import org.apache.tapestry.internal.util.InternalUtils;
+import org.apache.tapestry.services.WebSession;
+
+/**
+ * A thin wrapper around {@link HttpSession}.
+ */
+public class WebSessionImpl implements WebSession
+{
+    private final HttpSession _session;
+
+    public WebSessionImpl(final HttpSession session)
+    {
+        _session = session;
+    }
+
+    public Object getAttribute(String name)
+    {
+        return _session.getAttribute(name);
+    }
+
+    public List<String> getAttributeNames()
+    {
+        return InternalUtils.toList(_session.getAttributeNames());
+    }
+
+    public void setAttribute(String name, Object value)
+    {
+        _session.setAttribute(name, value);
+    }
+
+    public List<String> getAttributeNames(String prefix)
+    {
+        List<String> result = newList();
+
+        Enumeration e = _session.getAttributeNames();
+        while (e.hasMoreElements())
+        {
+            String name = (String) e.nextElement();
+
+            if (name.startsWith(prefix))
+                result.add(name);
+        }
+
+        Collections.sort(result);
+
+        return result;
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/ComponentPageElementImpl.java Wed Oct 18 22:20:00 2006
@@ -55,8 +55,6 @@
  * <p>
  * TODO: Possibly break this into two classes: the page element and the internal component resources
  * implementation. For the moment, it works as a single class.
- * 
- * 
  */
 public class ComponentPageElementImpl extends BaseLocatable implements ComponentPageElement,
         PageLifecycleListener
@@ -562,7 +560,7 @@
 
             if (event.isAborted())
                 return true;
-            
+
             component = component.getContainer();
         }
 
@@ -579,6 +577,25 @@
             throw new IllegalArgumentException(StructureMessages.noSuchComponent(this, embeddedId));
 
         return embeddedElement;
+    }
+
+    public void postFieldChange(String fieldName, Object newValue)
+    {
+        // While loading the page (i.e., when setting field defaults), ignore these
+        // changes.
+
+        if (_loaded)
+            _page.postFieldChange(this, fieldName, newValue);
+    }
+
+    public Object getFieldChange(String fieldName)
+    {
+        return _page.getFieldChange(this, fieldName);
+    }
+
+    public boolean hasFieldChange(String fieldName)
+    {
+        return getFieldChange(fieldName) != null;
     }
 
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/Page.java Wed Oct 18 22:20:00 2006
@@ -28,8 +28,9 @@
  * One of the most important aspects of a Page is that it <em>does not</em> have to be coded in a
  * thread-safe manner. Pages are always accessed within a single thread, associated with a single
  * incoming request.
- * 
- * 
+ * <p>
+ * The Page object is never visible to end-user code. The page also exists to provide a kind of
+ * service to components embedded (directly or indirectly) within the page.
  */
 public interface Page
 {
@@ -109,4 +110,27 @@
      */
     Link createActionLink(ComponentPageElement element, String action, boolean forForm,
             Object... context);
+
+    /**
+     * Posts a change to a persistent field.
+     * 
+     * @param element
+     *            the component element containing the field
+     * @param fieldName
+     *            the name of the field
+     * @param newValue
+     *            the new value for the field
+     */
+    void postFieldChange(ComponentPageElement element, String fieldName, Object newValue);
+
+    /**
+     * Gets a change for a field within the component.
+     * 
+     * @param element
+     *            the element for which a persistent field value is required
+     * @param fieldName
+     *            the name of the persistent field
+     * @return the value, or null if no value is stored
+     */
+    Object getFieldChange(ComponentPageElement element, String fieldName);
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/structure/PageImpl.java Wed Oct 18 22:20:00 2006
@@ -24,6 +24,8 @@
 import org.apache.tapestry.internal.annotations.SuppressNullCheck;
 import org.apache.tapestry.internal.services.LinkFactory;
 import org.apache.tapestry.runtime.PageLifecycleListener;
+import org.apache.tapestry.services.PersistentFieldBundle;
+import org.apache.tapestry.services.PersistentFieldManager;
 
 /**  */
 @SuppressNullCheck
@@ -35,15 +37,25 @@
 
     private final LinkFactory _linkFactory;
 
+    private final PersistentFieldManager _persistentFieldManager;
+
     private ComponentPageElement _rootElement;
 
     private final List<PageLifecycleListener> _listeners = newList();
 
-    public PageImpl(String name, Locale locale, LinkFactory linkFactory)
+    /**
+     * Obtained from the {@link PersistentFieldManager} when first needed, discarded at the end of
+     * the request.
+     */
+    private PersistentFieldBundle _fieldBundle;
+
+    public PageImpl(String name, Locale locale, LinkFactory linkFactory,
+            PersistentFieldManager persistentFieldManager)
     {
         _name = name;
         _locale = locale;
         _linkFactory = linkFactory;
+        _persistentFieldManager = persistentFieldManager;
     }
 
     @Override
@@ -91,6 +103,8 @@
     {
         for (PageLifecycleListener listener : _listeners)
             listener.containingPageDidDetach();
+
+        _fieldBundle = null;
     }
 
     public void loaded()
@@ -114,6 +128,23 @@
             Object... context)
     {
         return _linkFactory.createActionLink(element, action, forForm, context);
+    }
+
+    public void postFieldChange(ComponentPageElement element, String fieldName, Object newValue)
+    {
+        String strategy = element.getComponentModel().getFieldPersistenceStrategy(fieldName);
+
+        String componentId = element.getNestedId();
+
+        _persistentFieldManager.postChange(_name, componentId, fieldName, strategy, newValue);
+    }
+
+    public Object getFieldChange(ComponentPageElement element, String fieldName)
+    {
+        if (_fieldBundle == null)
+            _fieldBundle = _persistentFieldManager.gatherChanges(_name);
+
+        return _fieldBundle.getValue(element.getNestedId(), fieldName);
     }
 
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/util/InternalUtils.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/util/InternalUtils.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/util/InternalUtils.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/util/InternalUtils.java Wed Oct 18 22:20:00 2006
@@ -14,7 +14,12 @@
 
 package org.apache.tapestry.internal.util;
 
+import static org.apache.tapestry.util.CollectionFactory.newList;
+
 import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
 
 import org.apache.tapestry.internal.annotations.SuppressNullCheck;
 import org.apache.tapestry.internal.annotations.Utility;
@@ -101,6 +106,25 @@
     public static String createMemberName(String memberName)
     {
         return NAME_PREFIX + stripMemberPrefix(memberName);
+    }
+
+    /**
+     * Converts an enumeration (of Strings) into a sorted list of Strings.
+     */
+    public static List<String> toList(Enumeration e)
+    {
+        List<String> result = newList();
+    
+        while (e.hasMoreElements())
+        {
+            String name = (String) e.nextElement();
+    
+            result.add(name);
+        }
+    
+        Collections.sort(result);
+    
+        return result;
     }
 
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ObjectProvider.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ObjectProvider.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ObjectProvider.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/ObjectProvider.java Wed Oct 18 22:20:00 2006
@@ -33,10 +33,8 @@
  * "ClassFactory" service.
  * <p>
  * ObjectProviders exist to provide abstractions on top of the raw IoC services. Examples to follow,
- * especially XXX which provides the ability to easily override services within a Tapestry
- * application.
- * 
- * 
+ * especially "infrastructure:" which provides the ability to easily override services within a
+ * Tapestry application.
  */
 public interface ObjectProvider
 {

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/ComponentModel.java Wed Oct 18 22:20:00 2006
@@ -18,13 +18,13 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.tapestry.Resource;
+import org.apache.tapestry.annotations.Persist;
 
 /**
  * Defines a component in terms of its capabilities, parameters, sub-components, etc. During
  * <em>runtime</em>, the component model is immutable. During <em>construction</em> time, when
  * the class is being transformed a loaded, the model is mutable.
  * 
- * 
  * @see
  */
 public interface ComponentModel
@@ -67,4 +67,21 @@
      * @return the embedded component model, or null if no component exists with that id
      */
     EmbeddedComponentModel getEmbeddedComponentModel(String componentId);
+
+    /**
+     * Returns a list of the names of all persistent fields (within this class, or any super-class).
+     * The names are sorted alphabetically.
+     * 
+     * @see Persist
+     */
+    List<String> getPersistentFieldNames();
+
+    /**
+     * Returns the persistent strategy associated with the field.
+     * 
+     * @param fieldName
+     * @return the corresponding strategy
+     * @throw IllegalArgumentException if the named field is not marked as persistent
+     */
+    String getFieldPersistenceStrategy(String fieldName);
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/model/MutableComponentModel.java Wed Oct 18 22:20:00 2006
@@ -17,8 +17,6 @@
 /**
  * Mutable version of {@link org.apache.tapestry.model.ComponentModel} used during the
  * transformation phase.
- * 
- * 
  */
 public interface MutableComponentModel extends ComponentModel
 {
@@ -47,4 +45,9 @@
      */
     MutableEmbeddedComponentModel addEmbeddedComponent(String id, String type,
             String componentClassName);
+
+    /**
+     * @see ComponentModel#getFieldPersistenceStrategy(String)
+     */
+    void setFieldPersistenceStrategy(String fieldName, String strategy);
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/PageLifecycleListener.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/PageLifecycleListener.java?view=diff&rev=465496&r1=465495&r2=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/PageLifecycleListener.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/runtime/PageLifecycleListener.java Wed Oct 18 22:20:00 2006
@@ -16,8 +16,6 @@
 
 /**
  * A set of methods that allow components to know about page-level operations.
- * 
- * 
  */
 public interface PageLifecycleListener
 {

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldBundle.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldBundle.java?view=auto&rev=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldBundle.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldBundle.java Wed Oct 18 22:20:00 2006
@@ -0,0 +1,45 @@
+// Copyright 2006 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.tapestry.services;
+
+/**
+ * Encapsulates persisted property information for an entire page.
+ */
+public interface PersistentFieldBundle
+{
+    /**
+     * Checks to see if a persistent value has been stored for the indicated component and field.
+     * TODO: This method can probably be removed; it doesn't look like its used (instead, we if
+     * check getValue() returns null).
+     * 
+     * @param componentId
+     *            the nested id of the component (within the page), may be null or blank for the
+     *            root component of the page
+     * @param fieldName
+     *            the name of the field whose value was persisted
+     * @return true if a change has been stored
+     */
+    boolean containsValue(String componentId, String fieldName);
+
+    /**
+     * @param componentId
+     *            the nested if of the component (within the page), may be null or blank for the
+     *            root component of the page
+     * @param fieldName
+     *            the name of the field whose value was persisted
+     * @return the persisted value, possibly null
+     */
+    Object getValue(String componentId, String fieldName);
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldChange.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldChange.java?view=auto&rev=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldChange.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldChange.java Wed Oct 18 22:20:00 2006
@@ -0,0 +1,31 @@
+// Copyright 2006 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.tapestry.services;
+
+/**
+ * Represents a previously stored change to a persistent field, within the context of a particular
+ * page of the application.
+ */
+public interface PersistentFieldChange
+{
+    /** Returns the nested id of the component, or the empty string for the page's root component. */
+    String getComponentId();
+
+    /** Returns the name of the field for which a change was recorded. */
+    String getFieldName();
+
+    /** Returns the new value for the field (which may be null). */
+    Object getValue();
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldManager.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldManager.java?view=auto&rev=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldManager.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldManager.java Wed Oct 18 22:20:00 2006
@@ -0,0 +1,49 @@
+// Copyright 2006 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.tapestry.services;
+
+/**
+ * Handle persistent property changes. Primarily, delegates to a number of
+ * {@link PersistentFieldStrategy} instances.
+ */
+public interface PersistentFieldManager
+{
+    /**
+     * Posts a change of a persistent property.
+     * 
+     * @param pageName
+     *            the name of the page containing the component
+     * @param componentId
+     *            the nested id path of the component (or null for the page's root component)
+     * @param fieldName
+     *            the name of the field whose persistent value has changed
+     * @param strategy
+     *            the name of the stategy used to persist the property value
+     * @param newValue
+     *            the new value for the field, possibly null
+     */
+    void postChange(String pageName, String componentId, String fieldName, String strategy,
+            Object newValue);
+
+    /**
+     * Locates all persistently stored changes to all properties within the page (for the current
+     * session and request) and gathers them together into a bundle.
+     * 
+     * @param pageName
+     *            the name of the page to gather changes for
+     * @return a bundle identifying all such changes
+     */
+    PersistentFieldBundle gatherChanges(String pageName);
+}

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldStrategy.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldStrategy.java?view=auto&rev=465496
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldStrategy.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/PersistentFieldStrategy.java Wed Oct 18 22:20:00 2006
@@ -0,0 +1,42 @@
+// Copyright 2006 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.tapestry.services;
+
+import java.util.Collection;
+
+public interface PersistentFieldStrategy
+{
+    /**
+     * Posts a change of a persistent property.
+     * 
+     * @param pageName
+     *            the name of the page containing the component
+     * @param componentId
+     *            the nested id path of the component (or null for the page's root component)
+     * @param fieldName
+     *            the name of the field whose persistent value has changed
+     * @param strategy
+     *            the name of the stategy used to persist the property value
+     * @param newValue
+     *            the new value for the field, possibly null
+     */
+    void postChange(String pageName, String componentId, String fieldName, Object newValue);
+
+    /**
+     * Finds all persistent changes previously stored for the named page (for the current active
+     * session or client).
+     */
+    Collection<PersistentFieldChange> gatherFieldChanges(String pageName);
+}