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 2008/12/18 00:36:02 UTC

svn commit: r727577 - in /tapestry/tapestry5/branches/hlship-5.0-perf: src/site/apt/ tapestry-core/src/main/java/org/apache/tapestry5/internal/model/ tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ tapestry-core/src/main/java/org/ap...

Author: hlship
Date: Wed Dec 17 15:36:02 2008
New Revision: 727577

URL: http://svn.apache.org/viewvc?rev=727577&view=rev
Log:
TAP5-417: Tapestry 5.0 Performance Improvements
- Optimize each component's render state machine

Added:
    tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/RenderPhaseEventHandler.java
Modified:
    tapestry/tapestry5/branches/hlship-5.0-perf/src/site/apt/upgrade.apt
    tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java
    tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EventImpl.java
    tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageLoaderProcessor.java
    tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageResponseRendererImpl.java
    tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RenderQueueImpl.java
    tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java
    tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/AbstractIncludeAssetWorker.java
    tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorker.java
    tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/model/ComponentModel.java
    tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/model/MutableComponentModel.java
    tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformConstants.java
    tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/model/MutableComponentModelImplTest.java
    tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java
    tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/ComponentPageElementImplTest.java
    tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorkerTest.java
    tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/resources/log4j.properties
    tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/CollectionFactory.java

Modified: tapestry/tapestry5/branches/hlship-5.0-perf/src/site/apt/upgrade.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/src/site/apt/upgrade.apt?rev=727577&r1=727576&r2=727577&view=diff
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/src/site/apt/upgrade.apt (original)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/src/site/apt/upgrade.apt Wed Dec 17 15:36:02 2008
@@ -12,6 +12,16 @@
   You should also check the {{{release-notes.html}project-wide release notes}} for information
   about bugs fixes and other improvements.
 
+Release 5.0.19
+
+  As part of the changes related to
+  {{{https://issues.apache.org/jira/browse/TAP5-417}TAP5-417: Tapestry 5.0 Performance Improvements}},
+  you should be aware that Tapestry will now optimize the render phase state machine.
+  This is only relevant to advanced users who have written a ComponentClassTransformWorker
+  that adds logic to a render phase: it is necessary to
+  {{{apidocs/org/apache/tapestry5/model/MutableComponentModel.html#addRenderPhase(Class)}inform
+  Tapestry that the render phase should be invoked}}.
+
 Release 5.0.16
 
   The client-side class Tapestry.Zone has been renamed to Tapestry.ZoneManager.

Modified: tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java?rev=727577&r1=727576&r2=727577&view=diff
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java (original)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/model/MutableComponentModelImpl.java Wed Dec 17 15:36:02 2008
@@ -26,6 +26,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Internal implementation of {@link org.apache.tapestry5.model.MutableComponentModel}.
@@ -59,6 +60,8 @@
 
     private Map<String, String> metaData;
 
+    private Set<Class> handledRenderPhases;
+
     public MutableComponentModelImpl(String componentClassName, Logger logger, Resource baseResource,
                                      ComponentModel parentModel)
     {
@@ -272,6 +275,15 @@
         metaData.put(key, value);
     }
 
+    public void addRenderPhase(Class renderPhase)
+    {
+        Defense.notNull(renderPhase, "renderPhase");
+
+        if (handledRenderPhases == null) handledRenderPhases = CollectionFactory.newSet();
+
+        handledRenderPhases.add(renderPhase);
+    }
+
     public String getMeta(String key)
     {
         String result = InternalUtils.get(metaData, key);
@@ -281,4 +293,16 @@
         return result;
     }
 
+    public Set<Class> getHandledRenderPhases()
+    {
+        Set<Class> result = CollectionFactory.newSet();
+
+        if (parentModel != null)
+            result.addAll(parentModel.getHandledRenderPhases());
+
+        if (handledRenderPhases != null)
+            result.addAll(handledRenderPhases);
+
+        return result;
+    }
 }

Modified: tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EventImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EventImpl.java?rev=727577&r1=727576&r2=727577&view=diff
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EventImpl.java (original)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EventImpl.java Wed Dec 17 15:36:02 2008
@@ -30,6 +30,8 @@
 
     private final Logger logger;
 
+    private final boolean debugEnabled;
+
     /**
      * @param handler informed of return values from methods, deems when the event is aborted
      * @param logger  used to log method invocations
@@ -38,6 +40,8 @@
     {
         this.handler = notNull(handler, "handler");
         this.logger = logger;
+
+        debugEnabled = logger.isDebugEnabled();
     }
 
     public boolean isAborted()
@@ -47,8 +51,8 @@
 
     public void setMethodDescription(String methodDescription)
     {
-        if (logger.isDebugEnabled())
-            logger.debug(TapestryMarkers.EVENT_HANDLER_METHOD, "Invoking: {}", methodDescription);
+        if (debugEnabled)
+            logger.debug(TapestryMarkers.EVENT_HANDLER_METHOD, "Invoking: " + methodDescription);
 
         this.methodDescription = methodDescription;
     }
@@ -65,7 +69,8 @@
             throw new IllegalStateException(ServicesMessages.componentEventIsAborted(methodDescription));
 
 
-        if (result != null) aborted |= handler.handleResult(result);
+        if (result != null)
+            aborted |= handler.handleResult(result);
 
         return aborted;
     }
@@ -74,4 +79,10 @@
     {
         return methodDescription;
     }
+
+    public void reset()
+    {
+        aborted = false;
+        methodDescription = null;
+    }
 }

Modified: tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageLoaderProcessor.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageLoaderProcessor.java?rev=727577&r1=727576&r2=727577&view=diff
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageLoaderProcessor.java (original)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageLoaderProcessor.java Wed Dec 17 15:36:02 2008
@@ -561,15 +561,19 @@
         return result;
     }
 
-    private void cdata(CDATAToken token)
+    private void cdata(final CDATAToken token)
     {
-        final String content = token.getContent();
-
         PageElement element = new PageElement()
         {
             public void render(MarkupWriter writer, RenderQueue queue)
             {
-                writer.cdata(content);
+                writer.cdata(token.getContent());
+            }
+
+            @Override
+            public String toString()
+            {
+                return String.format("CDATA[%s]", token.getLocation());
             }
         };
 
@@ -584,6 +588,12 @@
             {
                 writer.defineNamespace(token.getNamespaceURI(), token.getNamespacePrefix());
             }
+
+            @Override
+            public String toString()
+            {
+                return String.format("DefineNamespace[%s %s]", token.getNamespacePrefix(), token.getNamespaceURI());
+            }
         };
 
         addToBody(element);

Modified: tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageResponseRendererImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageResponseRendererImpl.java?rev=727577&r1=727576&r2=727577&view=diff
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageResponseRendererImpl.java (original)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PageResponseRendererImpl.java Wed Dec 17 15:36:02 2008
@@ -20,6 +20,7 @@
 import org.apache.tapestry5.ioc.internal.util.Defense;
 import org.apache.tapestry5.services.MarkupWriterFactory;
 import org.apache.tapestry5.services.Response;
+import org.slf4j.Logger;
 
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -34,13 +35,16 @@
 
     private final Response response;
 
+    private final Logger logger;
+
     public PageResponseRendererImpl(MarkupWriterFactory markupWriterFactory, PageMarkupRenderer markupRenderer,
-                                    PageContentTypeAnalyzer pageContentTypeAnalyzer, Response response)
+                                    PageContentTypeAnalyzer pageContentTypeAnalyzer, Response response, Logger logger)
     {
         this.markupWriterFactory = markupWriterFactory;
         this.markupRenderer = markupRenderer;
         this.pageContentTypeAnalyzer = pageContentTypeAnalyzer;
         this.response = response;
+        this.logger = logger;
     }
 
     public void renderPageResponse(Page page) throws IOException
@@ -58,8 +62,21 @@
 
         PrintWriter pw = response.getPrintWriter(contentType.toString());
 
+        long startNanos = System.nanoTime();
+
         writer.toMarkup(pw);
 
+        long endNanos = System.nanoTime();
+
+        if (logger.isDebugEnabled())
+        {
+            long elapsedNanos = endNanos - startNanos;
+            double elapsedSeconds = ((float) elapsedNanos) / 1000000000F;
+
+            logger.debug(String.format("Response DOM streamed to markup in %.3f seconds",
+                                       elapsedSeconds));
+        }
+
         pw.flush();
     }
 }

Modified: tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RenderQueueImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RenderQueueImpl.java?rev=727577&r1=727576&r2=727577&view=diff
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RenderQueueImpl.java (original)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RenderQueueImpl.java Wed Dec 17 15:36:02 2008
@@ -41,6 +41,8 @@
 
     public void push(RenderCommand command)
     {
+        Defense.notNull(command, "command");
+
         queue.push(command);
     }
 

Modified: tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java?rev=727577&r1=727576&r2=727577&view=diff
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java (original)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/ComponentPageElementImpl.java Wed Dec 17 15:36:02 2008
@@ -15,6 +15,7 @@
 package org.apache.tapestry5.internal.structure;
 
 import org.apache.tapestry5.*;
+import org.apache.tapestry5.annotations.*;
 import org.apache.tapestry5.dom.Element;
 import org.apache.tapestry5.internal.InternalComponentResources;
 import org.apache.tapestry5.internal.TapestryInternalUtils;
@@ -31,6 +32,7 @@
 import org.apache.tapestry5.ioc.internal.util.TapestryException;
 import org.apache.tapestry5.model.ComponentModel;
 import org.apache.tapestry5.model.ParameterModel;
+import org.apache.tapestry5.runtime.Component;
 import org.apache.tapestry5.runtime.*;
 import org.slf4j.Logger;
 
@@ -67,16 +69,6 @@
 
     private static final Block PLACEHOLDER_BLOCK = new PlaceholderBlock();
 
-    /**
-     * @see #render(org.apache.tapestry5.MarkupWriter, org.apache.tapestry5.runtime.RenderQueue)
-     */
-    private static final RenderCommand POP_COMPONENT_ID = new RenderCommand()
-    {
-        public void render(MarkupWriter writer, RenderQueue queue)
-        {
-            queue.endComponent();
-        }
-    };
 
     private static final ComponentCallback CONTAINING_PAGE_DID_ATTACH = new LifecycleNotificationComponentCallback()
     {
@@ -125,318 +117,339 @@
         return list == null ? 0 : list.size();
     }
 
-    private static class RenderPhaseEventHandler implements ComponentEventCallback
+    private abstract class AbstractPhase extends AbstractComponentCallback implements RenderCommand
     {
-        private boolean result = true;
+        private final String name;
 
-        private List<RenderCommand> commands;
+        private MarkupWriter writer;
 
-        boolean getResult()
+        public AbstractPhase(String name)
         {
-            return result;
+            super(sharedEvent);
+
+            this.name = name;
         }
 
-        public boolean handleResult(Object result)
+        @Override
+        public String toString()
         {
-            if (result instanceof Boolean)
-            {
-                this.result = (Boolean) result;
-                return true; // abort other handler methods
-            }
-
-            if (result instanceof RenderCommand)
-            {
-                RenderCommand command = (RenderCommand) result;
-
-                add(command);
-
-                return false; // do not abort!
-            }
-
-            if (result instanceof Renderable)
-            {
-                final Renderable renderable = (Renderable) result;
+            return phaseToString(name);
+        }
 
-                RenderCommand wrapper = new RenderCommand()
-                {
-                    public void render(MarkupWriter writer, RenderQueue queue)
-                    {
-                        renderable.render(writer);
-                    }
-                };
+        void reset(RenderQueue queue)
+        {
+            sharedEventHandler.queueCommands(queue);
 
-                add(wrapper);
+            sharedEventHandler.reset();
 
-                return false;
-            }
+            sharedEvent.reset();
 
-            throw new RuntimeException(StructureMessages.wrongPhaseResultType(Boolean.class));
+            writer = null;
         }
 
-        private void add(RenderCommand command)
+        void callback(boolean reverse, MarkupWriter writer)
         {
-            if (commands == null) commands = CollectionFactory.newList();
+            this.writer = writer;
 
-            commands.add(command);
+            invoke(reverse, this);
         }
 
-        public void queueCommands(RenderQueue queue)
+        public void run(Component component)
         {
-            if (commands == null) return;
+            invokeComponent(component, writer, sharedEvent);
+        }
 
-            for (RenderCommand command : commands)
-                queue.push(command);
+        protected boolean getResult()
+        {
+            return sharedEventHandler.getResult();
         }
+
+        protected abstract void invokeComponent(Component component, MarkupWriter writer, Event event);
     }
 
-    private final RenderCommand afterRender = new RenderCommand()
+    private class SetupRenderPhase extends AbstractPhase
     {
-        public void render(final MarkupWriter writer, RenderQueue queue)
+        public SetupRenderPhase()
         {
-            RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler, getEventLogger());
+            super("SetupRender");
+        }
 
-            ComponentCallback callback = new AbstractComponentCallback(event)
-            {
-                public void run(Component component)
-                {
-                    component.afterRender(writer, event);
-                }
-            };
+        protected void invokeComponent(Component component, MarkupWriter writer, Event event)
+        {
+            component.setupRender(writer, event);
+        }
 
-            invoke(true, callback);
+        public void render(final MarkupWriter writer, RenderQueue queue)
+        {
+            callback(false, writer);
 
-            if (!handler.getResult()) queue.push(beginRender);
+            push(queue, getResult(), beginRenderPhase, cleanupRenderPhase);
 
-            handler.queueCommands(queue);
+            reset(queue);
         }
+    }
 
-        @Override
-        public String toString()
+    private class BeginRenderPhase extends AbstractPhase
+    {
+        private BeginRenderPhase()
         {
-            return phaseToString("AfterRender");
+            super("BeginRender");
         }
-    };
 
-    private final RenderCommand afterRenderBody = new RenderCommand()
-    {
-        public void render(final MarkupWriter writer, RenderQueue queue)
+        protected void invokeComponent(Component component, MarkupWriter writer, Event event)
         {
-            RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler, getEventLogger());
+            component.beginRender(writer, event);
+        }
 
-            ComponentCallback callback = new AbstractComponentCallback(event)
-            {
-                public void run(Component component)
-                {
-                    component.afterRenderBody(writer, event);
-                }
-            };
+        public void render(final MarkupWriter writer, final RenderQueue queue)
+        {
+            callback(false, writer);
 
-            invoke(true, callback);
+            push(queue, afterRenderPhase);
+            push(queue, getResult(), beforeRenderTemplatePhase, null);
 
-            if (!handler.getResult()) queue.push(beforeRenderBody);
+            reset(queue);
+        }
+    }
 
-            handler.queueCommands(queue);
+    /**
+     * Replaces {@link org.apache.tapestry5.internal.structure.ComponentPageElementImpl.BeginRenderPhase} when there a
+     * handler for AfterRender but not BeginRender.
+     */
+    private class OptimizedBeginRenderPhase implements RenderCommand
+    {
+        public void render(MarkupWriter writer, RenderQueue queue)
+        {
+            push(queue, afterRenderPhase);
+            push(queue, beforeRenderTemplatePhase);
         }
 
         @Override
         public String toString()
         {
-            return phaseToString("AfterRenderBody");
+            return phaseToString("OptimizedBeginRenderPhase");
         }
-    };
+    }
 
-    private final RenderCommand afterRenderTemplate = new RenderCommand()
+    /**
+     * Reponsible for rendering the component's template.  Even a component that doesn't have a template goes through
+     * this phase, as a synthetic template (used to trigger the rendering of the component's body) will be supplied.
+     */
+    private class BeforeRenderTemplatePhase extends AbstractPhase
     {
+        private BeforeRenderTemplatePhase()
+        {
+            super("BeforeRenderTemplate");
+        }
+
+
+        protected void invokeComponent(Component component, MarkupWriter writer, Event event)
+        {
+            component.beforeRenderTemplate(writer, event);
+        }
+
         public void render(final MarkupWriter writer, final RenderQueue queue)
         {
-            RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler, getEventLogger());
+            callback(false, writer);
 
-            ComponentCallback callback = new AbstractComponentCallback(event)
-            {
-                public void run(Component component)
-                {
-                    component.afterRenderTemplate(writer, event);
-                }
-            };
+            push(queue, afterRenderTemplatePhase);
 
-            invoke(true, callback);
+            if (getResult())
+                pushElements(queue, template);
 
-            if (!handler.getResult()) queue.push(beforeRenderTemplate);
+            reset(queue);
+        }
+    }
+
+    /**
+     * Alternative version of BeforeRenderTemplatePhase used when the BeforeRenderTemplate render phase is not handled.
+     */
+    private class RenderTemplatePhase implements RenderCommand
+    {
+        public void render(MarkupWriter writer, RenderQueue queue)
+        {
+            push(queue, afterRenderTemplatePhase);
 
-            handler.queueCommands(queue);
+            pushElements(queue, template);
         }
 
         @Override
         public String toString()
         {
-            return phaseToString("AfterRenderTemplate");
+            return phaseToString("RenderTemplate");
         }
-    };
+    }
 
-    private final RenderCommand beforeRenderBody = new RenderCommand()
+    private class BeforeRenderBodyPhase extends AbstractPhase
     {
-        public void render(final MarkupWriter writer, RenderQueue queue)
+        private BeforeRenderBodyPhase()
         {
-            RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler, getEventLogger());
+            super("BeforeRenderBody");
+        }
 
-            ComponentCallback callback = new AbstractComponentCallback(event)
-            {
-                public void run(Component component)
-                {
-                    component.beforeRenderBody(writer, event);
-                }
-            };
+        protected void invokeComponent(Component component, MarkupWriter writer, Event event)
+        {
+            component.beforeRenderBody(writer, event);
+        }
 
-            invoke(false, callback);
+        public void render(final MarkupWriter writer, RenderQueue queue)
+        {
+            callback(false, writer);
 
-            queue.push(afterRenderBody);
+            push(queue, afterRenderBodyPhase);
 
-            if (handler.getResult() && bodyBlock != null) queue.push(bodyBlock);
+            if (getResult() && bodyBlock != null)
+                queue.push(bodyBlock);
 
-            handler.queueCommands(queue);
+            reset(queue);
         }
+    }
 
-        @Override
-        public String toString()
-        {
-            return phaseToString("BeforeRenderBody");
-        }
-    };
 
-    private final RenderCommand beforeRenderTemplate = new RenderCommand()
+    private class AfterRenderBodyPhase extends AbstractPhase
     {
-        public void render(final MarkupWriter writer, final RenderQueue queue)
+
+        private AfterRenderBodyPhase()
         {
-            final RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler, getEventLogger());
+            super("AfterRenderBody");
+        }
 
-            ComponentCallback callback = new AbstractComponentCallback(event)
-            {
-                public void run(Component component)
-                {
-                    component.beforeRenderTemplate(writer, event);
-                }
-            };
 
-            invoke(false, callback);
+        protected void invokeComponent(Component component, MarkupWriter writer, Event event)
+        {
+            component.afterRenderBody(writer, event);
+        }
 
-            queue.push(afterRenderTemplate);
+        public void render(final MarkupWriter writer, RenderQueue queue)
+        {
+            callback(true, writer);
 
-            if (handler.getResult()) pushElements(queue, template);
+            push(queue, getResult(), null, beforeRenderBodyPhase);
 
-            handler.queueCommands(queue);
+            reset(queue);
         }
+    }
 
-        @Override
-        public String toString()
+    private class AfterRenderTemplatePhase extends AbstractPhase
+    {
+        private AfterRenderTemplatePhase()
         {
-            return phaseToString("BeforeRenderTemplate");
+            super("AfterRenderTemplate");
         }
-    };
 
-    private final RenderCommand beginRender = new RenderCommand()
-    {
-        public void render(final MarkupWriter writer, final RenderQueue queue)
+        protected void invokeComponent(Component component, MarkupWriter writer, Event event)
         {
-            RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler, getEventLogger());
-
-            ComponentCallback callback = new AbstractComponentCallback(event)
-            {
-                public void run(Component component)
-                {
-                    component.beginRender(writer, event);
-                }
-            };
-
-            invoke(false, callback);
-
-            queue.push(afterRender);
+            component.afterRenderTemplate(writer, event);
+        }
 
-            // If the component has no template whatsoever, then a
-            // renderBody element is added as the lone element of the component's template.
-            // So every component will have a non-empty template.
+        public void render(final MarkupWriter writer, final RenderQueue queue)
+        {
+            callback(true, writer);
 
-            if (handler.getResult()) queue.push(beforeRenderTemplate);
+            push(queue, getResult(), null, beforeRenderTemplatePhase);
 
-            handler.queueCommands(queue);
+            reset(queue);
         }
+    }
 
-        @Override
-        public String toString()
+    private class AfterRenderPhase extends AbstractPhase
+    {
+        private AfterRenderPhase()
         {
-            return phaseToString("BeginRender");
+            super("AfterRender");
         }
-    };
 
-    private Map<String, Block> blocks;
+        protected void invokeComponent(Component component, MarkupWriter writer, Event event)
+        {
+            component.afterRender(writer, event);
+        }
 
-    private BlockImpl bodyBlock;
+        public void render(final MarkupWriter writer, RenderQueue queue)
+        {
+            callback(true, writer);
 
-    private Map<String, ComponentPageElement> children;
+            push(queue, getResult(), cleanupRenderPhase, beginRenderPhase);
 
-    private final String elementName;
+            reset(queue);
+        }
+    }
 
-    private final PageResources pageResources;
 
-    private final Logger logger;
+    private class CleanupRenderPhase extends AbstractPhase
+    {
+        private CleanupRenderPhase()
+        {
+            super("CleanupRender");
+        }
 
-    private final Logger eventLogger;
+        protected void invokeComponent(Component component, MarkupWriter writer, Event event)
+        {
+            component.cleanupRender(writer, event);
+        }
 
-    private final RenderCommand cleanupRender = new RenderCommand()
-    {
         public void render(final MarkupWriter writer, RenderQueue queue)
         {
-            RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler, getEventLogger());
+            callback(true, writer);
 
-            ComponentCallback callback = new AbstractComponentCallback(event)
-            {
-                public void run(Component component)
-                {
-                    component.cleanupRender(writer, event);
-                }
-            };
+            push(queue, getResult(), null, setupRenderPhase);
 
-            invoke(true, callback);
+            reset(queue);
+        }
+    }
 
-            if (handler.getResult())
-            {
-                rendering = false;
+    private class PostRenderCleanupPhase implements RenderCommand
+    {
+        /**
+         * Used to detect mismatches calls to {@link MarkupWriter#element(String, Object[])} } and {@link
+         * org.apache.tapestry5.MarkupWriter#end()}.  The expectation is that any element(s) begun by this component
+         * during rendering will be balanced by end() calls, resulting in the current element reverting to its initial
+         * value.
+         */
+        private final Element expectedElementAtCompletion;
 
-                Element current = writer.getElement();
+        PostRenderCleanupPhase(Element expectedElementAtCompletion)
+        {
+            this.expectedElementAtCompletion = expectedElementAtCompletion;
+        }
 
-                if (current != elementAtSetup)
-                    throw new TapestryException(StructureMessages.unbalancedElements(completeId), getLocation(), null);
+        public void render(MarkupWriter writer, RenderQueue queue)
+        {
+            rendering = false;
 
-                elementAtSetup = null;
+            Element current = writer.getElement();
 
-                invoke(false, POST_RENDER_CLEANUP);
+            if (current != expectedElementAtCompletion)
+                throw new TapestryException(StructureMessages.unbalancedElements(completeId), getLocation(), null);
 
-                // NOW and only now the component is done rendering and fully cleaned up. Decrement
-                // the page's dirty count. If the entire render goes well, then the page will be
-                // clean and can be stored into the pool for later reuse.
+            invoke(false, POST_RENDER_CLEANUP);
 
-                page.decrementDirtyCount();
-            }
-            else
-            {
-                queue.push(setupRender);
-            }
+            queue.endComponent();
 
-            handler.queueCommands(queue);
+            // Now and only now the component is done rendering and fully cleaned up. Decrement
+            // the page's dirty count. If the entire render goes well, then the page will be
+            // clean and can be stored into the pool for later reuse.
+
+            page.decrementDirtyCount();
         }
 
         @Override
         public String toString()
         {
-            return phaseToString("CleanupRender");
+            return phaseToString("PostRenderCleanup");
         }
-    };
+    }
+
+    private Map<String, Block> blocks;
+
+    private BlockImpl bodyBlock;
+
+    private Map<String, ComponentPageElement> children;
+
+    private final String elementName;
+
+    private final PageResources pageResources;
+
+    private final Logger eventLogger;
 
     private final String completeId;
 
@@ -470,65 +483,20 @@
 
     private boolean rendering;
 
-    /**
-     * Used to detect mismatches calls to {@link MarkupWriter#element(String, Object[])} } and {@link
-     * org.apache.tapestry5.MarkupWriter#end()}.  The expectation is that any element(s) begun by this component during
-     * rendering will be balanced by end() calls, resulting in the current element reverting to its initial value.
-     */
-    private Element elementAtSetup;
-
-    private final RenderCommand setupRender = new RenderCommand()
-    {
-        public void render(final MarkupWriter writer, RenderQueue queue)
-        {
-            // TODO: Check for recursive rendering.
-
-            rendering = true;
-
-            elementAtSetup = writer.getElement();
-
-            RenderPhaseEventHandler handler = new RenderPhaseEventHandler();
-            final Event event = new EventImpl(handler, getEventLogger());
-
-            ComponentCallback callback = new AbstractComponentCallback(event)
-            {
-                public void run(Component component)
-                {
-                    component.setupRender(writer, event);
-                }
-            };
-
-            invoke(false, callback);
-
-            queue.push(cleanupRender);
-
-            if (handler.getResult()) queue.push(beginRender);
-
-            handler.queueCommands(queue);
-        }
-
-        @Override
-        public String toString()
-        {
-            return phaseToString("SetupRender");
-        }
-    };
 
     // 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<PageElement> template = CollectionFactory.newList();
 
+    private boolean renderPhasesInitalized;
 
-    public ComponentPageElement newChild(String id, String elementName, Instantiator instantiator, Location location)
-    {
-        ComponentPageElementImpl child = new ComponentPageElementImpl(page, this, id, elementName, instantiator,
-                                                                      location, pageResources);
+    private RenderCommand setupRenderPhase, beginRenderPhase, beforeRenderTemplatePhase, beforeRenderBodyPhase,
+            afterRenderBodyPhase, afterRenderTemplatePhase, afterRenderPhase, cleanupRenderPhase;
 
-        addEmbeddedElement(child);
+    private final RenderPhaseEventHandler sharedEventHandler = new RenderPhaseEventHandler();
 
-        return child;
-    }
+    private final EventImpl sharedEvent;
 
     /**
      * Constructor for other components embedded within the root component or at deeper levels of the hierarchy.
@@ -593,8 +561,10 @@
 
         coreComponent = coreResources.getComponent();
 
-        logger = coreResources.getLogger();
+        Logger logger = coreResources.getLogger();
         eventLogger = pageResources.getEventLogger(logger);
+
+        sharedEvent = new EventImpl(sharedEventHandler, eventLogger);
     }
 
     /**
@@ -605,6 +575,82 @@
         this(page, null, null, null, instantiator, null, pageResources);
     }
 
+    private void initializeRenderPhases()
+    {
+        setupRenderPhase = new SetupRenderPhase();
+        beginRenderPhase = new BeginRenderPhase();
+        beforeRenderTemplatePhase = new BeforeRenderTemplatePhase();
+        beforeRenderBodyPhase = new BeforeRenderBodyPhase();
+        afterRenderBodyPhase = new AfterRenderBodyPhase();
+        afterRenderTemplatePhase = new AfterRenderTemplatePhase();
+        afterRenderPhase = new AfterRenderPhase();
+        cleanupRenderPhase = new CleanupRenderPhase();
+
+
+        // Now the optimization, where we remove, replace and collapse unused phases. We use
+        // the component models to determine which phases have handler methods for the
+        // render phases.
+
+        Set<Class> handled = coreResources.getComponentModel().getHandledRenderPhases();
+
+        if (mixinIdToComponentResources != null)
+        {
+            for (ComponentResources r : mixinIdToComponentResources.values())
+                handled.addAll(r.getComponentModel().getHandledRenderPhases());
+        }
+
+        if (!handled.contains(CleanupRender.class)) cleanupRenderPhase = null;
+
+        // Now, work back to front.
+
+        if (!handled.contains(AfterRender.class))
+            afterRenderPhase = cleanupRenderPhase;
+
+        if (!handled.contains(AfterRenderTemplate.class))
+            afterRenderTemplatePhase = null;
+
+        if (!handled.contains(AfterRenderBody.class))
+            afterRenderBodyPhase = null;
+
+        if (!handled.contains(BeforeRenderTemplate.class))
+            beforeRenderTemplatePhase = new RenderTemplatePhase();
+
+        if (!handled.contains(BeginRender.class))
+        {
+            RenderCommand replacement = handled.contains(AfterRender.class)
+                                        ? new OptimizedBeginRenderPhase()
+                                        : beforeRenderTemplatePhase;
+
+            beginRenderPhase = replacement;
+        }
+
+        if (!handled.contains(SetupRender.class))
+            setupRenderPhase = beginRenderPhase;
+
+        renderPhasesInitalized = true;
+    }
+
+    public ComponentPageElement newChild(String id, String elementName, Instantiator instantiator, Location location)
+    {
+        ComponentPageElementImpl child = new ComponentPageElementImpl(page, this, id, elementName, instantiator,
+                                                                      location, pageResources);
+
+        addEmbeddedElement(child);
+
+        return child;
+    }
+
+    void push(RenderQueue queue, boolean forward, RenderCommand forwardPhase, RenderCommand backwardPhase)
+    {
+        push(queue, forward ? forwardPhase : backwardPhase);
+    }
+
+    void push(RenderQueue queue, RenderCommand nextPhase)
+    {
+        if (nextPhase != null)
+            queue.push(nextPhase);
+    }
+
     void addEmbeddedElement(ComponentPageElement child)
     {
         if (children == null) children = CollectionFactory.newCaseInsensitiveMap();
@@ -612,6 +658,7 @@
         String childId = child.getId();
 
         ComponentPageElement existing = children.get(childId);
+
         if (existing != null)
             throw new TapestryException(StructureMessages.duplicateChildComponent(this, childId), child, null);
 
@@ -789,7 +836,8 @@
     {
         // If no body, then no beforeRenderBody or afterRenderBody
 
-        if (bodyBlock != null) queue.push(beforeRenderBody);
+        if (bodyBlock != null)
+            push(queue, beforeRenderBodyPhase);
     }
 
     public String getCompleteId()
@@ -960,6 +1008,12 @@
      */
     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.
+
+        if (!renderPhasesInitalized)
+            initializeRenderPhases();
+
         // TODO: An error if the _render flag is already set (recursive rendering not
         // allowed or advisable).
 
@@ -967,14 +1021,15 @@
 
         page.incrementDirtyCount();
 
-        queue.startComponent(coreResources);
+        // TODO: Check for recursive rendering.
+
+        rendering = true;
 
-        // POP_COMPONENT_ID will remove the component we just started.
+        queue.startComponent(coreResources);
 
-        queue.push(POP_COMPONENT_ID);
+        queue.push(new PostRenderCleanupPhase(writer.getElement()));
 
-        // This is the start of the real state machine for the component.
-        queue.push(setupRender);
+        push(queue, setupRenderPhase);
     }
 
     @Override

Added: tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/RenderPhaseEventHandler.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/RenderPhaseEventHandler.java?rev=727577&view=auto
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/RenderPhaseEventHandler.java (added)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/RenderPhaseEventHandler.java Wed Dec 17 15:36:02 2008
@@ -0,0 +1,101 @@
+// Copyright 2008 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.internal.structure;
+
+import org.apache.tapestry5.ComponentEventCallback;
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.Renderable;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.runtime.RenderCommand;
+import org.apache.tapestry5.runtime.RenderQueue;
+
+import java.util.List;
+
+/**
+ * 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 boolean result = true;
+
+    private List<RenderCommand> commands;
+
+    boolean getResult()
+    {
+        return result;
+    }
+
+    void reset()
+    {
+        result = true;
+
+        commands = null;
+    }
+
+    public boolean handleResult(Object result)
+    {
+        if (result instanceof Boolean)
+        {
+            this.result = (Boolean) result;
+            return true; // abort other handler methods
+        }
+
+        if (result instanceof RenderCommand)
+        {
+            RenderCommand command = (RenderCommand) result;
+
+            add(command);
+
+            return false; // do not abort!
+        }
+
+        if (result instanceof Renderable)
+        {
+            final Renderable renderable = (Renderable) result;
+
+            RenderCommand wrapper = new RenderCommand()
+            {
+                public void render(MarkupWriter writer, RenderQueue queue)
+                {
+                    renderable.render(writer);
+                }
+            };
+
+            add(wrapper);
+
+            return false;
+        }
+
+        throw new RuntimeException(StructureMessages.wrongPhaseResultType(Boolean.class));
+    }
+
+    private void add(RenderCommand command)
+    {
+        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/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/AbstractIncludeAssetWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/AbstractIncludeAssetWorker.java?rev=727577&r1=727576&r2=727577&view=diff
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/AbstractIncludeAssetWorker.java (original)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/AbstractIncludeAssetWorker.java Wed Dec 17 15:36:02 2008
@@ -16,6 +16,7 @@
 
 import org.apache.tapestry5.Asset;
 import org.apache.tapestry5.ComponentResources;
+import org.apache.tapestry5.annotations.SetupRender;
 import org.apache.tapestry5.internal.services.ComponentResourcesOperation;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.services.SymbolSource;
@@ -94,6 +95,8 @@
         // the integration test, thank you.
 
         transformation.extendMethod(TransformConstants.SETUP_RENDER_SIGNATURE, body);
+
+        model.addRenderPhase(SetupRender.class);
     }
 
     /**

Modified: tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorker.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorker.java?rev=727577&r1=727576&r2=727577&view=diff
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorker.java (original)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorker.java Wed Dec 17 15:36:02 2008
@@ -112,6 +112,8 @@
 
         if (methods.isEmpty()) return;
 
+        model.addRenderPhase(methodAnnotation);
+
         BodyBuilder builder = new BodyBuilder();
         builder.begin();
 
@@ -188,5 +190,4 @@
         else
             builder.addln(";");
     }
-
 }

Modified: tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/model/ComponentModel.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/model/ComponentModel.java?rev=727577&r1=727576&r2=727577&view=diff
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/model/ComponentModel.java (original)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/model/ComponentModel.java Wed Dec 17 15:36:02 2008
@@ -21,6 +21,7 @@
 import org.slf4j.Logger;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * Defines a component in terms of its capabilities, parameters, sub-components, etc. During <em>runtime</em>, the
@@ -146,4 +147,13 @@
      * @return the value for the key (possibly inherited from a parent model), or null
      */
     String getMeta(String key);
+
+    /**
+     * Returns a set of all the render phases that this model (including parent models) that are handled. Render phases
+     * are represented by the corresponding annotation ({@link org.apache.tapestry5.annotations.BeginRender}, {@link
+     * org.apache.tapestry5.annotations.AfterRender}, etc.).
+     *
+     * @return set of classes
+     */
+    Set<Class> getHandledRenderPhases();
 }

Modified: tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/model/MutableComponentModel.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/model/MutableComponentModel.java?rev=727577&r1=727576&r2=727577&view=diff
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/model/MutableComponentModel.java (original)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/model/MutableComponentModel.java Wed Dec 17 15:36:02 2008
@@ -85,4 +85,13 @@
      * Stores a meta data value under the indicated key.
      */
     void setMeta(String key, String value);
+
+    /**
+     * Identifies that the component does handle the render phase.
+     *
+     * @param renderPhase annotation class corresponding to the render phase
+     * @see org.apache.tapestry5.model.ComponentModel#handlesRenderPhase(Class)
+     * @since 5.0.19
+     */
+    void addRenderPhase(Class renderPhase);
 }

Modified: tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformConstants.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformConstants.java?rev=727577&r1=727576&r2=727577&view=diff
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformConstants.java (original)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/main/java/org/apache/tapestry5/services/TransformConstants.java Wed Dec 17 15:36:02 2008
@@ -22,13 +22,18 @@
 
 /**
  * Constants used by implementations of {@link org.apache.tapestry5.services.ComponentClassTransformWorker}.
+ * <p/>
+ * <p/>
+ * Note: methods on transformed components will not be invoked <em>unless</em> {@linkplain
+ * org.apache.tapestry5.model.MutableComponentModel#addRenderPhase(Class) the component model is updated to identify the
+ * use of the corresponding render phase}.
  */
 public final class TransformConstants
 {
     // Shared parameters of a whole bunch of lifecycle methods, representing the different
     // component render states.
-    private static final String[] RENDER_PHASE_METHOD_PARAMETERS = { MarkupWriter.class.getName(),
-            Event.class.getName() };
+    private static final String[] RENDER_PHASE_METHOD_PARAMETERS = {MarkupWriter.class.getName(),
+            Event.class.getName()};
 
     /**
      * Signature for {@link org.apache.tapestry5.runtime.Component#dispatchComponentEvent(org.apache.tapestry5.runtime.ComponentEvent)}.
@@ -36,7 +41,7 @@
      * @see org.apache.tapestry5.annotations.OnEvent
      */
     public static final TransformMethodSignature DISPATCH_COMPONENT_EVENT = new TransformMethodSignature(
-            Modifier.PUBLIC, "boolean", "dispatchComponentEvent", new String[] { ComponentEvent.class.getName() },
+            Modifier.PUBLIC, "boolean", "dispatchComponentEvent", new String[] {ComponentEvent.class.getName()},
             null);
 
     /**

Modified: tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/model/MutableComponentModelImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/model/MutableComponentModelImplTest.java?rev=727577&r1=727576&r2=727577&view=diff
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/model/MutableComponentModelImplTest.java (original)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/model/MutableComponentModelImplTest.java Wed Dec 17 15:36:02 2008
@@ -15,6 +15,7 @@
 package org.apache.tapestry5.internal.model;
 
 import org.apache.tapestry5.BindingConstants;
+import org.apache.tapestry5.annotations.BeginRender;
 import org.apache.tapestry5.internal.test.InternalBaseTestCase;
 import org.apache.tapestry5.ioc.Location;
 import org.apache.tapestry5.ioc.Resource;
@@ -548,7 +549,6 @@
         }
 
         verify();
-
     }
 
     @Test
@@ -722,4 +722,65 @@
 
         verify();
     }
+
+    /**
+     * @since 5.0.19
+     */
+    @Test
+    public void does_not_handle_render_phase_and_no_parent()
+    {
+        Resource r = mockResource();
+        Logger logger = mockLogger();
+
+        replay();
+
+        MutableComponentModel model = new MutableComponentModelImpl(CLASS_NAME, logger, r, null);
+
+
+        assertFalse(model.getHandledRenderPhases().contains(BeginRender.class));
+
+        verify();
+    }
+
+    /**
+     * @since 5.0.19
+     */
+    @Test
+    public void handles_render_phase()
+    {
+        Resource r = mockResource();
+        Logger logger = mockLogger();
+
+        replay();
+
+        MutableComponentModel model = new MutableComponentModelImpl(CLASS_NAME, logger, r, null);
+
+        model.addRenderPhase(BeginRender.class);
+
+        assertTrue(model.getHandledRenderPhases().contains(BeginRender.class));
+
+        verify();
+    }
+
+    /**
+     * @since 5.0.19
+     */
+    @Test
+    public void parent_handles_render_phase()
+    {
+        Resource r = mockResource();
+        Logger logger = mockLogger();
+
+        replay();
+
+        MutableComponentModel parent = new MutableComponentModelImpl(CLASS_NAME, logger, r, null);
+        MutableComponentModel child = new MutableComponentModelImpl(CLASS_NAME, logger, r, parent);
+
+        parent.addRenderPhase(BeginRender.class);
+
+
+        assertTrue(child.getHandledRenderPhases().contains(BeginRender.class));
+
+        verify();
+    }
 }

Modified: tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java?rev=727577&r1=727576&r2=727577&view=diff
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java (original)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/ComponentEventImplTest.java Wed Dec 17 15:36:02 2008
@@ -50,12 +50,15 @@
     {
         ComponentEventCallback handler = mockComponentEventHandler();
         EventContext context = mockEventContext();
+        Logger logger = mockLogger();
+
+        train_isDebugEnabled(logger, false);
 
         train_getCount(context, 0);
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, logger);
 
         assertTrue(event.matches("eventType", "someId", 0));
         assertFalse(event.matches("foo", "someId", 0));
@@ -68,12 +71,15 @@
     {
         ComponentEventCallback handler = mockComponentEventHandler();
         EventContext context = mockEventContext();
+        Logger logger = mockLogger();
+
+        train_isDebugEnabled(logger, false);
 
         train_getCount(context, 0);
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, logger);
 
         assertTrue(event.matches("EVENTTYPE", "someid", 0));
 
@@ -85,12 +91,15 @@
     {
         ComponentEventCallback handler = mockComponentEventHandler();
         EventContext context = mockEventContext();
+        Logger logger = mockLogger();
+
+        train_isDebugEnabled(logger, false);
 
         train_getCount(context, 0);
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, logger);
 
         assertTrue(event.matches("eventType", "someId", 0));
 
@@ -104,12 +113,15 @@
     {
         ComponentEventCallback handler = mockComponentEventHandler();
         EventContext context = mockEventContext();
+        Logger logger = mockLogger();
+
+        train_isDebugEnabled(logger, false);
 
         train_getCount(context, 0);
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, null, logger);
 
         assertTrue(event.matches("eventtype", "SOMEID", 0));
 
@@ -123,6 +135,9 @@
         PageResources resources = mockPageResources();
         EventContext context = mockEventContext();
         Integer value = new Integer(27);
+        Logger logger = mockLogger();
+
+        train_isDebugEnabled(logger, false);
 
         train_toClass(resources, "java.lang.Integer", Integer.class);
 
@@ -131,7 +146,7 @@
 
         replay();
 
-        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, resources, null);
+        ComponentEvent event = new ComponentEventImpl("eventType", "someId", context, handler, resources, logger);
 
         assertSame(event.coerceContext(0, "java.lang.Integer"), value);
 
@@ -146,7 +161,7 @@
         Logger logger = mockLogger();
 
         train_isDebugEnabled(logger, true);
-        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class), isA(String.class));
+        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class));
 
         train_getCount(context, 0);
 
@@ -178,7 +193,7 @@
         Logger logger = mockLogger();
 
         train_isDebugEnabled(logger, true);
-        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class), isA(String.class));
+        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class));
 
         train_toClass(resources, Integer.class.getName(), Integer.class);
 
@@ -218,7 +233,7 @@
         train_isDebugEnabled(logger, true);
         EasyMock.expectLastCall().atLeastOnce();
 
-        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class), isA(String.class));
+        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class));
 
         ComponentEventCallback handler = mockComponentEventHandler();
 
@@ -248,7 +263,7 @@
         Logger logger = mockLogger();
 
         train_isDebugEnabled(logger, true);
-        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class), isA(String.class));
+        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class));
 
         train_handleResult(handler, result, false);
 
@@ -272,7 +287,7 @@
         Logger logger = mockLogger();
 
         train_isDebugEnabled(logger, true);
-        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class), isA(String.class));
+        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class));
 
         replay();
 
@@ -298,7 +313,7 @@
         train_isDebugEnabled(logger, true);
         EasyMock.expectLastCall().atLeastOnce();
 
-        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class), isA(String.class));
+        logger.debug(eq(TapestryMarkers.EVENT_HANDLER_METHOD), isA(String.class));
 
         EasyMock.expectLastCall().atLeastOnce();
 

Modified: tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/ComponentPageElementImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/ComponentPageElementImplTest.java?rev=727577&r1=727576&r2=727577&view=diff
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/ComponentPageElementImplTest.java (original)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/structure/ComponentPageElementImplTest.java Wed Dec 17 15:36:02 2008
@@ -54,6 +54,7 @@
         PageResources pr = mockPageResources(logger, eventLogger);
 
         train_getLogger(model, logger);
+        train_isDebugEnabled(eventLogger, false);
 
         Instantiator ins = mockInstantiator(component, model);
 
@@ -91,6 +92,7 @@
         Instantiator ins = mockInstantiator(component, model);
 
         train_getLogger(model, logger);
+        train_isDebugEnabled(eventLogger, false);
 
         replay();
 
@@ -121,6 +123,7 @@
         Instantiator ins = mockInstantiator(component, model);
 
         train_getLogger(model, logger);
+        train_isDebugEnabled(eventLogger, false);
 
         train_getParameterModel(model, "barney", null);
 
@@ -154,6 +157,7 @@
         PageResources pr = mockPageResources(logger, eventLogger);
 
         train_getLogger(model, logger);
+        train_isDebugEnabled(eventLogger, false);
 
         Instantiator ins = mockInstantiator(component, model);
 
@@ -190,6 +194,7 @@
         PageResources pr = mockPageResources(logger, eventLogger);
 
         train_getLogger(model, logger);
+        train_isDebugEnabled(eventLogger, false);
 
         Instantiator ins = mockInstantiator(component, model);
 
@@ -221,6 +226,7 @@
         PageResources pr = mockPageResources(logger, eventLogger);
 
         train_getLogger(model, logger);
+        train_isDebugEnabled(eventLogger, false);
 
         Instantiator ins = mockInstantiator(component, model);
 
@@ -254,6 +260,7 @@
         PageResources pr = mockPageResources(logger, eventLogger);
 
         train_getLogger(model, logger);
+        train_isDebugEnabled(eventLogger, false);
 
         Instantiator ins = mockInstantiator(component, model);
 
@@ -323,6 +330,7 @@
         PageResources pr = mockPageResources(logger, eventLogger);
 
         train_getLogger(model, logger);
+        train_isDebugEnabled(eventLogger, false);
 
         Instantiator ins = mockInstantiator(component, model);
 
@@ -356,6 +364,7 @@
         PageResources resources = mockPageResources(logger, eventLogger);
 
         train_getLogger(model, logger);
+        train_isDebugEnabled(eventLogger, false);
 
         train_getSupportsInformalParameters(model, true);
 
@@ -393,6 +402,7 @@
         PageResources resources = mockPageResources(logger, eventLogger);
 
         train_getLogger(model, logger);
+        train_isDebugEnabled(eventLogger, false);
 
         Instantiator ins = mockInstantiator(component, model);
 
@@ -428,11 +438,13 @@
         PageResources pr = mockPageResources(logger, eventLogger);
 
         train_getLogger(model, logger);
+        train_isDebugEnabled(eventLogger, false);
 
         Instantiator ins = mockInstantiator(component, model);
         Instantiator ins2 = mockInstantiator(component, model);
 
         train_getEventLogger(pr, logger, eventLogger);
+        train_isDebugEnabled(eventLogger, false);
 
         replay();
 
@@ -466,6 +478,7 @@
         PageResources pr = mockPageResources(logger, eventLogger);
 
         train_getLogger(model, logger);
+        train_isDebugEnabled(eventLogger, false);
 
         Instantiator ins = mockInstantiator(component, model);
 
@@ -501,6 +514,7 @@
         PageResources pr = mockPageResources(logger, eventLogger);
 
         train_getLogger(model, logger);
+        train_isDebugEnabled(eventLogger, false);
 
         Instantiator ins = mockInstantiator(pageComponent, model);
 
@@ -543,6 +557,7 @@
         PageResources pr = mockPageResources(logger, eventLogger);
 
         train_getLogger(model, logger);
+        train_isDebugEnabled(eventLogger, false);
 
         Instantiator ins = mockInstantiator(component, model);
         Instantiator mixinIns = mockInstantiator(mixin, mixinModel);
@@ -573,6 +588,7 @@
         PageResources pr = mockPageResources(logger, eventLogger);
 
         train_getLogger(model, logger);
+        train_isDebugEnabled(eventLogger, false);
 
         Instantiator ins = mockInstantiator(component, model);
         Instantiator mixinIns = mockInstantiator(mixin, mixinModel);
@@ -612,6 +628,7 @@
         PageResources pr = mockPageResources(logger, eventLogger);
 
         train_getLogger(model, logger);
+        train_isDebugEnabled(eventLogger, false);
 
         Instantiator ins = mockInstantiator(component, model);
         Instantiator mixinInstantiator = mockInstantiator(mixin, mixinModel);

Modified: tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorkerTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorkerTest.java?rev=727577&r1=727576&r2=727577&view=diff
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorkerTest.java (original)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/java/org/apache/tapestry5/internal/transform/RenderPhaseMethodWorkerTest.java Wed Dec 17 15:36:02 2008
@@ -23,10 +23,6 @@
 import org.apache.tapestry5.test.TapestryTestCase;
 import org.testng.annotations.Test;
 
-/**
- * Of course, we're committing the cardinal sin of testing the code that's generated, rather than the *behavior* of the
- * generated code. Fortunately, we back all this up with lots and lots of integration testing.
- */
 public class RenderPhaseMethodWorkerTest extends TapestryTestCase
 {
     @Test

Modified: tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/resources/log4j.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/resources/log4j.properties?rev=727577&r1=727576&r2=727577&view=diff
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/resources/log4j.properties (original)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-core/src/test/resources/log4j.properties Wed Dec 17 15:36:02 2008
@@ -23,5 +23,8 @@
 
 log4j.category.org.apache.tapestry5.integration.app2=debug
 
+log4j.category.tapestry.render=debug
+log4j.category.org.apache.tapestry5.internal.services.InternalModule.PageResponseRenderer=debug
+
 # log4j.category.org.apache.tapestry5.corelib.components=debug
 

Modified: tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/CollectionFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/CollectionFactory.java?rev=727577&r1=727576&r2=727577&view=diff
==============================================================================
--- tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/CollectionFactory.java (original)
+++ tapestry/tapestry5/branches/hlship-5.0-perf/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/CollectionFactory.java Wed Dec 17 15:36:02 2008
@@ -39,14 +39,12 @@
  */
 public final class CollectionFactory
 {
-    private static final int INITIAL_HASHMAP_CAPACITY = 31;
-
     /**
      * Constructs and returns a generic {@link HashMap} instance.
      */
     public static <K, V> Map<K, V> newMap()
     {
-        return new HashMap<K, V>(INITIAL_HASHMAP_CAPACITY);
+        return new HashMap<K, V>();
     }
 
     /**
@@ -84,7 +82,7 @@
      */
     public static <K, V> ConcurrentMap<K, V> newConcurrentMap()
     {
-        return new ConcurrentHashMap<K, V>(INITIAL_HASHMAP_CAPACITY);
+        return new ConcurrentHashMap<K, V>();
     }
 
     /**