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 2010/07/11 21:06:14 UTC

svn commit: r963132 - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry5/internal/pageload/ main/java/org/apache/tapestry5/internal/structure/ test/java/org/apache/tapestry5/internal/structure/

Author: hlship
Date: Sun Jul 11 19:06:14 2010
New Revision: 963132

URL: http://svn.apache.org/viewvc?rev=963132&view=rev
Log:
TAP5-1197: Refactor ComponentPageElementImpl to store external state in PerThreadManager

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/RenderPhaseEvent.java   (with props)
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageLoaderImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementResources.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementResourcesImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementResourcesSourceImpl.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/structure/RenderPhaseEventHandler.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/PageImplTest.java

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageLoaderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageLoaderImpl.java?rev=963132&r1=963131&r2=963132&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageLoaderImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/pageload/PageLoaderImpl.java Sun Jul 11 19:06:14 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.
@@ -30,6 +30,7 @@ import org.apache.tapestry5.ioc.Operatio
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.ioc.internal.util.TapestryException;
+import org.apache.tapestry5.ioc.services.PerthreadManager;
 import org.apache.tapestry5.ioc.util.Stack;
 import org.apache.tapestry5.model.ComponentModel;
 import org.apache.tapestry5.model.EmbeddedComponentModel;
@@ -45,17 +46,17 @@ import java.util.Map;
 
 /**
  * There's still a lot of room to beef up {@link org.apache.tapestry5.internal.pageload.ComponentAssembler} and
- * {@link org.apache.tapestry5.internal.pageload.EmbeddedComponentAssembler} to perform more static analysis.
+ * {@link org.apache.tapestry5.internal.pageload.EmbeddedComponentAssembler} to perform more static analysis, but
+ * that may no longer be necessary, given Tapestry 5.2's default use of non-pooled pages.
  * <p/>
- * Loading a page involves a recurive process of creating
+ * Loading a page involves a recursive process of creating
  * {@link org.apache.tapestry5.internal.pageload.ComponentAssembler}s: for the root component, then down the tree for
  * each embedded component. A ComponentAssembler is largely a collection of
  * {@link org.apache.tapestry5.internal.pageload.PageAssemblyAction}s. Once created, a ComponentAssembler can quickly
  * assemble any number of component instances. All of the expensive logic, such as fitting template tokens together and
  * matching parameters to bindings, is done as part of the one-time construction of the ComponentAssembler. The end
  * result removes a huge amount of computational redundancy that was present in Tapestry 5.0, but to understand this,
- * you need to split your mind into two phases: construction (of the ComponentAssemblers) and assembly. It's twisted ...
- * and perhaps a bit functional and Monadic.
+ * you need to split your mind into two phases: construction (of the ComponentAssemblers) and assembly.
  * <p/>
  * And truly, <em>This is the Tapestry Heart, This is the Tapestry Soul...</em>
  */
@@ -135,10 +136,12 @@ public class PageLoaderImpl implements P
 
     private final OperationTracker tracker;
 
+    private final PerthreadManager perThreadManager;
+
     public PageLoaderImpl(ComponentInstantiatorSource instantiatorSource, ComponentTemplateSource templateSource,
             PageElementFactory elementFactory, ComponentPageElementResourcesSource resourcesSource,
             ComponentClassResolver componentClassResolver, PersistentFieldManager persistentFieldManager,
-            StringInterner interner, OperationTracker tracker)
+            StringInterner interner, OperationTracker tracker, PerthreadManager perThreadManager)
     {
         this.instantiatorSource = instantiatorSource;
         this.templateSource = templateSource;
@@ -148,6 +151,7 @@ public class PageLoaderImpl implements P
         this.persistentFieldManager = persistentFieldManager;
         this.interner = interner;
         this.tracker = tracker;
+        this.perThreadManager = perThreadManager;
     }
 
     public void objectWasInvalidated()
@@ -163,7 +167,7 @@ public class PageLoaderImpl implements P
         {
             public Page invoke()
             {
-                Page page = new PageImpl(logicalPageName, locale, persistentFieldManager);
+                Page page = new PageImpl(logicalPageName, locale, persistentFieldManager, perThreadManager);
 
                 ComponentAssembler assembler = getAssembler(pageClassName, locale);
 
@@ -341,8 +345,8 @@ public class PageLoaderImpl implements P
         // Sanity check: since an extension point defines its own default, it's going to be hard to
         // not find an override, somewhere, for it.
 
-        throw new TapestryException(PageloadMessages.couldNotFindOverride(extensionPointId), extensionPointToken
-                .getLocation(), null);
+        throw new TapestryException(PageloadMessages.couldNotFindOverride(extensionPointId),
+                extensionPointToken.getLocation(), null);
     }
 
     private List<ComponentTemplate> buildOverrideSearch(ComponentAssembler assembler, ComponentTemplate template)
@@ -535,8 +539,8 @@ public class PageLoaderImpl implements P
 
                 ParameterBinder binder = embeddedAssembler.createParameterBinder(parameterName);
 
-                if (binder == null) { throw new TapestryException(PageloadMessages.parameterNotSupported(element
-                        .getCompleteId(), parameterName), token.getLocation(), null); }
+                if (binder == null) { throw new TapestryException(PageloadMessages.parameterNotSupported(
+                        element.getCompleteId(), parameterName), token.getLocation(), null); }
 
                 binder.bind(pageAssembly.createdElement.peek(), binding);
 

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java?rev=963132&r1=963131&r2=963132&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java Sun Jul 11 19:06:14 2010
@@ -14,24 +14,13 @@
 
 package org.apache.tapestry5.internal.structure;
 
-import static org.apache.tapestry5.ioc.internal.util.Defense.notBlank;
-
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
-import org.apache.tapestry5.Binding;
-import org.apache.tapestry5.Block;
-import org.apache.tapestry5.BlockNotFoundException;
-import org.apache.tapestry5.ComponentEventCallback;
-import org.apache.tapestry5.ComponentResources;
-import org.apache.tapestry5.EventContext;
-import org.apache.tapestry5.Link;
-import org.apache.tapestry5.MarkupWriter;
-import org.apache.tapestry5.Renderable;
-import org.apache.tapestry5.TapestryMarkers;
+import org.apache.tapestry5.*;
 import org.apache.tapestry5.annotations.AfterRender;
 import org.apache.tapestry5.annotations.AfterRenderBody;
 import org.apache.tapestry5.annotations.AfterRenderTemplate;
@@ -44,7 +33,6 @@ import org.apache.tapestry5.internal.Abs
 import org.apache.tapestry5.internal.InternalComponentResources;
 import org.apache.tapestry5.internal.InternalConstants;
 import org.apache.tapestry5.internal.services.ComponentEventImpl;
-import org.apache.tapestry5.internal.services.EventImpl;
 import org.apache.tapestry5.internal.services.Instantiator;
 import org.apache.tapestry5.internal.util.NotificationEventCallback;
 import org.apache.tapestry5.ioc.BaseLocatable;
@@ -55,6 +43,7 @@ import org.apache.tapestry5.ioc.internal
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.ioc.internal.util.Orderer;
 import org.apache.tapestry5.ioc.internal.util.TapestryException;
+import org.apache.tapestry5.ioc.services.PerThreadValue;
 import org.apache.tapestry5.ioc.util.AvailableValues;
 import org.apache.tapestry5.ioc.util.UnknownValueException;
 import org.apache.tapestry5.model.ComponentModel;
@@ -76,7 +65,10 @@ import org.slf4j.Logger;
  * Once instantiated, a ComponentPageElement should be registered as a
  * {@linkplain org.apache.tapestry5.internal.structure.Page#addLifecycleListener(org.apache.tapestry5.runtime.PageLifecycleListener)
  * lifecycle listener}. This could be done inside the constructors, but that tends to complicate unit tests, so its done
- * by {@link org.apache.tapestry5.internal.services.PageElementFactoryImpl}.
+ * by {@link org.apache.tapestry5.internal.services.PageElementFactoryImpl}. There's still a bit of refactoring in this
+ * class (and its many inner classes) that can improve overall efficiency.
+ * <p>
+ * Modified for Tapestry 5.2 to adjust for the no-pooling approach (shared instances with externalized mutable state).
  */
 public class ComponentPageElementImpl extends BaseLocatable implements ComponentPageElement, PageLifecycleListener
 {
@@ -153,17 +145,21 @@ public class ComponentPageElementImpl ex
         return list == null ? 0 : list.size();
     }
 
-    private abstract class AbstractPhase extends AbstractComponentCallback implements RenderCommand
+    private abstract class AbstractPhase implements RenderCommand
     {
         private final String name;
 
-        private MarkupWriter writer;
+        private final boolean reverse;
 
-        public AbstractPhase(String name)
+        AbstractPhase(String name)
         {
-            super(sharedEvent);
+            this(name, false);
+        }
 
+        AbstractPhase(String name, boolean reverse)
+        {
             this.name = name;
+            this.reverse = reverse;
         }
 
         @Override
@@ -172,34 +168,39 @@ public class ComponentPageElementImpl ex
             return phaseToString(name);
         }
 
-        void reset(RenderQueue queue)
+        void invoke(MarkupWriter writer, Event event)
         {
-            sharedEventHandler.queueCommands(queue);
-
-            sharedEventHandler.reset();
+            try
+            {
+                if (components == null)
+                {
+                    invokeComponent(coreComponent, writer, event);
+                    return;
+                }
 
-            sharedEvent.reset();
+                // Multiple components (i.e., some mixins).
 
-            writer = null;
-        }
+                Iterator<Component> i = reverse ? InternalUtils.reverseIterator(components) : components.iterator();
 
-        void callback(boolean reverse, MarkupWriter writer)
-        {
-            this.writer = writer;
+                while (i.hasNext())
+                {
+                    invokeComponent(i.next(), writer, event);
 
-            invoke(reverse, this);
-        }
-
-        public void run(Component component)
-        {
-            invokeComponent(component, writer, sharedEvent);
-        }
+                    if (event.isAborted())
+                        break;
+                }
+            }
+            catch (RuntimeException ex)
+            {
+                throw new TapestryException(ex.getMessage(), getLocation(), ex);
+            }
 
-        protected boolean getResult()
-        {
-            return sharedEventHandler.getResult();
         }
 
+        /**
+         * Each concrete class implements this method to branch to the corresponding method
+         * of {@link Component}.
+         */
         protected abstract void invokeComponent(Component component, MarkupWriter writer, Event event);
     }
 
@@ -215,13 +216,15 @@ public class ComponentPageElementImpl ex
             component.setupRender(writer, event);
         }
 
-        public void render(final MarkupWriter writer, RenderQueue queue)
+        public void render(MarkupWriter writer, RenderQueue queue)
         {
-            callback(false, writer);
+            RenderPhaseEvent event = createRenderEvent(queue);
+
+            invoke(writer, event);
 
-            push(queue, getResult(), beginRenderPhase, cleanupRenderPhase);
+            push(queue, event.getResult(), beginRenderPhase, cleanupRenderPhase);
 
-            reset(queue);
+            event.reset();
         }
     }
 
@@ -239,12 +242,14 @@ public class ComponentPageElementImpl ex
 
         public void render(final MarkupWriter writer, final RenderQueue queue)
         {
-            callback(false, writer);
+            RenderPhaseEvent event = createRenderEvent(queue);
+
+            invoke(writer, event);
 
             push(queue, afterRenderPhase);
-            push(queue, getResult(), beforeRenderTemplatePhase, null);
+            push(queue, event.getResult(), beforeRenderTemplatePhase, null);
 
-            reset(queue);
+            event.reset();
         }
     }
 
@@ -287,14 +292,16 @@ public class ComponentPageElementImpl ex
 
         public void render(final MarkupWriter writer, final RenderQueue queue)
         {
-            callback(false, writer);
+            RenderPhaseEvent event = createRenderEvent(queue);
+
+            invoke(writer, event);
 
             push(queue, afterRenderTemplatePhase);
 
-            if (getResult())
+            if (event.getResult())
                 pushElements(queue, template);
 
-            reset(queue);
+            event.reset();
         }
     }
 
@@ -332,14 +339,16 @@ public class ComponentPageElementImpl ex
 
         public void render(final MarkupWriter writer, RenderQueue queue)
         {
-            callback(false, writer);
+            RenderPhaseEvent event = createRenderEvent(queue);
+
+            invoke(writer, event);
 
             push(queue, afterRenderBodyPhase);
 
-            if (getResult() && bodyBlock != null)
+            if (event.getResult() && bodyBlock != null)
                 queue.push(bodyBlock);
 
-            reset(queue);
+            event.reset();
         }
     }
 
@@ -348,7 +357,7 @@ public class ComponentPageElementImpl ex
 
         private AfterRenderBodyPhase()
         {
-            super("AfterRenderBody");
+            super("AfterRenderBody", true);
         }
 
         protected void invokeComponent(Component component, MarkupWriter writer, Event event)
@@ -358,11 +367,13 @@ public class ComponentPageElementImpl ex
 
         public void render(final MarkupWriter writer, RenderQueue queue)
         {
-            callback(true, writer);
+            RenderPhaseEvent event = createRenderEvent(queue);
+
+            invoke(writer, event);
 
-            push(queue, getResult(), null, beforeRenderBodyPhase);
+            push(queue, event.getResult(), null, beforeRenderBodyPhase);
 
-            reset(queue);
+            event.reset();
         }
     }
 
@@ -370,7 +381,7 @@ public class ComponentPageElementImpl ex
     {
         private AfterRenderTemplatePhase()
         {
-            super("AfterRenderTemplate");
+            super("AfterRenderTemplate", true);
         }
 
         protected void invokeComponent(Component component, MarkupWriter writer, Event event)
@@ -380,11 +391,13 @@ public class ComponentPageElementImpl ex
 
         public void render(final MarkupWriter writer, final RenderQueue queue)
         {
-            callback(true, writer);
+            RenderPhaseEvent event = createRenderEvent(queue);
 
-            push(queue, getResult(), null, beforeRenderTemplatePhase);
+            invoke(writer, event);
 
-            reset(queue);
+            push(queue, event.getResult(), null, beforeRenderTemplatePhase);
+
+            event.reset();
         }
     }
 
@@ -392,7 +405,7 @@ public class ComponentPageElementImpl ex
     {
         private AfterRenderPhase()
         {
-            super("AfterRender");
+            super("AfterRender", true);
         }
 
         protected void invokeComponent(Component component, MarkupWriter writer, Event event)
@@ -402,21 +415,21 @@ public class ComponentPageElementImpl ex
 
         public void render(final MarkupWriter writer, RenderQueue queue)
         {
-            callback(true, writer);
+            RenderPhaseEvent event = createRenderEvent(queue);
+
+            invoke(writer, event);
 
-            push(queue, getResult(), cleanupRenderPhase, beginRenderPhase);
+            push(queue, event.getResult(), cleanupRenderPhase, beginRenderPhase);
 
-            reset(queue);
+            event.reset();
         }
     }
 
-    private final ComponentPageElementResources elementResources;
-
     private class CleanupRenderPhase extends AbstractPhase
     {
         private CleanupRenderPhase()
         {
-            super("CleanupRender");
+            super("CleanupRender", true);
         }
 
         protected void invokeComponent(Component component, MarkupWriter writer, Event event)
@@ -426,11 +439,13 @@ public class ComponentPageElementImpl ex
 
         public void render(final MarkupWriter writer, RenderQueue queue)
         {
-            callback(true, writer);
+            RenderPhaseEvent event = createRenderEvent(queue);
 
-            push(queue, getResult(), null, setupRenderPhase);
+            invoke(writer, event);
 
-            reset(queue);
+            push(queue, event.getResult(), null, setupRenderPhase);
+
+            event.reset();
         }
     }
 
@@ -453,7 +468,7 @@ public class ComponentPageElementImpl ex
 
         public void render(MarkupWriter writer, RenderQueue queue)
         {
-            rendering = false;
+            rendering.set(false);
 
             Element current = writer.getElement();
 
@@ -496,12 +511,13 @@ public class ComponentPageElementImpl ex
 
     /**
      * Component lifecycle instances for all mixins; the core component is added to this list during
-     * page load. This is
-     * only used in the case that a component has mixins (in which case, the core component is
+     * page load. This is only used in the case that a component has mixins (in which case, the core component is
      * listed last).
      */
     private List<Component> components = null;
 
+    private final ComponentPageElementResources elementResources;
+
     private final ComponentPageElement container;
 
     private final InternalComponentResources coreResources;
@@ -525,22 +541,18 @@ public class ComponentPageElementImpl ex
 
     private final Page page;
 
-    private boolean rendering;
+    private final PerThreadValue<RenderPhaseEvent> renderEvent;
+
+    private final PerThreadValue<Boolean> rendering;
 
     // We know that, at the very least, there will be an element to force the component to render
     // its body, so there's no reason to wait to initialize the list.
 
     private final List<RenderCommand> template = CollectionFactory.newList();
 
-    private boolean renderPhasesInitalized;
-
     private RenderCommand setupRenderPhase, beginRenderPhase, beforeRenderTemplatePhase, beforeRenderBodyPhase,
             afterRenderBodyPhase, afterRenderTemplatePhase, afterRenderPhase, cleanupRenderPhase;
 
-    private final RenderPhaseEventHandler sharedEventHandler = new RenderPhaseEventHandler();
-
-    private final EventImpl sharedEvent;
-
     /**
      * Constructor for other components embedded within the root component or at deeper levels of
      * the hierarchy.
@@ -563,7 +575,6 @@ public class ComponentPageElementImpl ex
      * @param elementResources
      *            Provides access to common methods of various services
      */
-
     ComponentPageElementImpl(Page page, ComponentPageElement container, String id, String nestedId, String completeId,
             String elementName, Instantiator instantiator, Location location,
             ComponentPageElementResources elementResources)
@@ -585,10 +596,10 @@ public class ComponentPageElementImpl ex
 
         coreComponent = coreResources.getComponent();
 
-        Logger logger = coreResources.getLogger();
-        eventLogger = elementResources.getEventLogger(logger);
+        eventLogger = elementResources.getEventLogger(coreResources.getLogger());
 
-        sharedEvent = new EventImpl(sharedEventHandler, eventLogger);
+        renderEvent = elementResources.createPerThreadValue("tapestry.internal.RenderEvent:" + completeId);
+        rendering = elementResources.createPerThreadValue("tapestry.internal.Rendering:" + completeId);
     }
 
     /**
@@ -601,9 +612,6 @@ public class ComponentPageElementImpl ex
 
     private void initializeRenderPhases()
     {
-        if (renderPhasesInitalized)
-            return;
-
         setupRenderPhase = new SetupRenderPhase();
         beginRenderPhase = new BeginRenderPhase();
         beforeRenderTemplatePhase = new BeforeRenderTemplatePhase();
@@ -652,8 +660,6 @@ public class ComponentPageElementImpl ex
 
         if (!handled.contains(SetupRender.class))
             setupRenderPhase = beginRenderPhase;
-
-        renderPhasesInitalized = true;
     }
 
     public ComponentPageElement newChild(String id, String nestedId, String completeId, String elementName,
@@ -689,8 +695,8 @@ public class ComponentPageElementImpl ex
 
         if (existing != null)
             throw new TapestryException(StructureMessages.duplicateChildComponent(this, childId), child,
-                    new TapestryException(StructureMessages.originalChildComponent(this, childId, existing
-                            .getLocation()), existing, null));
+                    new TapestryException(StructureMessages.originalChildComponent(this, childId,
+                            existing.getLocation()), existing, null));
 
         children.put(childId, child);
     }
@@ -818,24 +824,22 @@ public class ComponentPageElementImpl ex
             mixinAfterOrderer = null;
         }
 
-        loaded = true;
-
         // For some parameters, bindings (from defaults) are provided inside the callback method, so
         // that is invoked first, before we check for unbound parameters.
 
         invoke(false, CONTAINING_PAGE_DID_LOAD);
         verifyRequiredParametersAreBound();
-    }
 
-    public void enqueueBeforeRenderBody(RenderQueue queue)
-    {
-        // TAP5-1100: In certain Ajax cases, a component may be asked to render its body
-        // that has never rendered itself (and thus, never called initializeRenderPhases). Subtle.
+        // We assume that by the time we start to render, the structure (i.e., mixins) is nailed
+        // down. We could add a lock, but that seems wasteful.
 
         initializeRenderPhases();
 
-        // If no body, then no beforeRenderBody or afterRenderBody
+        loaded = true;
+    }
 
+    public void enqueueBeforeRenderBody(RenderQueue queue)
+    {
         if (bodyBlock != null)
             push(queue, beforeRenderBodyPhase);
     }
@@ -967,7 +971,7 @@ public class ComponentPageElementImpl ex
     {
         try
         { // Optimization: In the most general case (just the one component, no mixins)
-            // invoke the callback on the component and be done ... no iterators, no nothing.
+          // invoke the callback on the component and be done ... no iterators, no nothing.
 
             if (components == null)
             {
@@ -998,7 +1002,7 @@ public class ComponentPageElementImpl ex
 
     public boolean isRendering()
     {
-        return rendering;
+        return rendering.exists() && rendering.get();
     }
 
     /**
@@ -1014,11 +1018,6 @@ public class ComponentPageElementImpl ex
      */
     public final void render(MarkupWriter writer, RenderQueue queue)
     {
-        // We assume that by the time we start to render, the structure (i.e., mixins) is nailed
-        // down. We could add a lock, but that seems wasteful.
-
-        initializeRenderPhases();
-
         // TODO: An error if the render flag is already set (recursive rendering not
         // allowed or advisable).
 
@@ -1028,7 +1027,7 @@ public class ComponentPageElementImpl ex
 
         // TODO: Check for recursive rendering.
 
-        rendering = true;
+        rendering.set(true);
 
         queue.startComponent(coreResources);
 
@@ -1082,6 +1081,7 @@ public class ComponentPageElementImpl ex
         });
     }
 
+    @SuppressWarnings("all")
     private boolean processEventTriggering(String eventType, EventContext context, ComponentEventCallback callback)
     {
         boolean result = false;
@@ -1219,7 +1219,7 @@ public class ComponentPageElementImpl ex
 
     public Block findBlock(String id)
     {
-        notBlank(id, "id");
+        Defense.notBlank(id, "id");
 
         return InternalUtils.get(blocks, id);
     }
@@ -1285,4 +1285,21 @@ public class ComponentPageElementImpl ex
         return elementResources.createPageRenderLink(pageClass, override, context);
     }
 
+    protected RenderPhaseEvent createRenderEvent(RenderQueue queue)
+    {
+        RenderPhaseEvent result = renderEvent.get();
+
+        if (result != null)
+            return result;
+
+        // Create a per-thread value to use until the end of the render. 
+        // This assumes that the queue will not change during the current request,
+        // which should be valid. 
+
+        result = new RenderPhaseEvent(new RenderPhaseEventHandler(queue), eventLogger);
+
+        renderEvent.set(result);
+
+        return result;
+    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementResources.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementResources.java?rev=963132&r1=963131&r2=963132&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementResources.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementResources.java Sun Jul 11 19:06:14 2010
@@ -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,
@@ -18,6 +18,8 @@ import org.apache.tapestry5.ComponentRes
 import org.apache.tapestry5.Link;
 import org.apache.tapestry5.ioc.Messages;
 import org.apache.tapestry5.ioc.OperationTracker;
+import org.apache.tapestry5.ioc.services.PerThreadValue;
+import org.apache.tapestry5.ioc.services.PerthreadManager;
 import org.apache.tapestry5.model.ComponentModel;
 import org.apache.tapestry5.services.ContextValueEncoder;
 import org.slf4j.Logger;
@@ -32,7 +34,7 @@ public interface ComponentPageElementRes
      * Used to obtain a {@link org.apache.tapestry5.ioc.Messages} instance for a particular component. If the component
      * extends from another component, then its localized properties will merge with its parent's properties (with the
      * subclass overriding the super class on any conflicts).
-     *
+     * 
      * @param componentModel
      * @return the message catalog for the component, in the indicated locale
      * @see org.apache.tapestry5.services.messages.ComponentMessagesSource
@@ -44,11 +46,14 @@ public interface ComponentPageElementRes
      * conversion will be to the equivalent wrapper type. In some cases, the TypeCoercer will need to search for an
      * appropriate coercion, and may even combine existing coercions to form new ones; in those cases, the results of
      * the search are cached.
-     *
-     * @param <S>        source type (input)
-     * @param <T>        target type (output)
+     * 
+     * @param <S>
+     *            source type (input)
+     * @param <T>
+     *            target type (output)
      * @param input
-     * @param targetType defines the target type
+     * @param targetType
+     *            defines the target type
      * @return the coerced value
      * @see org.apache.tapestry5.ioc.services.TypeCoercer
      */
@@ -56,8 +61,9 @@ public interface ComponentPageElementRes
 
     /**
      * Gets the Class instance for then give name.
-     *
-     * @param className fully qualified class name
+     * 
+     * @param className
+     *            fully qualified class name
      * @return the class instance
      * @see org.apache.tapestry5.internal.services.ComponentClassCache
      */
@@ -65,11 +71,15 @@ public interface ComponentPageElementRes
 
     /**
      * Creates a link on behalf of a component.
-     *
-     * @param resources resources for the component
-     * @param eventType type of event to create
-     * @param forForm   true if generating for a form submission
-     * @param context   additional event context associated with the link
+     * 
+     * @param resources
+     *            resources for the component
+     * @param eventType
+     *            type of event to create
+     * @param forForm
+     *            true if generating for a form submission
+     * @param context
+     *            additional event context associated with the link
      * @return the link
      * @since 5.1.0.0
      */
@@ -77,12 +87,15 @@ public interface ComponentPageElementRes
 
     /**
      * Creates a page render request link to render a specific page.
-     *
-     * @param pageName the logical name of the page to link to
-     * @param override if true, the context is used even if empty (normally, the target page is allowed to passivate,
-     *                 providing a context, when the provided context is empty)
-     * @param context  the activation context for the page. If omitted, the activation context is obtained from the
-     *                 target page
+     * 
+     * @param pageName
+     *            the logical name of the page to link to
+     * @param override
+     *            if true, the context is used even if empty (normally, the target page is allowed to passivate,
+     *            providing a context, when the provided context is empty)
+     * @param context
+     *            the activation context for the page. If omitted, the activation context is obtained from the
+     *            target page
      * @return link for a render request to the targetted page
      * @since 5.1.0.0
      */
@@ -91,23 +104,34 @@ public interface ComponentPageElementRes
     /**
      * Creates a page render request link to render a specific page. Using a page class, rather than a page name, is
      * more refactoring safe (in the even the page is renamed or moved).
-     *
-     * @param pageClass identifies the page to link to
-     * @param override  if true, the context is used even if empty (normally, the target page is allowed to passivate,
-     *                  providing a context, when the provided context is empty)
-     * @param context   the activation context for the page. If omitted, the activation context is obtained from the
-     *                  target page
+     * 
+     * @param pageClass
+     *            identifies the page to link to
+     * @param override
+     *            if true, the context is used even if empty (normally, the target page is allowed to passivate,
+     *            providing a context, when the provided context is empty)
+     * @param context
+     *            the activation context for the page. If omitted, the activation context is obtained from the
+     *            target page
      * @return link for a render request to the targetted page
      * @since 5.1
      */
     Link createPageRenderLink(Class pageClass, boolean override, Object... context);
 
     /**
-     * Returns the event logger for the provided component logger.  The event logger is based on the component logger's
+     * Returns the event logger for the provided component logger. The event logger is based on the component logger's
      * name (which matches the component class name) with a "tapestry..events." prefix.
-     *
-     * @param componentLogger provides base name for logger
+     * 
+     * @param componentLogger
+     *            provides base name for logger
      * @return the logger
      */
     Logger getEventLogger(Logger componentLogger);
+
+    /**
+     * Wrapper around {@link PerthreadManager#createValue(Object)}.
+     * 
+     * @since 5.2.0
+     */
+    <T> PerThreadValue<T> createPerThreadValue(Object key);
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementResourcesImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementResourcesImpl.java?rev=963132&r1=963131&r2=963132&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementResourcesImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementResourcesImpl.java Sun Jul 11 19:06:14 2010
@@ -16,6 +16,7 @@ package org.apache.tapestry5.internal.st
 
 import org.apache.tapestry5.ComponentResources;
 import org.apache.tapestry5.Link;
+import org.apache.tapestry5.internal.InternalConstants;
 import org.apache.tapestry5.internal.services.ComponentClassCache;
 import org.apache.tapestry5.internal.services.LinkSource;
 import org.apache.tapestry5.internal.services.RequestPageCache;
@@ -24,6 +25,8 @@ import org.apache.tapestry5.ioc.LoggerSo
 import org.apache.tapestry5.ioc.Messages;
 import org.apache.tapestry5.ioc.OperationTracker;
 import org.apache.tapestry5.ioc.internal.util.Defense;
+import org.apache.tapestry5.ioc.services.PerThreadValue;
+import org.apache.tapestry5.ioc.services.PerthreadManager;
 import org.apache.tapestry5.ioc.services.TypeCoercer;
 import org.apache.tapestry5.model.ComponentModel;
 import org.apache.tapestry5.services.ComponentClassResolver;
@@ -35,8 +38,6 @@ import java.util.Locale;
 
 public class ComponentPageElementResourcesImpl implements ComponentPageElementResources
 {
-    private static final Object[] EMPTY = new Object[0];
-
     private final Locale locale;
 
     private final ComponentMessagesSource componentMessagesSource;
@@ -57,10 +58,12 @@ public class ComponentPageElementResourc
 
     private final OperationTracker tracker;
 
+    private final PerthreadManager perThreadManager;
+
     public ComponentPageElementResourcesImpl(Locale locale, ComponentMessagesSource componentMessagesSource,
             TypeCoercer typeCoercer, ComponentClassCache componentClassCache, ContextValueEncoder contextValueEncoder,
             LinkSource linkSource, RequestPageCache requestPageCache, ComponentClassResolver componentClassResolver,
-            LoggerSource loggerSource, OperationTracker tracker)
+            LoggerSource loggerSource, OperationTracker tracker, PerthreadManager perThreadManager)
     {
         this.componentMessagesSource = componentMessagesSource;
         this.locale = locale;
@@ -72,6 +75,7 @@ public class ComponentPageElementResourc
         this.componentClassResolver = componentClassResolver;
         this.loggerSource = loggerSource;
         this.tracker = tracker;
+        this.perThreadManager = perThreadManager;
     }
 
     public Messages getMessages(ComponentModel componentModel)
@@ -131,7 +135,7 @@ public class ComponentPageElementResourc
 
     private Object[] defaulted(Object[] context)
     {
-        return context == null ? EMPTY : context;
+        return context == null ? InternalConstants.EMPTY_STRING_ARRAY: context;
     }
 
     public <T> T invoke(String description, Invokable<T> operation)
@@ -143,4 +147,10 @@ public class ComponentPageElementResourc
     {
         tracker.run(description, operation);
     }
+
+    public <T> PerThreadValue<T> createPerThreadValue(Object key)
+    {
+        return perThreadManager.createValue(key);
+    }
+
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementResourcesSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementResourcesSourceImpl.java?rev=963132&r1=963131&r2=963132&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementResourcesSourceImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementResourcesSourceImpl.java Sun Jul 11 19:06:14 2010
@@ -21,6 +21,7 @@ import org.apache.tapestry5.ioc.LoggerSo
 import org.apache.tapestry5.ioc.OperationTracker;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.Defense;
+import org.apache.tapestry5.ioc.services.PerthreadManager;
 import org.apache.tapestry5.ioc.services.TypeCoercer;
 import org.apache.tapestry5.services.ComponentClassResolver;
 import org.apache.tapestry5.services.ContextValueEncoder;
@@ -51,10 +52,12 @@ public class ComponentPageElementResourc
 
     private final OperationTracker tracker;
 
+    private final PerthreadManager perThreadManager;
+
     public ComponentPageElementResourcesSourceImpl(ComponentMessagesSource componentMessagesSource,
             TypeCoercer typeCoercer, ComponentClassCache componentClassCache, ContextValueEncoder contextValueEncoder,
             LinkSource linkSource, RequestPageCache requestPageCache, ComponentClassResolver componentClassResolver,
-            LoggerSource loggerSource, OperationTracker tracker)
+            LoggerSource loggerSource, OperationTracker tracker, PerthreadManager perThreadManager)
     {
         this.componentMessagesSource = componentMessagesSource;
         this.typeCoercer = typeCoercer;
@@ -65,6 +68,7 @@ public class ComponentPageElementResourc
         this.componentClassResolver = componentClassResolver;
         this.loggerSource = loggerSource;
         this.tracker = tracker;
+        this.perThreadManager = perThreadManager;
     }
 
     public ComponentPageElementResources get(Locale locale)
@@ -77,7 +81,7 @@ public class ComponentPageElementResourc
         {
             result = new ComponentPageElementResourcesImpl(locale, componentMessagesSource, typeCoercer,
                     componentClassCache, contextValueEncoder, linkSource, requestPageCache, componentClassResolver,
-                    loggerSource, tracker);
+                    loggerSource, tracker, perThreadManager);
 
             // Small race condition here, where we may create two instances of the CPER for the same locale,
             // but that's not worth worrying about.

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=963132&r1=963131&r2=963132&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 Sun Jul 11 19:06:14 2010
@@ -15,6 +15,7 @@
 package org.apache.tapestry5.internal.structure;
 
 import org.apache.tapestry5.ComponentResources;
+import org.apache.tapestry5.ioc.services.PerthreadManager;
 import org.apache.tapestry5.runtime.Component;
 import org.apache.tapestry5.runtime.PageLifecycleListener;
 import org.slf4j.Logger;
@@ -26,8 +27,14 @@ import java.util.Locale;
  * 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.
+ * Starting in release 5.2, the nature of pages changed considerably. Pages are no longer pooled instances. Instead,
+ * pages are shared instances (per locale) but all internal <em>mutable</em> state is stored inside
+ * {@link PerthreadManager}. Page construction time is considered to extend past the
+ * {@link PageLifecycleListener#containingPageDidLoad()} lifecycle notification. This is not quite perfect from a
+ * threading point-of-view (arguably, even write-once-read-many fields should be protected by synchronized blocks or
+ * other mechanisms). At best, we can be assured that the entire page construction phase is protected by a single
+ * synchronized block (but not on the page itself). An ideal system would build the page bottom to top so that all
+ * assignments could take place in constructors, assigning to final fields. Maybe some day.
  * <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.
@@ -71,9 +78,13 @@ public interface Page
      * <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().
+     * containingPageDidDetach().
      * <p/>
      * The page pool should discard pages that are dirty, rather than store them into the pool.
+     * <p>
+     * Under Tapestry 5.2 and pool-less pages, the result is ignored; all mutable state is expected to be discarded
+     * automatically from the {@link PerthreadManager}. A future release of Tapestry will likely convert this method to
+     * type void.
      * 
      * @return true if the page is "dirty", false otherwise
      * @see org.apache.tapestry5.runtime.PageLifecycleListener#containingPageDidDetach()
@@ -82,11 +93,12 @@ public interface Page
 
     /**
      * 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
+     * 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()}.
+     * <p>
+     * First all listeners have {@link PageLifecycleListener#restoreStateBeforePageAttach()} invoked, followed by
+     * {@link PageLifecycleListener#containingPageDidAttach()}.
      */
-
     void attached();
 
     /**
@@ -94,7 +106,6 @@ public interface Page
      * 
      * @see org.apache.tapestry5.runtime.PageLifecycleListener#containingPageDidLoad()
      */
-
     void loaded();
 
     /**
@@ -157,11 +168,15 @@ public interface Page
      * where a component
      * causes a runtime exception that aborts the render early, leaving the page in an invalid
      * state.
+     * 
+     * @deprecated No longer useful with non-pooled pages, to be removed for efficiency
      */
     void incrementDirtyCount();
 
     /**
      * Called as a component finishes rendering itself.
+     * 
+     * @deprecated No longer useful with non-pooled pages, to be removed for efficiency
      */
     void decrementDirtyCount();
 

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=963132&r1=963131&r2=963132&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 Sun Jul 11 19:06:14 2010
@@ -18,9 +18,12 @@ import org.apache.tapestry5.ComponentRes
 import org.apache.tapestry5.internal.services.PersistentFieldManager;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.Defense;
+import org.apache.tapestry5.ioc.internal.util.OneShotLock;
 
 import static org.apache.tapestry5.ioc.internal.util.Defense.notNull;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.ioc.services.PerThreadValue;
+import org.apache.tapestry5.ioc.services.PerthreadManager;
 import org.apache.tapestry5.runtime.Component;
 import org.apache.tapestry5.runtime.PageLifecycleListener;
 import org.apache.tapestry5.services.PersistentFieldBundle;
@@ -43,10 +46,12 @@ public class PageImpl implements Page
 
     private final List<PageResetListener> resetListeners = CollectionFactory.newList();
 
-    private int dirtyCount;
+    private final PerThreadValue<Integer> dirtyCount;
 
     private boolean loadComplete;
 
+    private final OneShotLock lock = new OneShotLock();
+
     /**
      * Obtained from the {@link org.apache.tapestry5.internal.services.PersistentFieldManager} when
      * first needed,
@@ -54,11 +59,14 @@ public class PageImpl implements Page
      */
     private PersistentFieldBundle fieldBundle;
 
-    public PageImpl(String name, Locale locale, PersistentFieldManager persistentFieldManager)
+    public PageImpl(String name, Locale locale, PersistentFieldManager persistentFieldManager,
+            PerthreadManager perThreadManager)
     {
         this.name = name;
         this.locale = locale;
         this.persistentFieldManager = persistentFieldManager;
+
+        dirtyCount = perThreadManager.createValue("PageDirtyCount:" + name);
     }
 
     @Override
@@ -93,6 +101,8 @@ public class PageImpl implements Page
 
     public void setRootElement(ComponentPageElement component)
     {
+        lock.check();
+
         rootElement = component;
     }
 
@@ -108,17 +118,21 @@ public class PageImpl implements Page
 
     public void addLifecycleListener(PageLifecycleListener listener)
     {
+        lock.check();
+
         lifecycleListeners.add(listener);
     }
 
     public void removeLifecycleListener(PageLifecycleListener listener)
     {
+        lock.check();
+
         lifecycleListeners.remove(listener);
     }
 
     public boolean detached()
     {
-        boolean result = dirtyCount > 0;
+        boolean result = dirtyCount.exists() ? dirtyCount.get() > 0 : false;
 
         for (PageLifecycleListener listener : lifecycleListeners)
         {
@@ -140,15 +154,19 @@ public class PageImpl implements Page
 
     public void loaded()
     {
+        lock.check();
+
         for (PageLifecycleListener listener : lifecycleListeners)
             listener.containingPageDidLoad();
 
         loadComplete = true;
+        
+        lock.lock();
     }
 
     public void attached()
     {
-        if (dirtyCount != 0)
+        if (dirtyCount.exists() && !dirtyCount.get().equals(0))
             throw new IllegalStateException(StructureMessages.pageIsDirty(this));
 
         for (PageLifecycleListener listener : lifecycleListeners)
@@ -181,7 +199,9 @@ public class PageImpl implements Page
 
     public void decrementDirtyCount()
     {
-        dirtyCount--;
+        int newCount = dirtyCount.get() - 1;
+
+        dirtyCount.set(newCount);
     }
 
     public void discardPersistentFieldChanges()
@@ -191,7 +211,9 @@ public class PageImpl implements Page
 
     public void incrementDirtyCount()
     {
-        dirtyCount++;
+        int newCount = dirtyCount.exists() ? dirtyCount.get() + 1 : 1;
+
+        dirtyCount.set(newCount);
     }
 
     public String getName()
@@ -202,6 +224,8 @@ public class PageImpl implements Page
     public void addResetListener(PageResetListener listener)
     {
         Defense.notNull(listener, "listener");
+        
+        lock.check();
 
         resetListeners.add(listener);
     }

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/RenderPhaseEvent.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/RenderPhaseEvent.java?rev=963132&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/RenderPhaseEvent.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/RenderPhaseEvent.java Sun Jul 11 19:06:14 2010
@@ -0,0 +1,43 @@
+// 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;
+
+import org.apache.tapestry5.internal.services.EventImpl;
+import org.slf4j.Logger;
+
+public class RenderPhaseEvent extends EventImpl
+{
+    private final RenderPhaseEventHandler handler;
+
+    public RenderPhaseEvent(RenderPhaseEventHandler handler, Logger logger)
+    {
+        super(handler, logger);
+
+        this.handler = handler;
+
+    }
+
+    public void reset()
+    {
+        super.reset();
+
+        handler.reset();
+    }
+
+    public boolean getResult()
+    {
+        return handler.getResult();
+    }
+}

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

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/RenderPhaseEventHandler.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/RenderPhaseEventHandler.java?rev=963132&r1=963131&r2=963132&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/RenderPhaseEventHandler.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/RenderPhaseEventHandler.java Sun Jul 11 19:06:14 2010
@@ -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,
@@ -27,15 +27,22 @@ import org.apache.tapestry5.runtime.Rend
 /**
  * Used by {@link org.apache.tapestry5.internal.structure.ComponentPageElementImpl} to track the results of invoking the
  * component methods for a render phase event.
- *
+ * 
  * @since 5.0.19
  */
 class RenderPhaseEventHandler implements ComponentEventCallback
 {
+    private final RenderQueue renderQueue;
+
     private boolean result = true;
 
     private List<RenderCommand> commands;
 
+    public RenderPhaseEventHandler(RenderQueue renderQueue)
+    {
+        this.renderQueue = renderQueue;
+    }
+
     boolean getResult()
     {
         return result;
@@ -43,6 +50,12 @@ class RenderPhaseEventHandler implements
 
     void reset()
     {
+        if (commands != null)
+        {
+            for (RenderCommand command : commands)
+                renderQueue.push(command);
+        }
+
         result = true;
 
         commands = null;
@@ -82,22 +95,16 @@ class RenderPhaseEventHandler implements
             return false;
         }
 
-        throw new RuntimeException(StructureMessages.wrongPhaseResultType(
-                Arrays.asList(Boolean.class.getName(), Renderable.class.getName(), RenderCommand.class.getName())));
+        throw new RuntimeException(StructureMessages.wrongPhaseResultType(Arrays.asList(Boolean.class.getName(),
+                Renderable.class.getName(), RenderCommand.class.getName())));
     }
 
     private void add(RenderCommand command)
     {
-        if (commands == null) commands = CollectionFactory.newList();
+        if (commands == null)
+            commands = CollectionFactory.newList();
 
         commands.add(command);
     }
 
-    public void queueCommands(RenderQueue queue)
-    {
-        if (commands == null) return;
-
-        for (RenderCommand command : commands)
-            queue.push(command);
-    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/PageImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/PageImplTest.java?rev=963132&r1=963131&r2=963132&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/PageImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/PageImplTest.java Sun Jul 11 19:06:14 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,10 +15,13 @@
 package org.apache.tapestry5.internal.structure;
 
 import org.apache.tapestry5.internal.test.InternalBaseTestCase;
+import org.apache.tapestry5.ioc.services.PerthreadManager;
 import org.apache.tapestry5.runtime.PageLifecycleListener;
 import static org.easymock.EasyMock.contains;
 import static org.easymock.EasyMock.same;
 import org.slf4j.Logger;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import java.util.Locale;
@@ -29,6 +32,20 @@ public class PageImplTest extends Intern
 
     private static final String LOGICAL_PAGE_NAME = "MyPage";
 
+    private PerthreadManager perThreadManager;
+    
+    @BeforeClass
+    public void setup()
+    {
+        perThreadManager = getService(PerthreadManager.class);
+    }
+    
+    @AfterMethod
+    public void cleanup()
+    {
+        perThreadManager.cleanup();
+    }
+    
     @Test
     public void accessor_methods()
     {
@@ -36,7 +53,7 @@ public class PageImplTest extends Intern
 
         replay();
 
-        Page page = new PageImpl(LOGICAL_PAGE_NAME, locale, null);
+        Page page = new PageImpl(LOGICAL_PAGE_NAME, locale, null, perThreadManager);
 
         assertNull(page.getRootElement());
 
@@ -60,7 +77,7 @@ public class PageImplTest extends Intern
 
         replay();
 
-        Page page = new PageImpl(null, locale, null);
+        Page page = new PageImpl(null, locale, null, perThreadManager);
 
         page.addLifecycleListener(listener1);
         page.addLifecycleListener(listener2);
@@ -82,7 +99,7 @@ public class PageImplTest extends Intern
 
         replay();
 
-        Page page = new PageImpl(null, locale, null);
+        Page page = new PageImpl(null, locale, null, perThreadManager);
 
         page.addLifecycleListener(listener);
 
@@ -116,7 +133,7 @@ public class PageImplTest extends Intern
 
         replay();
 
-        Page page = new PageImpl(null, locale, null);
+        Page page = new PageImpl(null, locale, null, perThreadManager);
         page.setRootElement(element);
 
         page.addLifecycleListener(listener1);
@@ -146,7 +163,7 @@ public class PageImplTest extends Intern
 
         replay();
 
-        Page page = new PageImpl(null, locale, null);
+        Page page = new PageImpl(null, locale, null, perThreadManager);
 
         page.addLifecycleListener(listener1);
         page.addLifecycleListener(listener2);
@@ -172,7 +189,7 @@ public class PageImplTest extends Intern
 
         replay();
 
-        Page page = new PageImpl(LOGICAL_PAGE_NAME, locale, null);
+        Page page = new PageImpl(LOGICAL_PAGE_NAME, locale, null, perThreadManager);
 
         page.addLifecycleListener(listener1);
         page.addLifecycleListener(listener2);
@@ -189,7 +206,7 @@ public class PageImplTest extends Intern
 
         replay();
 
-        Page page = new PageImpl(LOGICAL_PAGE_NAME, locale, null);
+        Page page = new PageImpl(LOGICAL_PAGE_NAME, locale, null, perThreadManager);
 
         page.setRootElement(root);