You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by sv...@apache.org on 2018/01/26 10:07:07 UTC

[4/9] wicket git commit: WICKET-6503 beforeRender clean-up

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 <sv...@apache.org>
Authored: Thu Dec 14 07:55:53 2017 +0100
Committer: Sven Meier <sv...@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);
 	}
 
 	/**