You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2010/01/06 18:36:44 UTC

svn commit: r896559 [1/2] - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry5/annotations/ main/java/org/apache/tapestry5/internal/ main/java/org/apache/tapestry5/internal/services/ main/java/org/apache/tapestry5/internal/s...

Author: hlship
Date: Wed Jan  6 17:35:59 2010
New Revision: 896559

URL: http://svn.apache.org/viewvc?rev=896559&view=rev
Log:
First pass at TAP5-948 support

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/PageReset.java   (with props)
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java   (with props)
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PageResetAnnotationWorker.java   (with props)
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/InitializeActivePageName.java   (with props)
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/PageResetDemo.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PageResetDemo.java   (with props)
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PageResetFailure.java   (with props)
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PageRenderRequestHandlerImplTest.java   (with props)
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventLinkEncoderImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageRenderRequestHandlerImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestGlobalsImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/SessionAttributeWorker.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentEventRequestParameters.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/PageRenderRequestParameters.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/RequestGlobals.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformMethodSignature.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/Index.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventDispatcherTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventLinkEncoderImplTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PageRenderDispatcherTest.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/urlrewriter/IntegrationTests.java

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/PageReset.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/PageReset.java?rev=896559&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/PageReset.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/PageReset.java Wed Jan  6 17:35:59 2010
@@ -0,0 +1,51 @@
+// Copyright 2010 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 static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static org.apache.tapestry5.ioc.annotations.AnnotationUseContext.COMPONENT;
+import static org.apache.tapestry5.ioc.annotations.AnnotationUseContext.MIXIN;
+import static org.apache.tapestry5.ioc.annotations.AnnotationUseContext.PAGE;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.apache.tapestry5.corelib.components.Grid;
+import org.apache.tapestry5.internal.transform.PageResetAnnotationWorker;
+import org.apache.tapestry5.ioc.annotations.UseWith;
+
+/**
+ * Marker annotation for a method that should be invoked when a page is reset. A page reset occurs
+ * when a page is linked to from another page. This is an opportunity to re-initialize aspects of a
+ * page when the user returns to a page after visiting other pages. A common example is to
+ * reset the active page of a {@link Grid} component.
+ * <p>
+ * Methods marked with this annotation are invoked <em>after</em> the page is sent the
+ * <code>activate</code> event. This is to allow the page to reset itself as appropriate for
+ * whatever persistent state was encoded in its page activation context.
+ * 
+ * @since 5.2.0
+ * @see PageResetAnnotationWorker
+ */
+@Target(METHOD)
+@Retention(RUNTIME)
+@Documented
+@UseWith(
+{ COMPONENT, MIXIN, PAGE })
+public @interface PageReset
+{
+}

Propchange: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/annotations/PageReset.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java?rev=896559&r1=896558&r2=896559&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalComponentResources.java Wed Jan  6 17:35:59 2010
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010 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.
@@ -17,18 +17,22 @@
 import org.apache.tapestry5.ComponentResources;
 import org.apache.tapestry5.internal.services.PersistentFieldManager;
 import org.apache.tapestry5.internal.structure.Page;
+import org.apache.tapestry5.internal.structure.PageResetListener;
 import org.apache.tapestry5.runtime.RenderQueue;
 
 /**
- * An extension of {@link org.apache.tapestry5.ComponentResources} that represents additional methods that are private
+ * An extension of {@link org.apache.tapestry5.ComponentResources} that represents additional
+ * methods that are private
  * to the framework and not exposed in any public APIs.
  */
-public interface InternalComponentResources extends ComponentResources, InternalComponentResourcesCommon
+public interface InternalComponentResources extends ComponentResources,
+        InternalComponentResourcesCommon
 {
     /**
      * Get the current persisted value of the field.
-     *
-     * @param fieldName the name of the field to access
+     * 
+     * @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);
@@ -39,8 +43,10 @@
     boolean hasFieldChange(String fieldName);
 
     /**
-     * Posts a change to a persistent field. If the component is still loading, then this change is ignored. Otherwise,
-     * it is propagated, via the {@link Page#persistFieldChange(org.apache.tapestry5.ComponentResources, String, Object)
+     * Posts a change to a persistent field. If the component is still loading, then this change is
+     * ignored. Otherwise,
+     * it is propagated, via the
+     * {@link Page#persistFieldChange(org.apache.tapestry5.ComponentResources, String, Object)
      * page} to the {@link PersistentFieldManager}.
      */
     void persistFieldChange(String fieldName, Object newValue);
@@ -57,19 +63,31 @@
 
     /**
      * Gets access object for the parameter.
-     *
+     * 
      * @param parameterName
      * @return object used to read and update the parameter
      */
     ParameterAccess getParameterAccess(String parameterName);
 
     /**
-     * Gets access object suitable for handling mixin fields which are bound to a parameter of the core component
-     * @param boundParameterName the name of the mixin field that should be linked to the core component's field.
-     * @param parentParameterNames the list of parameter names to try in the parent. The first name that matches a
-     *          declared parameter name in the core component will be used. This allows BindParameter to be used with
-     *          mixins that have a similar parameter type with different parameter names (eg:
-     * @since 5.2.0.0
+     * Gets access object suitable for handling mixin fields which are bound to a parameter of the
+     * core component
+     * 
+     * @param boundParameterName
+     *            the name of the mixin field that should be linked to the core component's field.
+     * @param parentParameterNames
+     *            the list of parameter names to try in the parent. The first name that matches a
+     *            declared parameter name in the core component will be used. This allows
+     *            BindParameter to be used with
+     *            mixins that have a similar parameter type with different parameter names (eg:
+     * @since 5.2.0
      */
-    ParameterAccess getContainerBoundParameterAccess(String boundParameterName, String... parentParameterNames);
+    ParameterAccess getContainerBoundParameterAccess(String boundParameterName,
+            String... parentParameterNames);
+
+    /**
+     * Delegates to {@link Page#addResetListener(org.apache.tapestry5.internal.structure.PageResetListener)}.
+     * @param listener to register
+     */
+    void addPageResetListener(PageResetListener listener);
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java?rev=896559&r1=896558&r2=896559&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java Wed Jan  6 17:35:59 2010
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010 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,12 +14,14 @@
 
 package org.apache.tapestry5.internal;
 
+import org.apache.tapestry5.internal.structure.PageResetListener;
 import org.apache.tapestry5.ioc.util.TimeInterval;
 
 public final class InternalConstants
 {
     /**
-     * Init parameter used to identify the package from which application classes are loaded. Such classes are in the
+     * Init parameter used to identify the package from which application classes are loaded. Such
+     * classes are in the
      * pages, components and mixins sub-packages.
      */
     public static final String TAPESTRY_APP_PACKAGE_PARAM = "tapestry.app-package";
@@ -30,13 +32,15 @@
     public static final String DISABLE_DEFAULT_MODULES_PARAM = "tapestry.disable-default-modules";
 
     /**
-     * The extension used for Tapestry component template files, <em>T</em>apestry <em>M</em>arkup <em>L</em>anguage.
+     * The extension used for Tapestry component template files, <em>T</em>apestry <em>M</em>arkup
+     * <em>L</em>anguage.
      * Template files are well-formed XML files.
      */
     public static final String TEMPLATE_EXTENSION = "tml";
 
     /**
-     * The name of the query parameter that stores the page activation context inside an action request.
+     * The name of the query parameter that stores the page activation context inside an action
+     * request.
      */
     public static final String PAGE_CONTEXT_NAME = "t:ac";
 
@@ -46,12 +50,27 @@
     public static final String GRID_INPLACE_UPDATE = "inplaceupdate";
 
     /**
-     * The name of a query parameter that stores the containing page (used in action links when the page containing the
-     * component is not the same as the page that was rendering). The active page (the page which initiated the render)
+     * The name of a query parameter that stores the containing page (used in action links when the
+     * page containing the
+     * component is not the same as the page that was rendering). The active page (the page which
+     * initiated the render)
      * is encoded into the URL, and the containing page is tacked on as this query parameter.
      */
     public static final String CONTAINER_PAGE_NAME = "t:cp";
 
+    /**
+     * Name of query parameter that is placed on "loopback" links (page render links for the same
+     * page). This mostly includes the redirects sent after a component event request. Page render
+     * requests
+     * that do <em>not</em> have the LOOPBACK query parameter will trigger a
+     * {@linkplain PageResetListener reset notification} after the initialization event; the
+     * LOOPBACK
+     * prevents this reset notification.
+     * 
+     * @since 5.2.0
+     */
+    public static final String LOOPBACK = "t:lb";
+
     public static final String OBJECT_RENDER_DIV_SECTION = "t-env-data-section";
 
     public static final String MIXINS_SUBPACKAGE = "mixins";
@@ -63,7 +82,8 @@
     public static final String BASE_SUBPACKAGE = "base";
 
     /**
-     * Used in some Ajax scenarios to set the content type for the response early, when the Page instance (the authority
+     * Used in some Ajax scenarios to set the content type for the response early, when the Page
+     * instance (the authority
      * on content types) is known. The value is of type {@link org.apache.tapestry5.ContentType}.
      */
     public static final String CONTENT_TYPE_ATTRIBUTE_NAME = "content-type";
@@ -71,44 +91,52 @@
     public static final String CHARSET_CONTENT_TYPE_PARAMETER = "charset";
 
     /**
-     * Request attribute that stores a {@link org.apache.tapestry5.internal.structure.Page} instance that will be
-     * rendered as the {@linkplain org.apache.tapestry5.SymbolConstants#SUPPRESS_REDIRECT_FROM_ACTION_REQUESTS immediate
+     * Request attribute that stores a {@link org.apache.tapestry5.internal.structure.Page} instance
+     * that will be
+     * rendered as the
+     * {@linkplain org.apache.tapestry5.SymbolConstants#SUPPRESS_REDIRECT_FROM_ACTION_REQUESTS
+     * immediate
      * mode response}.
      */
     public static final String IMMEDIATE_RESPONSE_PAGE_ATTRIBUTE = "tapestry.immediate-response-page";
 
     /**
-     * Request attribute that forces {@link org.apache.tapestry5.internal.services.RequestPathOptimizer} to use not
-     * optimize URLs (this is necessitated by {@link org.apache.tapestry5.services.PageDocumentGenerator}). Any non-null
+     * Request attribute that forces
+     * {@link org.apache.tapestry5.internal.services.RequestPathOptimizer} to use not
+     * optimize URLs (this is necessitated by
+     * {@link org.apache.tapestry5.services.PageDocumentGenerator}). Any non-null
      * value will force the URLs to be non-optimized.
      */
     public static final String GENERATING_RENDERED_PAGE = "tapestry.generating-rendered-page";
 
     /**
-     * Required MIME type for JSON responses. If this MIME type is not used, the client-side Prototype code will not
+     * Required MIME type for JSON responses. If this MIME type is not used, the client-side
+     * Prototype code will not
      * recognize the response as JSON, and the Ajax.Response.responseJSON property will be null.
      */
     public static final String JSON_MIME_TYPE = "application/json";
 
     /**
-     * Request attribute key; if non-null, then automatic GZIP compression of response stream is suppressed. This is
-     * useful when the code opening the response stream wants to explicitly control whether GZIP compression occurs or
+     * Request attribute key; if non-null, then automatic GZIP compression of response stream is
+     * suppressed. This is
+     * useful when the code opening the response stream wants to explicitly control whether GZIP
+     * compression occurs or
      * not.
-     *
+     * 
      * @since 5.1.0.0
      */
     public static final String SUPPRESS_COMPRESSION = "tapestry.supress-compression";
 
     /**
      * Name of response header for content encoding.
-     *
+     * 
      * @since 5.1.0.0
      */
     public static final String CONTENT_ENCODING_HEADER = "Content-Encoding";
 
     /**
      * Response content encoding value indicating use of GZIP compression.
-     *
+     * 
      * @since 5.1.0.0
      */
     public static final String GZIP_CONTENT_ENCODING = "gzip";

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventLinkEncoderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventLinkEncoderImpl.java?rev=896559&r1=896558&r2=896559&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventLinkEncoderImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ComponentEventLinkEncoderImpl.java Wed Jan  6 17:35:59 2010
@@ -1,4 +1,4 @@
-// Copyright 2009 The Apache Software Foundation
+// Copyright 2009, 2010 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.
@@ -47,27 +47,31 @@
 
     private final boolean encodeLocaleIntoPath;
 
+    private final RequestGlobals requestGlobals;
+
     private static final int BUFFER_SIZE = 100;
 
     private static final char SLASH = '/';
 
     // A beast that recognizes all the elements of a path in a single go.
     // We skip the leading slash, then take the next few terms (until a dot or a colon)
-    // as the page name.  Then there's a sequence that sees a dot
+    // as the page name. Then there's a sequence that sees a dot
     // and recognizes the nested component id (which may be missing), which ends
-    // at the colon, or at the slash (or the end of the string).  The colon identifies
-    // the event name (the event name is also optional).  A valid path will always have
+    // at the colon, or at the slash (or the end of the string). The colon identifies
+    // the event name (the event name is also optional). A valid path will always have
     // a nested component id or an event name (or both) ... when both are missing, then the
-    // path is most likely a page render request.  After the optional event name,
+    // path is most likely a page render request. After the optional event name,
     // the next piece is the action context, which is the remainder of the path.
 
     private final Pattern PATH_PATTERN = Pattern.compile(
 
-            "^/" +      // The leading slash is recognized but skipped
-                    "(((\\w+)/)*(\\w+))" + // A series of folder names leading up to the page name, forming the logical page name
-                    "(\\.(\\w+(\\.\\w+)*))?" + // The first dot separates the page name from the nested component id
-                    "(\\:(\\w+))?" + // A colon, then the event type
-                    "(/(.*))?", //  A slash, then the action context
+    "^/" + // The leading slash is recognized but skipped
+            "(((\\w+)/)*(\\w+))" + // A series of folder names leading up to the page name, forming
+            // the logical page name
+            "(\\.(\\w+(\\.\\w+)*))?" + // The first dot separates the page name from the nested
+            // component id
+            "(\\:(\\w+))?" + // A colon, then the event type
+            "(/(.*))?", // A slash, then the action context
             Pattern.COMMENTS);
 
     // Constants for the match groups in the above pattern.
@@ -77,16 +81,12 @@
     private static final int CONTEXT = 11;
 
     public ComponentEventLinkEncoderImpl(ComponentClassResolver componentClassResolver,
-                                         ContextPathEncoder contextPathEncoder,
-                                         LocalizationSetter localizationSetter,
-                                         Request request,
-                                         Response response,
-                                         RequestSecurityManager requestSecurityManager,
-                                         RequestPathOptimizer optimizer,
-                                         PersistentLocale persistentLocale,
+            ContextPathEncoder contextPathEncoder, LocalizationSetter localizationSetter,
+            Request request, Response response, RequestSecurityManager requestSecurityManager,
+            RequestPathOptimizer optimizer, PersistentLocale persistentLocale,
 
-                                         @Symbol(SymbolConstants.ENCODE_LOCALE_INTO_PATH)
-                                         boolean encodeLocaleIntoPath)
+            @Symbol(SymbolConstants.ENCODE_LOCALE_INTO_PATH)
+            boolean encodeLocaleIntoPath, RequestGlobals requestGlobals)
     {
         this.componentClassResolver = componentClassResolver;
         this.contextPathEncoder = contextPathEncoder;
@@ -97,6 +97,7 @@
         this.optimizer = optimizer;
         this.persistentLocale = persistentLocale;
         this.encodeLocaleIntoPath = encodeLocaleIntoPath;
+        this.requestGlobals = requestGlobals;
     }
 
     public Link createPageRenderLink(PageRenderRequestParameters parameters)
@@ -124,16 +125,28 @@
 
         appendContext(encodedPageName.length() > 0, parameters.getActivationContext(), builder);
 
-        return new LinkImpl(builder.toString(), baseURL == null, false, response, optimizer);
+        Link link = new LinkImpl(builder.toString(), baseURL == null, false, response, optimizer);
+
+        String requestPageName = requestGlobals.getActivePageName();
+
+        // TODO: It should only be necessary to encode the LOOPBACK for pages that actually have
+        // reset listener.
+
+        if (activePageName.equals(requestPageName))
+            link.addParameter(InternalConstants.LOOPBACK, "t");
+
+        return link;
     }
 
     private String encodePageName(String pageName)
     {
-        if (pageName.equalsIgnoreCase("index")) return "";
+        if (pageName.equalsIgnoreCase("index"))
+            return "";
 
         String encoded = pageName.toLowerCase();
 
-        if (!encoded.endsWith("/index")) return encoded;
+        if (!encoded.endsWith("/index"))
+            return encoded;
 
         return encoded.substring(0, encoded.length() - 6);
     }
@@ -191,7 +204,8 @@
 
         appendContext(true, parameters.getEventContext(), builder);
 
-        Link result = new LinkImpl(builder.toString(), baseURL == null, forForm, response, optimizer);
+        Link result = new LinkImpl(builder.toString(), baseURL == null, forForm, response,
+                optimizer);
 
         EventContext pageActivationContext = parameters.getPageActivationContext();
 
@@ -209,7 +223,8 @@
         // need to differentiate that.
 
         if (!containingPageName.equalsIgnoreCase(activePageName))
-            result.addParameter(InternalConstants.CONTAINER_PAGE_NAME, encodePageName(containingPageName));
+            result.addParameter(InternalConstants.CONTAINER_PAGE_NAME,
+                    encodePageName(containingPageName));
 
         return result;
     }
@@ -218,51 +233,54 @@
     {
         Matcher matcher = PATH_PATTERN.matcher(request.getPath());
 
-        if (!matcher.matches()) return null;
+        if (!matcher.matches())
+            return null;
 
         String nestedComponentId = matcher.group(NESTED_ID);
 
         String eventType = matcher.group(EVENT_NAME);
 
-        if (nestedComponentId == null && eventType == null) return null;
+        if (nestedComponentId == null && eventType == null)
+            return null;
 
         String activePageName = matcher.group(LOGICAL_PAGE_NAME);
 
         int slashx = activePageName.indexOf('/');
 
-        String possibleLocaleName = slashx > 0
-                                    ? activePageName.substring(0, slashx)
-                                    : "";
+        String possibleLocaleName = slashx > 0 ? activePageName.substring(0, slashx) : "";
 
         if (localizationSetter.setLocaleFromLocaleName(possibleLocaleName))
             activePageName = activePageName.substring(slashx + 1);
 
-        if (!componentClassResolver.isPageName(activePageName)) return null;
+        if (!componentClassResolver.isPageName(activePageName))
+            return null;
+
+        activePageName = componentClassResolver.canonicalizePageName(activePageName);
 
         EventContext eventContext = contextPathEncoder.decodePath(matcher.group(CONTEXT));
 
-        EventContext activationContext = contextPathEncoder.decodePath(
-                request.getParameter(InternalConstants.PAGE_CONTEXT_NAME));
+        EventContext activationContext = contextPathEncoder.decodePath(request
+                .getParameter(InternalConstants.PAGE_CONTEXT_NAME));
 
         // The event type is often omitted, and defaults to "action".
 
-        if (eventType == null) eventType = EventConstants.ACTION;
+        if (eventType == null)
+            eventType = EventConstants.ACTION;
 
-        if (nestedComponentId == null) nestedComponentId = "";
+        if (nestedComponentId == null)
+            nestedComponentId = "";
 
         String containingPageName = request.getParameter(InternalConstants.CONTAINER_PAGE_NAME);
 
-        if (containingPageName == null) containingPageName = activePageName;
+        if (containingPageName == null)
+            containingPageName = activePageName;
+        else
+            containingPageName = componentClassResolver.canonicalizePageName(containingPageName);
 
-        return new ComponentEventRequestParameters(activePageName,
-                                                   containingPageName,
-                                                   nestedComponentId,
-                                                   eventType,
-                                                   activationContext,
-                                                   eventContext);
+        return new ComponentEventRequestParameters(activePageName, containingPageName,
+                nestedComponentId, eventType, activationContext, eventContext);
     }
 
-
     public PageRenderRequestParameters decodePageRenderRequest(Request request)
     {
         // The extended name may include a page activation context. The trick is
@@ -290,15 +308,11 @@
         // 5. Just activation context (for root Index page)
         // 6. A locale name followed by activation context
 
-        String possibleLocaleName = slashx > 0
-                                    ? extendedName.substring(0, slashx)
-                                    : extendedName;
+        String possibleLocaleName = slashx > 0 ? extendedName.substring(0, slashx) : extendedName;
 
         if (localizationSetter.setLocaleFromLocaleName(possibleLocaleName))
         {
-            extendedName = slashx > 0
-                           ? extendedName.substring(slashx + 1)
-                           : "";
+            extendedName = slashx > 0 ? extendedName.substring(slashx + 1) : "";
         }
 
         slashx = extendedName.length();
@@ -307,8 +321,7 @@
         while (slashx > 0)
         {
             String pageName = extendedName.substring(0, slashx);
-            String pageActivationContext = atEnd ? "" :
-                                           extendedName.substring(slashx + 1);
+            String pageActivationContext = atEnd ? "" : extendedName.substring(slashx + 1);
 
             PageRenderRequestParameters parameters = checkIfPage(pageName, pageActivationContext);
 
@@ -328,11 +341,14 @@
 
     private PageRenderRequestParameters checkIfPage(String pageName, String pageActivationContext)
     {
-        if (!componentClassResolver.isPageName(pageName)) return null;
+        if (!componentClassResolver.isPageName(pageName))
+            return null;
 
         EventContext activationContext = contextPathEncoder.decodePath(pageActivationContext);
 
-        return new PageRenderRequestParameters(pageName, activationContext);
+        String canonicalized = componentClassResolver.canonicalizePageName(pageName);
+
+        return new PageRenderRequestParameters(canonicalized, activationContext);
     }
 
     public void appendContext(boolean seperatorRequired, EventContext context, StringBuilder builder)

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageRenderRequestHandlerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageRenderRequestHandlerImpl.java?rev=896559&r1=896558&r2=896559&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageRenderRequestHandlerImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageRenderRequestHandlerImpl.java Wed Jan  6 17:35:59 2010
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010 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.
@@ -15,18 +15,20 @@
 package org.apache.tapestry5.internal.services;
 
 import org.apache.tapestry5.EventConstants;
+import org.apache.tapestry5.internal.InternalConstants;
 import org.apache.tapestry5.internal.structure.Page;
 import org.apache.tapestry5.ioc.annotations.Primary;
 import org.apache.tapestry5.services.ComponentEventResultProcessor;
 import org.apache.tapestry5.services.PageRenderRequestHandler;
 import org.apache.tapestry5.services.PageRenderRequestParameters;
+import org.apache.tapestry5.services.Request;
 import org.apache.tapestry5.services.Traditional;
 
 import java.io.IOException;
 
 /**
  * Handles a page render request by activating and then rendering the page.
- *
+ * 
  * @see org.apache.tapestry5.internal.services.PageRenderDispatcher
  */
 public class PageRenderRequestHandlerImpl implements PageRenderRequestHandler
@@ -37,28 +39,36 @@
 
     private final PageResponseRenderer pageResponseRenderer;
 
-    public PageRenderRequestHandlerImpl(RequestPageCache cache,
-                                        @Traditional @Primary
-                                        ComponentEventResultProcessor resultProcessor,
-                                        PageResponseRenderer pageResponseRenderer)
+    private final Request request;
+
+    public PageRenderRequestHandlerImpl(RequestPageCache cache, @Traditional
+    @Primary
+    ComponentEventResultProcessor resultProcessor, PageResponseRenderer pageResponseRenderer,
+            Request request)
     {
         this.cache = cache;
         this.resultProcessor = resultProcessor;
         this.pageResponseRenderer = pageResponseRenderer;
+        this.request = request;
     }
 
     public void handle(PageRenderRequestParameters parameters) throws IOException
     {
         Page page = cache.get(parameters.getLogicalPageName());
 
-        ComponentResultProcessorWrapper callback = new ComponentResultProcessorWrapper(resultProcessor);
+        ComponentResultProcessorWrapper callback = new ComponentResultProcessorWrapper(
+                resultProcessor);
 
-        page.getRootElement().triggerContextEvent(EventConstants.ACTIVATE, parameters.getActivationContext(),
-                                                  callback);
+        page.getRootElement().triggerContextEvent(EventConstants.ACTIVATE,
+                parameters.getActivationContext(), callback);
 
         // The handler will have asked the result processor to send a response.
 
-        if (callback.isAborted()) return;
+        if (callback.isAborted())
+            return;
+
+        if (request.getParameter(InternalConstants.LOOPBACK) == null)
+            page.pageReset();
 
         pageResponseRenderer.renderPageResponse(page);
     }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestGlobalsImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestGlobalsImpl.java?rev=896559&r1=896558&r2=896559&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestGlobalsImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestGlobalsImpl.java Wed Jan  6 17:35:59 2010
@@ -37,6 +37,8 @@
 
     private Response response;
 
+    private String activePageName;
+
     public void storeServletRequestResponse(HttpServletRequest request, HttpServletResponse response)
     {
         servletRequest = request;
@@ -68,4 +70,15 @@
     {
         return response;
     }
+
+    public String getActivePageName()
+    {
+        return activePageName;
+    }
+
+    public void storeActivePageName(String pageName)
+    {
+        this.activePageName = pageName;
+    }
+
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java?rev=896559&r1=896558&r2=896559&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/InternalComponentResourcesImpl.java Wed Jan  6 17:35:59 2010
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010 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.
@@ -39,7 +39,8 @@
 import java.util.Set;
 
 /**
- * The bridge between a component and its {@link ComponentPageElement}, that supplies all kinds of resources to the
+ * The bridge between a component and its {@link ComponentPageElement}, that supplies all kinds of
+ * resources to the
  * component, including access to its parameters, parameter bindings, and persistent field data.
  */
 public class InternalComponentResourcesImpl implements InternalComponentResources
@@ -80,7 +81,8 @@
     private boolean informalsComputed;
 
     /**
-     * We keep a linked list of informal parameters, which saves us the expense of determining which bindings are formal
+     * We keep a linked list of informal parameters, which saves us the expense of determining which
+     * bindings are formal
      * and which are informal. Each Informal points to the next.
      */
     private class Informal
@@ -102,16 +104,17 @@
         {
             Object value = binding.get();
 
-            if (value == null) return;
+            if (value == null)
+                return;
 
-            if (value instanceof Block) return;
+            if (value instanceof Block)
+                return;
 
             // If it's already a String, don't use the TypeCoercer (renderInformalParameters is
             // a CPU hotspot, as is TypeCoercer.coerce).
 
-            String valueString = value instanceof String
-                                 ? (String) value
-                                 : elementResources.coerce(value, String.class);
+            String valueString = value instanceof String ? (String) value : elementResources
+                    .coerce(value, String.class);
 
             writer.attributes(name, valueString);
         }
@@ -120,10 +123,8 @@
     private Informal firstInformal;
 
     public InternalComponentResourcesImpl(Page page, ComponentPageElement element,
-                                          ComponentResources containerResources,
-                                          ComponentPageElementResources elementResources,
-                                          String completeId, String nestedId, Instantiator componentInstantiator
-    )
+            ComponentResources containerResources, ComponentPageElementResources elementResources,
+            String completeId, String nestedId, Instantiator componentInstantiator)
     {
         this.page = page;
         this.element = element;
@@ -242,7 +243,8 @@
         return getBinding(parameterName) != null;
     }
 
-    public <T extends Annotation> T getParameterAnnotation(String parameterName, Class<T> annotationType)
+    public <T extends Annotation> T getParameterAnnotation(String parameterName,
+            Class<T> annotationType)
     {
         return getParameterAccess(parameterName).getAnnotation(annotationType);
     }
@@ -262,7 +264,8 @@
         return input == null ? EMPTY : input;
     }
 
-    public boolean triggerContextEvent(String eventType, EventContext context, ComponentEventCallback callback)
+    public boolean triggerContextEvent(String eventType, EventContext context,
+            ComponentEventCallback callback)
     {
         return element.triggerContextEvent(eventType, context, callback);
     }
@@ -290,19 +293,19 @@
         }
         catch (Exception ex)
         {
-            throw new TapestryException(StructureMessages.fieldPersistFailure(getCompleteId(), fieldName, ex),
-                                        getLocation(), ex);
+            throw new TapestryException(StructureMessages.fieldPersistFailure(getCompleteId(),
+                    fieldName, ex), getLocation(), ex);
         }
     }
 
     public void bindParameter(String parameterName, Binding binding)
     {
-        if (bindings == null) bindings = CollectionFactory.newCaseInsensitiveMap();
+        if (bindings == null)
+            bindings = CollectionFactory.newCaseInsensitiveMap();
 
         bindings.put(parameterName, binding);
     }
 
-
     public Class getBoundType(String parameterName)
     {
         return getParameterAccess(parameterName).getBoundType();
@@ -340,7 +343,8 @@
 
     public void renderInformalParameters(MarkupWriter writer)
     {
-        if (bindings == null) return;
+        if (bindings == null)
+            return;
 
         if (!informalsComputed)
         {
@@ -358,7 +362,8 @@
 
     public Component getContainer()
     {
-        if (containerResources == null) return null;
+        if (containerResources == null)
+            return null;
 
         return containerResources.getComponent();
     }
@@ -380,7 +385,8 @@
 
     public Messages getMessages()
     {
-        if (messages == null) messages = elementResources.getMessages(componentModel);
+        if (messages == null)
+            messages = elementResources.getMessages(componentModel);
 
         return messages;
     }
@@ -429,7 +435,8 @@
             for (String name : bindings.keySet())
             {
 
-                if (componentModel.getParameterModel(name) != null) continue;
+                if (componentModel.getParameterModel(name) != null)
+                    continue;
 
                 result.put(name, bindings.get(name));
             }
@@ -443,9 +450,9 @@
         Object result = InternalUtils.get(renderVariables, name);
 
         if (result == null)
-            throw new IllegalArgumentException(StructureMessages.missingRenderVariable(getCompleteId(),
-                                                                                       name,
-                                                                                       renderVariables == null ? null : renderVariables.keySet()));
+            throw new IllegalArgumentException(StructureMessages.missingRenderVariable(
+                    getCompleteId(), name, renderVariables == null ? null : renderVariables
+                            .keySet()));
 
         return result;
     }
@@ -456,16 +463,19 @@
         Defense.notNull(value, "value");
 
         if (!element.isRendering())
-            throw new IllegalStateException(StructureMessages.renderVariableSetWhenNotRendering(getCompleteId(), name));
+            throw new IllegalStateException(StructureMessages.renderVariableSetWhenNotRendering(
+                    getCompleteId(), name));
 
-        if (renderVariables == null) renderVariables = CollectionFactory.newCaseInsensitiveMap();
+        if (renderVariables == null)
+            renderVariables = CollectionFactory.newCaseInsensitiveMap();
 
         renderVariables.put(name, value);
     }
 
     public void postRenderCleanup()
     {
-        if (renderVariables != null) renderVariables.clear();
+        if (renderVariables != null)
+            renderVariables.clear();
     }
 
     public void addPageLifecycleListener(PageLifecycleListener listener)
@@ -475,7 +485,8 @@
 
     public ParameterAccess getParameterAccess(final String parameterName)
     {
-        if (access == null) access = CollectionFactory.newCaseInsensitiveMap();
+        if (access == null)
+            access = CollectionFactory.newCaseInsensitiveMap();
 
         ParameterAccess result = access.get(parameterName);
 
@@ -488,46 +499,50 @@
         return result;
     }
 
-    public ParameterAccess getContainerBoundParameterAccess(final String boundParameterName, String... parentParameterNames)
+    public ParameterAccess getContainerBoundParameterAccess(final String boundParameterName,
+            String... parentParameterNames)
     {
-        if (containerParameterAccess == null) containerParameterAccess = CollectionFactory.newCaseInsensitiveMap();
+        if (containerParameterAccess == null)
+            containerParameterAccess = CollectionFactory.newCaseInsensitiveMap();
 
         ParameterAccess result = containerParameterAccess.get(boundParameterName);
         if (result == null)
         {
             final InternalComponentResources res = (InternalComponentResources) getContainerResources();
-            //Ideally, this check would occur at class fabrication time. But there's not currently a way
-            //to tell if a component class is a mixin class, short of checking for "mixins" in the FQCN.
-            //So we check to make sure that this component class name is in the set of mixins defined for the container
-            //resources.
-            if (!res.isMixingIn(this.getComponentModel().getComponentClassName())) {
-                //then we're not a mixin, we're a component in the tree.
-                throw new TapestryException(StructureMessages.bindParameterOnlyOnMixin(boundParameterName, this),this,null);
-            }
-            //Have to be careful here. Problem is that if the mixin is not @MixinAfter, its PAGE_DID_LOAD will be called
-            //before the core component's. That can potentially result in missing default bindings if we
-            //call getParameterAcces at the wrong time (the unbound parameter access will be cached...).
+            // Ideally, this check would occur at class fabrication time. But there's not currently
+            // a way
+            // to tell if a component class is a mixin class, short of checking for "mixins" in the
+            // FQCN.
+            // So we check to make sure that this component class name is in the set of mixins
+            // defined for the container
+            // resources.
+            if (!res.isMixingIn(this.getComponentModel().getComponentClassName()))
+            {
+                // then we're not a mixin, we're a component in the tree.
+                throw new TapestryException(StructureMessages.bindParameterOnlyOnMixin(
+                        boundParameterName, this), this, null);
+            }
+            // Have to be careful here. Problem is that if the mixin is not @MixinAfter, its
+            // PAGE_DID_LOAD will be called
+            // before the core component's. That can potentially result in missing default bindings
+            // if we
+            // call getParameterAcces at the wrong time (the unbound parameter access will be
+            // cached...).
             String parentParameterName = findParentParameterName(parentParameterNames);
-            if (parentParameterName == null)
-            {
-                throw new TapestryException(
-                        StructureMessages.noSuchCoreComponentParameter(this,boundParameterName,parentParameterNames), 
-                        this,null);
-            }
+            if (parentParameterName == null) { throw new TapestryException(StructureMessages
+                    .noSuchCoreComponentParameter(this, boundParameterName, parentParameterNames),
+                    this, null); }
             result = createContainerParameterAccess(parentParameterName);
-            containerParameterAccess.put(boundParameterName,result);
+            containerParameterAccess.put(boundParameterName, result);
         }
         return result;
     }
 
     private String findParentParameterName(String... queries)
     {
-        for(String query : queries)
+        for (String query : queries)
         {
-            if(getContainerResources().getComponentModel().getParameterModel(query) != null)
-            {
-                return query;
-            }
+            if (getContainerResources().getComponentModel().getParameterModel(query) != null) { return query; }
         }
         return null;
     }
@@ -559,7 +574,8 @@
 
             public <T> T read(Class<T> desiredType)
             {
-                if (binding == null) return null;
+                if (binding == null)
+                    return null;
 
                 T result;
 
@@ -574,16 +590,16 @@
                 }
                 catch (Exception ex)
                 {
-                    throw new TapestryException(
-                            StructureMessages.getParameterFailure(parameterName, getCompleteId(), ex), binding,
-                            ex);
+                    throw new TapestryException(StructureMessages.getParameterFailure(
+                            parameterName, getCompleteId(), ex), binding, ex);
                 }
 
                 if (result == null && !allowNull)
-                    throw new TapestryException(String.format(
-                            "Parameter '%s' of component %s is bound to null. This parameter is not allowed to be null.",
-                            parameterName,
-                            getCompleteId()), binding, null);
+                    throw new TapestryException(
+                            String
+                                    .format(
+                                            "Parameter '%s' of component %s is bound to null. This parameter is not allowed to be null.",
+                                            parameterName, getCompleteId()), binding, null);
 
                 return result;
             }
@@ -593,10 +609,10 @@
 
                 if (binding == null)
                 {
-                    //have to fire in case there's a mixin watching value;
-                    //even if it's not bound to any other value,
-                    //the mixin needs to know that the value internal to the component
-                    //was changed.
+                    // have to fire in case there's a mixin watching value;
+                    // even if it's not bound to any other value,
+                    // the mixin needs to know that the value internal to the component
+                    // was changed.
                     fireParameterChanged(parameterName, parameterValue);
                     return;
                 }
@@ -607,13 +623,12 @@
                     Object coerced = elementResources.coerce(parameterValue, bindingType);
 
                     binding.set(coerced);
-                    fireParameterChanged(parameterName,coerced);
+                    fireParameterChanged(parameterName, coerced);
                 }
                 catch (Exception ex)
                 {
-                    throw new TapestryException(
-                            StructureMessages.writeParameterFailure(parameterName, getCompleteId(), ex), binding,
-                            ex);
+                    throw new TapestryException(StructureMessages.writeParameterFailure(
+                            parameterName, getCompleteId(), ex), binding, ex);
                 }
             }
 
@@ -636,14 +651,16 @@
 
             public void registerParameterChangeListener(ParameterChangeListener listener)
             {
-                Defense.notNull(listener,"listener");
-                if (listeners == null) listeners = CollectionFactory.newSet();
+                Defense.notNull(listener, "listener");
+                if (listeners == null)
+                    listeners = CollectionFactory.newSet();
                 listeners.add(listener);
             }
 
             public void unregisterParameterChangeListener(ParameterChangeListener listener)
             {
-                if (listeners == null) return;
+                if (listeners == null)
+                    return;
                 listeners.remove(listener);
             }
 
@@ -654,8 +671,8 @@
 
             protected void fireParameterChanged(String parameterName, Object newValue)
             {
-                ParameterChangedEvent event = new ParameterChangedEvent(parameterName,newValue);
-                for(ParameterChangeListener l : listeners)
+                ParameterChangedEvent event = new ParameterChangedEvent(parameterName, newValue);
+                for (ParameterChangeListener l : listeners)
                 {
                     l.parameterChanged(event);
                 }
@@ -707,14 +724,16 @@
 
             public void registerParameterChangeListener(final ParameterChangeListener listener)
             {
-                //if it's not bound, try defering.
+                // if it's not bound, try defering.
                 if (isBound())
                 {
                     access().registerParameterChangeListener(listener);
-                } else
+                }
+                else
                 {
-                    //try waiting for it. If it's not bound after load, then it's not bound at all.
-                    element.deferLoadAction(new Runnable() {
+                    // try waiting for it. If it's not bound after load, then it's not bound at all.
+                    element.deferLoadAction(new Runnable()
+                    {
                         public void run()
                         {
                             access().registerParameterChangeListener(listener);
@@ -740,4 +759,9 @@
         };
 
     }
+
+    public void addPageResetListener(PageResetListener listener)
+    {
+        page.addResetListener(listener);
+    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java?rev=896559&r1=896558&r2=896559&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java Wed Jan  6 17:35:59 2010
@@ -22,36 +22,43 @@
 import java.util.Locale;
 
 /**
- * Represents a unique page within the application. Pages are part of the <em>internal</em> structure of a Tapestry
- * application; end developers who refer to "page" are really referring to the {@link #getRootComponent() root
+ * Represents a unique page within the application. Pages are part of the <em>internal</em>
+ * structure of a Tapestry
+ * application; end developers who refer to "page" are really referring to the
+ * {@link #getRootComponent() root
  * component} of the actual page.
  * <p/>
- * 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.
+ * 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.
+ * 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
 {
     /**
-     * Returns the short, logical name for the page. This is the page name as it might included in an action or page
+     * Returns the short, logical name for the page. This is the page name as it might included in
+     * an action or page
      * render URL (though it will be converted to lower case when it is included).
      */
     String getName();
 
     /**
-     * The locale for which the page is localized. This is set when the page is created and does not change.
+     * The locale for which the page is localized. This is set when the page is created and does not
+     * change.
      */
     Locale getLocale();
 
     /**
-     * Invoked during page construction time to connect the page's root component to the page instance.
+     * Invoked during page construction time to connect the page's root component to the page
+     * instance.
      */
     void setRootElement(ComponentPageElement component);
 
     /**
-     * The root component of the page. This is the wrapper around the end developer's view of the page.
+     * The root component of the page. This is the wrapper around the end developer's view of the
+     * page.
      */
     ComponentPageElement getRootElement();
 
@@ -61,31 +68,33 @@
     Component getRootComponent();
 
     /**
-     * Invoked to inform the page that it is being detached from the current request. This occurs just before the page
+     * Invoked to inform the page that it is being detached from the current request. This occurs
+     * just before the page
      * is returned to the page pool.
      * <p/>
-     * A page may be clean or dirty. A page is dirty if its dirty count is greater than zero (meaning that, during the
-     * render of the page, some components did not fully render), or if any of its listeners throw an exception from
-     * containingPageDidDetech().
+     * A page may be clean or dirty. A page is dirty if its dirty count is greater than zero
+     * (meaning that, during the render of the page, some components did not fully render), or if
+     * any of its listeners throw an exception from containingPageDidDetech().
      * <p/>
      * The page pool should discard pages that are dirty, rather than store them into the pool.
-     *
+     * 
      * @return true if the page is "dirty", false otherwise
      * @see org.apache.tapestry5.runtime.PageLifecycleListener#containingPageDidDetach()
      */
     boolean detached();
 
     /**
-     * Invoked to inform the page that it is attached to the current request. This occurs when a page is first
-     * referenced within a request. If the page was created from scratch for this request, the call to {@link #loaded()}
-     * will preceded the call to {@link #attached()}.
+     * Invoked to inform the page that it is attached to the current request. This occurs when a
+     * page is first
+     * referenced within a request. If the page was created from scratch for this request, the call
+     * to {@link #loaded()} will preceded the call to {@link #attached()}.
      */
 
     void attached();
 
     /**
      * Inform the page that it is now completely loaded.
-     *
+     * 
      * @see org.apache.tapestry5.runtime.PageLifecycleListener#containingPageDidLoad()
      */
 
@@ -97,41 +106,53 @@
     void addLifecycleListener(PageLifecycleListener listener);
 
     /**
-     * Returns the logger of the root component element. Any logging about page construction or activity should be sent
+     * Returns the logger of the root component element. Any logging about page construction or
+     * activity should be sent
      * to this logger.
      */
     Logger getLogger();
 
     /**
-     * Retrieves a component element by its nested id (a sequence of simple ids, separated by dots). The individual
-     * names in the nested id are matched without regards to case. A nested id of '' (the empty string) returns the root
+     * Retrieves a component element by its nested id (a sequence of simple ids, separated by dots).
+     * The individual
+     * names in the nested id are matched without regards to case. A nested id of '' (the empty
+     * string) returns the root
      * element of the page.
-     *
-     * @throws IllegalArgumentException if the nestedId does not correspond to a component
+     * 
+     * @throws IllegalArgumentException
+     *             if the nestedId does not correspond to a component
      */
     ComponentPageElement getComponentElementByNestedId(String nestedId);
 
     /**
      * Posts a change to a persistent field.
-     *
-     * @param resources the component resources for the component or mixin containing the field whose value changed
-     * @param fieldName the name of the field
-     * @param newValue  the new value for the field
+     * 
+     * @param resources
+     *            the component resources for the component or mixin containing the field whose
+     *            value changed
+     * @param fieldName
+     *            the name of the field
+     * @param newValue
+     *            the new value for the field
      */
     void persistFieldChange(ComponentResources resources, String fieldName, Object newValue);
 
     /**
      * Gets a change for a field within the component.
-     *
-     * @param nestedId  the nested component id of the component containing the field
-     * @param fieldName the name of the persistent field
+     * 
+     * @param nestedId
+     *            the nested component id of the component containing the field
+     * @param fieldName
+     *            the name of the persistent field
      * @return the value, or null if no value is stored
      */
     Object getFieldChange(String nestedId, String fieldName);
 
     /**
-     * Called as a component initially starts to render itself. This is used to check for the cases where a component
-     * causes a runtime exception that aborts the render early, leaving the page in an invalid state.
+     * Called as a component initially starts to render itself. This is used to check for the cases
+     * where a component
+     * causes a runtime exception that aborts the render early, leaving the page in an invalid
+     * state.
      */
     void incrementDirtyCount();
 
@@ -141,9 +162,24 @@
     void decrementDirtyCount();
 
     /**
-     * Discards all persistent field changes for the page containing the component.  Changes are eliminated from
-     * persistent storage (such as the {@link org.apache.tapestry5.services.Session}) which will take effect in the
-     * <em>next</em> request (the attached page instance is not affected).
+     * Discards all persistent field changes for the page containing the component. Changes are
+     * eliminated from
+     * persistent storage (such as the {@link org.apache.tapestry5.services.Session}) which will
+     * take effect in the <em>next</em> request (the attached page instance is not affected).
      */
     void discardPersistentFieldChanges();
+
+    /**
+     * Adds a new listener for page reset events.
+     * 
+     * @param listener
+     *            will receive notifications when the page is accessed from a different page
+     * @since 5.2.0
+     */
+    void addResetListener(PageResetListener listener);
+
+    /**
+     * Invoked to notify {@link PageResetListener} listeners.
+     */
+    void pageReset();
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageImpl.java?rev=896559&r1=896558&r2=896559&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageImpl.java Wed Jan  6 17:35:59 2010
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010 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.
@@ -17,6 +17,8 @@
 import org.apache.tapestry5.ComponentResources;
 import org.apache.tapestry5.internal.services.PersistentFieldManager;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.internal.util.Defense;
+
 import static org.apache.tapestry5.ioc.internal.util.Defense.notNull;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.runtime.Component;
@@ -37,14 +39,17 @@
 
     private ComponentPageElement rootElement;
 
-    private final List<PageLifecycleListener> listeners = CollectionFactory.newList();
+    private final List<PageLifecycleListener> lifecycleListeners = CollectionFactory.newList();
+
+    private final List<PageResetListener> resetListeners = CollectionFactory.newList();
 
     private int dirtyCount;
 
     private boolean loadComplete;
 
     /**
-     * Obtained from the {@link org.apache.tapestry5.internal.services.PersistentFieldManager} when first needed,
+     * Obtained from the {@link org.apache.tapestry5.internal.services.PersistentFieldManager} when
+     * first needed,
      * discarded at the end of the request.
      */
     private PersistentFieldBundle fieldBundle;
@@ -103,14 +108,14 @@
 
     public void addLifecycleListener(PageLifecycleListener listener)
     {
-        listeners.add(listener);
+        lifecycleListeners.add(listener);
     }
 
     public boolean detached()
     {
         boolean result = dirtyCount > 0;
 
-        for (PageLifecycleListener listener : listeners)
+        for (PageLifecycleListener listener : lifecycleListeners)
         {
             try
             {
@@ -130,7 +135,7 @@
 
     public void loaded()
     {
-        for (PageLifecycleListener listener : listeners)
+        for (PageLifecycleListener listener : lifecycleListeners)
             listener.containingPageDidLoad();
 
         loadComplete = true;
@@ -138,12 +143,13 @@
 
     public void attached()
     {
-        if (dirtyCount != 0) throw new IllegalStateException(StructureMessages.pageIsDirty(this));
+        if (dirtyCount != 0)
+            throw new IllegalStateException(StructureMessages.pageIsDirty(this));
 
-        for (PageLifecycleListener listener : listeners)
+        for (PageLifecycleListener listener : lifecycleListeners)
             listener.restoreStateBeforePageAttach();
 
-        for (PageLifecycleListener listener : listeners)
+        for (PageLifecycleListener listener : lifecycleListeners)
             listener.containingPageDidAttach();
     }
 
@@ -162,7 +168,8 @@
 
     public Object getFieldChange(String nestedId, String fieldName)
     {
-        if (fieldBundle == null) fieldBundle = persistentFieldManager.gatherChanges(name);
+        if (fieldBundle == null)
+            fieldBundle = persistentFieldManager.gatherChanges(name);
 
         return fieldBundle.getValue(nestedId, fieldName);
     }
@@ -186,4 +193,20 @@
     {
         return name;
     }
+
+    public void addResetListener(PageResetListener listener)
+    {
+        Defense.notNull(listener, "listener");
+
+        resetListeners.add(listener);
+    }
+
+    public void pageReset()
+    {
+        for (PageResetListener l : resetListeners)
+        {
+            l.pageDidReset();
+        }
+    }
+
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java?rev=896559&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java Wed Jan  6 17:35:59 2010
@@ -0,0 +1,23 @@
+// Copyright 2010 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.structure;
+
+public interface PageResetListener
+{
+    /**
+     * Invoked when the page is first accessed 
+     */
+    void pageDidReset();
+}

Propchange: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PageResetAnnotationWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PageResetAnnotationWorker.java?rev=896559&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PageResetAnnotationWorker.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PageResetAnnotationWorker.java Wed Jan  6 17:35:59 2010
@@ -0,0 +1,77 @@
+// Copyright 2010 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.transform;
+
+import java.util.List;
+
+import org.apache.tapestry5.annotations.PageReset;
+import org.apache.tapestry5.internal.structure.PageResetListener;
+import org.apache.tapestry5.model.MutableComponentModel;
+import org.apache.tapestry5.services.ClassTransformation;
+import org.apache.tapestry5.services.ComponentClassTransformWorker;
+import org.apache.tapestry5.services.TransformConstants;
+import org.apache.tapestry5.services.TransformMethodSignature;
+
+/**
+ * Implementation of the {@link PageReset} annotation. Makes the component implement
+ * {@link PageResetListener} and,
+ * optionally,
+ * 
+ * @since 5.2.0
+ */
+public class PageResetAnnotationWorker implements ComponentClassTransformWorker
+{
+    private static final String META_KEY = "tapestry.page-reset-listener";
+
+    private static final TransformMethodSignature PAGE_DID_RESET = new TransformMethodSignature(
+            "pageDidReset");
+
+    public void transform(ClassTransformation transformation, MutableComponentModel model)
+    {
+        List<TransformMethodSignature> methods = transformation
+                .findMethodsWithAnnotation(PageReset.class);
+
+        if (methods.isEmpty())
+            return;
+
+        String resourcesFieldName = transformation.getResourcesFieldName();
+
+        if (model.getMeta(META_KEY) == null)
+        {
+            transformation.addImplementedInterface(PageResetListener.class);
+
+            transformation.extendMethod(TransformConstants.CONTAINING_PAGE_DID_LOAD_SIGNATURE,
+                    String.format("%s.addPageResetListener(this);", resourcesFieldName));
+
+            model.setMeta(META_KEY, "true");
+        }
+
+        for (TransformMethodSignature sig : methods)
+        {
+            boolean valid = sig.getParameterTypes().length == 0
+                    && sig.getReturnType().equals("void") && sig.getExceptionTypes().length == 0;
+
+            if (!valid)
+                throw new RuntimeException(
+                        String
+                                .format(
+                                        "Method %s of class %s is invalid: methods with the @PageReset annotation must return void, and have no parameters or thrown exceptions.",
+                                        sig, model.getComponentClassName()));
+
+            transformation.extendMethod(PAGE_DID_RESET, sig.getMethodName() + "();");
+        }
+
+    }
+}

Propchange: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/PageResetAnnotationWorker.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/SessionAttributeWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/SessionAttributeWorker.java?rev=896559&r1=896558&r2=896559&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/SessionAttributeWorker.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/SessionAttributeWorker.java Wed Jan  6 17:35:59 2010
@@ -1,4 +1,4 @@
-// Copyright 2009 The Apache Software Foundation
+// Copyright 2009, 2010 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.
@@ -27,21 +27,17 @@
 import org.apache.tapestry5.services.Session;
 import org.apache.tapestry5.services.TransformMethodSignature;
 
-
 /**
- * Looks for the {@link SessionAttribute} annotation and converts read and write access on such 
- * fields into calls to the {@link Session#getAttribute(String)} and 
+ * Looks for the {@link SessionAttribute} annotation and converts read and write access on such
+ * fields into calls to the {@link Session#getAttribute(String)} and
  * {@link Session#setAttribute(String, Object)}.
- *
  */
 public class SessionAttributeWorker implements ComponentClassTransformWorker
 {
-
-    private ObjectLocator objectLocator;
+    private final ObjectLocator objectLocator;
 
     public SessionAttributeWorker(ObjectLocator objectLocator)
     {
-        super();
         this.objectLocator = objectLocator;
     }
 
@@ -51,8 +47,7 @@
 
         for (String fieldName : names)
         {
-            SessionAttribute annotation = transformation.getFieldAnnotation(
-                    fieldName,
+            SessionAttribute annotation = transformation.getFieldAnnotation(fieldName,
                     SessionAttribute.class);
 
             String sessionKey = annotation.value();
@@ -66,9 +61,7 @@
 
             Request request = objectLocator.getService(Request.class);
 
-            String requestField = transformation.addInjectedField(
-                    Request.class,
-                    "_request",
+            String requestField = transformation.addInjectedField(Request.class, "_request",
                     request);
 
             replaceReadAccess(transformation, fieldName, fieldType, sessionKey, requestField);
@@ -84,11 +77,8 @@
         TransformMethodSignature readMethodSignature = new TransformMethodSignature(
                 Modifier.PRIVATE, fieldType, readMethodName, null, null);
 
-        String body = String.format(
-                "return (%s) %s.getSession(true).getAttribute(\"%s\");",
-                fieldType,
-                requestField,
-                sessionKey);
+        String body = String.format("return (%s) %s.getSession(true).getAttribute(\"%s\");",
+                fieldType, requestField, sessionKey);
 
         transformation.addMethod(readMethodSignature, body);
         transformation.replaceReadAccess(fieldName, readMethodName);
@@ -103,9 +93,7 @@
                 "void", writeMethodName, new String[]
                 { fieldType }, null);
 
-        String body = String.format(
-                "%s.getSession(true).setAttribute(\"%s\", $1);",
-                requestField,
+        String body = String.format("%s.getSession(true).setAttribute(\"%s\", $1);", requestField,
                 sessionKey);
 
         transformation.addMethod(writeSignature, body);

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentEventRequestParameters.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentEventRequestParameters.java?rev=896559&r1=896558&r2=896559&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentEventRequestParameters.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/ComponentEventRequestParameters.java Wed Jan  6 17:35:59 2010
@@ -1,4 +1,4 @@
-// Copyright 2008, 2009 The Apache Software Foundation
+// Copyright 2008, 2009, 2010 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.
@@ -77,6 +77,8 @@
     /**
      * The name of the active page which rendered the link.  This is usually, but not always, the page which contains
      * the component.
+     * 
+     * @return {@link ComponentClassResolver#canonicalizePageName(String) canonicalized} page name
      */
     public String getActivePageName()
     {
@@ -87,6 +89,8 @@
      * The name of the page containing the component that was triggered. Usually this is the same as the active page,
      * but because of {@link org.apache.tapestry5.Block} and similar constructs, a component from other than the active
      * page may be rendered with the active page.
+     * 
+     * @return {@link ComponentClassResolver#canonicalizePageName(String) canonicalized} page name
      */
     public String getContainingPageName()
     {

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/InitializeActivePageName.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/InitializeActivePageName.java?rev=896559&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/InitializeActivePageName.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/InitializeActivePageName.java Wed Jan  6 17:35:59 2010
@@ -0,0 +1,50 @@
+// Copyright 2010 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 java.io.IOException;
+
+/**
+ * Filter contributed into the {@link ComponentRequestHandler} pipeline to set the
+ * {@link RequestGlobals#getActivePageName() activePageName property}.
+ * 
+ * @since 5.2.0
+ */
+public class InitializeActivePageName implements ComponentRequestFilter
+{
+    private final RequestGlobals globals;
+
+    public InitializeActivePageName(RequestGlobals globals)
+    {
+        this.globals = globals;
+    }
+
+    public void handleComponentEvent(ComponentEventRequestParameters parameters,
+            ComponentRequestHandler handler) throws IOException
+    {
+        globals.storeActivePageName(parameters.getActivePageName());
+
+        handler.handleComponentEvent(parameters);
+    }
+
+    public void handlePageRender(PageRenderRequestParameters parameters,
+            ComponentRequestHandler handler) throws IOException
+    {
+        globals.storeActivePageName(parameters.getLogicalPageName());
+
+        handler.handlePageRender(parameters);
+    }
+
+}

Propchange: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/InitializeActivePageName.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/PageRenderRequestParameters.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/PageRenderRequestParameters.java?rev=896559&r1=896558&r2=896559&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/PageRenderRequestParameters.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/PageRenderRequestParameters.java Wed Jan  6 17:35:59 2010
@@ -19,8 +19,9 @@
 import org.apache.tapestry5.ioc.internal.util.Defense;
 
 /**
- * Used with {@link org.apache.tapestry5.services.PageRenderRequestHandler} and {@link
- * org.apache.tapestry5.services.PageRenderRequestFilter} to define the logical page name and activation context for the
+ * Used with {@link org.apache.tapestry5.services.PageRenderRequestHandler} and
+ * {@link org.apache.tapestry5.services.PageRenderRequestFilter} to define the logical page name and
+ * activation context for the
  * request.
  */
 public class PageRenderRequestParameters
@@ -38,6 +39,9 @@
         this.activationContext = activationContext;
     }
 
+    /**
+     * Returns a {@linkplain ComponentClassResolver#canonicalizePageName(String) canonicalized} version of the page name.
+     */
     public String getLogicalPageName()
     {
         return logicalPageName;
@@ -51,17 +55,18 @@
     @Override
     public boolean equals(Object obj)
     {
-        if (this == obj) return true;
+        if (this == obj)
+            return true;
 
-        if (obj == null || getClass() != obj.getClass()) return false;
+        if (obj == null || getClass() != obj.getClass())
+            return false;
 
         PageRenderRequestParameters other = (PageRenderRequestParameters) obj;
 
-        return logicalPageName.equals(other.logicalPageName) &&
-                TapestryInternalUtils.isEqual(activationContext, other.activationContext);
+        return logicalPageName.equals(other.logicalPageName)
+                && TapestryInternalUtils.isEqual(activationContext, other.activationContext);
     }
 
-
     @Override
     public String toString()
     {

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/RequestGlobals.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/RequestGlobals.java?rev=896559&r1=896558&r2=896559&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/RequestGlobals.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/RequestGlobals.java Wed Jan  6 17:35:59 2010
@@ -21,7 +21,8 @@
 import javax.servlet.http.HttpServletResponse;
 
 /**
- * Service used to store the current request objects, both the Servlet API versions, and the Tapestry generic versions.
+ * Service used to store the current request objects, both the Servlet API versions, and the
+ * Tapestry generic versions.
  * The service has a per-thread scope.
  */
 public interface RequestGlobals
@@ -49,4 +50,22 @@
      * The current response. This is exposed as service Response.
      */
     Response getResponse();
+
+    /**
+     * Stores the {@linkplain ComponentClassResolver#canonicalizePageName(String) canonicalized}
+     * name of the active page for this request.
+     * 
+     * @param pageName
+     *            name of page (probably extracted from the URL)
+     * @since 5.2.0
+     */
+    void storeActivePageName(String pageName);
+
+    /**
+     * Returns the active page name previously stored.
+     * 
+     * @return canonicalized page name
+     * @since 5.2.0
+     */
+    String getActivePageName();
 }

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=896559&r1=896558&r2=896559&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 Jan  6 17:35:59 2010
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010 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.
@@ -504,6 +504,8 @@
      * <dd>Checks for the {@link org.apache.tapestry5.annotations.Cached} annotation</dd>
      * <dt>Log</dt>
      * <dd>Checks for the {@link org.apache.tapestry5.annotations.Log} annotation</dd>
+     * <dt>PageReset
+     * <dd>Checks for the {@link PageReset} annotation
      * </dl>
      */
     public static void contributeComponentClassTransformWorker(
@@ -551,7 +553,7 @@
         // have been properly setup.
 
         configuration.addInstance("BindParameter", BindParameterWorker.class, "after:Parameter");
-
+        
         // Workers for the component rendering state machine methods; this is in
         // typical
         // execution order.
@@ -607,6 +609,8 @@
 
         configuration.addInstance("Log", LogWorker.class);
 
+        configuration.addInstance("PageReset", PageResetAnnotationWorker.class);
+
         // This one is always last. Any additional private fields that aren't
         // annotated will
         // be converted to clear out at the end of the request.
@@ -2749,6 +2753,21 @@
     }
 
     /**
+     * Contributes:
+     * <dl>
+     * <dt>InitializeActivePageName
+     * <dd>{@link InitializeActivePageName}
+     * </dl>
+     * 
+     * @since 5.2.0
+     */
+    public void contributeComponentRequestHandler(
+            OrderedConfiguration<ComponentRequestFilter> configuration)
+    {
+        configuration.addInstance("InitializeActivePageName", InitializeActivePageName.class);
+    }
+
+    /**
      * @throws Exception
      * @since 5.1.0.2
      */
@@ -2855,24 +2874,24 @@
     }
 
     public void contributeRegexAuthorizer(Configuration<String> regex,
-            
-                                        @Symbol("tapestry.scriptaculous.path")
-                                        String scriptPath, 
-                                        
-                                        @Symbol("tapestry.blackbird.path")
-                                        String blackbirdPath, 
-                                        
-                                        @Symbol("tapestry.datepicker.path")
-                                        String datepickerPath, 
-                                        
-                                        @Symbol(SymbolConstants.CONTEXT_ASSETS_AVAILABLE)
-                                        boolean contextAvailable, 
-                                        
-                                        @Symbol(SymbolConstants.APPLICATION_VERSION)
-                                        String appVersion,
-                                        
-                                        @Symbol(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM)
-                                        String appPackageName)
+
+    @Symbol("tapestry.scriptaculous.path")
+    String scriptPath,
+
+    @Symbol("tapestry.blackbird.path")
+    String blackbirdPath,
+
+    @Symbol("tapestry.datepicker.path")
+    String datepickerPath,
+
+    @Symbol(SymbolConstants.CONTEXT_ASSETS_AVAILABLE)
+    boolean contextAvailable,
+
+    @Symbol(SymbolConstants.APPLICATION_VERSION)
+    String appVersion,
+
+    @Symbol(InternalConstants.TAPESTRY_APP_PACKAGE_PARAM)
+    String appPackageName)
     {
         // allow any js, jpg, jpeg, png, or css under org/apache/tapestry5,
         // along with
@@ -2891,7 +2910,7 @@
         // allow access to virtual assets. Critical for tapestry-combined js
         // files.
         regex.add("virtual/" + pathPattern);
-        
+
         regex.add("^" + appPackageName.replace(".", "/") + "/" + pathPattern);
 
         if (contextAvailable)