WICKET-6503 beforeRender clean-up

- prepare all components before writing them to Ajax response
- simplified delayed preparation of feedbacks
- clean-up internal component API an flags

this closes #250


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/810185e6
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/810185e6
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/810185e6

Branch: refs/heads/WICKET-6523-ajax-timers
Commit: 810185e66b574b66a5f2039f17f6e32ffdf47cea
Parents: da3b69f
Author: Sven Meier <svenme...@apache.org>
Authored: Thu Dec 14 07:55:53 2017 +0100
Committer: Sven Meier <svenme...@apache.org>
Committed: Thu Jan 25 17:13:01 2018 +0100

----------------------------------------------------------------------
 .../main/java/org/apache/wicket/Component.java  | 332 ++++++-------------
 .../java/org/apache/wicket/MarkupContainer.java |  22 --
 .../src/main/java/org/apache/wicket/Page.java   |  36 +-
 .../ComponentRenderingRequestHandler.java       |   9 +-
 .../handler/PageAndComponentProvider.java       |   2 +-
 .../core/util/string/ComponentRenderer.java     |   3 +-
 .../apache/wicket/feedback/FeedbackDelay.java   | 119 +++++++
 .../org/apache/wicket/feedback/IFeedback.java   |   5 +-
 .../apache/wicket/page/PartialPageUpdate.java   |  74 ++++-
 .../wicket/page/XmlPartialPageUpdate.java       |  50 +--
 .../wicket/feedback/FeedbackRenderTest.java     |  22 +-
 .../apache/wicket/feedback/FeedbacksPage.html   |  16 +-
 .../apache/wicket/feedback/FeedbacksPage.java   |  31 +-
 .../markup/html/internal/EnclosurePage_1.java   |  50 ++-
 .../markup/html/internal/EnclosureTest.java     |   4 +
 15 files changed, 422 insertions(+), 353 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/810185e6/wicket-core/src/main/java/org/apache/wicket/Component.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/Component.java 
b/wicket-core/src/main/java/org/apache/wicket/Component.java
index 7240a13..fc32b09 100644
--- a/wicket-core/src/main/java/org/apache/wicket/Component.java
+++ b/wicket-core/src/main/java/org/apache/wicket/Component.java
@@ -17,11 +17,11 @@
 package org.apache.wicket;
 
 import java.io.Serializable;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
+import java.util.Optional;
 
 import org.apache.wicket.ajax.IAjaxRegionMarkupIdProvider;
 import org.apache.wicket.application.IComponentInstantiationListener;
@@ -40,6 +40,7 @@ import org.apache.wicket.event.Broadcast;
 import org.apache.wicket.event.IEvent;
 import org.apache.wicket.event.IEventSink;
 import org.apache.wicket.event.IEventSource;
+import org.apache.wicket.feedback.FeedbackDelay;
 import org.apache.wicket.feedback.FeedbackMessage;
 import org.apache.wicket.feedback.FeedbackMessages;
 import org.apache.wicket.feedback.IFeedback;
@@ -86,7 +87,6 @@ import org.apache.wicket.util.lang.Classes;
 import org.apache.wicket.util.string.PrependingStringBuffer;
 import org.apache.wicket.util.string.Strings;
 import org.apache.wicket.util.value.ValueMap;
-import org.apache.wicket.util.visit.IVisit;
 import org.apache.wicket.util.visit.IVisitFilter;
 import org.apache.wicket.util.visit.IVisitor;
 import org.apache.wicket.util.visit.Visit;
@@ -312,12 +312,6 @@ public abstract class Component
                }
        };
 
-       /** an unused flag */
-       private static final int FLAG_UNUSED0 = 0x20000000;
-       private static final int FLAG_UNUSED1 = 0x800000;
-       private static final int FLAG_UNUSED2 = 0x1000000;
-       private static final int FLAG_UNUSED3 = 0x10000000;
-
        /** True when a component is being auto-added */
        private static final int FLAG_AUTO = 0x0001;
 
@@ -389,17 +383,6 @@ public abstract class Component
         */
        private static final int FLAG_MODEL_SET = 0x100000;
 
-       /** True when a component is being removed from the hierarchy */
-       protected static final int FLAG_REMOVING_FROM_HIERARCHY = 0x200000;
-
-       /**
-        * Flag that makes we are in before-render callback phase Set after 
component.onBeforeRender is
-        * invoked (right before invoking beforeRender on children)
-        */
-       protected static final int FLAG_RENDERING = 0x2000000;
-       protected static final int FLAG_PREPARED_FOR_RENDER = 0x4000000;
-       protected static final int FLAG_AFTER_RENDERING = 0x8000000;
-
        /**
         * Flag that restricts visibility of a component when set to true. This 
is usually used when a
         * component wants to restrict visibility of another component. Calling
@@ -408,8 +391,6 @@ public abstract class Component
         */
        private static final int FLAG_VISIBILITY_ALLOWED = 0x40000000;
 
-       private static final int FLAG_DETACHING = 0x80000000;
-       
        /**
         * The name of attribute that will hold markup id
         */
@@ -447,6 +428,16 @@ public abstract class Component
        private static final short RFLAG_INITIALIZE_SUPER_CALL_VERIFIED = 0x40;
        protected static final short RFLAG_CONTAINER_DEQUEING = 0x80;
        private static final short RFLAG_ON_RE_ADD_SUPER_CALL_VERIFIED = 0x100;
+       /**
+        * Flag that makes we are in before-render callback phase Set after 
component.onBeforeRender is
+        * invoked (right before invoking beforeRender on children)
+        */
+       private static final short RFLAG_RENDERING = 0x200;
+       private static final short RFLAG_PREPARED_FOR_RENDER = 0x400;
+       private static final short RFLAG_AFTER_RENDERING = 0x800;
+       private static final short RFLAG_DETACHING = 0x1000;    
+       /** True when a component is being removed from the hierarchy */
+       private static final short RFLAG_REMOVING_FROM_HIERARCHY = 0x2000;
 
        /**
         * Flags that only keep their value during the request. Useful for 
cache markers, etc. At the
@@ -789,7 +780,7 @@ public abstract class Component
        }
 
        /**
-        * Called once per request on components before they are about to be 
rendered. This method
+        * Called on all components before any component is rendered. This 
method
         * should be used to configure such things as visibility and enabled 
flags.
         * <p>
         * Overrides must call {@code super.onConfigure()}, usually before any 
other code
@@ -857,7 +848,7 @@ public abstract class Component
        }
 
        /**
-        * THIS METHOD IS NOT PART OF THE PUBLIC API, DO NOT CALL IT
+        * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT!
         * 
         * Used to call {@link #onInitialize()}
         */
@@ -903,22 +894,19 @@ public abstract class Component
        }
 
        /**
-        * Called on every component after the page is rendered. It will call 
onAfterRender for it self
-        * and its children.
+        * Called on every component after the page is rendered. Calls hook 
{@link #onAfterRender()}.
         */
-       public final void afterRender()
+       final void afterRender()
        {
+               setRequestFlag(RFLAG_PREPARED_FOR_RENDER, false);
+               
                try
                {
-                       setFlag(FLAG_AFTER_RENDERING, true);
-
-                       // always detach children because components can be 
attached
-                       // independently of their parents
-                       onAfterRenderChildren();
+                       setRequestFlag(RFLAG_AFTER_RENDERING, true);
 
                        onAfterRender();
                        
getApplication().getComponentOnAfterRenderListeners().onAfterRender(this);
-                       if (getFlag(FLAG_AFTER_RENDERING))
+                       if (getRequestFlag(RFLAG_AFTER_RENDERING))
                        {
                                throw new 
IllegalStateException(Component.class.getName() +
                                        " has not been properly detached. 
Something in the hierarchy of " +
@@ -929,91 +917,53 @@ public abstract class Component
                finally
                {
                        // this flag must always be set to false.
-                       markRendering(false);
+                       setRequestFlag(RFLAG_RENDERING, false);
                }
        }
 
-       private void internalBeforeRender()
-       {
-               configure();
-
-               if ((determineVisibility()) && !getFlag(FLAG_RENDERING) &&
-                       !getFlag(FLAG_PREPARED_FOR_RENDER))
-               {
-                       setRequestFlag(RFLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED, 
false);
-
-                       Application application = getApplication();
-                       
application.getComponentPreOnBeforeRenderListeners().onBeforeRender(this);
-
-                       onBeforeRender();
-                       
application.getComponentPostOnBeforeRenderListeners().onBeforeRender(this);
-
-                       if 
(!getRequestFlag(RFLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED))
-                       {
-                               throw new 
IllegalStateException(Component.class.getName() +
-                                       " has not been properly rendered. 
Something in the hierarchy of " +
-                                       getClass().getName() +
-                                       " has not called super.onBeforeRender() 
in the override of onBeforeRender() method");
-                       }
-               }
-       }
-
-       /**
-        * We need to postpone calling beforeRender() on components that 
implement {@link IFeedback}, to
-        * be sure that all other component's beforeRender() has been already 
called, so that IFeedbacks
-        * can collect all feedback messages. This is the key under list of 
postponed {@link IFeedback}
-        * is stored to request cycle metadata. The List is then iterated over 
in
-        * {@link #prepareForRender()} after calling {@link #beforeRender()}, 
to initialize postponed
-        * components.
-        */
-       private static final MetaDataKey<List<Component>> FEEDBACK_LIST = new 
MetaDataKey<List<Component>>()
-       {
-               private static final long serialVersionUID = 1L;
-       };
-
        /**
-        * Called for every component when the page is getting to be rendered. 
it will call
-        * {@link #configure()} and {@link #onBeforeRender()} for this 
component and all the child
-        * components
+        * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT!
+        * 
+        * Called on all components before any component is rendered. Calls 
hooks
+        * {@link #configure()} and (if visible) {@link #onBeforeRender()}
+        * and delegates to {@link #beforeRender()} of all child components.
         */
        public final void beforeRender()
        {
                if (this instanceof IFeedback)
                {
-                       // this component is a feedback. Feedback must be 
initialized last, so that
-                       // they can collect messages from other components
-                       List<Component> feedbacks = 
getRequestCycle().getMetaData(FEEDBACK_LIST);
-                       if (feedbacks == null)
-                       {
-                               feedbacks = new ArrayList<>();
-                               getRequestCycle().setMetaData(FEEDBACK_LIST, 
feedbacks);
+                       Optional<FeedbackDelay> delay = 
FeedbackDelay.get(getRequestCycle());
+                       if (delay.isPresent()) {
+                               delay.get().postpone((IFeedback)this);
+                               return;
                        }
+               }
 
-                       if (this instanceof MarkupContainer)
-                       {
-                               
((MarkupContainer)this).visitChildren(IFeedback.class,
-                                       new IVisitor<Component, Void>()
-                                       {
-                                               @Override
-                                               public void component(Component 
feedback, IVisit<Void> visit)
-                                               {
-                                                       feedback.beforeRender();
-
-                                                       // don't need to go 
deeper,
-                                                       // as the feedback will 
visit its children on its own
-                                                       visit.dontGoDeeper();
-                                               }
-                                       });
-                       }
+               configure();
 
-                       if (!feedbacks.contains(this))
-                       {
-                               feedbacks.add(this);
-                       }
-               }
-               else
+               if ((determineVisibility()) && !getRequestFlag(RFLAG_RENDERING) 
&&
+                       !getRequestFlag(RFLAG_PREPARED_FOR_RENDER))
                {
-                       internalBeforeRender();
+                       try {
+                               
setRequestFlag(RFLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED, false);
+
+                               Application application = getApplication();
+                               
application.getComponentPreOnBeforeRenderListeners().onBeforeRender(this);
+
+                               onBeforeRender();
+                               
application.getComponentPostOnBeforeRenderListeners().onBeforeRender(this);
+
+                               if 
(!getRequestFlag(RFLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED))
+                               {
+                                       throw new 
IllegalStateException(Component.class.getName() +
+                                               " has not been properly 
rendered. Something in the hierarchy of " +
+                                               getClass().getName() +
+                                               " has not called 
super.onBeforeRender() in the override of onBeforeRender() method");
+                               }
+                       } catch (RuntimeException ex) {
+                               setRequestFlag(RFLAG_PREPARED_FOR_RENDER, 
false);
+                               throw ex;
+                       }
                }
        }
 
@@ -1072,9 +1022,6 @@ public abstract class Component
        }
 
        /**
-        * 
-        * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT!
-        * 
         * Called after the {@link #onConfigure()}, but before {@link 
#onBeforeRender()}
         */
        void internalOnAfterConfigure()
@@ -1134,10 +1081,10 @@ public abstract class Component
         */
        final void internalOnRemove()
        {
-               setFlag(FLAG_REMOVING_FROM_HIERARCHY, true);
+               setRequestFlag(RFLAG_REMOVING_FROM_HIERARCHY, true);
                onRemove();
                setFlag(FLAG_REMOVED, true);
-               if (getFlag(FLAG_REMOVING_FROM_HIERARCHY))
+               if (getRequestFlag(RFLAG_REMOVING_FROM_HIERARCHY))
                {
                        throw new 
IllegalStateException(Component.class.getName() +
                                " has not been properly removed from hierachy. 
Something in the hierarchy of " +
@@ -1157,9 +1104,9 @@ public abstract class Component
        {
                try
                {
-                       setFlag(FLAG_DETACHING, true);
+                       setRequestFlag(RFLAG_DETACHING, true);
                        onDetach();
-                       if (getFlag(FLAG_DETACHING))
+                       if (getRequestFlag(RFLAG_DETACHING))
                        {
                                throw new 
IllegalStateException(Component.class.getName() +
                                                " has not been properly 
detached. Something in the hierarchy of " +
@@ -1469,7 +1416,7 @@ public abstract class Component
        }
 
        /**
-        * THIS IS WICKET INTERNAL ONLY. DO NOT USE IT.
+        * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT!
         * 
         * Get a copy of the markup's attributes which are associated with the 
component.
         * <p>
@@ -2170,23 +2117,6 @@ public abstract class Component
        }
 
        /**
-        * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT!
-        * 
-        * Sets the RENDERING flag and removes the PREPARED_FOR_RENDER flag on 
component and it's
-        * children.
-        * 
-        * @param setRenderingFlag
-        *            if this is false only the PREPARED_FOR_RENDER flag is 
removed from component, the
-        *            RENDERING flag is not set.
-        * 
-        * @see #internalPrepareForRender(boolean)
-        */
-       public final void markRendering(boolean setRenderingFlag)
-       {
-               internalMarkRendering(setRenderingFlag);
-       }
-
-       /**
         * Called to indicate that the model content for this component has 
been changed
         */
        public final void modelChanged()
@@ -2215,62 +2145,6 @@ public abstract class Component
        }
 
        /**
-        * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT!
-        * <p>
-        * Prepares the component and it's children for rendering. On whole 
page render this method must
-        * be called on the page. On AJAX request, this method must be called 
on the updated component.
-        * 
-        * @param setRenderingFlag
-        *            Whether to set the rendering flag. This must be true if 
the page is about to be
-        *            rendered. However, there are usecases to call this method 
without an immediate
-        *            render (e.g. on stateless listener request target to 
build the component
-        *            hierarchy), in that case setRenderingFlag should be false.
-        */
-       public void internalPrepareForRender(boolean setRenderingFlag)
-       {
-               beforeRender();
-
-               if (setRenderingFlag)
-               {
-                       // only process feedback panel when we are about to be 
rendered.
-                       // setRenderingFlag is false in case prepareForRender 
is called only to build component
-                       // hierarchy (i.e. in 
BookmarkableListenerRequestHandler).
-                       // prepareForRender(true) is always called before the 
actual rendering is done so
-                       // that's where feedback panels gather the messages
-
-                       List<Component> feedbacks = 
getRequestCycle().getMetaData(FEEDBACK_LIST);
-                       if (feedbacks != null)
-                       {
-                               // iterate over a copy because a IFeedback may 
add more IFeedback children
-// (WICKET-4687)
-                               Component[] feedbacksCopy = 
feedbacks.toArray(new Component[feedbacks.size()]);
-                               for (Component feedback : feedbacksCopy)
-                               {
-                                       // render it only if it is still in the 
page hierarchy (WICKET-4895)
-                                       if (feedback.findPage() != null)
-                                       {
-                                               feedback.internalBeforeRender();
-                                       }
-                               }
-                       }
-                       getRequestCycle().setMetaData(FEEDBACK_LIST, null);
-               }
-
-               markRendering(setRenderingFlag);
-       }
-
-       /**
-        * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT!
-        * 
-        * Prepares the component and it's children for rendering. On whole 
page render this method must
-        * be called on the page. On AJAX request, this method must be called 
on updated component.
-        */
-       public final void prepareForRender()
-       {
-               internalPrepareForRender(true);
-       }
-
-       /**
         * Redirects browser to an intermediate page such as a sign-in page. 
The current request's url
         * is saved for future use by method continueToOriginalDestination(); 
Only use this method when
         * you plan to continue to the current url at some later time; 
otherwise just use
@@ -2303,24 +2177,42 @@ public abstract class Component
                parent.remove(this);
        }
 
+       /**
+        * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT!
+        * <p>
+        * Renders this component as a part of a response - the caller has to
+        * make sure that this component is prepared for render.
+        * 
+        * @see #beforeRender()
+        */
+       public final void renderPart() {
+               Page page = getPage();
+
+               page.startComponentRender(this);
+
+               render();
+               
+               page.endComponentRender(this);
+       }
 
        /**
-        * Render the Component.
+        * Render this component and all its children. Always calls hook {@link 
#onAfterRender()}
+        * regardless of any exception.
         */
        public final void render()
        {
-               RuntimeException exception = null;
+               if (isAuto())
+               {
+                       // auto components are prepared when rendered
+                       beforeRender();
+               }
 
+               // Do the render
+               RuntimeException exception = null;
                try
                {
-                       // Invoke prepareForRender only if this is the root 
component to be rendered
-                       MarkupContainer parent = getParent();
-                       if ((parent == null) || (parent.getFlag(FLAG_RENDERING) 
== false) || isAuto())
-                       {
-                               internalPrepareForRender(true);
-                       }
-
-                       // Do the render
+                       setRequestFlag(RFLAG_RENDERING, true);
+                       
                        internalRender();
                }
                catch (final RuntimeException ex)
@@ -2367,9 +2259,6 @@ public abstract class Component
                // MarkupStream is an Iterator for the markup
                MarkupStream markupStream = new MarkupStream(markup);
 
-               // Flag: we started the render process
-               markRendering(true);
-
                MarkupElement elem = markup.get(0);
                if (elem instanceof ComponentTag)
                {
@@ -2513,13 +2402,13 @@ public abstract class Component
 
 
        /**
-        * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT.
+        * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT!
         * <p>
         * Renders the component at the current position in the given markup 
stream. The method
         * onComponentTag() is called to allow the component to mutate the 
start tag. The method
         * onComponentTagBody() is then called to permit the component to 
render its body.
         */
-       public final void internalRenderComponent()
+       protected final void internalRenderComponent()
        {
                final IMarkupFragment markup = getMarkup();
                if (markup == null)
@@ -2698,7 +2587,7 @@ public abstract class Component
        }
 
        /**
-        * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT.
+        * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT!
         * 
         * Print to the web response what ever the component wants to 
contribute to the head section.
         * Make sure that all attached behaviors are asked as well.
@@ -3645,7 +3534,7 @@ public abstract class Component
        protected void checkHierarchyChange(final Component component)
        {
                // Throw exception if modification is attempted during rendering
-               if (getFlag(FLAG_RENDERING) && !component.isAuto())
+               if (getRequestFlag(RFLAG_RENDERING) && !component.isAuto())
                {
                        throw new WicketRuntimeException(
                                "Cannot modify component hierarchy after render 
phase has started (page version cant change then anymore)");
@@ -3876,15 +3765,16 @@ public abstract class Component
        }
 
        /**
-        * Called just after a component is rendered.
+        * Called immediately after a component and all its children have been 
rendered,
+        * regardless of any exception.
         */
        protected void onAfterRender()
        {
-               setFlag(FLAG_AFTER_RENDERING, false);
+               setRequestFlag(RFLAG_AFTER_RENDERING, false);
        }
 
        /**
-        * Called just before a component is rendered only if the component is 
visible.
+        * Called on all visible components before any component is rendered.
         * <p>
         * <strong>NOTE</strong>: If you override this, you *must* call 
super.onBeforeRender() within
         * your implementation.
@@ -3900,7 +3790,6 @@ public abstract class Component
         */
        protected void onBeforeRender()
        {
-               setFlag(FLAG_PREPARED_FOR_RENDER, true);
                onBeforeRenderChildren();
                setRequestFlag(RFLAG_BEFORE_RENDER_SUPER_CALL_VERIFIED, true);
        }
@@ -3958,7 +3847,7 @@ public abstract class Component
         */
        protected void onDetach()
        {
-               setFlag(FLAG_DETACHING, false);
+               setRequestFlag(RFLAG_DETACHING, false);
        }
 
        /**
@@ -3969,7 +3858,7 @@ public abstract class Component
         */
        protected void onRemove()
        {
-               setFlag(FLAG_REMOVING_FROM_HIERARCHY, false);
+               setRequestFlag(RFLAG_REMOVING_FROM_HIERARCHY, false);
        }
 
        /**
@@ -4161,14 +4050,12 @@ public abstract class Component
        }
 
        /**
-        * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT!
-        * 
         * @param flag
         *            The flag to set
         * @param set
         *            True to turn the flag on, false to turn it off
         */
-       protected final Component setRequestFlag(final short flag, final 
boolean set)
+       final Component setRequestFlag(final short flag, final boolean set)
        {
                if (set)
                {
@@ -4245,18 +4132,6 @@ public abstract class Component
        }
 
        /**
-        * @param setRenderingFlag
-        *            rendering flag
-        */
-       void internalMarkRendering(boolean setRenderingFlag)
-       {
-               // WICKET-5460 no longer prepared for render
-               setFlag(FLAG_PREPARED_FOR_RENDER, false);
-
-               setFlag(FLAG_RENDERING, setRenderingFlag);
-       }
-
-       /**
         * @return True if this component or any of its parents is in auto-add 
mode
         */
        public final boolean isAuto()
@@ -4278,14 +4153,7 @@ public abstract class Component
         */
        boolean isPreparedForRender()
        {
-               return getFlag(FLAG_PREPARED_FOR_RENDER);
-       }
-
-       /**
-        * 
-        */
-       protected void onAfterRenderChildren()
-       {
+               return getRequestFlag(RFLAG_PREPARED_FOR_RENDER);
        }
 
        /**
@@ -4474,7 +4342,7 @@ public abstract class Component
         */
        public final boolean isRendering()
        {
-               return getFlag(FLAG_RENDERING);
+               return getRequestFlag(RFLAG_RENDERING);
        }
 
        /**

http://git-wip-us.apache.org/repos/asf/wicket/blob/810185e6/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java 
b/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java
index 36b7684..aad0aac 100644
--- a/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java
+++ b/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java
@@ -1694,17 +1694,6 @@ public abstract class MarkupContainer extends Component 
implements Iterable<Comp
                }
        }
 
-       @Override
-       void internalMarkRendering(boolean setRenderingFlag)
-       {
-               super.internalMarkRendering(setRenderingFlag);
-
-               for (Component child : this)
-               {
-                       child.internalMarkRendering(setRenderingFlag);
-               }
-       }
-
        /**
         * @return a copy of the children array.
         */
@@ -1793,17 +1782,6 @@ public abstract class MarkupContainer extends Component 
implements Iterable<Comp
        }
 
        @Override
-       protected void onAfterRenderChildren()
-       {
-               for (Component child : this)
-               {
-                       // set RENDERING_FLAG to false for auto-component's 
children (like Enclosure)
-                       child.markRendering(false);
-               }
-               super.onAfterRenderChildren();
-       }
-
-       @Override
        protected void onDetach()
        {
                super.onDetach();

http://git-wip-us.apache.org/repos/asf/wicket/blob/810185e6/wicket-core/src/main/java/org/apache/wicket/Page.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/Page.java 
b/wicket-core/src/main/java/org/apache/wicket/Page.java
index fb4f04d..9b8dc7f 100644
--- a/wicket-core/src/main/java/org/apache/wicket/Page.java
+++ b/wicket-core/src/main/java/org/apache/wicket/Page.java
@@ -24,6 +24,7 @@ import java.util.Set;
 
 import org.apache.wicket.authorization.UnauthorizedActionException;
 import org.apache.wicket.core.util.lang.WicketObjects;
+import org.apache.wicket.feedback.FeedbackDelay;
 import org.apache.wicket.markup.MarkupException;
 import org.apache.wicket.markup.MarkupStream;
 import org.apache.wicket.markup.MarkupType;
@@ -229,14 +230,15 @@ public abstract class Page extends MarkupContainer
        }
 
        @Override
-       public void internalPrepareForRender(boolean setRenderingFlag)
+       protected void onConfigure()
        {
                if (!isInitialized())
                {
                        // initialize the page if not yet initialized
                        internalInitialize();
                }
-               super.internalPrepareForRender(setRenderingFlag);
+               
+               super.onConfigure();
        }
 
        /**
@@ -300,14 +302,14 @@ public abstract class Page extends MarkupContainer
        }
 
        /**
-        * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL.
-        * 
-        * This method is called when a component was rendered standalone. If 
it is a <code>
+        * This method is called when a component was rendered as a part. If it 
is a <code>
         * MarkupContainer</code> then the rendering for that container is 
checked.
         * 
         * @param component
+        * 
+        * @see Component#renderPart()
         */
-       public final void endComponentRender(Component component)
+       final void endComponentRender(Component component)
        {
                if (component instanceof MarkupContainer)
                {
@@ -527,14 +529,13 @@ public abstract class Page extends MarkupContainer
        }
 
        /**
-        * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL.
-        * 
-        * This method is called when a component will be rendered standalone.
+        * This method is called when a component will be rendered as a part.
         * 
         * @param component
         * 
+        * @see Component#renderPart()
         */
-       public final void startComponentRender(Component component)
+       final void startComponentRender(Component component)
        {
                renderedComponents = null;
        }
@@ -984,9 +985,17 @@ public abstract class Page extends MarkupContainer
                try
                {
                        ++renderCount;
-                       render();
 
-                       // stateless = null;
+                       FeedbackDelay delay = new 
FeedbackDelay(getRequestCycle());
+                       try {
+                               beforeRender();
+                               
+                               delay.beforeRender();
+                       } finally {
+                               delay.release();
+                       }
+
+                       render();
                }
                finally
                {
@@ -1004,5 +1013,4 @@ public abstract class Page extends MarkupContainer
        {
                return renderedComponents != null && 
renderedComponents.contains(component);
        }
-
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/810185e6/wicket-core/src/main/java/org/apache/wicket/core/request/handler/ComponentRenderingRequestHandler.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/core/request/handler/ComponentRenderingRequestHandler.java
 
b/wicket-core/src/main/java/org/apache/wicket/core/request/handler/ComponentRenderingRequestHandler.java
index 1636707..e6d5874 100644
--- 
a/wicket-core/src/main/java/org/apache/wicket/core/request/handler/ComponentRenderingRequestHandler.java
+++ 
b/wicket-core/src/main/java/org/apache/wicket/core/request/handler/ComponentRenderingRequestHandler.java
@@ -64,13 +64,8 @@ public class ComponentRenderingRequestHandler implements 
IComponentRequestHandle
                        response.disableCaching();
                }
 
-               Page page = component.getPage();
-
-               page.startComponentRender(component);
-
-               component.render();
-
-               page.endComponentRender(component);
+               component.beforeRender();
+               component.renderPart();
        }
 
        @Override

http://git-wip-us.apache.org/repos/asf/wicket/blob/810185e6/wicket-core/src/main/java/org/apache/wicket/core/request/handler/PageAndComponentProvider.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/core/request/handler/PageAndComponentProvider.java
 
b/wicket-core/src/main/java/org/apache/wicket/core/request/handler/PageAndComponentProvider.java
index 8bdfa93..de59ecc 100644
--- 
a/wicket-core/src/main/java/org/apache/wicket/core/request/handler/PageAndComponentProvider.java
+++ 
b/wicket-core/src/main/java/org/apache/wicket/core/request/handler/PageAndComponentProvider.java
@@ -169,7 +169,7 @@ public class PageAndComponentProvider extends PageProvider 
implements IPageAndCo
                                {
                                        Page p = (Page)page;
                                        p.internalInitialize();
-                                       p.internalPrepareForRender(false);
+                                       p.beforeRender();
                                        component = page.get(componentPath);
                                }
                        }

http://git-wip-us.apache.org/repos/asf/wicket/blob/810185e6/wicket-core/src/main/java/org/apache/wicket/core/util/string/ComponentRenderer.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/core/util/string/ComponentRenderer.java
 
b/wicket-core/src/main/java/org/apache/wicket/core/util/string/ComponentRenderer.java
index d583057..9104eb9 100644
--- 
a/wicket-core/src/main/java/org/apache/wicket/core/util/string/ComponentRenderer.java
+++ 
b/wicket-core/src/main/java/org/apache/wicket/core/util/string/ComponentRenderer.java
@@ -416,7 +416,8 @@ public class ComponentRenderer
                        RenderPage page = new RenderPage(component);
                        page.internalInitialize();
 
-                       component.render();
+                       component.beforeRender();
+                       component.renderPart();
                }
                finally
                {

http://git-wip-us.apache.org/repos/asf/wicket/blob/810185e6/wicket-core/src/main/java/org/apache/wicket/feedback/FeedbackDelay.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/feedback/FeedbackDelay.java 
b/wicket-core/src/main/java/org/apache/wicket/feedback/FeedbackDelay.java
new file mode 100644
index 0000000..4d0ef30
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/feedback/FeedbackDelay.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.wicket.feedback;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.MetaDataKey;
+import org.apache.wicket.Page;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.request.cycle.RequestCycle;
+
+/**
+ * Postpone calling {@link IFeedback#beforeRender()} after other components.
+ * <p>
+ * This gives other {@link Component#beforeRender()} the possibility to report 
feedbacks,
+ * which can then be collected by {@link IFeedback}s afterwards.
+ */
+public class FeedbackDelay implements Serializable
+{
+       private static final MetaDataKey<FeedbackDelay> KEY = new 
MetaDataKey<FeedbackDelay>()
+       {
+               private static final long serialVersionUID = 1L;
+       };
+       
+       private List<IFeedback> feedbacks = new ArrayList<>();
+
+       private RequestCycle cycle;
+       
+       /**
+        * Delay all feedbacks for the given cycle.
+        * <p>
+        * All postponed feedbacks will be prepared for render with {@link 
#beforeRender()}.
+        * 
+        * @param cycle
+        *            request cycle
+        */
+       public FeedbackDelay(RequestCycle cycle) {
+               if (get(cycle).isPresent()) {
+                       throw new WicketRuntimeException("feedbacks are already 
delayed");
+               }
+               
+               cycle.setMetaData(KEY, this);
+               
+               this.cycle = cycle;
+       }
+       
+       /**
+        * Get the current delay.
+        * 
+        * @param cycle
+        * @return optional delay
+        */
+       public static Optional<FeedbackDelay> get(RequestCycle cycle) {
+               return Optional.ofNullable(cycle.getMetaData(KEY));
+       }
+
+       /**
+        * Postpone {@link Component#beforeRender()} on the given feedback.
+        * 
+        * @param feedback
+        * @return
+        */
+       public FeedbackDelay postpone(IFeedback feedback) {
+               feedbacks.add(feedback);
+               
+               return this;
+       }
+
+       /**
+        * Prepares all postponed feedbacks for render.
+        * 
+        * @see IFeedback#beforeRender()
+        */
+       public void beforeRender() {
+               cycle.setMetaData(KEY, null);
+               cycle = null;
+               
+               for (IFeedback feedback : feedbacks)
+               {
+                       if (feedback instanceof Component) {
+                               Component component = (Component)feedback;
+                               
+                               // render only if it is still in the page 
hierarchy (WICKET-4895)
+                               if (component.findParent(Page.class) == null)
+                               {
+                                       continue;
+                               }                       
+                       }
+               
+                       feedback.beforeRender();
+               }
+       }
+       
+       public void release() {
+               if (cycle != null) {
+                       cycle.setMetaData(KEY, null);
+                       cycle = null;
+                       feedbacks.clear();
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/810185e6/wicket-core/src/main/java/org/apache/wicket/feedback/IFeedback.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/feedback/IFeedback.java 
b/wicket-core/src/main/java/org/apache/wicket/feedback/IFeedback.java
index a1bd08c..6e0085e 100644
--- a/wicket-core/src/main/java/org/apache/wicket/feedback/IFeedback.java
+++ b/wicket-core/src/main/java/org/apache/wicket/feedback/IFeedback.java
@@ -20,12 +20,13 @@ package org.apache.wicket.feedback;
  * Interface for components that present some kind of feedback to the user, 
normally based on the
  * feedback messages attached to various components on a given page.
  * 
- * This is basically a marker interface that tells Wicket that this 
component's onBeforeRender
- * method must be called after all non feedback components have been 
initialized.
+ * This is tells Wicket that a component's {@link Component#beforeRender()} 
must be called after all non
+ * feedback components have been initialized.
  * 
  * @author Jonathan Locke
  * @author Eelco Hillenius
  */
 public interface IFeedback
 {
+       void beforeRender();
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/810185e6/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java 
b/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java
index 2bef5e2..caec116 100644
--- a/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java
+++ b/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java
@@ -28,6 +28,7 @@ import javax.servlet.http.Cookie;
 
 import org.apache.wicket.Component;
 import org.apache.wicket.Page;
+import org.apache.wicket.feedback.FeedbackDelay;
 import org.apache.wicket.markup.head.HeaderItem;
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.head.IWrappedHeaderItem;
@@ -238,19 +239,36 @@ public abstract class PartialPageUpdate
        {
                componentsFrozen = true;
 
-               // process component markup
-               for (Map.Entry<String, Component> stringComponentEntry : 
markupIdToComponent.entrySet())
-               {
-                       final Component component = 
stringComponentEntry.getValue();
-
-                       if (!containsAncestorFor(component))
+               List<Component> prepared = new 
ArrayList<>(markupIdToComponent.size());
+               
+               // prepare components
+               FeedbackDelay delay = new FeedbackDelay(RequestCycle.get());
+               try {
+                       for (Component component : markupIdToComponent.values())
                        {
-                               writeComponent(response, 
component.getAjaxRegionMarkupId(), component, encoding);
+                               if (!containsAncestorFor(component))
+                               {
+                                       prepareComponent(component);
+                                       prepared.add(component);
+                               }
                        }
+
+                       // .. now prepare all postponed feedbacks
+                       delay.beforeRender();
+               } finally {
+                       delay.release();
+               }
+
+               // write components
+               for (Component component : prepared)
+               {
+                       writeComponent(response, 
component.getAjaxRegionMarkupId(), component, encoding);
                }
 
                if (header != null)
                {
+                       RequestCycle cycle = RequestCycle.get();
+                       
                        // some header responses buffer all calls to render*** 
until close is called.
                        // when they are closed, they do something (i.e. 
aggregate all JS resource urls to a
                        // single url), and then "flush" (by writing to the 
real response) before closing.
@@ -258,14 +276,14 @@ public abstract class PartialPageUpdate
                        // tag, which we do here:
                        headerRendering = true;
                        // save old response, set new
-                       Response oldResponse = 
RequestCycle.get().setResponse(headerBuffer);
+                       Response oldResponse = cycle.setResponse(headerBuffer);
                        headerBuffer.reset();
 
                        // now, close the response (which may render things)
                        header.getHeaderResponse().close();
 
                        // revert to old response
-                       RequestCycle.get().setResponse(oldResponse);
+                       cycle.setResponse(oldResponse);
 
                        // write the XML tags and we're done
                        writeHeaderContribution(response);
@@ -274,6 +292,44 @@ public abstract class PartialPageUpdate
        }
 
        /**
+        * Prepare a single component
+        *
+        * @param component
+        *      the component to prepare
+        */
+       protected void prepareComponent(Component component)
+       {
+               if (component.getRenderBodyOnly() == true)
+               {
+                       throw new IllegalStateException(
+                                       "A partial update is not possible for a 
component that has renderBodyOnly enabled. Component: " +
+                                                       component.toString());
+               }
+
+               component.setOutputMarkupId(true);
+
+               // Initialize temporary variables
+               final Page page = component.findParent(Page.class);
+               if (page == null)
+               {
+                       // dont throw an exception but just ignore this 
component, somehow
+                       // it got removed from the page.
+                       LOG.warn("Component '{}' not rendered because it was 
already removed from page", component);
+                       return;
+               }
+
+               try
+               {
+                       component.beforeRender();
+               }
+               catch (RuntimeException e)
+               {
+                       bodyBuffer.reset();
+                       throw e;
+               }
+       }
+
+       /**
         * Writes a single component
         *
         * @param response

http://git-wip-us.apache.org/repos/asf/wicket/blob/810185e6/wicket-core/src/main/java/org/apache/wicket/page/XmlPartialPageUpdate.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/page/XmlPartialPageUpdate.java 
b/wicket-core/src/main/java/org/apache/wicket/page/XmlPartialPageUpdate.java
index 28c2ffa..5f78ef3 100644
--- a/wicket-core/src/main/java/org/apache/wicket/page/XmlPartialPageUpdate.java
+++ b/wicket-core/src/main/java/org/apache/wicket/page/XmlPartialPageUpdate.java
@@ -59,29 +59,11 @@ public class XmlPartialPageUpdate extends PartialPageUpdate
                response.write("\"?>");
                response.write(START_ROOT_ELEMENT);
        }
-
+       
        @Override
        protected void writeComponent(Response response, String markupId, 
Component component, String encoding)
        {
-               if (component.getRenderBodyOnly() == true)
-               {
-                       throw new IllegalStateException(
-                                       "A partial update is not possible for a 
component that has renderBodyOnly enabled. Component: " +
-                                                       component.toString());
-               }
-
-               component.setOutputMarkupId(true);
-
-               // Initialize temporary variables
                final Page page = component.findParent(Page.class);
-               if (page == null)
-               {
-                       // dont throw an exception but just ignore this 
component, somehow
-                       // it got removed from the page.
-                       LOG.warn("Component '{}' with markupid: '{}' not 
rendered because it was already removed from page",
-                                       component, markupId);
-                       return;
-               }
 
                // substitute our encoding response for the old one so we can 
capture
                // component's markup in a manner safe for transport inside 
CDATA block
@@ -89,42 +71,20 @@ public class XmlPartialPageUpdate extends PartialPageUpdate
 
                try
                {
+                       // render any associated headers of the component
+                       writeHeaderContribution(response, component);
+                       
                        bodyBuffer.reset();
                        
-                       page.startComponentRender(component);
-
-                       try
-                       {
-                               component.prepareForRender();
-
-                               // render any associated headers of the 
component
-                               writeHeaderContribution(response, component);
-                       }
-                       catch (RuntimeException e)
-                       {
-                               try
-                               {
-                                       component.afterRender();
-                               }
-                               catch (RuntimeException e2)
-                               {
-                                       // ignore this one could be a result 
off.
-                               }
-                               bodyBuffer.reset();
-                               throw e;
-                       }
-
                        try
                        {
-                               component.render();
+                               component.renderPart();
                        }
                        catch (RuntimeException e)
                        {
                                bodyBuffer.reset();
                                throw e;
                        }
-
-                       page.endComponentRender(component);
                }
                finally
                {

http://git-wip-us.apache.org/repos/asf/wicket/blob/810185e6/wicket-core/src/test/java/org/apache/wicket/feedback/FeedbackRenderTest.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/feedback/FeedbackRenderTest.java 
b/wicket-core/src/test/java/org/apache/wicket/feedback/FeedbackRenderTest.java
index 8f0f7cb..c12fb2a 100644
--- 
a/wicket-core/src/test/java/org/apache/wicket/feedback/FeedbackRenderTest.java
+++ 
b/wicket-core/src/test/java/org/apache/wicket/feedback/FeedbackRenderTest.java
@@ -36,7 +36,25 @@ public class FeedbackRenderTest extends WicketTestCase
 
                tester.startPage(page);
 
-               // non-IFeedback first, then IFeedback from nested to top
-               assertEquals("|id4|id3|id2|id1", 
page.onBeforeRenderOrder.toString());
+               // non-IFeedback first, then IFeedback top-down
+               assertEquals("|id4|id1|id2|id3", 
page.onBeforeRenderOrder.toString());
+       }
+       
+       /**
+        * @throws Exception
+        */
+       @Test
+       public void testAjax() throws Exception
+       {
+               final FeedbacksPage page = new FeedbacksPage();
+
+               tester.startPage(page);
+               
+               page.onBeforeRenderOrder.setLength(0);
+
+               tester.executeAjaxEvent(page.getAjaxLink(), "click");
+               
+               // non-IFeedback first, then IFeedback top-down
+               assertEquals("|id4|id1|id2|id3", 
page.onBeforeRenderOrder.toString());
        }
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/810185e6/wicket-core/src/test/java/org/apache/wicket/feedback/FeedbacksPage.html
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/feedback/FeedbacksPage.html 
b/wicket-core/src/test/java/org/apache/wicket/feedback/FeedbacksPage.html
index 8d9000f..bc6c4f1 100644
--- a/wicket-core/src/test/java/org/apache/wicket/feedback/FeedbacksPage.html
+++ b/wicket-core/src/test/java/org/apache/wicket/feedback/FeedbacksPage.html
@@ -14,14 +14,20 @@
 -->
 <html>
 <body>
-<span wicket:id="id1">
-       <span wicket:id="id2">
-               <span wicket:id="id3">
+<span wicket:id="feedbacks">
+       <span wicket:id="id1">
+               <span wicket:id="id2">
+                       <span wicket:id="id3">
+                       </span>
                </span>
-       </span>
-</span>        
+       </span> 
+</span>
 
 <span wicket:id="id4">
 </span>
+
+<span wicket:id="ajax">
+</span>
+
 </body>
 </html>

http://git-wip-us.apache.org/repos/asf/wicket/blob/810185e6/wicket-core/src/test/java/org/apache/wicket/feedback/FeedbacksPage.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/feedback/FeedbacksPage.java 
b/wicket-core/src/test/java/org/apache/wicket/feedback/FeedbacksPage.java
index 840495c..50c920f 100644
--- a/wicket-core/src/test/java/org/apache/wicket/feedback/FeedbacksPage.java
+++ b/wicket-core/src/test/java/org/apache/wicket/feedback/FeedbacksPage.java
@@ -16,6 +16,8 @@
  */
 package org.apache.wicket.feedback;
 
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
 import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.WebPage;
 
@@ -28,13 +30,18 @@ public class FeedbacksPage extends WebPage
 
        public final StringBuilder onBeforeRenderOrder = new StringBuilder();
 
+       private AjaxLink<Void> ajaxLink;
+
        /**
         */
        public FeedbacksPage()
        {
-
+               WebMarkupContainer feedbacks = new 
WebMarkupContainer("feedbacks");
+               feedbacks.setOutputMarkupId(true);
+               add(feedbacks);
+               
                Impl impl1 = new FeedbackImpl("id1");
-               add(impl1);
+               feedbacks.add(impl1);
 
                Impl impl2 = new FeedbackImpl("id2");
                impl1.add(impl2);
@@ -43,7 +50,25 @@ public class FeedbacksPage extends WebPage
                impl2.add(impl3);
 
                Impl impl4 = new Impl("id4");
+               impl4.setOutputMarkupId(true);
                add(impl4);
+               
+               ajaxLink = new AjaxLink<Void>("ajax")
+               {
+                       @Override
+                       public void onClick(AjaxRequestTarget target)
+                       {
+                               // feedbacks added first, but should be 
prepared last
+                               target.add(feedbacks);
+                               target.add(impl4);
+                       }
+               };
+               add(ajaxLink);
+       }
+       
+       public AjaxLink<Void> getAjaxLink()
+       {
+               return ajaxLink;
        }
 
        private class Impl extends WebMarkupContainer
@@ -76,4 +101,4 @@ public class FeedbacksPage extends WebPage
                        super(id);
                }
        }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/810185e6/wicket-core/src/test/java/org/apache/wicket/markup/html/internal/EnclosurePage_1.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/markup/html/internal/EnclosurePage_1.java
 
b/wicket-core/src/test/java/org/apache/wicket/markup/html/internal/EnclosurePage_1.java
index 650bdbf..22a4a3f 100644
--- 
a/wicket-core/src/test/java/org/apache/wicket/markup/html/internal/EnclosurePage_1.java
+++ 
b/wicket-core/src/test/java/org/apache/wicket/markup/html/internal/EnclosurePage_1.java
@@ -29,23 +29,53 @@ import org.apache.wicket.markup.html.basic.Label;
 public class EnclosurePage_1 extends WebPage
 {
        private static final long serialVersionUID = 1L;
+       
+       public int pendingAfterRenderCount = 0; 
 
        /**
         * Construct.
         */
        public EnclosurePage_1()
        {
-               add(new Label("label1", "Test Label 1"));
-               add(new Label("label2", "Test Label 2"));
-               add(new Label("label3", "Test Label 3").setVisible(false));
-               add(new Label("label4", "Test Label 2"));
-               add(new Label("label5", "Test Label 2"));
-               add(new Label("label6", "Test Label 2"));
-               add(new Label("label7", "Test Label 2"));
+               add(new AfterRenderCountingLabel("label1", "Test Label 1"));
+               add(new AfterRenderCountingLabel("label2", "Test Label 2"));
+               add(new AfterRenderCountingLabel("label3", "Test Label 
3").setVisible(false));
+               add(new AfterRenderCountingLabel("label4", "Test Label 2"));
+               add(new AfterRenderCountingLabel("label5", "Test Label 2"));
+               add(new AfterRenderCountingLabel("label6", "Test Label 2"));
+               add(new AfterRenderCountingLabel("label7", "Test Label 2"));
+               
                WebMarkupContainer container = new 
WebMarkupContainer("container");
                add(container);
-               container.add(new Label("label8", "Test Label 2"));
-               add(new Label("label9", "Test Label 2"));
-               add(new Label("label10", "Test Label 3"));
+               
+               container.add(new AfterRenderCountingLabel("label8", "Test 
Label 2"));
+               
+               add(new AfterRenderCountingLabel("label9", "Test Label 2"));
+               add(new AfterRenderCountingLabel("label10", "Test Label 3"));
+       }
+       
+       class AfterRenderCountingLabel extends Label {
+
+               public AfterRenderCountingLabel(String id, String model)
+               {
+                       super(id, model);
+               }
+               
+               @Override
+               protected void onBeforeRender()
+               {
+                       super.onBeforeRender();
+                       
+                       
+                       pendingAfterRenderCount++;
+               }
+               
+               @Override
+               protected void onAfterRender()
+               {
+                       super.onAfterRender();
+                       
+                       pendingAfterRenderCount--;
+               }
        }
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/810185e6/wicket-core/src/test/java/org/apache/wicket/markup/html/internal/EnclosureTest.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/markup/html/internal/EnclosureTest.java
 
b/wicket-core/src/test/java/org/apache/wicket/markup/html/internal/EnclosureTest.java
index a34ea5c..7067c7d 100644
--- 
a/wicket-core/src/test/java/org/apache/wicket/markup/html/internal/EnclosureTest.java
+++ 
b/wicket-core/src/test/java/org/apache/wicket/markup/html/internal/EnclosureTest.java
@@ -52,6 +52,10 @@ public class EnclosureTest extends WicketTestCase
        public void testRenderHomePage() throws Exception
        {
                executeTest(EnclosurePage_1.class, 
"EnclosurePageExpectedResult_1.html");
+               
+               EnclosurePage_1 page = 
(EnclosurePage_1)tester.getLastRenderedPage();
+               
+               assertEquals(0, page.pendingAfterRenderCount);
        }
 
        /**

Reply via email to