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 2017/10/20 12:25:30 UTC

wicket git commit: WICKET-6055 non-blocking lazy loading

Repository: wicket
Updated Branches:
  refs/heads/WICKET-6055-non-blocking-lazy [created] cfb75dd64


WICKET-6055 non-blocking lazy loading


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

Branch: refs/heads/WICKET-6055-non-blocking-lazy
Commit: cfb75dd64d54972a9b604101ff840c7c5cf80d35
Parents: eb64b2a
Author: Sven Meier <sv...@apache.org>
Authored: Fri Oct 20 14:25:15 2017 +0200
Committer: Sven Meier <sv...@apache.org>
Committed: Fri Oct 20 14:25:15 2017 +0200

----------------------------------------------------------------------
 .../wicket/ajax/AbstractAjaxTimerBehavior.java  |  16 +-
 .../examples/ajax/builtin/LazyLoadingPage.html  |  21 +-
 .../examples/ajax/builtin/LazyLoadingPage.java  | 137 ++++++++--
 .../ajax/markup/html/AjaxLazyLoadPanel.java     | 258 ++++++++++++-------
 .../markup/html/AjaxLazyLoadPanelTester.java    |  10 +-
 .../html/AjaxLazyLoadPanelTesterTest.java       |   2 +-
 .../org/apache/wicket/util/value/LongValue.java |  21 ++
 7 files changed, 331 insertions(+), 134 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/cfb75dd6/wicket-core/src/main/java/org/apache/wicket/ajax/AbstractAjaxTimerBehavior.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/ajax/AbstractAjaxTimerBehavior.java b/wicket-core/src/main/java/org/apache/wicket/ajax/AbstractAjaxTimerBehavior.java
index 0c9a276..10dbb9b 100644
--- a/wicket-core/src/main/java/org/apache/wicket/ajax/AbstractAjaxTimerBehavior.java
+++ b/wicket-core/src/main/java/org/apache/wicket/ajax/AbstractAjaxTimerBehavior.java
@@ -112,6 +112,8 @@ public abstract class AbstractAjaxTimerBehavior extends AbstractDefaultAjaxBehav
 	@Override
 	protected final void respond(final AjaxRequestTarget target)
 	{
+		Component component = getComponent();
+		
 		if (shouldTrigger())
 		{
 			onTimer(target);
@@ -124,7 +126,7 @@ public abstract class AbstractAjaxTimerBehavior extends AbstractDefaultAjaxBehav
 			}
 		}
 
-		clearTimeout(target.getHeaderResponse());
+		clearTimeout(component, target.getHeaderResponse());
 	}
 
 	/**
@@ -181,9 +183,9 @@ public abstract class AbstractAjaxTimerBehavior extends AbstractDefaultAjaxBehav
 		headerResponse.render(OnLoadHeaderItem.forScript(getJsTimeoutCall(updateInterval)));
 	}
 
-	private void clearTimeout(IHeaderResponse headerResponse)
+	private void clearTimeout(Component component, IHeaderResponse headerResponse)
 	{
-		headerResponse.render(OnLoadHeaderItem.forScript("Wicket.Timer.clear('" + getComponent().getMarkupId() + "');"));
+		headerResponse.render(OnLoadHeaderItem.forScript("Wicket.Timer.clear('" + component.getMarkupId() + "');"));
 	}
 
 	/**
@@ -200,7 +202,7 @@ public abstract class AbstractAjaxTimerBehavior extends AbstractDefaultAjaxBehav
 
 			if (target != null)
 			{
-				clearTimeout(target.getHeaderResponse());
+				clearTimeout(getComponent(), target.getHeaderResponse());
 			}
 		}
 	}
@@ -208,13 +210,15 @@ public abstract class AbstractAjaxTimerBehavior extends AbstractDefaultAjaxBehav
 	@Override
 	public void onRemove(Component component)
 	{
-		component.getRequestCycle().find(IPartialPageRequestHandler.class).ifPresent(target -> clearTimeout(target.getHeaderResponse()));
+		component.getRequestCycle().find(IPartialPageRequestHandler.class).ifPresent(target -> clearTimeout(component, target.getHeaderResponse()));
 	}
 
 	@Override
 	protected void onUnbind()
 	{
-		getComponent().getRequestCycle().find(IPartialPageRequestHandler.class).ifPresent(target -> clearTimeout(target.getHeaderResponse()));
+		Component component = getComponent();
+		
+		component.getRequestCycle().find(IPartialPageRequestHandler.class).ifPresent(target -> clearTimeout(component, target.getHeaderResponse()));
 	}
 
 	/**

http://git-wip-us.apache.org/repos/asf/wicket/blob/cfb75dd6/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/LazyLoadingPage.html
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/LazyLoadingPage.html b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/LazyLoadingPage.html
index 821833d..576b194 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/LazyLoadingPage.html
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/LazyLoadingPage.html
@@ -1,12 +1,27 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <wicket:extend xmlns:wicket="http://wicket.apache.org">
 
+<p>
 This example demonstrates the AjaxLazyLoadPanel
 It will lazy load a panel after the page is first fully rendered.
 So panels that can take a while too create can be lazy created
 by an ajax call after the page is rendered.
-  
-<br/><br/>
+</p>
+
+<div wicket:id="nonblocking">
+	<h2>Non-blocking lazy panels</h2>
+
+	<p><a href="#" wicket:id="start">Start non-blocking panels</a> (<a href="#" wicket:id="startAjax">via Ajax</a>)</p>
+
+	<div wicket:id="repeater"></div>
+</div>
+
+<div wicket:id="blocking">
+	<h2>Blocking lazy panels</h2>
+	
+	<p><a href="#" wicket:id="start">Start blocking panels</a> (<a href="#" wicket:id="startAjax">via Ajax</a>)</p>
+
+	<div wicket:id="repeater"></div>
+</div>
 
-<div wicket:id="lazy"></div>
 </wicket:extend>

http://git-wip-us.apache.org/repos/asf/wicket/blob/cfb75dd6/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/LazyLoadingPage.java
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/LazyLoadingPage.java b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/LazyLoadingPage.java
index 23b4403..ddf1de6 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/LazyLoadingPage.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/ajax/builtin/LazyLoadingPage.java
@@ -16,38 +16,139 @@
  */
 package org.apache.wicket.examples.ajax.builtin;
 
-import org.apache.wicket.Component;
+import java.util.Random;
+
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
 import org.apache.wicket.extensions.ajax.markup.html.AjaxLazyLoadPanel;
+import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.Link;
+import org.apache.wicket.markup.repeater.RepeatingView;
+import org.apache.wicket.util.time.Duration;
 
-/**
- * @author jcompagner
- */
+@SuppressWarnings({ "javadoc", "serial" })
 public class LazyLoadingPage extends BasePage
 {
-	/**
-	 * Construct.
-	 */
+	private Random r = new Random();
+	private WebMarkupContainer nonblocking;
+	private WebMarkupContainer blocking;
+	private RepeatingView blockingRepeater;
+	private RepeatingView nonBlockingRepeater;
+
 	public LazyLoadingPage()
 	{
-		add(new AjaxLazyLoadPanel("lazy")
+		nonblocking = new WebMarkupContainer("nonblocking");
+		nonblocking.setOutputMarkupId(true);
+		add(nonblocking);
+		
+		nonblocking.add(new Link<Void>("start")
+		{
+			@Override
+			public void onClick()
+			{
+				addNonBlockingPanels();
+			}
+		});
+		nonblocking.add(new AjaxLink<Void>("startAjax")
+		{
+			@Override
+			public void onClick(AjaxRequestTarget target)
+			{
+				addNonBlockingPanels();
+			}
+		});
+		
+		nonBlockingRepeater = new RepeatingView("repeater");
+		nonblocking.add(nonBlockingRepeater);
+		
+		blocking = new WebMarkupContainer("blocking");
+		blocking.setOutputMarkupId(true);
+		add(blocking);
+		
+		blocking.add(new Link<Void>("start")
 		{
-
 			@Override
-			public Component getLazyLoadComponent(String id)
+			public void onClick()
 			{
-				// sleep for 5 seconds to show the behavior
-				try
+				addBlockingPanels();
+			}
+		});
+		blocking.add(new AjaxLink<Void>("startAjax")
+		{
+			@Override
+			public void onClick(AjaxRequestTarget target)
+			{
+				addBlockingPanels();
+			}
+		});
+
+		blockingRepeater = new RepeatingView("repeater");
+		blocking.add(blockingRepeater);
+	}
+
+	private void addNonBlockingPanels()
+	{
+		nonBlockingRepeater.removeAll();
+
+		for (int i = 0; i < 10; i++)
+			nonBlockingRepeater.add(new AjaxLazyLoadPanel<Label>(nonBlockingRepeater.newChildId())
+			{
+				private static final long serialVersionUID = 1L;
+
+				private long startTime = System.currentTimeMillis();
+
+				private int seconds = r.nextInt(10);
+
+				@Override
+				protected boolean isContentReady()
 				{
-					Thread.sleep(5000);
+					return Duration.milliseconds(System.currentTimeMillis() - startTime)
+						.seconds() > seconds;
 				}
-				catch (InterruptedException e)
+				
+				@Override
+				protected Duration getUpdateInterval()
 				{
-					throw new RuntimeException(e);
+					return Duration.milliseconds(seconds * 1000 / 10);
 				}
-				return new Label(id, "Lazy Loaded after 5 seconds");
-			}
 
-		});
+				@Override
+				public Label createContentComponent(String id)
+				{
+					return new Label(id, "Lazy Loaded after " + seconds + " seconds");
+				}
+			});
+		
+		getRequestCycle().find(AjaxRequestTarget.class).ifPresent(t -> t.add(nonblocking));
+	}
+
+	private void addBlockingPanels()
+	{
+		blockingRepeater.removeAll();
+
+		for (int i = 0; i < 5; i++)
+			blockingRepeater.add(new AjaxLazyLoadPanel<Label>(blockingRepeater.newChildId())
+			{
+				private static final long serialVersionUID = 1L;
+
+				private int seconds = r.nextInt(5);
+
+				@Override
+				public Label createContentComponent(String markupId)
+				{
+					try
+					{
+						Thread.sleep(seconds * 1000);
+					}
+					catch (InterruptedException e)
+					{
+					}
+					return new Label(markupId,
+						"Lazy loaded after blocking the Wicket thread for " + seconds + " seconds");
+				}
+			});
+		
+		getRequestCycle().find(AjaxRequestTarget.class).ifPresent(t -> t.add(blocking));
 	}
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/cfb75dd6/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/AjaxLazyLoadPanel.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/AjaxLazyLoadPanel.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/AjaxLazyLoadPanel.java
index ce473ad..01cef17 100644
--- a/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/AjaxLazyLoadPanel.java
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/AjaxLazyLoadPanel.java
@@ -16,42 +16,48 @@
  */
 package org.apache.wicket.extensions.ajax.markup.html;
 
+import java.util.List;
+import java.util.Optional;
+
 import org.apache.wicket.Component;
+import org.apache.wicket.ajax.AbstractAjaxTimerBehavior;
 import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
 import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
-import org.apache.wicket.markup.head.IHeaderResponse;
-import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.request.IRequestHandler;
 import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.request.handler.resource.ResourceReferenceRequestHandler;
+import org.apache.wicket.util.time.Duration;
+import org.apache.wicket.util.visit.IVisit;
+import org.apache.wicket.util.visit.IVisitor;
 
 /**
- * A panel where you can lazy load another panel. This can be used if you have a panel/component
- * that is pretty heavy in creation and you first want to show the user the page and then replace
- * the panel when it is ready.
- * 
- * @author jcompagner
+ * A panel which load lazily a single content component. This can be used if you have a
+ * component that is pretty heavy in creation and you first want to show the user the page and
+ * then replace the panel when it is ready.
+ * <p>
+ * This panel will wait with adding the content until {@link #isContentReady()} returns
+ * {@code true}. It will poll using an AJAX timer behavior that is installed on the page. When the
+ * component is replaced, the timer stops. When you have multiple {@code AjaxLazyLoadPanel}s on the
+ * same page, only one timer is used and all panels piggyback on this single timer.
+ * <p> 
+ * This component will also replace the contents when a normal request comes through and the
+ * content is ready.
  * 
  * @since 1.3
  */
-public abstract class AjaxLazyLoadPanel extends Panel
+public abstract class AjaxLazyLoadPanel<T extends Component> extends Panel
 {
 	private static final long serialVersionUID = 1L;
 
 	/**
 	 * The component id which will be used to load the lazily loaded component.
 	 */
-	public static final String LAZY_LOAD_COMPONENT_ID = "content";
+	private static final String CONTENT_ID = "content";
 
-	// state,
-	// 0:add loading component
-	// 1:loading component added, waiting for ajax replace
-	// 2:ajax replacement completed
-	private byte state = 0;
+	private boolean loaded;
 
 	/**
 	 * Constructor
@@ -74,121 +80,173 @@ public abstract class AjaxLazyLoadPanel extends Panel
 		super(id, model);
 
 		setOutputMarkupId(true);
-
-		add(new AbstractDefaultAjaxBehavior()
-		{
-			private static final long serialVersionUID = 1L;
-
-			@Override
-			protected void respond(final AjaxRequestTarget target)
-			{
-				if (state < 2)
-				{
-					Component component = getLazyLoadComponent(LAZY_LOAD_COMPONENT_ID);
-					AjaxLazyLoadPanel.this.replace(component);
-					setState((byte) 2);
-					AjaxLazyLoadPanel.this.onComponentLoaded(component, target);
-				}
-				target.add(AjaxLazyLoadPanel.this);
-
-			}
-
-			@Override
-			protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
-			{
-				super.updateAjaxAttributes(attributes);
-				AjaxLazyLoadPanel.this.updateAjaxAttributes(attributes);
-			}
-
-			@Override
-			public void renderHead(final Component component, final IHeaderResponse response)
-			{
-				super.renderHead(component, response);
-				if (state < 2)
-				{
-					CharSequence js = getCallbackScript(component);
-					handleCallbackScript(response, js, component);
-				}
-			}
-		});
-	}
-
-	protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
-	{
 	}
 
 	/**
-	 * Allows subclasses to change the callback script if needed.
+	 * Determines that the content we're waiting for is ready, typically used in polling background
+	 * threads for their result. Override this to implement your own check.
+	 * <p>
+	 * This default implementation returns {@code true}, i.e. assuming the content is ready immediately.
 	 * 
-	 * @param response
-	 *      the current response that writes to the header
-	 * @param callbackScript
-	 *      the JavaScript to write in the header
-	 * @param component
-	 *      the component which produced the callback script
-	 */
-	protected void handleCallbackScript(final IHeaderResponse response,
-		final CharSequence callbackScript, final Component component)
-	{
-		response.render(OnDomReadyHeaderItem.forScript(callbackScript));
-	}
-
-	/**
-	 * @see org.apache.wicket.Component#onBeforeRender()
+	 * @return whether the actual content is ready
 	 */
-	@Override
-	protected void onBeforeRender()
+	protected boolean isContentReady()
 	{
-		if (state == 0)
-		{
-			add(getLoadingComponent(LAZY_LOAD_COMPONENT_ID));
-			setState((byte)1);
-		}
-		super.onBeforeRender();
+		return true;
 	}
 
 	/**
+	 * Create a loading component shown instead of the actual content until it is {@link #isContentReady()}.
 	 * 
-	 * @param state
+	 * @param markupId
+	 *            The components markupid.
+	 * @return The component to show while the real content isn't ready yet
 	 */
-	private void setState(final byte state)
+	protected Component createLoadingComponent(final String markupId)
 	{
-		this.state = state;
-		getPage().dirty();
+		IRequestHandler handler = new ResourceReferenceRequestHandler(
+			AbstractDefaultAjaxBehavior.INDICATOR);
+		return new Label(markupId,
+			"<img alt=\"Loading...\" src=\"" + RequestCycle.get().urlFor(handler) + "\"/>")
+				.setEscapeModelStrings(false);
 	}
 
 	/**
+	 * Factory method for creating the lazily loaded content that replaces the loading component after
+	 * {@link #isContentReady()} returns {@code true}. You may call setRenderBodyOnly(true)
+	 * on this component if you need the body only.
 	 * 
 	 * @param markupId
 	 *            The components markupid.
-	 * @return The component that must be lazy created. You may call setRenderBodyOnly(true) on this
-	 *         component if you need the body only.
+	 * @return the content to show after {@link #isContentReady()}
 	 */
-	public abstract Component getLazyLoadComponent(String markupId);
+	protected abstract T createContentComponent(String markupId);
 
 	/**
-	 * Called when the placeholder component is replaced with the lazy loaded one.
+	 * Called after the loading component was replaced with the lazy loaded content.
+	 * <p>
+	 * This default implementation does nothing.
 	 *
 	 * @param component
-	 *      The lazy loaded component
+	 *            The lazy loaded content
 	 * @param target
-	 *      The Ajax request handler
+	 *            optional Ajax request handler
 	 */
-	protected void onComponentLoaded(Component component, AjaxRequestTarget target)
+	protected void onContentLoaded(T component, Optional<AjaxRequestTarget> target)
 	{
 	}
 
+	@Override
+	protected void onInitialize()
+	{
+		super.onInitialize();
+
+		AjaxRequestTarget target = getRequestCycle().find(AjaxRequestTarget.class).orElse(null);
+		
+		AjaxLazyLoadTimer timer;
+		// when the timer is not yet installed add it
+		List<AjaxLazyLoadTimer> behaviors = getPage().getBehaviors(AjaxLazyLoadTimer.class);
+		if (behaviors.isEmpty()) {
+			timer = new AjaxLazyLoadTimer();
+			getPage().add(timer);
+			if (target != null) {
+				// the timer will not be rendered, so stop it first
+				// and restart it immediately on the Ajax request
+				timer.stop(null);
+				timer.restart(target);
+			}
+		}
+	}
+	
+	@Override
+	protected void onConfigure()
+	{
+		super.onConfigure();
+
+		if (get(CONTENT_ID) == null) {
+			add(createLoadingComponent(CONTENT_ID));
+		} else {
+			isLoaded();
+		}
+	}
+
 	/**
-	 * @param markupId
-	 *            The components markupid.
-	 * @return The component to show while the real component is being created.
+	 * Get the preferred interval for updates.
+	 * <p>
+	 * Since all LazyLoadingPanels on a page share the same Ajax timer, its update interval
+	 * is derived from the minimum of all panel's update intervals.
+	 * 
+	 * @return update interval
 	 */
-	public Component getLoadingComponent(final String markupId)
-	{
-		IRequestHandler handler = new ResourceReferenceRequestHandler(
-			AbstractDefaultAjaxBehavior.INDICATOR);
-		return new Label(markupId, "<img alt=\"Loading...\" src=\"" +
-			RequestCycle.get().urlFor(handler) + "\"/>").setEscapeModelStrings(false);
+	protected Duration getUpdateInterval() {
+		return Duration.seconds(1);
 	}
 
+	private boolean isLoaded() {
+		if (loaded == false)
+		{
+			if (isContentReady())
+			{
+				loaded = true;
+
+				// create the lazy load component
+				T content = createContentComponent(CONTENT_ID);
+
+				// replace the spinner with the new component
+				AjaxLazyLoadPanel.this.replace(content);
+
+				Optional<AjaxRequestTarget> target = getRequestCycle().find(AjaxRequestTarget.class);
+
+				// notify our subclasses of the updated component
+				onContentLoaded(content, target);
+
+				// repaint our selves if there's an AJAX request in play, otherwise let the page
+				// redraw itself
+				target.ifPresent(t -> t.add(AjaxLazyLoadPanel.this));
+			}
+		}
+		
+		return loaded;
+	}
+
+	/**
+	 * The AJAX timer for updating the AjaxLazyLoadPanel. Is designed to be a page-local singleton
+	 * running as long as LazyLoadPanels are still loading.
+	 * 
+	 * @see AjaxLazyLoadPanel#isLoaded()
+	 */
+	private static class AjaxLazyLoadTimer extends AbstractAjaxTimerBehavior
+	{
+		private static final long serialVersionUID = 1L;
+
+		public AjaxLazyLoadTimer()
+		{
+			super(Duration.ONE_SECOND);
+		}
+
+		@Override
+		protected void onTimer(AjaxRequestTarget target)
+		{
+			setUpdateInterval(Duration.MAXIMUM);
+			
+			getComponent().getPage().visitChildren(AjaxLazyLoadPanel.class, new IVisitor<AjaxLazyLoadPanel<?>, Void>()
+			{
+				@Override
+				public void component(AjaxLazyLoadPanel<?> panel, IVisit<Void> visit)
+				{
+					if (panel.isLoaded() == false) {
+						setUpdateInterval(Duration.min(getUpdateInterval(), panel.getUpdateInterval()));
+					}						
+				}
+			});
+
+			// all panels have completed their replacements, we can stop the timer
+			if (Duration.MAXIMUM.equals(getUpdateInterval()))
+			{
+				stop(target);
+				
+				getComponent().remove(this);
+			}
+		}
+	}
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/cfb75dd6/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/AjaxLazyLoadPanelTester.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/AjaxLazyLoadPanelTester.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/AjaxLazyLoadPanelTester.java
index a141108..81f0224 100644
--- a/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/AjaxLazyLoadPanelTester.java
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/AjaxLazyLoadPanelTester.java
@@ -19,9 +19,9 @@ package org.apache.wicket.extensions.ajax.markup.html;
 import java.util.List;
 
 import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.ajax.AbstractAjaxTimerBehavior;
 import org.apache.wicket.ajax.AjaxSelfUpdatingTimerBehavior;
 import org.apache.wicket.behavior.AbstractAjaxBehavior;
-import org.apache.wicket.behavior.Behavior;
 import org.apache.wicket.util.tester.BaseWicketTester;
 import org.apache.wicket.util.visit.IVisit;
 import org.apache.wicket.util.visit.IVisitor;
@@ -35,7 +35,6 @@ import org.slf4j.LoggerFactory;
  */
 public class AjaxLazyLoadPanelTester
 {
-
 	private static final Logger logger = LoggerFactory.getLogger(AjaxLazyLoadPanelTester.class);
 
 	/**
@@ -59,12 +58,13 @@ public class AjaxLazyLoadPanelTester
 			{
 				// get the AbstractAjaxBehaviour which is responsible for
 				// getting the contents of the lazy panel
-				List<AbstractAjaxBehavior> behaviors = component.getBehaviors(AbstractAjaxBehavior.class);
+				List<AbstractAjaxTimerBehavior> behaviors = component.getPage()
+					.getBehaviors(AbstractAjaxTimerBehavior.class);
 				if (behaviors.size() == 0)
 				{
 					logger.warn("AjaxLazyLoadPanel child found, but no attached AbstractAjaxBehaviors found. A curious situation...");
 				}
-				for (Behavior b : behaviors)
+				for (AbstractAjaxTimerBehavior b : behaviors)
 				{
 					if (!(b instanceof AjaxSelfUpdatingTimerBehavior))
 					{
@@ -78,6 +78,4 @@ public class AjaxLazyLoadPanelTester
 			}
 		});
 	}
-
-
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/cfb75dd6/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/AjaxLazyLoadPanelTesterTest.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/AjaxLazyLoadPanelTesterTest.java b/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/AjaxLazyLoadPanelTesterTest.java
index 5ee746a..5cfa13a 100644
--- a/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/AjaxLazyLoadPanelTesterTest.java
+++ b/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/AjaxLazyLoadPanelTesterTest.java
@@ -43,7 +43,7 @@ public class AjaxLazyLoadPanelTesterTest extends WicketTestCase
 			private static final long serialVersionUID = 1L;
 
 			@Override
-			public Component getLazyLoadComponent(final String markupId)
+			public Component createContentComponent(final String markupId)
 			{
 				return new Label(markupId, "lazy panel test").setRenderBodyOnly(true);
 			}

http://git-wip-us.apache.org/repos/asf/wicket/blob/cfb75dd6/wicket-util/src/main/java/org/apache/wicket/util/value/LongValue.java
----------------------------------------------------------------------
diff --git a/wicket-util/src/main/java/org/apache/wicket/util/value/LongValue.java b/wicket-util/src/main/java/org/apache/wicket/util/value/LongValue.java
index 8c96060..3e5e4bc 100755
--- a/wicket-util/src/main/java/org/apache/wicket/util/value/LongValue.java
+++ b/wicket-util/src/main/java/org/apache/wicket/util/value/LongValue.java
@@ -218,6 +218,27 @@ public class LongValue implements Comparable<LongValue>, Serializable
 	}
 
 	/**
+	 * Returns the min of the two long values.
+	 * 
+	 * @param <T>
+	 * @param lhs
+	 * @param rhs
+	 * @throws IllegalArgumentException
+	 *             if either argument is {@code null}
+	 * @return min value
+	 */
+	public static <T extends LongValue> T min(final T lhs, final T rhs)
+	{
+		Args.notNull(lhs, "lhs");
+		Args.notNull(rhs, "rhs");
+		if (lhs.compareTo(rhs) < 0)
+		{
+			return lhs;
+		}
+		return rhs;
+	}
+
+	/**
 	 * Returns the max of the two long values.
 	 * 
 	 * @param <T>