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/07/02 21:25:20 UTC

[1/5] wicket git commit: WICKET-6563 new IPageStore implementation

Repository: wicket
Updated Branches:
  refs/heads/WICKET-6563 [created] bcf76f517


http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/request/handler/PageIdPoliticTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/request/handler/PageIdPoliticTest.java b/wicket-core/src/test/java/org/apache/wicket/request/handler/PageIdPoliticTest.java
index 978e87b..f9877c0 100644
--- a/wicket-core/src/test/java/org/apache/wicket/request/handler/PageIdPoliticTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/request/handler/PageIdPoliticTest.java
@@ -26,17 +26,15 @@ import org.apache.wicket.markup.IMarkupResourceStreamProvider;
 import org.apache.wicket.markup.html.WebComponent;
 import org.apache.wicket.markup.html.WebPage;
 import org.apache.wicket.mock.MockApplication;
+import org.apache.wicket.page.IManageablePage;
 import org.apache.wicket.page.IPageManager;
-import org.apache.wicket.page.IPageManagerContext;
-import org.apache.wicket.page.PageStoreManager;
-import org.apache.wicket.pageStore.DefaultPageStore;
-import org.apache.wicket.pageStore.IPageStore;
+import org.apache.wicket.page.PageManager;
+import org.apache.wicket.pageStore.IPageContext;
+import org.apache.wicket.pageStore.InMemoryPageStore;
 import org.apache.wicket.request.Url;
-import org.apache.wicket.serialize.java.JavaSerializer;
 import org.apache.wicket.util.resource.IResourceStream;
 import org.apache.wicket.util.resource.StringResourceStream;
 import org.apache.wicket.util.tester.WicketTester;
-import org.apache.wicket.versioning.InMemoryPageStore;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -48,7 +46,7 @@ import org.junit.Test;
 public class PageIdPoliticTest extends Assert
 {
 	private WicketTester tester;
-	private InMemoryPageStore dataStore;
+	private InMemoryPageStore pageStore;
 	private MockApplication application;
 	private int storeCount;
 
@@ -87,12 +85,12 @@ public class PageIdPoliticTest extends Assert
 	public void setUp() throws Exception
 	{
 		application = new MockApplication();
-		dataStore = new InMemoryPageStore()
+		pageStore = new InMemoryPageStore("test", Integer.MAX_VALUE)
 		{
 			@Override
-			public void storeData(String sessionId, int pageId, byte[] pageAsBytes)
+			public void addPage(IPageContext context, IManageablePage page)
 			{
-				super.storeData(sessionId, pageId, pageAsBytes);
+				super.addPage(context, page);
 				storeCount++;
 			}
 		};
@@ -104,12 +102,9 @@ public class PageIdPoliticTest extends Assert
 				return new IPageManagerProvider()
 				{
 					@Override
-					public IPageManager apply(IPageManagerContext pageManagerContext)
+					public IPageManager get()
 					{
-						IPageStore pageStore = new DefaultPageStore(new JavaSerializer(
-							application.getApplicationKey()), dataStore, 4);
-						return new PageStoreManager(application.getName(), pageStore,
-							pageManagerContext);
+						return new PageManager(pageStore);
 					}
 				};
 			}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/request/handler/PageProviderTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/request/handler/PageProviderTest.java b/wicket-core/src/test/java/org/apache/wicket/request/handler/PageProviderTest.java
index 3f87b08..1f9740a 100644
--- a/wicket-core/src/test/java/org/apache/wicket/request/handler/PageProviderTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/request/handler/PageProviderTest.java
@@ -62,8 +62,8 @@ public class PageProviderTest extends WicketTestCase
 
 		// storing test page
 		TestMapperContext mapperContext = new TestMapperContext();
-		mapperContext.getPageManager().touchPage(testPage);
-		mapperContext.getPageManager().commitRequest();
+		mapperContext.getPageManager().addPage(testPage);
+		mapperContext.getPageManager().detach();
 
 		// by cleaning session cache we make sure of not being testing the same in-memory instance
 		mapperContext.cleanSessionCache();
@@ -77,8 +77,8 @@ public class PageProviderTest extends WicketTestCase
 		// changing some sate
 		StatefullMockPage providedPage = (StatefullMockPage)pageProvider.getPageInstance();
 		providedPage.state = newState;
-		mapperContext.getPageManager().touchPage(providedPage);
-		mapperContext.getPageManager().commitRequest();
+		mapperContext.getPageManager().addPage(providedPage);
+		mapperContext.getPageManager().detach();
 
 
 		mapperContext.cleanSessionCache();
@@ -212,8 +212,8 @@ public class PageProviderTest extends WicketTestCase
 	{
 		TestMapperContext mapperContext = new TestMapperContext();
 		Page page = new TestPage();
-		mapperContext.getPageManager().touchPage(page);
-		mapperContext.getPageManager().commitRequest();
+		mapperContext.getPageManager().addPage(page);
+		mapperContext.getPageManager().detach();
 
 		// by cleaning session cache we make sure of not being testing the same in-memory instance
 		mapperContext.cleanSessionCache();
@@ -235,8 +235,8 @@ public class PageProviderTest extends WicketTestCase
 	{
 		TestMapperContext mapperContext = new TestMapperContext();
 		Page page = new TestPage();
-		mapperContext.getPageManager().touchPage(page);
-		mapperContext.getPageManager().commitRequest();
+		mapperContext.getPageManager().addPage(page);
+		mapperContext.getPageManager().detach();
 
 		// by cleaning session cache we make sure of not being testing the same in-memory instance
 		mapperContext.cleanSessionCache();
@@ -252,8 +252,8 @@ public class PageProviderTest extends WicketTestCase
 	{
 		TestMapperContext mapperContext = new TestMapperContext();
 		Page page = new TestPage();
-		mapperContext.getPageManager().touchPage(page);
-		mapperContext.getPageManager().commitRequest();
+		mapperContext.getPageManager().addPage(page);
+		mapperContext.getPageManager().detach();
 
 		PageProvider pageProvider = new PageProvider(page.getPageId(), page.getRenderCount());
 		JavaSerializer javaSerializer = new JavaSerializer("app");

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/versioning/InMemoryPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/versioning/InMemoryPageStore.java b/wicket-core/src/test/java/org/apache/wicket/versioning/InMemoryPageStore.java
deleted file mode 100644
index f69a918..0000000
--- a/wicket-core/src/test/java/org/apache/wicket/versioning/InMemoryPageStore.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * 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.versioning;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.wicket.pageStore.IDataStore;
-
-/**
- * An implementation of {@link IDataStore} that stores the data in memory. Used only for testing
- * purposes.
- * 
- * @author martin-g
- */
-public class InMemoryPageStore implements IDataStore
-{
-
-	/**
-	 * A map of : sessionId => pageId => pageAsBytes
-	 */
-	private final ConcurrentHashMap<String, Map<Integer, byte[]>> store;
-
-	/**
-	 * Construct.
-	 */
-	public InMemoryPageStore()
-	{
-		store = new ConcurrentHashMap<> ();
-	}
-
-	@Override
-	public void destroy()
-	{
-		store.clear();
-	}
-
-	/**
-	 * @see org.apache.wicket.pageStore.IDataStore#getData(java.lang.String, int)
-	 */
-	@Override
-	public byte[] getData(String sessionId, int pageId)
-	{
-		byte[] pageAsBytes = null;
-
-		final Map<Integer, byte[]> sessionPages = store.get(sessionId);
-		if (sessionPages != null)
-		{
-			pageAsBytes = sessionPages.get(pageId);
-		}
-
-		return pageAsBytes;
-	}
-
-	/**
-	 * @see org.apache.wicket.pageStore.IDataStore#removeData(java.lang.String, int)
-	 */
-	@Override
-	public void removeData(String sessionId, int pageId)
-	{
-		final Map<Integer, byte[]> sessionPages = store.get(sessionId);
-		if (sessionPages != null)
-		{
-			sessionPages.remove(pageId);
-		}
-	}
-
-	/**
-	 * @see org.apache.wicket.pageStore.IDataStore#removeData(java.lang.String)
-	 */
-	@Override
-	public void removeData(String sessionId)
-	{
-		store.remove(sessionId);
-	}
-
-	/**
-	 * @see org.apache.wicket.pageStore.IDataStore#storeData(java.lang.String, int, byte[])
-	 */
-	@Override
-	public void storeData(String sessionId, int pageId, byte[] pageAsBytes)
-	{
-		Map<Integer, byte[]> sessionPages = store.get(sessionId);
-		if (sessionPages == null)
-		{
-			sessionPages = new ConcurrentHashMap<>();
-			Map<Integer, byte[]> tmpSessionPages = store.putIfAbsent(sessionId, sessionPages);
-			if (tmpSessionPages != null)
-			{
-				sessionPages = tmpSessionPages;
-			}
-		}
-
-		sessionPages.put(pageId, pageAsBytes);
-	}
-
-	/**
-	 * @see org.apache.wicket.pageStore.IDataStore#isReplicated()
-	 */
-	@Override
-	public boolean isReplicated()
-	{
-		return false;
-	}
-
-	@Override
-	public boolean canBeAsynchronous()
-	{
-		return false;
-	}
-
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/versioning/PageVersioningTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/versioning/PageVersioningTest.java b/wicket-core/src/test/java/org/apache/wicket/versioning/PageVersioningTest.java
index 9ac60fc..4e6420e 100644
--- a/wicket-core/src/test/java/org/apache/wicket/versioning/PageVersioningTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/versioning/PageVersioningTest.java
@@ -22,13 +22,12 @@ import static org.junit.Assert.assertNotNull;
 import org.apache.wicket.IPageManagerProvider;
 import org.apache.wicket.Page;
 import org.apache.wicket.page.IPageManager;
-import org.apache.wicket.page.IPageManagerContext;
-import org.apache.wicket.page.PageStoreManager;
-import org.apache.wicket.pageStore.AsynchronousDataStore;
-import org.apache.wicket.pageStore.DefaultPageStore;
-import org.apache.wicket.pageStore.IDataStore;
+import org.apache.wicket.page.PageManager;
+import org.apache.wicket.pageStore.IPageStore;
+import org.apache.wicket.pageStore.InMemoryPageStore;
+import org.apache.wicket.pageStore.InSessionPageStore;
+import org.apache.wicket.pageStore.NoopPageStore;
 import org.apache.wicket.protocol.http.WebApplication;
-import org.apache.wicket.serialize.java.JavaSerializer;
 import org.apache.wicket.util.tester.WicketTester;
 import org.junit.After;
 import org.junit.Before;
@@ -58,21 +57,10 @@ public class PageVersioningTest
 			@Override
 			protected IPageManagerProvider newTestPageManagerProvider()
 			{
-				return new IPageManagerProvider()
+				return () ->
 				{
-
-					@Override
-					public IPageManager apply(IPageManagerContext pageManagerContext)
-					{
-
-						final IDataStore dataStore = new InMemoryPageStore();
-						final AsynchronousDataStore asyncDS = new AsynchronousDataStore(dataStore,
-							100);
-						final DefaultPageStore pageStore = new DefaultPageStore(new JavaSerializer(
-							application.getApplicationKey()), asyncDS, 40);
-						return new PageStoreManager(application.getName(), pageStore,
-							pageManagerContext);
-					}
+					final IPageStore store = new InSessionPageStore(new NoopPageStore(), Integer.MAX_VALUE);
+					return new PageManager(store);
 				};
 			}
 

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/DebugBarInitializer.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/DebugBarInitializer.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/DebugBarInitializer.java
index ae17d8e..2fcdca3 100644
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/DebugBarInitializer.java
+++ b/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/DebugBarInitializer.java
@@ -38,7 +38,7 @@ public class DebugBarInitializer implements IInitializer
 			DebugBar.registerContributor(VersionDebugContributor.DEBUG_BAR_CONTRIB, application);
 			DebugBar.registerContributor(InspectorDebugPanel.DEBUG_BAR_CONTRIB, application);
 			DebugBar.registerContributor(SessionSizeDebugPanel.DEBUG_BAR_CONTRIB, application);
-			DebugBar.registerContributor(PageSizeDebugPanel.DEBUG_BAR_CONTRIB, application);
+			DebugBar.registerContributor(PageStoreDebugPanel.DEBUG_BAR_CONTRIB, application);
 		}
 	}
 

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/InspectorDebugPanel.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/InspectorDebugPanel.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/InspectorDebugPanel.java
index 33a0705..4de5cd4 100644
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/InspectorDebugPanel.java
+++ b/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/InspectorDebugPanel.java
@@ -18,12 +18,13 @@ package org.apache.wicket.devutils.debugbar;
 
 import org.apache.wicket.Component;
 import org.apache.wicket.Page;
+import org.apache.wicket.core.util.lang.WicketObjects;
 import org.apache.wicket.devutils.inspector.InspectorPage;
 import org.apache.wicket.model.IModel;
-import org.apache.wicket.model.Model;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.request.resource.PackageResourceReference;
 import org.apache.wicket.request.resource.ResourceReference;
+import org.apache.wicket.util.lang.Bytes;
 
 /**
  * A panel that adds a link to the inspector to the debug bar.
@@ -73,7 +74,22 @@ public class InspectorDebugPanel extends StandardDebugPanel
 	@Override
 	protected IModel<String> getDataModel()
 	{
-		return new Model<>("Inspector");
+		return new IModel<String>()
+		{
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public String getObject()
+			{
+				Page enclosingPage = getPage();
+				long pageSize = WicketObjects.sizeof(enclosingPage);
+				Bytes pageSizeInBytes = (pageSize > -1 ? Bytes.bytes(pageSize) : null);
+				String pageSizeAsString = pageSizeInBytes != null ? pageSizeInBytes.toString()
+					: "unknown";
+
+				return "Page: " + pageSizeAsString;
+			}
+		};
 	}
 
 	@Override

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/PageSizeDebugPanel.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/PageSizeDebugPanel.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/PageSizeDebugPanel.java
deleted file mode 100644
index f386783..0000000
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/PageSizeDebugPanel.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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.devutils.debugbar;
-
-import org.apache.wicket.Component;
-import org.apache.wicket.Page;
-import org.apache.wicket.core.util.lang.WicketObjects;
-import org.apache.wicket.markup.html.WebPage;
-import org.apache.wicket.markup.html.link.BookmarkablePageLink;
-import org.apache.wicket.model.IModel;
-import org.apache.wicket.request.resource.PackageResourceReference;
-import org.apache.wicket.request.resource.ResourceReference;
-import org.apache.wicket.util.lang.Bytes;
-
-/**
- * A panel for the debug bar that shows the size of the currently shown page.
- * <p>
- * <strong>Note</strong>: this size includes the size of the debug bar itself too!
- */
-public class PageSizeDebugPanel extends StandardDebugPanel
-{
-	private static final long serialVersionUID = 1L;
-
-	/** */
-	public static final IDebugBarContributor DEBUG_BAR_CONTRIB = new IDebugBarContributor()
-	{
-		private static final long serialVersionUID = 1L;
-
-		@Override
-		public Component createComponent(final String id, final DebugBar debugBar)
-		{
-			return new PageSizeDebugPanel(id);
-		}
-
-	};
-
-	/**
-	 * Construct.
-	 * 
-	 * @param id
-	 */
-	public PageSizeDebugPanel(final String id)
-	{
-		super(id);
-	}
-
-	@Override
-	protected Class<? extends Page> getLinkPageClass()
-	{
-		// not used
-		return WebPage.class;
-	}
-
-	// Disable the link because there is no page with more detailed information
-	@Override
-	protected BookmarkablePageLink<Void> createLink(final String id)
-	{
-		BookmarkablePageLink<Void> bookmarkablePageLink = super.createLink(id);
-		bookmarkablePageLink.setEnabled(false);
-		return bookmarkablePageLink;
-	}
-
-	@Override
-	protected ResourceReference getImageResourceReference()
-	{
-		// TODO: need better image for this:
-		return new PackageResourceReference(SessionSizeDebugPanel.class, "harddrive.png");
-	}
-
-	@Override
-	protected IModel<String> getDataModel()
-	{
-		return new IModel<String>()
-		{
-			private static final long serialVersionUID = 1L;
-
-			@Override
-			public String getObject()
-			{
-				Page enclosingPage = getPage();
-				long pageSize = WicketObjects.sizeof(enclosingPage);
-				Bytes pageSizeInBytes = (pageSize > -1 ? Bytes.bytes(pageSize) : null);
-				String pageSizeAsString = pageSizeInBytes != null ? pageSizeInBytes.toString()
-					: "unknown";
-
-				return "Page: " + pageSizeAsString;
-			}
-		};
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/PageStoreDebugPanel.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/PageStoreDebugPanel.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/PageStoreDebugPanel.java
new file mode 100644
index 0000000..eedb590
--- /dev/null
+++ b/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/PageStoreDebugPanel.java
@@ -0,0 +1,84 @@
+/*
+ * 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.devutils.debugbar;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.Page;
+import org.apache.wicket.devutils.diskstore.PageStorePage;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.pageStore.IPersistentPageStore;
+import org.apache.wicket.request.resource.PackageResourceReference;
+import org.apache.wicket.request.resource.ResourceReference;
+
+/**
+ * A panel that adds a link to persisted pages to the debug bar.
+ */
+public class PageStoreDebugPanel extends StandardDebugPanel
+{
+	private static final long serialVersionUID = 1L;
+
+	/** */
+	public static final IDebugBarContributor DEBUG_BAR_CONTRIB = new IDebugBarContributor()
+	{
+		private static final long serialVersionUID = 1L;
+
+		@Override
+		public Component createComponent(final String id, final DebugBar debugBar)
+		{
+			return new PageStoreDebugPanel(id);
+		}
+	};
+
+	/**
+	 * Construct.
+	 * 
+	 * @param id
+	 *          The component id
+	 */
+	public PageStoreDebugPanel(final String id)
+	{
+		super(id);
+	}
+
+	@Override
+	protected Class<? extends Page> getLinkPageClass()
+	{
+		return PageStorePage.class;
+	}
+
+	@Override
+	protected ResourceReference getImageResourceReference()
+	{
+		return new PackageResourceReference(PageStoreDebugPanel.class, "harddrive.png");
+	}
+
+	@Override
+	protected IModel<String> getDataModel()
+	{
+		return new IModel<String>()
+		{
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public String getObject()
+			{
+				IPersistentPageStore store = PageStorePage.getPersistentPageStore();
+				return String.format("Persisted: %s", store == null ? "N/A" : store.getTotalSize());
+			}
+		};
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/StandardDebugPanel.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/StandardDebugPanel.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/StandardDebugPanel.java
index c88499e..921b009 100644
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/StandardDebugPanel.java
+++ b/wicket-devutils/src/main/java/org/apache/wicket/devutils/debugbar/StandardDebugPanel.java
@@ -50,7 +50,7 @@ public abstract class StandardDebugPanel extends DevUtilsPanel
 	protected void onInitialize()
 	{
 		super.onInitialize();
-		BookmarkablePageLink<Void> link = createLink("link");
+		WebMarkupContainer link = createLink("link");
 		add(link);
 		ResourceReference img = getImageResourceReference();
 		if (img == null)
@@ -64,7 +64,7 @@ public abstract class StandardDebugPanel extends DevUtilsPanel
 		link.add(new Label("data", getDataModel()));
 	}
 
-	protected BookmarkablePageLink<Void> createLink(final String id)
+	protected WebMarkupContainer createLink(final String id)
 	{
 		return new BookmarkablePageLink<>(id, getLinkPageClass(), getLinkPageParameters());
 	}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/DebugDiskDataStore.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/DebugDiskDataStore.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/DebugDiskDataStore.java
deleted file mode 100644
index 9c3d882..0000000
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/DebugDiskDataStore.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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.devutils.diskstore;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.wicket.Application;
-import org.apache.wicket.pageStore.DiskDataStore;
-import org.apache.wicket.pageStore.PageWindowManager;
-import org.apache.wicket.pageStore.PageWindowManager.PageWindow;
-import org.apache.wicket.protocol.http.WebApplication;
-import org.apache.wicket.util.lang.Bytes;
-
-/**
- * An extension of {@link DiskDataStore} that is able to browse the content of the file storage.
- * <p>
- * To enable it add in YourApplication#init():
- * 
- * <pre>
- * <code>
- * DebugDiskDataStore.register(this);
- * </code>
- * </pre>
- * 
- * </p>
- * <p>
- * The data can be browsed at: <em>/wicket/internal/debug/diskDataStore</em>
- */
-public class DebugDiskDataStore extends DiskDataStore
-{
-
-	/**
-	 * Construct.
-	 * 
-	 * @param applicationName
-	 * @param fileStoreFolder
-	 * @param maxSizePerSession
-	 */
-	public DebugDiskDataStore(String applicationName, File fileStoreFolder, Bytes maxSizePerSession)
-	{
-		super(applicationName, fileStoreFolder, maxSizePerSession);
-
-	}
-
-	/**
-	 * 
-	 * @param sessionId
-	 * @param count
-	 * @return a list of the last N page windows
-	 */
-	public List<PageWindow> getLastPageWindows(String sessionId, int count)
-	{
-		List<PageWindow> pageWindows = new ArrayList<>();
-
-		SessionEntry sessionEntry = getSessionEntry(sessionId, false);
-		if (sessionEntry != null)
-		{
-			PageWindowManager windowManager = sessionEntry.getManager();
-			pageWindows.addAll(windowManager.getLastPageWindows(count));
-		}
-		return pageWindows;
-	}
-
-	@Override
-	public File getStoreFolder()
-	{
-		return super.getStoreFolder();
-	}
-
-	/**
-	 * Configures the page manager provider and mounts the page at
-	 * <em>wicket/internal/debug/diskDataStore</em>
-	 * 
-	 * @param application
-	 */
-	public static void register(final Application application)
-	{
-		application.setPageManagerProvider(new DebugPageManagerProvider(application));
-
-		((WebApplication)application).mountPage("wicket/internal/debug/diskDataStore",
-			DiskStoreBrowserPage.class);
-	}
-
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/DebugPageManagerProvider.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/DebugPageManagerProvider.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/DebugPageManagerProvider.java
deleted file mode 100644
index 7893638..0000000
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/DebugPageManagerProvider.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.devutils.diskstore;
-
-import java.io.File;
-
-import org.apache.wicket.Application;
-import org.apache.wicket.DefaultPageManagerProvider;
-import org.apache.wicket.pageStore.DiskDataStore;
-import org.apache.wicket.pageStore.IDataStore;
-import org.apache.wicket.settings.StoreSettings;
-import org.apache.wicket.util.lang.Bytes;
-
-/**
- */
-public class DebugPageManagerProvider extends DefaultPageManagerProvider
-{
-
-	private DebugDiskDataStore dataStore;
-
-	/**
-	 * Construct.
-	 * 
-	 * @param application
-	 */
-	public DebugPageManagerProvider(Application application)
-	{
-		super(application);
-	}
-
-	/**
-	 * @return the extended with debug information {@link DiskDataStore}
-	 */
-	public DebugDiskDataStore getDataStore()
-	{
-		return dataStore;
-	}
-
-	@Override
-	protected IDataStore newDataStore()
-	{
-		StoreSettings storeSettings = application.getStoreSettings();
-		File fileStoreFolder = storeSettings.getFileStoreFolder();
-		Bytes maxSizePerSession = storeSettings.getMaxSizePerSession();
-		dataStore = new DebugDiskDataStore(application.getName(), fileStoreFolder,
-			maxSizePerSession);
-		return dataStore;
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/DiskStoreBrowserPage.html
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/DiskStoreBrowserPage.html b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/DiskStoreBrowserPage.html
deleted file mode 100644
index 307884c..0000000
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/DiskStoreBrowserPage.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-   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.
--->
-<!DOCTYPE html>
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
-	
-	<head>
-		<title>Debug DiskDataStore page</title>
-	</head>
-	
-	<body>
-
-        <div wicket:id="tree">default</div>	
- 
-	</body>
-	
-</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/DiskStoreBrowserPage.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/DiskStoreBrowserPage.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/DiskStoreBrowserPage.java
deleted file mode 100644
index 066f9e6..0000000
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/DiskStoreBrowserPage.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.devutils.diskstore;
-
-import org.apache.wicket.Component;
-import org.apache.wicket.devutils.diskstore.browser.BrowserPanel;
-import org.apache.wicket.markup.html.WebPage;
-import org.apache.wicket.request.mapper.parameter.PageParameters;
-
-/**
- * A page that shows the attributes (id, name, size) of the pages stored in the data stores.
- */
-public class DiskStoreBrowserPage extends WebPage
-{
-
-	/**
-	 * Construct.
-	 * 
-	 * @param parameters
-	 *            the request parameters
-	 */
-	public DiskStoreBrowserPage(final PageParameters parameters)
-	{
-		super(parameters);
-
-		Component tree;
-// tree = new LabelTree("tree", new PageWindowModel(sessionId, dataStore));
-		tree = new BrowserPanel("tree");
-		add(tree);
-	}
-
-	@Override
-	public boolean isVersioned()
-	{
-		return false;
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/PageStorePage.html
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/PageStorePage.html b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/PageStorePage.html
new file mode 100644
index 0000000..307884c
--- /dev/null
+++ b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/PageStorePage.html
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   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.
+-->
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+	
+	<head>
+		<title>Debug DiskDataStore page</title>
+	</head>
+	
+	<body>
+
+        <div wicket:id="tree">default</div>	
+ 
+	</body>
+	
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/PageStorePage.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/PageStorePage.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/PageStorePage.java
new file mode 100644
index 0000000..c5c52aa
--- /dev/null
+++ b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/PageStorePage.java
@@ -0,0 +1,68 @@
+/*
+ * 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.devutils.diskstore;
+
+import org.apache.wicket.Session;
+import org.apache.wicket.devutils.diskstore.browser.BrowserPanel;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.pageStore.DelegatingPageStore;
+import org.apache.wicket.pageStore.IPageStore;
+import org.apache.wicket.pageStore.IPersistentPageStore;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+/**
+ * A page that shows the attributes (id, name, size) of the pages stored in the data stores.
+ */
+public class PageStorePage extends WebPage
+{
+
+	/**
+	 * Construct.
+	 * 
+	 * @param parameters
+	 *            the request parameters
+	 */
+	public PageStorePage(final PageParameters parameters)
+	{
+		super(parameters);
+
+		add(new BrowserPanel("tree"));
+	}
+
+	@Override
+	public boolean isVersioned()
+	{
+		return false;
+	}
+	
+	public static IPersistentPageStore getPersistentPageStore() {
+		IPageStore store = Session.get().getPageManager().getPageStore();
+		while (true) {
+			if (store instanceof IPersistentPageStore) {
+				return (IPersistentPageStore)store;
+			}
+			
+			if (store instanceof DelegatingPageStore) {
+				store = ((DelegatingPageStore)store).getDelegate();
+			} else {
+				break;
+			}
+		}
+		
+		return null;
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/BrowserPanel.html
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/BrowserPanel.html b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/BrowserPanel.html
index aba3252..c4ac731 100644
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/BrowserPanel.html
+++ b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/BrowserPanel.html
@@ -18,11 +18,14 @@
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
 <wicket:panel>
 
-    <h3>Active sessions</h3>
+    <h3>Persistent store</h3>
+    <span wicket:id="store"></span>
+    
+    <h3>Persisted sessions</h3>
     <select wicket:id="sessions"></select> <a wicket:id="currentSessionLink" title="Selects the current session">Current session</a>
 
-    <h3>Stored pages</h3> <a wicket:id="refresh" title="Refreshes the content of the table below">refresh</a>
-	<table wicket:id="table" border="1" cellpadding="3" cellspacing="1"></table>
+    <h3>Persisted pages</h3> <a wicket:id="refresh" title="Refreshes the content of the table below">refresh</a>
+	<table wicket:id="table"></table>
 
 </wicket:panel>
 </html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/BrowserPanel.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/BrowserPanel.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/BrowserPanel.java
index 51cd5f7..6147fa8 100644
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/BrowserPanel.java
+++ b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/BrowserPanel.java
@@ -20,15 +20,33 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
 
+import org.apache.wicket.PageReference;
+import org.apache.wicket.Session;
+import org.apache.wicket.ajax.AbstractAjaxTimerBehavior;
 import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.ajax.AjaxSelfUpdatingTimerBehavior;
 import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
 import org.apache.wicket.ajax.markup.html.AjaxFallbackLink;
+import org.apache.wicket.devutils.diskstore.PageStorePage;
+import org.apache.wicket.devutils.inspector.InspectorPage;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.DefaultDataTable;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.MarkupStream;
+import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.form.DropDownChoice;
+import org.apache.wicket.markup.html.link.Link;
 import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.markup.repeater.Item;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
+import org.apache.wicket.pageStore.DefaultPageContext;
+import org.apache.wicket.pageStore.IPageContext;
+import org.apache.wicket.pageStore.IPersistedPage;
+import org.apache.wicket.pageStore.IPersistentPageStore;
 import org.apache.wicket.util.time.Duration;
 
 /**
@@ -47,10 +65,24 @@ public class BrowserPanel extends Panel
 	{
 		super(id);
 
+		final Label storeLabel = new Label("store", () -> {
+			IPersistentPageStore store = PageStorePage.getPersistentPageStore();
+			
+			if (store == null) {
+				return "N/A";
+			}
+			
+			return String.format("%s - %s", store.getClass().getName(), store.getTotalSize());
+		});
+		storeLabel.setOutputMarkupId(true);
+		add(storeLabel);
+		
 		final DropDownChoice<String> sessionsSelector = createSessionsSelector("sessions");
+		sessionsSelector.setOutputMarkupId(true);
 		add(sessionsSelector);
 
-		final BrowserTable table = createTable("table", sessionsSelector.getModel());
+		final DataTable<IPersistedPage, String> table = createTable("table", sessionsSelector.getModel());
+		table.setOutputMarkupId(true);
 		add(table);
 
 		AjaxFallbackLink<Void> refreshLink = new AjaxFallbackLink<Void>("refresh")
@@ -68,7 +100,7 @@ public class BrowserPanel extends Panel
 			@Override
 			public void onClick(Optional<AjaxRequestTarget> targetOptional)
 			{
-				sessionsSelector.setModelObject(getCurrentSession().getObject());
+				sessionsSelector.setModelObject(getCurrentSessionIdentifier());
 				targetOptional.ifPresent(target -> target.add(sessionsSelector, table));
 			}
 
@@ -86,6 +118,18 @@ public class BrowserPanel extends Panel
 			@Override
 			protected void onUpdate(AjaxRequestTarget target)
 			{
+				target.add(storeLabel);
+				target.add(sessionsSelector);
+				target.add(table);
+			}
+		});
+
+		add(new AbstractAjaxTimerBehavior(Duration.seconds(5)) {
+
+			@Override
+			protected void onTimer(AjaxRequestTarget target)
+			{
+				target.add(storeLabel);
 				target.add(table);
 			}
 		});
@@ -93,40 +137,65 @@ public class BrowserPanel extends Panel
 
 	private DropDownChoice<String> createSessionsSelector(String id)
 	{
-		IModel<String> defaultSession = getCurrentSession();
-
 		DropDownChoice<String> sessionsSelector = new DropDownChoice<String>("sessions",
-			defaultSession, new SessionsProviderModel());
-
+			Model.of(getCurrentSessionIdentifier()), new SessionIdentifiersModel());
 
 		return sessionsSelector;
 	}
 
-	private IModel<String> getCurrentSession()
+	private String getCurrentSessionIdentifier()
 	{
-		return Model.of(getSession().getId());
+		IPersistentPageStore store = PageStorePage.getPersistentPageStore();
+		if (store == null) {
+			return null;
+		}
+
+		IPageContext context = new DefaultPageContext(Session.get());
+		
+		return store.getSessionIdentifier(context);
 	}
 
-	private BrowserTable createTable(String id, IModel<String> sessionId)
+	private DataTable<IPersistedPage, String> createTable(String id, IModel<String> sessionId)
 	{
-		PageWindowProvider provider = new PageWindowProvider(sessionId);
-
-		List<IColumn<PageWindowDescription, String>> columns = new ArrayList<>();
-
-		PageWindowColumn pageIdColumn = new PageWindowColumn(Model.of("Id"), "id");
-		columns.add(pageIdColumn);
+		PersistedPagesProvider provider = new PersistedPagesProvider(sessionId);
 
-		PageWindowColumn pageNameColumn = new PageWindowColumn(Model.of("Name"), "name");
-		columns.add(pageNameColumn);
+		List<IColumn<IPersistedPage, String>> columns = new ArrayList<>();
 
-		PageWindowColumn pageSizeColumn = new PageWindowColumn(Model.of("Size"), "size");
-		columns.add(pageSizeColumn);
+		columns.add(new AbstractColumn<IPersistedPage, String>(Model.of("Id"), "pageId")
+		{
+			@Override
+			public void populateItem(Item<ICellPopulator<IPersistedPage>> cellItem, String componentId, IModel<IPersistedPage> rowModel)
+			{
+				cellItem.add(new Link<IPersistedPage>(componentId, rowModel)
+				{
+					@Override
+					protected void onComponentTag(ComponentTag tag)
+					{
+						tag.setName("a");
+						
+						super.onComponentTag(tag);
+					}
+					
+					@Override
+					public void onComponentTagBody(MarkupStream markupStream, ComponentTag openTag)
+					{
+						replaceComponentTagBody(markupStream, openTag, "" + getModelObject().getPageId());
+					}
+					
+					@Override
+					public void onClick()
+					{
+						setResponsePage(new InspectorPage(new PageReference(getModelObject().getPageId())));
+					}
+				});
+			}
+		});
+		columns.add(new PropertyColumn<>(Model.of("Type"), "pageType", "pageType"));
+		columns.add(new PropertyColumn<>(Model.of("Size"), "pageSize", "pageSize"));
 
-		BrowserTable browserTable = new BrowserTable(id, columns, provider);
+		DefaultDataTable<IPersistedPage, String> browserTable = new DefaultDataTable<>(id, columns, provider, 20);
 		browserTable.setOutputMarkupId(true);
 
-		browserTable.add(new AjaxSelfUpdatingTimerBehavior(Duration.seconds(5)));
-
 		return browserTable;
 	}
 

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/BrowserTable.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/BrowserTable.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/BrowserTable.java
deleted file mode 100644
index 264d88a..0000000
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/BrowserTable.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.devutils.diskstore.browser;
-
-import java.util.List;
-
-import org.apache.wicket.extensions.markup.html.repeater.data.table.DefaultDataTable;
-import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
-
-/**
- * A data table that shows the attributes of the recently stored pages in the data store. The last
- * used pages are rendered first.
- */
-class BrowserTable extends DefaultDataTable<PageWindowDescription, String>
-{
-
-	/**
-	 * Construct.
-	 * 
-	 * @param id
-	 *            the component id
-	 * @param columns
-	 *            the columns that show the page attributes
-	 * @param provider
-	 *            the provider of page descriptions
-	 */
-	public BrowserTable(String id, List<IColumn<PageWindowDescription, String>> columns,
-		PageWindowProvider provider)
-	{
-		super(id, columns, provider, 20);
-	}
-
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/DataStoreHelper.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/DataStoreHelper.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/DataStoreHelper.java
deleted file mode 100644
index ff4bf67..0000000
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/DataStoreHelper.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.devutils.diskstore.browser;
-
-import org.apache.wicket.Application;
-import org.apache.wicket.devutils.diskstore.DebugDiskDataStore;
-import org.apache.wicket.devutils.diskstore.DebugPageManagerProvider;
-
-/**
- * A helper to work with {@link DebugDiskDataStore}
- */
-public final class DataStoreHelper
-{
-
-	private DataStoreHelper()
-	{
-	}
-
-	/**
-	 * @return the configured {@link DebugDiskDataStore}
-	 */
-	public static DebugDiskDataStore getDataStore()
-	{
-		DebugPageManagerProvider pageManagerProvider = (DebugPageManagerProvider)Application.get()
-			.getPageManagerProvider();
-		DebugDiskDataStore dataStore = pageManagerProvider.getDataStore();
-		return dataStore;
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/PageWindowColumn.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/PageWindowColumn.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/PageWindowColumn.java
deleted file mode 100644
index ea21877..0000000
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/PageWindowColumn.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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.devutils.diskstore.browser;
-
-import org.apache.wicket.Application;
-import org.apache.wicket.devutils.diskstore.DebugDiskDataStore;
-import org.apache.wicket.devutils.diskstore.DebugPageManagerProvider;
-import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
-import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.repeater.Item;
-import org.apache.wicket.model.IModel;
-import org.apache.wicket.serialize.ISerializer;
-
-/**
- * A column that shows the page attributes (id, name, size)
- */
-class PageWindowColumn extends PropertyColumn<PageWindowDescription, String>
-{
-	/**
-	 * Construct.
-	 * 
-	 * @param displayModel
-	 *            the header
-	 * @param propertyExpression
-	 *            the page attribute
-	 */
-	public PageWindowColumn(IModel<String> displayModel, String propertyExpression)
-	{
-		super(displayModel, propertyExpression);
-	}
-
-	@Override
-	public void populateItem(Item<ICellPopulator<PageWindowDescription>> cellItem,
-		String componentId, IModel<PageWindowDescription> rowModel)
-	{
-		String label;
-		PageWindowDescription windowDescription = rowModel.getObject();
-		if ("name".equals(getPropertyExpression()))
-		{
-			int pageId = windowDescription.getId();
-			DebugPageManagerProvider pageManagerProvider = (DebugPageManagerProvider)Application.get()
-				.getPageManagerProvider();
-			DebugDiskDataStore dataStore = pageManagerProvider.getDataStore();
-			String sessionId = windowDescription.getSessionId();
-			byte[] data = dataStore.getData(sessionId, pageId);
-			ISerializer serializer = Application.get().getFrameworkSettings().getSerializer();
-			Object page = serializer.deserialize(data);
-			label = page.getClass().getName();
-		}
-		else if ("id".equals(getPropertyExpression()))
-		{
-			label = Integer.toString(windowDescription.getId());
-		}
-		else if ("size".equals(getPropertyExpression()))
-		{
-			label = Integer.toString(windowDescription.getSize());
-		}
-		else
-		{
-			label = "unknown: " + getPropertyExpression();
-		}
-
-		cellItem.add(new Label(componentId, label));
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/PageWindowDescription.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/PageWindowDescription.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/PageWindowDescription.java
deleted file mode 100644
index b07c005..0000000
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/PageWindowDescription.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.devutils.diskstore.browser;
-
-import org.apache.wicket.pageStore.PageWindowManager.PageWindow;
-import org.apache.wicket.util.io.IClusterable;
-
-/**
- * A serializable representation of the page information
- */
-class PageWindowDescription implements IClusterable
-{
-	/** the page id */
-	private final int id;
-
-	/** the page size */
-	private final int size;
-
-	/** the id of the session for which this page has been used */
-	private final String sessionId;
-
-	PageWindowDescription(PageWindow pageWindow, String sessionId)
-	{
-		id = pageWindow.getPageId();
-		size = pageWindow.getFilePartSize();
-		this.sessionId = sessionId;
-	}
-
-	public String getSessionId()
-	{
-		return sessionId;
-	}
-
-	public int getId()
-	{
-		return id;
-	}
-
-	public int getSize()
-	{
-		return size;
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/PageWindowProvider.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/PageWindowProvider.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/PageWindowProvider.java
deleted file mode 100644
index dd8397c..0000000
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/PageWindowProvider.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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.devutils.diskstore.browser;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import org.apache.wicket.devutils.diskstore.DebugDiskDataStore;
-import org.apache.wicket.extensions.markup.html.repeater.data.sort.ISortState;
-import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider;
-import org.apache.wicket.markup.repeater.data.IDataProvider;
-import org.apache.wicket.model.IModel;
-import org.apache.wicket.model.Model;
-import org.apache.wicket.pageStore.PageWindowManager.PageWindow;
-
-/**
- * An {@link IDataProvider} that extracts the information about the stored pages
- */
-class PageWindowProvider implements ISortableDataProvider<PageWindowDescription, String>
-{
-	private static final int MAX_PAGES_TO_READ = 1000;
-
-	/**
-	 * The model that brings the currently selected session id
-	 */
-	private final IModel<String> sessionId;
-
-	PageWindowProvider(final IModel<String> sessionId)
-	{
-		this.sessionId = sessionId;
-	}
-
-	@Override
-	public Iterator<? extends PageWindowDescription> iterator(long first, long count)
-	{
-		List<PageWindow> lastPageWindows = getPageWindows();
-		List<PageWindow> subList = lastPageWindows.subList((int)first, (int)(first + count));
-		List<PageWindowDescription> pageDescriptions = new ArrayList<>();
-		for (PageWindow pw : subList)
-		{
-			pageDescriptions.add(new PageWindowDescription(pw, sessionId.getObject()));
-		}
-
-		return pageDescriptions.iterator();
-	}
-
-	private List<PageWindow> getPageWindows()
-	{
-		List<PageWindow> lastPageWindows = new ArrayList<>();
-		if (sessionId != null && sessionId.getObject() != null)
-		{
-			String sessId = sessionId.getObject();
-			DebugDiskDataStore dataStore = DataStoreHelper.getDataStore();
-			List<PageWindow> pageWindows = dataStore.getLastPageWindows(sessId, MAX_PAGES_TO_READ);
-			lastPageWindows.addAll(pageWindows);
-		}
-		return lastPageWindows;
-	}
-
-	@Override
-	public long size()
-	{
-		return getPageWindows().size();
-	}
-
-	/**
-	 * @param description
-	 * 
-	 *            {@inheritDoc}
-	 */
-	@Override
-	public IModel<PageWindowDescription> model(PageWindowDescription description)
-	{
-		return new Model<>(description);
-	}
-
-	@Override
-	public void detach()
-	{
-		sessionId.detach();
-	}
-
-	/*
-	 * No sort state for now. The provider is ISortableDataProvider just because we use
-	 * DefaultDataTable
-	 */
-	@Override
-	public ISortState<String> getSortState()
-	{
-		return null;
-	}
-
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/PersistedPagesProvider.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/PersistedPagesProvider.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/PersistedPagesProvider.java
new file mode 100644
index 0000000..a93e60f
--- /dev/null
+++ b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/PersistedPagesProvider.java
@@ -0,0 +1,130 @@
+/*
+ * 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.devutils.diskstore.browser;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.wicket.core.util.lang.PropertyResolver;
+import org.apache.wicket.devutils.diskstore.PageStorePage;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
+import org.apache.wicket.markup.repeater.data.IDataProvider;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.pageStore.IPersistedPage;
+import org.apache.wicket.pageStore.IPersistentPageStore;
+import org.apache.wicket.util.lang.Objects;
+
+/**
+ * An {@link IDataProvider} that extracts the information about {@link IPersistedPage}s.
+ */
+class PersistedPagesProvider extends SortableDataProvider<IPersistedPage, String>
+{
+	/**
+	 * The model that brings the currently selected session id
+	 */
+	private final IModel<String> sessionId;
+
+	private List<IPersistedPage> pages;
+
+	PersistedPagesProvider(final IModel<String> sessionId)
+	{
+		this.sessionId = sessionId;
+	}
+
+	@Override
+	public Iterator<? extends IPersistedPage> iterator(long first, long count)
+	{
+		List<IPersistedPage> pages = getPages();
+
+		if (getSort() != null) {
+			Collections.sort(pages, new SortComparator());
+		}
+
+		return pages.iterator();
+	}
+
+	private List<IPersistedPage> getPages()
+	{
+		if (pages == null)
+		{
+			pages = new ArrayList<>();
+
+			if (sessionId.getObject() != null)
+			{
+				String sessId = sessionId.getObject();
+
+				IPersistentPageStore dataStore = PageStorePage.getPersistentPageStore();
+
+				if (dataStore != null)
+				{
+					pages.addAll(dataStore.getPersistentPages(sessId));
+				}
+			}
+		}
+
+		return pages;
+	}
+
+	@Override
+	public long size()
+	{
+		return getPages().size();
+	}
+
+	/**
+	 * @param description
+	 * 
+	 *            {@inheritDoc}
+	 */
+	@Override
+	public IModel<IPersistedPage> model(IPersistedPage description)
+	{
+		return new Model<>(description);
+	}
+
+	@Override
+	public void detach()
+	{
+		sessionId.detach();
+
+		pages = null;
+	}
+
+	private class SortComparator implements Comparator<IPersistedPage>
+	{
+
+		@Override
+		public int compare(IPersistedPage page0, IPersistedPage page1)
+		{
+			Object value0 = PropertyResolver.getValue(getSort().getProperty(), page0);
+			Object value1 = PropertyResolver.getValue(getSort().getProperty(), page1);
+
+			int c = Objects.compareWithConversion(value0, value1);
+
+			if (getSort().isAscending() == false)
+			{
+				c = c * -1;
+			}
+
+			return c;
+		}
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/SessionIdentifiersModel.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/SessionIdentifiersModel.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/SessionIdentifiersModel.java
new file mode 100644
index 0000000..5a39804
--- /dev/null
+++ b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/SessionIdentifiersModel.java
@@ -0,0 +1,59 @@
+/*
+ * 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.devutils.diskstore.browser;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.wicket.Session;
+import org.apache.wicket.devutils.diskstore.PageStorePage;
+import org.apache.wicket.model.LoadableDetachableModel;
+import org.apache.wicket.pageStore.DefaultPageContext;
+import org.apache.wicket.pageStore.DiskPageStore;
+import org.apache.wicket.pageStore.IPageContext;
+import org.apache.wicket.pageStore.IPersistentPageStore;
+
+/**
+ * A model that collects the keys from the {@link DiskPageStore} folder
+ */
+public class SessionIdentifiersModel extends LoadableDetachableModel<List<String>>
+{
+
+	@Override
+	protected List<String> load()
+	{
+		IPersistentPageStore store = PageStorePage.getPersistentPageStore();
+		if (store == null)
+		{
+			return Collections.emptyList();
+		}
+
+		ArrayList<String> identifiers = new ArrayList<>(store.getSessionIdentifiers());
+
+		IPageContext context = new DefaultPageContext(Session.get());
+		String current = store.getSessionIdentifier(context);
+		if (identifiers.contains(current) == false)
+		{
+			// identifiers of the store seem no to match their sessions ids,
+			// thus add the default identifier so the select works properly  
+			identifiers.add(current);
+		}
+
+		return identifiers;
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/SessionsProviderModel.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/SessionsProviderModel.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/SessionsProviderModel.java
deleted file mode 100644
index 6638303..0000000
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/diskstore/browser/SessionsProviderModel.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.devutils.diskstore.browser;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.wicket.Application;
-import org.apache.wicket.devutils.diskstore.DebugDiskDataStore;
-import org.apache.wicket.model.LoadableDetachableModel;
-import org.apache.wicket.pageStore.DiskDataStore;
-
-/**
- * A model that collects the session ids from the {@link DiskDataStore} folder
- */
-public class SessionsProviderModel extends LoadableDetachableModel<List<String>>
-{
-
-	@Override
-	protected List<String> load()
-	{
-		List<String> sessionIds = new ArrayList<>();
-		if (Application.exists())
-		{
-			DebugDiskDataStore dataStore = DataStoreHelper.getDataStore();
-			File appStoreFolder = dataStore.getStoreFolder();
-			if (appStoreFolder.isDirectory())
-			{
-				String[] sessionIdFileNames = appStoreFolder.list();
-				if (sessionIdFileNames != null)
-				{
-					for (String sessionId : sessionIdFileNames)
-					{
-						sessionIds.add(sessionId);
-					}
-				}
-			}
-		}
-
-		return sessionIds;
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/inspector/EnhancedPageView.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/inspector/EnhancedPageView.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/inspector/EnhancedPageView.java
index 50856ed..fef1826 100644
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/inspector/EnhancedPageView.java
+++ b/wicket-devutils/src/main/java/org/apache/wicket/devutils/inspector/EnhancedPageView.java
@@ -86,42 +86,13 @@ public final class EnhancedPageView extends GenericPanel<Page>
 	 * 
 	 * @param id
 	 *            See Component
-	 * @param page
+	 * @param model
 	 *            The page to be analyzed
 	 */
-	public EnhancedPageView(String id, Page page)
+	public EnhancedPageView(String id, IModel<Page> model)
 	{
-		this(id, getModelFor(page == null ? null : page.getPageReference()));
-	}
-
-	private static IModel<Page> getModelFor(final PageReference pageRef)
-	{
-		return new LoadableDetachableModel<Page>()
-		{
-			private static final long serialVersionUID = 1L;
-
-			@Override
-			protected Page load()
-			{
-				if (pageRef == null)
-					return null;
-				Page page = pageRef.getPage();
-				return page;
-			}
-		};
-	}
-
-	/**
-	 * Constructor.
-	 * 
-	 * @param id
-	 *            See Component
-	 * @param pageModel
-	 *            The page to be analyzed
-	 */
-	public EnhancedPageView(String id, IModel<Page> pageModel)
-	{
-		super(id, pageModel);
+		super(id, model);
+		
 		expandState = new ExpandState();
 		expandState.expandAll();
 		showStatefulAndParentsOnly = false;

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-devutils/src/main/java/org/apache/wicket/devutils/inspector/InspectorPage.java
----------------------------------------------------------------------
diff --git a/wicket-devutils/src/main/java/org/apache/wicket/devutils/inspector/InspectorPage.java b/wicket-devutils/src/main/java/org/apache/wicket/devutils/inspector/InspectorPage.java
index 023583d..0ab7837 100644
--- a/wicket-devutils/src/main/java/org/apache/wicket/devutils/inspector/InspectorPage.java
+++ b/wicket-devutils/src/main/java/org/apache/wicket/devutils/inspector/InspectorPage.java
@@ -18,15 +18,15 @@ package org.apache.wicket.devutils.inspector;
 
 import org.apache.wicket.Application;
 import org.apache.wicket.Page;
+import org.apache.wicket.PageReference;
 import org.apache.wicket.Session;
 import org.apache.wicket.devutils.DevUtilsPage;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.image.Image;
 import org.apache.wicket.markup.html.link.BookmarkablePageLink;
-import org.apache.wicket.page.IManageablePage;
+import org.apache.wicket.model.IModel;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.request.resource.PackageResourceReference;
-import org.apache.wicket.util.string.StringValueConversionException;
 
 
 /**
@@ -47,23 +47,19 @@ public final class InspectorPage extends DevUtilsPage
 	 */
 	public InspectorPage(final PageParameters parameters)
 	{
+		this(new PageReference(parameters.get("pageId").toInt()));
+	}
+
+	public InspectorPage(PageReference reference)
+	{
 		add(new ApplicationView("application", Application.get()));
 		add(new SessionView("session", Session.get()));
-		IManageablePage page = null;
-		try
-		{
-			final int pageId = parameters.get("pageId").toInt();
-			page = Session.get().getPageManager().getPage(pageId);
-		}
-		catch (StringValueConversionException e)
-		{
-			// Ignore
-		}
-		catch (NullPointerException e)
-		{
-			// Ignore
-		}
-		add(new EnhancedPageView("page", (Page)page));
+		
+		IModel<Page> page = () -> {
+			return reference.getPage();
+		};
+		
+		add(new EnhancedPageView("page", page));
 		add(new Image("bug", new PackageResourceReference(InspectorPage.class, "bug.png")));
 		add(new BookmarkablePageLink<>("allsessions", LiveSessionsPage.class));
 		add(new Label("wicketVersion", getApplication().getFrameworkSettings().getVersion()));

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-examples/src/main/java/org/apache/wicket/examples/frames/BodyFrame.java
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/frames/BodyFrame.java b/wicket-examples/src/main/java/org/apache/wicket/examples/frames/BodyFrame.java
index 3896404..f557a59 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/frames/BodyFrame.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/frames/BodyFrame.java
@@ -16,10 +16,10 @@
  */
 package org.apache.wicket.examples.frames;
 
-import org.apache.wicket.markup.html.WebPage;
-import org.apache.wicket.request.IRequestHandler;
 import org.apache.wicket.core.request.handler.PageProvider;
 import org.apache.wicket.core.request.handler.RenderPageRequestHandler;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.request.IRequestHandler;
 
 
 /**
@@ -41,7 +41,7 @@ public class BodyFrame extends WebPage
 	{
 		// create a new page instance, passing this 'master page' as an argument
 		LeftFrame leftFrame = new LeftFrame(this);
-		getSession().getPageManager().touchPage(leftFrame);
+		getSession().getPageManager().addPage(leftFrame);
 		// get the url to that page
 		IRequestHandler leftFrameHandler = new RenderPageRequestHandler(new PageProvider(leftFrame));
 		// and create a simple component that modifies it's src attribute to

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-examples/src/test/java/org/apache/wicket/examples/StartExamples.java
----------------------------------------------------------------------
diff --git a/wicket-examples/src/test/java/org/apache/wicket/examples/StartExamples.java b/wicket-examples/src/test/java/org/apache/wicket/examples/StartExamples.java
index 18e6c26..9f08961 100644
--- a/wicket-examples/src/test/java/org/apache/wicket/examples/StartExamples.java
+++ b/wicket-examples/src/test/java/org/apache/wicket/examples/StartExamples.java
@@ -16,6 +16,7 @@
  */
 package org.apache.wicket.examples;
 
+import java.io.File;
 import java.lang.management.ManagementFactory;
 
 import javax.management.MBeanServer;
@@ -28,6 +29,8 @@ import org.eclipse.jetty.server.SecureRequestCustomizer;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
 import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.server.session.DefaultSessionCache;
+import org.eclipse.jetty.server.session.FileSessionDataStore;
 import org.eclipse.jetty.util.resource.Resource;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.webapp.WebAppContext;
@@ -97,12 +100,18 @@ public class StartExamples
 		bb.setContextPath("/");
 		bb.setWar("src/main/webapp");
 
+		// uncomment next lines if you want to test with session persistence
+//		DefaultSessionCache sessionCache = new DefaultSessionCache(bb.getSessionHandler());
+//		FileSessionDataStore sessionStore = new FileSessionDataStore();
+//		sessionStore.setStoreDir(new File("./jetty-session-data"));
+//		sessionCache.setSessionDataStore(sessionStore);
+//		bb.getSessionHandler().setSessionCache(sessionCache);
+		
 		ServerContainer serverContainer = WebSocketServerContainerInitializer.configureContext(bb);
 		serverContainer.addEndpoint(new WicketServerEndpointConfig());
 
 		// uncomment next line if you want to test with JSESSIONID encoded in the urls
-		// ((AbstractSessionManager)
-		// bb.getSessionHandler().getSessionManager()).setUsingCookies(false);
+//		((AbstractSessionManager) bb.getSessionHandler().getSessionManager()).setUsingCookies(false);
 
 		server.setHandler(bb);
 

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/modal/ModalWindow.java
----------------------------------------------------------------------
diff --git a/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/modal/ModalWindow.java b/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/modal/ModalWindow.java
index a8a70ef..8302650 100644
--- a/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/modal/ModalWindow.java
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/modal/ModalWindow.java
@@ -16,7 +16,6 @@
  */
 package org.apache.wicket.extensions.ajax.markup.html.modal;
 
-import com.github.openjson.JSONObject;
 import org.apache.wicket.Component;
 import org.apache.wicket.Page;
 import org.apache.wicket.WicketRuntimeException;
@@ -45,6 +44,8 @@ import org.apache.wicket.util.io.IClusterable;
 import org.apache.wicket.util.lang.EnumeratedType;
 import org.apache.wicket.util.string.AppendingStringBuffer;
 
+import com.github.openjson.JSONObject;
+
 /**
  * Modal window component.
  * <p>
@@ -1043,7 +1044,7 @@ public class ModalWindow extends Panel
 			CharSequence pageUrl;
 			RequestCycle requestCycle = RequestCycle.get();
 
-			page.getSession().getPageManager().touchPage(page);
+			page.getSession().getPageManager().addPage(page);
 			if (page.isPageStateless())
 			{
 				pageUrl = requestCycle.urlFor(page.getClass(), page.getPageParameters());

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-jmx/src/main/java/org/apache/wicket/jmx/StoreSettingsMBean.java
----------------------------------------------------------------------
diff --git a/wicket-jmx/src/main/java/org/apache/wicket/jmx/StoreSettingsMBean.java b/wicket-jmx/src/main/java/org/apache/wicket/jmx/StoreSettingsMBean.java
index 7f40a26..4e18730 100644
--- a/wicket-jmx/src/main/java/org/apache/wicket/jmx/StoreSettingsMBean.java
+++ b/wicket-jmx/src/main/java/org/apache/wicket/jmx/StoreSettingsMBean.java
@@ -16,8 +16,7 @@
  */
 package org.apache.wicket.jmx;
 
-import org.apache.wicket.pageStore.DiskDataStore;
-import org.apache.wicket.pageStore.IDataStore;
+import org.apache.wicket.pageStore.DiskPageStore;
 
 /**
  * JMX MBean for Application's StoreSettings
@@ -26,19 +25,13 @@ public interface StoreSettingsMBean
 {
 
 	/**
-	 * @return the number of page instances which will be stored in the http session for faster
-	 *         retrieval
-	 */
-	int getInmemoryCacheSize();
-
-	/**
-	 * @return maximum page size. After this size is exceeded, the {@link DiskDataStore} will start
+	 * @return maximum page size. After this size is exceeded, the {@link DiskPageStore} will start
 	 *         saving the pages at the beginning of file.
 	 */
 	long getMaxSizePerSession();
 
 	/**
-	 * @return the location of the folder where {@link DiskDataStore} will store the files with page
+	 * @return the location of the folder where {@link DiskPageStore} will store the files with page
 	 *         instances per session
 	 */
 	String getFileStoreFolder();

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-jmx/src/main/java/org/apache/wicket/jmx/wrapper/StoreSettings.java
----------------------------------------------------------------------
diff --git a/wicket-jmx/src/main/java/org/apache/wicket/jmx/wrapper/StoreSettings.java b/wicket-jmx/src/main/java/org/apache/wicket/jmx/wrapper/StoreSettings.java
index 9a658fd..b40793e 100644
--- a/wicket-jmx/src/main/java/org/apache/wicket/jmx/wrapper/StoreSettings.java
+++ b/wicket-jmx/src/main/java/org/apache/wicket/jmx/wrapper/StoreSettings.java
@@ -37,12 +37,6 @@ public class StoreSettings implements StoreSettingsMBean
 	}
 
 	@Override
-	public int getInmemoryCacheSize()
-	{
-		return application.getStoreSettings().getInmemoryCacheSize();
-	}
-
-	@Override
 	public long getMaxSizePerSession()
 	{
 		return application.getStoreSettings().getMaxSizePerSession().bytes();

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java
index ca3569d..d3c9d4f 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java
@@ -300,7 +300,7 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 			{
 				if (Session.exists())
 				{
-					Session.get().getPageManager().commitRequest();
+					Session.get().getPageManager().detach();
 				}
 			}
 		});


[4/5] wicket git commit: WICKET-6563 new IPageStore implementation

Posted by sv...@apache.org.
http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/AsynchronousPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/AsynchronousPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/AsynchronousPageStore.java
index 43993ba..3428d02 100644
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/AsynchronousPageStore.java
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/AsynchronousPageStore.java
@@ -17,41 +17,43 @@
 package org.apache.wicket.pageStore;
 
 import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.wicket.MetaDataKey;
+import org.apache.wicket.WicketRuntimeException;
 import org.apache.wicket.page.IManageablePage;
 import org.apache.wicket.util.lang.Args;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Facade for {@link IPageStore} that does the actual saving in worker thread.
+ * Facade for {@link IPageStore} moving {@link #addPage(IPageContext, IManageablePage)} to a worker thread.
  * <p>
- * Creates an {@link Entry} for each double (sessionId, page) and puts it in {@link #entries} queue
- * if there is room. Acts as producer.<br/>
- * Later {@link PageSavingRunnable} reads in blocking manner from {@link #entries} and saves each
- * entry. Acts as consumer.
- * </p>
- * It starts only one instance of {@link PageSavingRunnable} because all we need is to make the page
+ * Creates an {@link PendingAdd} for {@link #addPage(IPageContext, IManageablePage)} and puts ito a {@link #queue}.
+ * Later {@link PageAddingRunnable} reads in blocking manner from {@link #queue} and performs the add.
+ * <p>
+ * It starts only one instance of {@link PageAddingRunnable} because all we need is to make the page
  * storing asynchronous. We don't want to write concurrently in the wrapped {@link IPageStore},
  * though it may happen in the extreme case when the queue is full. These cases should be avoided.
  * 
- * Based on AsynchronousDataStore (@author Matej Knopp).
- * 
+ * @author Matej Knopp
  * @author manuelbarzi
  */
-public class AsynchronousPageStore implements IPageStore
+public class AsynchronousPageStore extends DelegatingPageStore
 {
 
 	/** Log for reporting. */
 	private static final Logger log = LoggerFactory.getLogger(AsynchronousPageStore.class);
 
 	/**
-	 * The time to wait when adding an {@link Entry} into the entries. In millis.
+	 * The time to wait when adding an {@link PendingAdd} into the entries. In millis.
 	 */
 	private static final long OFFER_WAIT = 30L;
 
@@ -66,20 +68,15 @@ public class AsynchronousPageStore implements IPageStore
 	private final Thread pageSavingThread;
 
 	/**
-	 * The wrapped {@link IPageStore} that actually stores that pages
-	 */
-	private final IPageStore delegate;
-
-	/**
 	 * The queue where the entries which have to be saved are temporary stored
 	 */
-	private final BlockingQueue<Entry> entries;
+	private final BlockingQueue<PendingAdd> queue;
 
 	/**
-	 * A map 'sessionId:::pageId' -> {@link Entry}. Used for fast retrieval of {@link Entry}s which
+	 * A map 'sessionId:::pageId' -> {@link PendingAdd}. Used for fast retrieval of {@link PendingAdd}s which
 	 * are not yet stored by the wrapped {@link IPageStore}
 	 */
-	private final ConcurrentMap<String, Entry> entryMap;
+	private final ConcurrentMap<String, PendingAdd> queueMap;
 
 	/**
 	 * Construct.
@@ -91,29 +88,18 @@ public class AsynchronousPageStore implements IPageStore
 	 */
 	public AsynchronousPageStore(final IPageStore delegate, final int capacity)
 	{
-		this.delegate = Args.notNull(delegate, "delegate");
-		entries = new LinkedBlockingQueue<>(capacity);
-		entryMap = new ConcurrentHashMap<>();
+		super(delegate);
+		
+		queue = new LinkedBlockingQueue<>(capacity);
+		queueMap = new ConcurrentHashMap<>();
 
-		PageSavingRunnable savingRunnable = new PageSavingRunnable(delegate, entries, entryMap);
+		PageAddingRunnable savingRunnable = new PageAddingRunnable(delegate, queue, queueMap);
 		pageSavingThread = new Thread(savingRunnable, "Wicket-AsyncPageStore-PageSavingThread");
 		pageSavingThread.setDaemon(true);
 		pageSavingThread.start();
 	}
 
 	/**
-	 * Little helper
-	 * 
-	 * @param sessionId
-	 * @param pageId
-	 * @return Entry
-	 */
-	private Entry getEntry(final String sessionId, final int pageId)
-	{
-		return entryMap.get(getKey(sessionId, pageId));
-	}
-
-	/**
 	 * 
 	 * @param pageId
 	 * @param sessionId
@@ -125,83 +111,198 @@ public class AsynchronousPageStore implements IPageStore
 	}
 
 	/**
-	 * 
-	 * @param entry
-	 * @return generated key
+	 * A pending asynchronous add in the queue.
+	 * <p>
+	 * Used as {@link IPageContext} for the call to the delegate {@link IPageStore#addPage(IPageContext, IManageablePage)}.
 	 */
-	private static String getKey(final Entry entry)
-	{
-		return getKey(entry.sessionId, entry.page.getPageId());
-	}
-
-	/**
-	 * The structure used for an entry in the queue
-	 */
-	private static class Entry
+	private static class PendingAdd implements IPageContext
 	{
+		private final IPageContext context;
+		
 		private final String sessionId;
+		
 		private final IManageablePage page;
 
-		public Entry(final String sessionId, final IManageablePage page)
+		/**
+		 * Is this context passed to an asynchronously called {@link IPageStore#addPage(IPageContext, IManageablePage)}.
+		 */
+		private boolean asynchronous = false;
+		
+		/**
+		 * Cache of session attributes which may filled in {@link IPageStore#canBeAsynchronous(IPageContext)},
+		 * so these are available asynchronously later on.
+		 */
+		private Map<String, Serializable> attributeCache = new HashMap<>();
+
+		public PendingAdd(final IPageContext context, final IManageablePage page)
 		{
-			this.sessionId = Args.notNull(sessionId, "sessionId");
+			this.context = Args.notNull(context, "context");
 			this.page = Args.notNull(page, "page");
+
+			context.bind();
+			this.sessionId = context.getSessionId();
+		}
+
+		/**
+		 * 
+		 * @param entry
+		 * @return generated key
+		 */
+		private String getKey()
+		{
+			return AsynchronousPageStore.getKey(sessionId, page.getPageId());
 		}
 
 		@Override
-		public int hashCode()
+		public String toString()
 		{
-			final int prime = 31;
-			int result = 1;
-			result = prime * result + page.getPageId();
-			result = prime * result + sessionId.hashCode();
-			return result;
+			return "Entry [sessionId=" + sessionId + ", pageId=" + page.getPageId() + "]";
 		}
 
+		/**
+		 * Prevents access to request when called asynchronously.
+		 */
 		@Override
-		public boolean equals(Object obj)
+		public <T> void setRequestData(MetaDataKey<T> key, T value)
 		{
-			if (this == obj)
-				return true;
-			if (obj == null)
-				return false;
-			if (getClass() != obj.getClass())
-				return false;
-			Entry other = (Entry)obj;
-			if (page.getPageId() != other.page.getPageId())
-				return false;
-			if (!sessionId.equals(other.sessionId))
-				return false;
-			return true;
+			if (asynchronous) {
+				throw new WicketRuntimeException("no request available asynchronuously");
+			}
+			
+			context.setRequestData(key, value);
 		}
 
+		/**
+		 * Prevents access to request when called asynchronously.
+		 */
 		@Override
-		public String toString()
+		public <T> T getRequestData(MetaDataKey<T> key)
 		{
-			return "Entry [sessionId=" + sessionId + ", pageId=" + page.getPageId() + "]";
+			if (asynchronous) {
+				throw new WicketRuntimeException("no request available asynchronuously");
+			}
+			
+			return context.getRequestData(key);
 		}
 
+		/**
+		 * Prevents access to request when called asynchronously.
+		 * <p>
+		 * All values set from {@link IPageStore#canBeAsynchronous(IPageContext)} are kept
+		 * for later retrieval.
+		 */
+		@Override
+		public <T extends Serializable> void setSessionAttribute(String key, T value)
+		{
+			if (asynchronous) {
+				throw new WicketRuntimeException("no session available asynchronuously");
+			}
+			
+			if (value != null) {
+				attributeCache.put(key, value);
+			}
+
+			context.setSessionAttribute(key, value);
+		}
+		
+		/**
+		 * Prevents access to the session when called asynchronously.
+		 * <p>
+		 * All values set from {@link IPageStore#canBeAsynchronous(IPageContext)} are still
+		 * available.
+		 */
+		@SuppressWarnings("unchecked")
+		@Override
+		public <T extends Serializable> T getSessionAttribute(String key)
+		{
+			if (asynchronous) {
+				T value = (T)attributeCache.get(key);
+				if (value != null) {
+					return value;
+				}
+				
+				throw new WicketRuntimeException("no session available asynchronuously");
+			}
+			
+			T value = context.getSessionAttribute(key);
+			if (value != null) {
+				attributeCache.put(key, value);
+			}
+			
+			return value;
+		}
+		
+		/**
+		 * Prevents access to the session when called asynchronously.
+		 */
+		@Override
+		public <T extends Serializable> void setSessionData(MetaDataKey<T> key, T value)
+		{
+			if (asynchronous) {
+				throw new WicketRuntimeException("no session available asynchronuously");
+			}
+			
+			context.setSessionData(key, value);
+		}
+
+		/**
+		 * Gets data from the session.
+		 */
+		@Override
+		public <T extends Serializable> T getSessionData(MetaDataKey<T> key)
+		{
+			return context.getSessionData(key);
+		}
+
+		/**
+		 * Prevents access to the session when called asynchronously.
+		 * <p>
+		 * Has no effect when session is already bound. 
+		 */
+		@Override
+		public void bind()
+		{
+			if (sessionId != null) {
+				// already bound
+				return;
+			}
+			
+			if (asynchronous) {
+				throw new WicketRuntimeException("no session available asynchronuously");
+			}
+			
+			context.bind();
+		}
+
+		/**
+		 * Get the identifier of the session, maybe <code>null</code> if not bound.
+		 */
+		@Override
+		public String getSessionId()
+		{
+			return sessionId;
+		}
 	}
 
 	/**
-	 * The thread that acts as consumer of {@link Entry}ies
+	 * The consumer of {@link PendingAdd}s.
 	 */
-	private static class PageSavingRunnable implements Runnable
+	private static class PageAddingRunnable implements Runnable
 	{
-		private static final Logger log = LoggerFactory.getLogger(PageSavingRunnable.class);
+		private static final Logger log = LoggerFactory.getLogger(PageAddingRunnable.class);
 
-		private final BlockingQueue<Entry> entries;
+		private final BlockingQueue<PendingAdd> entries;
 
-		private final ConcurrentMap<String, Entry> entryMap;
+		private final ConcurrentMap<String, PendingAdd> addQueue;
 
 		private final IPageStore delegate;
 
-		private PageSavingRunnable(IPageStore delegate, BlockingQueue<Entry> entries,
-		                           ConcurrentMap<String, Entry> entryMap)
+		private PageAddingRunnable(IPageStore delegate, BlockingQueue<PendingAdd> entries,
+		                           ConcurrentMap<String, PendingAdd> entryMap)
 		{
 			this.delegate = delegate;
 			this.entries = entries;
-			this.entryMap = entryMap;
+			this.addQueue = entryMap;
 		}
 
 		@Override
@@ -209,21 +310,21 @@ public class AsynchronousPageStore implements IPageStore
 		{
 			while (!Thread.interrupted())
 			{
-				Entry entry = null;
+				PendingAdd add = null;
 				try
 				{
-					entry = entries.poll(POLL_WAIT, TimeUnit.MILLISECONDS);
+					add = entries.poll(POLL_WAIT, TimeUnit.MILLISECONDS);
 				}
 				catch (InterruptedException e)
 				{
 					Thread.currentThread().interrupt();
 				}
 
-				if (entry != null)
+				if (add != null)
 				{
-					log.debug("Saving asynchronously: {}...", entry);
-					delegate.storePage(entry.sessionId, entry.page);
-					entryMap.remove(getKey(entry));
+					log.debug("Saving asynchronously: {}...", add);
+					delegate.addPage(add, add.page);
+					addQueue.remove(add.getKey());
 				}
 			}
 		}
@@ -245,102 +346,85 @@ public class AsynchronousPageStore implements IPageStore
 			}
 		}
 
-		delegate.destroy();
+		super.destroy();
 	}
 
 	@Override
-	public IManageablePage getPage(String sessionId, int pageId)
+	public IManageablePage getPage(IPageContext context, int pageId)
 	{
-		Entry entry = getEntry(sessionId, pageId);
+		PendingAdd entry = queueMap.get(getKey(context.getSessionId(), pageId));
 		if (entry != null)
 		{
-			log.debug(
-				"Returning the page of a non-stored entry with session id '{}' and page id '{}'",
-				sessionId, pageId);
+			log.debug("Returning the page of a non-stored entry with page id '{}'", pageId);
 			return entry.page;
 		}
-		IManageablePage page = delegate.getPage(sessionId, pageId);
+		IManageablePage page = super.getPage(context, pageId);
 
-		log.debug("Returning the page of a stored entry with session id '{}' and page id '{}'",
-			sessionId, pageId);
+		log.debug("Returning the page of a stored entry with page id '{}'", pageId);
 
 		return page;
 	}
 
 	@Override
-	public void removePage(String sessionId, int pageId)
+	public void removePage(IPageContext context, IManageablePage page)
 	{
-		String key = getKey(sessionId, pageId);
+		String key = getKey(context.getSessionId(), page.getPageId());
 		if (key != null)
 		{
-			Entry entry = entryMap.remove(key);
+			PendingAdd entry = queueMap.remove(key);
 			if (entry != null)
 			{
-				entries.remove(entry);
+				queue.remove(entry);
 			}
 		}
 
-		delegate.removePage(sessionId, pageId);
+		super.removePage(context, page);
 	}
 
 	@Override
-	public void storePage(String sessionId, IManageablePage page)
+	public void addPage(IPageContext context, IManageablePage page)
 	{
-		Entry entry = new Entry(sessionId, page);
-		String key = getKey(entry);
-		entryMap.put(key, entry);
-
-		try
-		{
-			if (entries.offer(entry, OFFER_WAIT, TimeUnit.MILLISECONDS))
+		PendingAdd add = new PendingAdd(context, page);
+		if (getDelegate().canBeAsynchronous(add)) {
+			add.asynchronous = true;
+			
+			String key = add.getKey();
+			queueMap.put(key, add);
+			try
 			{
-				log.debug("Offered for storing asynchronously page with id '{}' in session '{}'",
-					page.getPageId(), sessionId);
+				if (queue.offer(add, OFFER_WAIT, TimeUnit.MILLISECONDS))
+				{
+					log.debug("Offered for storing asynchronously page with id '{}'", page.getPageId());
+					return;
+				}
+				else
+				{
+					log.debug("Storing synchronously page with id '{}'", page.getPageId());
+					queueMap.remove(key);
+				}
 			}
-			else
+			catch (InterruptedException e)
 			{
-				log.debug("Storing synchronously page with id '{}' in session '{}'",
-					page.getPageId(), sessionId);
-				entryMap.remove(key);
-				delegate.storePage(sessionId, page);
+				log.error(e.getMessage(), e);
+				queueMap.remove(key);
 			}
 		}
-		catch (InterruptedException e)
-		{
-			log.error(e.getMessage(), e);
-			entryMap.remove(key);
-			delegate.storePage(sessionId, page);
-		}
-	}
-
-	@Override
-	public void unbind(String sessionId)
-	{
-		delegate.unbind(sessionId);
-	}
-
-	@Override
-	public Serializable prepareForSerialization(String sessionId, Serializable page)
-	{
-		return delegate.prepareForSerialization(sessionId, page);
-	}
-
-	@Override
-	public Object restoreAfterSerialization(Serializable serializable)
-	{
-		return delegate.restoreAfterSerialization(serializable);
+		
+		super.addPage(context, page);
 	}
 
 	@Override
-	public IManageablePage convertToPage(Object page)
+	public void removeAllPages(IPageContext context)
 	{
-		return delegate.convertToPage(page);
-	}
-
-	@Override
-	public boolean canBeAsynchronous()
-	{
-		// should not wrap in another AsynchronousPageStore
-		return false;
+		Iterator<PendingAdd> iterator = queue.iterator();
+		while (iterator.hasNext()) {
+			PendingAdd add = iterator.next(); 
+		
+			if (add.sessionId.equals(context.getSessionId())) {
+				iterator.remove();
+			}
+		}
+		
+		super.removeAllPages(context);
 	}
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/DefaultPageContext.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/DefaultPageContext.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/DefaultPageContext.java
new file mode 100644
index 0000000..257f593
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/DefaultPageContext.java
@@ -0,0 +1,103 @@
+/*
+ * 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.pageStore;
+
+import java.io.Serializable;
+
+import org.apache.wicket.MetaDataKey;
+import org.apache.wicket.Session;
+import org.apache.wicket.request.cycle.RequestCycle;
+
+/**
+ * Default page context using a {@link Session}.
+ * 
+ * @author Juergen Donnerstag
+ * @author svenmeier
+ */
+public class DefaultPageContext implements IPageContext
+{
+	private Session session;
+
+	public DefaultPageContext(Session session) {
+		this.session = Session.get();
+	}
+	
+	/**
+	 * @see org.apache.wicket.pageStore.IPageContext#bind()
+	 */
+	@Override
+	public void bind()
+	{
+		session.bind();
+	}
+
+	/**
+	 * @see org.apache.wicket.pageStore.IPageContext#getSessionId()
+	 */
+	@Override
+	public String getSessionId()
+	{
+		return session.getId();
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	public <T extends Serializable> T getSessionAttribute(String key)
+	{
+		return (T)session.getAttribute(key);
+	}
+	
+	@Override
+	public <T extends Serializable> void setSessionAttribute(String key, T value)
+	{
+		session.setAttribute(key, value);
+	}
+	
+	@Override
+	public <T extends Serializable> T getSessionData(MetaDataKey<T> key)
+	{
+		return session.getMetaData(key);
+	}
+
+	@Override
+	public <T extends Serializable> void setSessionData(MetaDataKey<T> key, T value)
+	{
+		session.setMetaData(key, value);
+	}
+
+	@Override
+	public <T> T getRequestData(MetaDataKey<T> key)
+	{
+		RequestCycle requestCycle = RequestCycle.get();
+		if (requestCycle == null)
+		{
+			throw new IllegalStateException("Not a request thread.");
+		}
+		return requestCycle.getMetaData(key);
+	}
+
+	@Override
+	public <T> void setRequestData(MetaDataKey<T> key, T value)
+	{
+		RequestCycle requestCycle = RequestCycle.get();
+		if (requestCycle == null)
+		{
+			throw new IllegalStateException("Not a request thread.");
+		}
+		requestCycle.setMetaData(key, value);
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/DefaultPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/DefaultPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/DefaultPageStore.java
deleted file mode 100644
index f324b85..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/DefaultPageStore.java
+++ /dev/null
@@ -1,469 +0,0 @@
-/*
- * 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.pageStore;
-
-import java.io.Serializable;
-import java.lang.ref.SoftReference;
-import java.util.Iterator;
-import java.util.concurrent.ConcurrentLinkedDeque;
-
-import org.apache.wicket.page.IManageablePage;
-import org.apache.wicket.serialize.ISerializer;
-import org.apache.wicket.util.lang.Args;
-import org.apache.wicket.util.lang.Objects;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The {@link IPageStore} that converts {@link IManageablePage} instances to {@link SerializedPage}s
- * before passing them to the {@link IDataStore} to store them and the same in the opposite
- * direction when loading {@link SerializedPage} from the data store.
- * 
- */
-public class DefaultPageStore extends AbstractCachingPageStore<DefaultPageStore.SerializedPage>
-{
-	private static final Logger LOG = LoggerFactory.getLogger(DefaultPageStore.class);
-
-	/**
-	 * Construct.
-	 * 
-	 * @param pageSerializer
-	 *            the {@link ISerializer} that will be used to convert pages from/to byte arrays
-	 * @param dataStore
-	 *            the {@link IDataStore} that actually stores the pages
-	 * @param cacheSize
-	 *            the number of pages to cache in memory before passing them to
-	 *            {@link IDataStore#storeData(String, int, byte[])}
-	 */
-	public DefaultPageStore(final ISerializer pageSerializer, final IDataStore dataStore,
-		final int cacheSize)
-	{
-		super(pageSerializer, dataStore, new SerializedPagesCache(cacheSize));
-	}
-
-	@Override
-	public void storePage(final String sessionId, final IManageablePage page)
-	{
-		SerializedPage serialized = createSerializedPage(sessionId, page);
-		if (serialized != null)
-		{
-			int pageId = page.getPageId();
-			pagesCache.storePage(sessionId, pageId, serialized);
-			storePageData(sessionId, pageId, serialized.getData());
-		}
-	}
-
-	@Override
-	public IManageablePage convertToPage(final Object object)
-	{
-		if (object == null)
-		{
-			return null;
-		}
-		else if (object instanceof IManageablePage)
-		{
-			return (IManageablePage)object;
-		}
-		else if (object instanceof SerializedPage)
-		{
-			SerializedPage page = (SerializedPage)object;
-			byte data[] = page.getData();
-			if (data == null)
-			{
-				data = getPageData(page.getSessionId(), page.getPageId());
-			}
-			if (data != null)
-			{
-				return deserializePage(data);
-			}
-			return null;
-		}
-
-		String type = object.getClass().getName();
-		throw new IllegalArgumentException("Unknown object type " + type);
-	}
-
-	/**
-	 * Reloads the {@link SerializedPage} from the backing {@link IDataStore} if the
-	 * {@link SerializedPage#data} is stripped earlier
-	 * 
-	 * @param serializedPage
-	 *            the {@link SerializedPage} with empty {@link SerializedPage#data} slot
-	 * @return the fully functional {@link SerializedPage}
-	 */
-	private SerializedPage restoreStrippedSerializedPage(final SerializedPage serializedPage)
-	{
-		SerializedPage result = pagesCache.getPage(serializedPage.getSessionId(),
-			serializedPage.getPageId());
-		if (result != null)
-		{
-			return result;
-		}
-
-		byte data[] = getPageData(serializedPage.getSessionId(), serializedPage.getPageId());
-		return new SerializedPage(serializedPage.getSessionId(), serializedPage.getPageId(), data);
-	}
-
-	@Override
-	public Serializable prepareForSerialization(final String sessionId, final Serializable page)
-	{
-		if (dataStore.isReplicated())
-		{
-			return null;
-		}
-
-		SerializedPage result = null;
-
-		if (page instanceof IManageablePage)
-		{
-			IManageablePage _page = (IManageablePage)page;
-			result = pagesCache.getPage(sessionId, _page.getPageId());
-			if (result == null)
-			{
-				result = createSerializedPage(sessionId, _page);
-				if (result != null)
-				{
-					pagesCache.storePage(sessionId, _page.getPageId(), result);
-				}
-			}
-		}
-		else if (page instanceof SerializedPage)
-		{
-			SerializedPage _page = (SerializedPage)page;
-			if (_page.getData() == null)
-			{
-				result = restoreStrippedSerializedPage(_page);
-			}
-			else
-			{
-				result = _page;
-			}
-		}
-
-		if (result != null)
-		{
-			return result;
-		}
-		return page;
-	}
-
-	/**
-	 * 
-	 * @return Always true for this implementation
-	 */
-	protected boolean storeAfterSessionReplication()
-	{
-		return true;
-	}
-
-	@Override
-	public Object restoreAfterSerialization(final Serializable serializable)
-	{
-		if (serializable == null)
-		{
-			return null;
-		}
-		else if (!storeAfterSessionReplication() || serializable instanceof IManageablePage)
-		{
-			return serializable;
-		}
-		else if (serializable instanceof SerializedPage)
-		{
-			SerializedPage page = (SerializedPage)serializable;
-			if (page.getData() != null)
-			{
-				storePageData(page.getSessionId(), page.getPageId(), page.getData());
-				return new SerializedPage(page.getSessionId(), page.getPageId(), null);
-			}
-			return page;
-		}
-
-		String type = serializable.getClass().getName();
-		throw new IllegalArgumentException("Unknown object type " + type);
-	}
-
-	/**
-	 * A representation of {@link IManageablePage} that knows additionally the id of the http
-	 * session in which this {@link IManageablePage} instance is used. The {@link #sessionId} and
-	 * {@link #pageId} are used for better clustering in the {@link IDataStore} structures.
-	 */
-	protected static class SerializedPage implements Serializable
-	{
-		private static final long serialVersionUID = 1L;
-
-		/**
-		 * The id of the serialized {@link IManageablePage}
-		 */
-		private final int pageId;
-
-		/**
-		 * The id of the http session in which the serialized {@link IManageablePage} is used.
-		 */
-		private final String sessionId;
-
-		/**
-		 * The serialized {@link IManageablePage}
-		 */
-		private final byte[] data;
-
-		public SerializedPage(String sessionId, int pageId, byte[] data)
-		{
-			this.pageId = pageId;
-			this.sessionId = sessionId;
-			this.data = data;
-		}
-
-		public byte[] getData()
-		{
-			return data;
-		}
-
-		public int getPageId()
-		{
-			return pageId;
-		}
-
-		public String getSessionId()
-		{
-			return sessionId;
-		}
-
-		@Override
-		public boolean equals(Object obj)
-		{
-			if (this == obj)
-			{
-				return true;
-			}
-			if ((obj instanceof SerializedPage) == false)
-			{
-				return false;
-			}
-			SerializedPage rhs = (SerializedPage)obj;
-			return Objects.equal(getPageId(), rhs.getPageId()) &&
-				Objects.equal(getSessionId(), rhs.getSessionId());
-		}
-
-		@Override
-		public int hashCode()
-		{
-			return Objects.hashCode(getPageId(), getSessionId());
-		}
-	}
-
-	/**
-	 * 
-	 * @param sessionId
-	 * @param page
-	 * @return the serialized page information
-	 */
-	protected SerializedPage createSerializedPage(final String sessionId, final IManageablePage page)
-	{
-		Args.notNull(sessionId, "sessionId");
-		Args.notNull(page, "page");
-
-		SerializedPage serializedPage = null;
-
-		byte[] data = serializePage(page);
-
-		if (data != null)
-		{
-			serializedPage = new SerializedPage(sessionId, page.getPageId(), data);
-		}
-		else if (LOG.isWarnEnabled())
-		{
-			LOG.warn("Page {} cannot be serialized. See previous logs for possible reasons.", page);
-		}
-		return serializedPage;
-	}
-
-	/**
-	 * Cache that stores serialized pages. This is important to make sure that a single page is not
-	 * serialized twice or more when not necessary.
-	 * <p>
-	 * For example a page is serialized during request, but it might be also later serialized on
-	 * session replication. The purpose of this cache is to make sure that the data obtained from
-	 * first serialization is reused on second serialization.
-	 * 
-	 * @author Matej Knopp
-	 */
-	static class SerializedPagesCache implements SecondLevelPageCache<String, Integer, SerializedPage>
-	{
-		private final int maxSize;
-
-		private final ConcurrentLinkedDeque<SoftReference<SerializedPage>> cache;
-
-		/**
-		 * Constructor.
-		 * 
-		 * @param maxSize
-		 *          The maximum number of entries to cache
-		 */
-		public SerializedPagesCache(final int maxSize)
-		{
-			this.maxSize = maxSize;
-			cache = new ConcurrentLinkedDeque<>();
-		}
-
-		/**
-		 * 
-		 * @param sessionId
-		 * @param pageId
-		 * @return the removed {@link SerializedPage} or <code>null</code> - otherwise
-		 */
-		@Override
-		public SerializedPage removePage(final String sessionId, final Integer pageId)
-		{
-			if (maxSize > 0)
-			{
-				Args.notNull(sessionId, "sessionId");
-				Args.notNull(pageId, "pageId");
-
-				SerializedPage sample = new SerializedPage(sessionId, pageId, null);
-
-				for (Iterator<SoftReference<SerializedPage>> i = cache.iterator(); i.hasNext();)
-				{
-					SoftReference<SerializedPage> ref = i.next();
-					SerializedPage entry = ref.get();
-					if (sample.equals(entry))
-					{
-						i.remove();
-						return entry;
-					}
-				}
-			}
-			return null;
-		}
-
-		/**
-		 * Removes all {@link SerializedPage}s for the session with <code>sessionId</code> from the
-		 * cache.
-		 * 
-		 * @param sessionId
-		 */
-		@Override
-		public void removePages(String sessionId)
-		{
-			if (maxSize > 0)
-			{
-				Args.notNull(sessionId, "sessionId");
-
-				for (Iterator<SoftReference<SerializedPage>> i = cache.iterator(); i.hasNext();)
-				{
-					SoftReference<SerializedPage> ref = i.next();
-					SerializedPage entry = ref.get();
-					if (entry != null && entry.getSessionId().equals(sessionId))
-					{
-						i.remove();
-					}
-				}
-			}
-		}
-
-		/**
-		 * Returns a {@link SerializedPage} by looking it up by <code>sessionId</code> and
-		 * <code>pageId</code>. If there is a match then it is <i>touched</i>, i.e. it is moved at
-		 * the top of the cache.
-		 * 
-		 * @param sessionId
-		 * @param pageId
-		 * @return the found serialized page or <code>null</code> when not found
-		 */
-		@Override
-		public SerializedPage getPage(String sessionId, Integer pageId)
-		{
-			SerializedPage result = null;
-			if (maxSize > 0)
-			{
-				Args.notNull(sessionId, "sessionId");
-				Args.notNull(pageId, "pageId");
-
-				SerializedPage sample = new SerializedPage(sessionId, pageId, null);
-
-				for (Iterator<SoftReference<SerializedPage>> i = cache.iterator(); i.hasNext();)
-				{
-					SoftReference<SerializedPage> ref = i.next();
-					SerializedPage entry = ref.get();
-					if (sample.equals(entry))
-					{
-						i.remove();
-						result = entry;
-						break;
-					}
-				}
-
-				if (result != null)
-				{
-					// move to top
-					internalStore(result);
-				}
-			}
-			return result;
-		}
-
-		/**
-		 * Store the serialized page in cache
-		 * 
-		 * @param page
-		 *      the data to serialize (page id, session id, bytes)
-		 */
-		@Override
-		public void storePage(String sessionId, Integer pageId, SerializedPage page)
-		{
-			if (maxSize > 0)
-			{
-				Args.notNull(sessionId, "sessionId");
-				Args.notNull(pageId, "pageId");
-				Args.notNull(page, "page");
-
-				for (Iterator<SoftReference<SerializedPage>> i = cache.iterator(); i.hasNext();)
-				{
-					SoftReference<SerializedPage> r = i.next();
-					SerializedPage entry = r.get();
-					if (entry != null && entry.equals(page))
-					{
-						i.remove();
-						break;
-					}
-				}
-
-				internalStore(page);
-			}
-		}
-
-		private void internalStore(SerializedPage page)
-		{
-			cache.push(new SoftReference<>(page));
-			while (cache.size() > maxSize)
-			{
-				cache.pollLast();
-			}
-		}
-
-		@Override
-		public void destroy()
-		{
-			cache.clear();
-		}
-	}
-
-	@Override
-	public boolean canBeAsynchronous()
-	{
-		return true;
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/DelegatingPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/DelegatingPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/DelegatingPageStore.java
new file mode 100644
index 0000000..304988a
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/DelegatingPageStore.java
@@ -0,0 +1,67 @@
+/*
+ * 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.pageStore;
+
+import org.apache.wicket.page.IManageablePage;
+import org.apache.wicket.util.lang.Args;
+
+/**
+ * An {@link IPageStore} that delegates to another storage.
+ */
+public abstract class DelegatingPageStore implements IPageStore
+{
+	private IPageStore delegate;
+	
+	protected DelegatingPageStore(IPageStore delegate) {
+		this.delegate = Args.notNull(delegate, "delegate");
+	}
+
+	public IPageStore getDelegate()
+	{
+		return delegate;
+	}
+	
+	@Override
+	public void addPage(IPageContext context, IManageablePage page) {
+		delegate.addPage(context, page);
+	}
+
+	@Override
+	public void removePage(IPageContext context, IManageablePage page) {
+		delegate.removePage(context, page);
+	}
+
+	@Override
+	public void removeAllPages(IPageContext context) {
+		delegate.removeAllPages(context);
+	}
+	
+	@Override
+	public IManageablePage getPage(IPageContext context, int id) {
+		return delegate.getPage(context, id);
+	}
+
+	@Override
+	public void detach(IPageContext context) {
+		delegate.detach(context);
+	}
+
+	@Override
+	public void destroy() {
+		delegate.destroy();
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/DiskDataStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/DiskDataStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/DiskDataStore.java
deleted file mode 100644
index 2dd95e4..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/DiskDataStore.java
+++ /dev/null
@@ -1,589 +0,0 @@
-/*
- * 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.pageStore;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.io.Serializable;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import org.apache.wicket.WicketRuntimeException;
-import org.apache.wicket.pageStore.PageWindowManager.PageWindow;
-import org.apache.wicket.util.file.Files;
-import org.apache.wicket.util.io.IOUtils;
-import org.apache.wicket.util.lang.Args;
-import org.apache.wicket.util.lang.Bytes;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A data store implementation which stores the data on disk (in a file system)
- */
-public class DiskDataStore implements IDataStore
-{
-	private static final Logger log = LoggerFactory.getLogger(DiskDataStore.class);
-
-	private static final String INDEX_FILE_NAME = "DiskDataStoreIndex";
-
-	private final String applicationName;
-
-	private final Bytes maxSizePerPageSession;
-
-	private final File fileStoreFolder;
-
-	private final ConcurrentMap<String, SessionEntry> sessionEntryMap;
-
-	/**
-	 * Construct.
-	 * 
-	 * @param applicationName
-	 * @param fileStoreFolder
-	 * @param maxSizePerSession
-	 */
-	public DiskDataStore(final String applicationName, final File fileStoreFolder,
-		final Bytes maxSizePerSession)
-	{
-		this.applicationName = applicationName;
-		this.fileStoreFolder = fileStoreFolder;
-		maxSizePerPageSession = Args.notNull(maxSizePerSession, "maxSizePerSession");
-		sessionEntryMap = new ConcurrentHashMap<>();
-
-		try
-		{
-			if (this.fileStoreFolder.exists() || this.fileStoreFolder.mkdirs())
-			{
-				loadIndex();
-			}
-			else
-			{
-				log.warn("Cannot create file store folder for some reason.");
-			}
-		}
-		catch (SecurityException e)
-		{
-			throw new WicketRuntimeException(
-				"SecurityException occurred while creating DiskDataStore. Consider using a non-disk based IDataStore implementation. "
-					+ "See org.apache.wicket.Application.setPageManagerProvider(IPageManagerProvider)",
-				e);
-		}
-	}
-
-	/**
-	 * @see org.apache.wicket.pageStore.IDataStore#destroy()
-	 */
-	@Override
-	public void destroy()
-	{
-		log.debug("Destroying...");
-		saveIndex();
-		log.debug("Destroyed.");
-	}
-
-	/**
-	 * @see org.apache.wicket.pageStore.IDataStore#getData(java.lang.String, int)
-	 */
-	@Override
-	public byte[] getData(final String sessionId, final int id)
-	{
-		byte[] pageData = null;
-		SessionEntry sessionEntry = getSessionEntry(sessionId, false);
-		if (sessionEntry != null)
-		{
-			pageData = sessionEntry.loadPage(id);
-		}
-
-		if (log.isDebugEnabled())
-		{
-			log.debug("Returning data{} for page with id '{}' in session with id '{}'",
-					pageData != null ? "" : "(null)", id, sessionId);
-		}
-		return pageData;
-	}
-
-	/**
-	 * @see org.apache.wicket.pageStore.IDataStore#isReplicated()
-	 */
-	@Override
-	public boolean isReplicated()
-	{
-		return false;
-	}
-
-	/**
-	 * @see org.apache.wicket.pageStore.IDataStore#removeData(java.lang.String, int)
-	 */
-	@Override
-	public void removeData(final String sessionId, final int id)
-	{
-		SessionEntry sessionEntry = getSessionEntry(sessionId, false);
-		if (sessionEntry != null)
-		{
-			if (log.isDebugEnabled())
-			{
-				log.debug("Removing data for page with id '{}' in session with id '{}'", id, sessionId);
-			}
-			sessionEntry.removePage(id);
-		}
-	}
-
-	/**
-	 * @see org.apache.wicket.pageStore.IDataStore#removeData(java.lang.String)
-	 */
-	@Override
-	public void removeData(final String sessionId)
-	{
-		SessionEntry sessionEntry = getSessionEntry(sessionId, false);
-		if (sessionEntry != null)
-		{
-			log.debug("Removing data for pages in session with id '{}'", sessionId);
-			synchronized (sessionEntry)
-			{
-				sessionEntryMap.remove(sessionEntry.sessionId);
-				sessionEntry.unbind();
-			}
-		}
-	}
-
-	/**
-	 * @see org.apache.wicket.pageStore.IDataStore#storeData(java.lang.String, int, byte[])
-	 */
-	@Override
-	public void storeData(final String sessionId, final int id, final byte[] data)
-	{
-		SessionEntry sessionEntry = getSessionEntry(sessionId, true);
-		if (sessionEntry != null)
-		{
-			log.debug("Storing data for page with id '{}' in session with id '{}'", id, sessionId);
-			sessionEntry.savePage(id, data);
-		}
-	}
-
-	/**
-	 * 
-	 * @param sessionId
-	 * @param create
-	 * @return the session entry
-	 */
-	protected SessionEntry getSessionEntry(final String sessionId, final boolean create)
-	{
-		if (!create)
-		{
-			return sessionEntryMap.get(sessionId);
-		}
-
-		SessionEntry entry = new SessionEntry(this, sessionId);
-		SessionEntry existing = sessionEntryMap.putIfAbsent(sessionId, entry);
-		return existing != null ? existing : entry;
-	}
-
-	/**
-	 * Load the index
-	 */
-	@SuppressWarnings("unchecked")
-	private void loadIndex()
-	{
-		File storeFolder = getStoreFolder();
-		File index = new File(storeFolder, INDEX_FILE_NAME);
-		if (index.exists() && index.length() > 0)
-		{
-			try
-			{
-				InputStream stream = new FileInputStream(index);
-				ObjectInputStream ois = new ObjectInputStream(stream);
-				try
-				{
-					Map<String, SessionEntry> map = (Map<String, SessionEntry>)ois.readObject();
-					sessionEntryMap.clear();
-					sessionEntryMap.putAll(map);
-
-					for (Entry<String, SessionEntry> entry : sessionEntryMap.entrySet())
-					{
-						// initialize the diskPageStore reference
-						SessionEntry sessionEntry = entry.getValue();
-						sessionEntry.diskDataStore = this;
-					}
-				} finally {
-					stream.close();
-					ois.close();
-				}
-			}
-			catch (Exception e)
-			{
-				log.error("Couldn't load DiskDataStore index from file " + index + ".", e);
-			}
-		}
-		Files.remove(index);
-	}
-
-	/**
-	 * 
-	 */
-	private void saveIndex()
-	{
-		File storeFolder = getStoreFolder();
-		if (storeFolder.exists())
-		{
-			File index = new File(storeFolder, INDEX_FILE_NAME);
-			Files.remove(index);
-			try
-			{
-				OutputStream stream = new FileOutputStream(index);
-				ObjectOutputStream oos = new ObjectOutputStream(stream);
-				try
-				{
-					Map<String, SessionEntry> map = new HashMap<>(sessionEntryMap.size());
-					for (Entry<String, SessionEntry> e : sessionEntryMap.entrySet())
-					{
-						if (e.getValue().unbound == false)
-						{
-							map.put(e.getKey(), e.getValue());
-						}
-					}
-					oos.writeObject(map);
-				} finally {
-					stream.close();
-					oos.close();
-				}
-			}
-			catch (Exception e)
-			{
-				log.error("Couldn't write DiskDataStore index to file " + index + ".", e);
-			}
-		}
-	}
-
-	/**
-	 * 
-	 */
-	protected static class SessionEntry implements Serializable
-	{
-		private static final long serialVersionUID = 1L;
-
-		private final String sessionId;
-		private transient DiskDataStore diskDataStore;
-		private String fileName;
-		private PageWindowManager manager;
-		private boolean unbound = false;
-
-		protected SessionEntry(DiskDataStore diskDataStore, String sessionId)
-		{
-			this.diskDataStore = diskDataStore;
-			this.sessionId = sessionId;
-		}
-
-		public PageWindowManager getManager()
-		{
-			if (manager == null)
-			{
-				manager = new PageWindowManager(diskDataStore.maxSizePerPageSession.bytes());
-			}
-			return manager;
-		}
-
-		private String getFileName()
-		{
-			if (fileName == null)
-			{
-				fileName = diskDataStore.getSessionFileName(sessionId, true);
-			}
-			return fileName;
-		}
-
-		/**
-		 * @return session id
-		 */
-		public String getSessionId()
-		{
-			return sessionId;
-		}
-
-		/**
-		 * Saves the serialized page to appropriate file.
-		 * 
-		 * @param pageId
-		 * @param data
-		 */
-		public synchronized void savePage(int pageId, byte data[])
-		{
-			if (unbound)
-			{
-				return;
-			}
-			// only save page that has some data
-			if (data != null)
-			{
-				// allocate window for page
-				PageWindow window = getManager().createPageWindow(pageId, data.length);
-
-				FileChannel channel = getFileChannel(true);
-				if (channel != null)
-				{
-					try
-					{
-						// write the content
-						channel.write(ByteBuffer.wrap(data), window.getFilePartOffset());
-					}
-					catch (IOException e)
-					{
-						log.error("Error writing to a channel " + channel, e);
-					}
-					finally
-					{
-						IOUtils.closeQuietly(channel);
-					}
-				}
-				else
-				{
-					log.warn(
-						"Cannot save page with id '{}' because the data file cannot be opened.",
-						pageId);
-				}
-			}
-		}
-
-		/**
-		 * Removes the page from pagemap file.
-		 * 
-		 * @param pageId
-		 */
-		public synchronized void removePage(int pageId)
-		{
-			if (unbound)
-			{
-				return;
-			}
-			getManager().removePage(pageId);
-		}
-
-		/**
-		 * Loads the part of pagemap file specified by the given PageWindow.
-		 * 
-		 * @param window
-		 * @return serialized page data
-		 */
-		public byte[] loadPage(PageWindow window)
-		{
-			byte[] result = null;
-			FileChannel channel = getFileChannel(false);
-			if (channel != null)
-			{
-				ByteBuffer buffer = ByteBuffer.allocate(window.getFilePartSize());
-				try
-				{
-					channel.read(buffer, window.getFilePartOffset());
-					if (buffer.hasArray())
-					{
-						result = buffer.array();
-					}
-				}
-				catch (IOException e)
-				{
-					log.error("Error reading from file channel " + channel, e);
-				}
-				finally
-				{
-					IOUtils.closeQuietly(channel);
-				}
-			}
-			return result;
-		}
-
-		private FileChannel getFileChannel(boolean create)
-		{
-			FileChannel channel = null;
-			File file = new File(getFileName());
-			if (create || file.exists())
-			{
-				String mode = create ? "rw" : "r";
-				try
-				{
-					RandomAccessFile randomAccessFile = new RandomAccessFile(file, mode);
-					channel = randomAccessFile.getChannel();
-				}
-				catch (FileNotFoundException fnfx)
-				{
-					// can happen if the file is locked. WICKET-4176
-					log.error(fnfx.getMessage(), fnfx);
-				}
-			}
-			return channel;
-		}
-
-		/**
-		 * Loads the specified page data.
-		 * 
-		 * @param id
-		 * @return page data or null if the page is no longer in pagemap file
-		 */
-		public synchronized byte[] loadPage(int id)
-		{
-			if (unbound)
-			{
-				return null;
-			}
-			byte[] result = null;
-			PageWindow window = getManager().getPageWindow(id);
-			if (window != null)
-			{
-				result = loadPage(window);
-			}
-			return result;
-		}
-
-		/**
-		 * Deletes all files for this session.
-		 */
-		public synchronized void unbind()
-		{
-			File sessionFolder = diskDataStore.getSessionFolder(sessionId, false);
-			if (sessionFolder.exists())
-			{
-				Files.removeFolder(sessionFolder);
-				cleanup(sessionFolder);
-			}
-			unbound = true;
-		}
-
-		/**
-		 * deletes the sessionFolder's parent and grandparent, if (and only if) they are empty.
-		 *
-		 * @see #createPathFrom(String sessionId)
-		 * @param sessionFolder
-		 *            must not be null
-		 */
-		private void cleanup(final File sessionFolder)
-		{
-			File high = sessionFolder.getParentFile();
-			if (high != null && high.list().length == 0)
-			{
-				if (Files.removeFolder(high))
-				{
-					File low = high.getParentFile();
-					if (low != null && low.list().length == 0)
-					{
-						Files.removeFolder(low);
-					}
-				}
-			}
-		}
-	}
-
-	/**
-	 * Returns the file name for specified session. If the session folder (folder that contains the
-	 * file) does not exist and createSessionFolder is true, the folder will be created.
-	 * 
-	 * @param sessionId
-	 * @param createSessionFolder
-	 * @return file name for pagemap
-	 */
-	private String getSessionFileName(String sessionId, boolean createSessionFolder)
-	{
-		File sessionFolder = getSessionFolder(sessionId, createSessionFolder);
-		return new File(sessionFolder, "data").getAbsolutePath();
-	}
-
-	/**
-	 * This folder contains sub-folders named as the session id for which they hold the data.
-	 * 
-	 * @return the folder where the pages are stored
-	 */
-	protected File getStoreFolder()
-	{
-		return new File(fileStoreFolder, applicationName + "-filestore");
-	}
-
-	/**
-	 * Returns the folder for the specified sessions. If the folder doesn't exist and the create
-	 * flag is set, the folder will be created.
-	 * 
-	 * @param sessionId
-	 * @param create
-	 * @return folder used to store session data
-	 */
-	protected File getSessionFolder(String sessionId, final boolean create)
-	{
-		File storeFolder = getStoreFolder();
-
-		sessionId = sessionId.replace('*', '_');
-		sessionId = sessionId.replace('/', '_');
-		sessionId = sessionId.replace(':', '_');
-
-		sessionId = createPathFrom(sessionId);
-
-		File sessionFolder = new File(storeFolder, sessionId);
-		if (create && sessionFolder.exists() == false)
-		{
-			Files.mkdirs(sessionFolder);
-		}
-		return sessionFolder;
-	}
-
-	/**
-	 * creates a three-level path from the sessionId in the format 0000/0000/<sessionId>. The two
-	 * prefixing directories are created from the sessionId's hashcode and thus, should be well
-	 * distributed.
-	 *
-	 * This is used to avoid problems with Filesystems allowing no more than 32k entries in a
-	 * directory.
-	 *
-	 * Note that the prefix paths are created from Integers and not guaranteed to be four chars
-	 * long.
-	 *
-	 * @param sessionId
-	 *      must not be null
-	 * @return path in the form 0000/0000/sessionId
-	 */
-	private String createPathFrom(final String sessionId)
-	{
-		int sessionIdHashCode = sessionId.hashCode();
-		if (sessionIdHashCode == Integer.MIN_VALUE) {
-			// Math.abs(MIN_VALUE) == MIN_VALUE, so avoid it
-			sessionIdHashCode += 1;
-		}
-		int hash = Math.abs(sessionIdHashCode);
-		String low = String.valueOf(hash % 9973);
-		String high = String.valueOf((hash / 9973) % 9973);
-		StringBuilder bs = new StringBuilder(sessionId.length() + 10);
-		bs.append(low);
-		bs.append(File.separator);
-		bs.append(high);
-		bs.append(File.separator);
-		bs.append(sessionId);
-
-		return bs.toString();
-	}
-
-	@Override
-	public boolean canBeAsynchronous()
-	{
-		return true;
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/DiskPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/DiskPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/DiskPageStore.java
new file mode 100644
index 0000000..b810218
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/DiskPageStore.java
@@ -0,0 +1,719 @@
+/*
+ * 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.pageStore;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+
+import org.apache.wicket.Session;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.page.IManageablePage;
+import org.apache.wicket.pageStore.disk.PageWindowManager;
+import org.apache.wicket.pageStore.disk.PageWindowManager.FileWindow;
+import org.apache.wicket.serialize.ISerializer;
+import org.apache.wicket.util.file.Files;
+import org.apache.wicket.util.io.IOUtils;
+import org.apache.wicket.util.lang.Args;
+import org.apache.wicket.util.lang.Bytes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A storage of pages on disk.
+ */
+public class DiskPageStore implements IPersistentPageStore
+{
+	private static final Logger log = LoggerFactory.getLogger(DiskPageStore.class);
+	
+	private static final String KEY = "wicket:DiskPageStore";
+
+	private static final String INDEX_FILE_NAME = "DiskDataStoreIndex";
+	
+	/**
+	 * A cache that holds all page stores.
+	 */
+	private static final ConcurrentMap<String, DiskPageStore> DISK_STORES = new ConcurrentHashMap<>();
+
+	private final String applicationName;
+
+	private final ISerializer serializer;
+	
+	private final Bytes maxSizePerSession;
+	
+	private final File fileStoreFolder;
+
+	private final ConcurrentMap<String, DiskData> diskDatas;
+
+	public DiskPageStore(String applicationName, ISerializer serializer, File fileStoreFolder, Bytes maxSizePerSession)
+	{
+		this.applicationName = Args.notNull(applicationName, "applicationName");
+		this.serializer = Args.notNull(serializer, "serializer");
+		this.maxSizePerSession = Args.notNull(maxSizePerSession, "maxSizePerSession");
+		this.fileStoreFolder = Args.notNull(fileStoreFolder, "fileStoreFolder");
+		
+		this.diskDatas = new ConcurrentHashMap<>();
+
+		try
+		{
+			if (this.fileStoreFolder.exists() || this.fileStoreFolder.mkdirs())
+			{
+				loadIndex();
+			}
+			else
+			{
+				log.warn("Cannot create file store folder for some reason.");
+			}
+		}
+		catch (SecurityException e)
+		{
+			throw new WicketRuntimeException(
+				"SecurityException occurred while creating DiskDataStore. Consider using a non-disk based IDataStore implementation. "
+					+ "See org.apache.wicket.Application.setPageManagerProvider(IPageManagerProvider)",
+				e);
+		}
+		
+		if (DISK_STORES.containsKey(applicationName))
+		{
+			throw new IllegalStateException("Store for application with key '" + applicationName + "' already exists.");
+		}
+		DISK_STORES.put(applicationName, this);
+	}
+
+	@Override
+	public void destroy()
+	{
+		log.debug("Destroying...");
+		saveIndex();
+		
+		DISK_STORES.remove(applicationName);
+
+		log.debug("Destroyed.");
+	}
+	
+	@Override
+	public boolean canBeAsynchronous(IPageContext context)
+	{
+		// session attribute must be added here *before* any asynchronous calls
+		// when session is no longer available
+		getSessionAttribute(context, true);
+		
+		return true;
+	}
+	
+	@Override
+	public IManageablePage getPage(IPageContext context, int id)
+	{
+		IManageablePage page = null;
+			
+		DiskData diskData = getDiskData(context, false);
+		if (diskData != null)
+		{
+			byte[] data = diskData.loadPage(id);
+			if (data != null) {
+				page = (IManageablePage)serializer.deserialize(data);
+			}
+		}
+		
+		if (log.isDebugEnabled())
+		{
+			log.debug("Returning page with id '{}' in session with id '{}'", page != null ? "" : "(null)", id, context.getSessionId());
+		}
+
+		return page;
+	}
+
+	@Override
+	public void removePage(IPageContext context, IManageablePage page)
+	{
+		DiskData diskData = getDiskData(context, false);
+		if (diskData != null)
+		{
+			if (log.isDebugEnabled())
+			{
+				log.debug("Removing page with id '{}' in session with id '{}'", page.getPageId(), context.getSessionId());
+			}
+			diskData.removeData(page.getPageId());
+		}
+	}
+
+	@Override
+	public void removeAllPages(IPageContext context)
+	{
+		DiskData diskData = getDiskData(context, false);
+		if (diskData != null)
+		{
+			removeDiskData(diskData);
+		}
+	}
+	
+	protected void removeDiskData(DiskData diskData) {
+		synchronized (diskDatas)
+		{
+			diskDatas.remove(diskData.sessionIdentifier);
+			diskData.unbind();
+		}
+	}
+
+	@Override
+	public void addPage(IPageContext context, IManageablePage page)
+	{
+		DiskData diskData = getDiskData(context, true);
+		if (diskData != null)
+		{
+			log.debug("Storing data for page with id '{}' in session with id '{}'", page.getPageId(), context.getSessionId());
+			
+			byte[] data = serializer.serialize(page);
+			
+			diskData.savePage(page.getPageId(), page.getClass(), data);
+		}
+	}
+
+	/**
+	 * 
+	 * @param context
+	 * @param create
+	 * @return the session entry
+	 */
+	protected DiskData getDiskData(final IPageContext context, final boolean create)
+	{
+		SessionAttribute attribute = getSessionAttribute(context, create);
+		
+		if (!create && attribute == null) {
+			return null;
+		}
+		
+		return getDiskData(attribute.identifier, create);
+	}
+	
+	protected DiskData getDiskData(String sessionIdentifier, boolean create) {
+		if (!create)
+		{
+			return diskDatas.get(sessionIdentifier);
+		}
+
+		DiskData data = new DiskData(this, sessionIdentifier);
+		DiskData existing = diskDatas.putIfAbsent(sessionIdentifier, data);
+		return existing != null ? existing : data;
+	}
+
+	protected SessionAttribute getSessionAttribute(IPageContext context, boolean create)
+	{
+		context.bind();
+		
+		SessionAttribute attribute = context.getSessionAttribute(KEY);
+		if (attribute == null && create) {
+			attribute = new SessionAttribute(applicationName, context.getSessionId());
+			context.setSessionAttribute(KEY, attribute);
+		}
+		return attribute;
+	}
+
+	/**
+	 * Load the index
+	 */
+	@SuppressWarnings("unchecked")
+	private void loadIndex()
+	{
+		File storeFolder = getStoreFolder();
+		
+		File index = new File(storeFolder, INDEX_FILE_NAME);
+		if (index.exists() && index.length() > 0)
+		{
+			try
+			{
+				InputStream stream = new FileInputStream(index);
+				ObjectInputStream ois = new ObjectInputStream(stream);
+				try
+				{
+					diskDatas.clear();
+					
+					for (DiskData diskData : (List<DiskData>)ois.readObject()) {
+						diskData.pageStore = this;
+						diskDatas.put(diskData.sessionIdentifier, diskData);
+					}
+				} finally {
+					stream.close();
+					ois.close();
+				}
+			}
+			catch (Exception e)
+			{
+				log.error("Couldn't load DiskDataStore index from file " + index + ".", e);
+			}
+		}
+		Files.remove(index);
+	}
+
+	/**
+	 * 
+	 */
+	private void saveIndex()
+	{
+		File storeFolder = getStoreFolder();
+		if (storeFolder.exists())
+		{
+			File index = new File(storeFolder, INDEX_FILE_NAME);
+			Files.remove(index);
+			try
+			{
+				OutputStream stream = new FileOutputStream(index);
+				ObjectOutputStream oos = new ObjectOutputStream(stream);
+				try
+				{
+					List<DiskData> list = new ArrayList<>(diskDatas.size());
+					for (DiskData diskData : diskDatas.values())
+					{
+						if (diskData.sessionIdentifier != null)
+						{
+							list.add(diskData);
+						}
+					}
+					oos.writeObject(list);
+				} finally {
+					stream.close();
+					oos.close();
+				}
+			}
+			catch (Exception e)
+			{
+				log.error("Couldn't write DiskDataStore index to file " + index + ".", e);
+			}
+		}
+	}
+
+	@Override
+	public Set<String> getSessionIdentifiers()
+	{
+		return Collections.unmodifiableSet(diskDatas.keySet());
+	}
+
+	/**
+	 * 
+	 * @param session key
+	 * @return a list of the last N page windows
+	 */
+	@Override
+	public List<IPersistedPage> getPersistentPages(String sessionIdentifier)
+	{
+		List<IPersistedPage> pages = new ArrayList<>();
+
+		DiskData diskData = getDiskData(sessionIdentifier, false);
+		if (diskData != null)
+		{
+			PageWindowManager windowManager = diskData.getManager();
+			
+			pages.addAll(windowManager.getFileWindows());
+		}
+		return pages;
+	}
+
+	@Override
+	public String getSessionIdentifier(IPageContext context)
+	{
+		SessionAttribute sessionAttribute = getSessionAttribute(context, true);
+		
+		return sessionAttribute.identifier;
+	}
+
+	@Override
+	public Bytes getTotalSize()
+	{
+		long size = 0;
+		
+		synchronized (diskDatas)
+		{
+			for (DiskData diskData : diskDatas.values()) {
+				size = size + diskData.size();
+			}
+		}
+		
+		return Bytes.bytes(size);
+	}
+
+	/**
+	 * Data held on disk. 
+	 */
+	protected static class DiskData implements Serializable
+	{
+		private static final long serialVersionUID = 1L;
+
+		private transient DiskPageStore pageStore;
+		
+		private transient String fileName;
+		
+		private String sessionIdentifier;
+		
+		private PageWindowManager manager;
+		
+		protected DiskData(DiskPageStore pageStore, String sessionIdentifier)
+		{
+			this.pageStore = pageStore;
+			
+			this.sessionIdentifier = sessionIdentifier;
+		}
+
+		public long size()
+		{
+			return manager.getTotalSize();
+		}
+
+		public PageWindowManager getManager()
+		{
+			if (manager == null)
+			{
+				manager = new PageWindowManager(pageStore.maxSizePerSession.bytes());
+			}
+			return manager;
+		}
+
+		private String getFileName()
+		{
+			if (fileName == null)
+			{
+				fileName = pageStore.getSessionFileName(sessionIdentifier, true);
+			}
+			return fileName;
+		}
+
+		/**
+		 * @return session id
+		 */
+		public String getKey()
+		{
+			return sessionIdentifier;
+		}
+
+		/**
+		 * Saves the serialized page to appropriate file.
+		 * 
+		 * @param pageId
+		 * @param clazz 
+		 * @param data
+		 */
+		public synchronized void savePage(int pageId, Class<? extends IManageablePage> clazz, byte data[])
+		{
+			if (sessionIdentifier == null)
+			{
+				return;
+			}
+			
+			// only save page that has some data
+			if (data != null)
+			{
+				// allocate window for page
+				FileWindow window = getManager().createPageWindow(pageId, clazz, data.length);
+
+				FileChannel channel = getFileChannel(true);
+				if (channel != null)
+				{
+					try
+					{
+						// write the content
+						channel.write(ByteBuffer.wrap(data), window.getFilePartOffset());
+					}
+					catch (IOException e)
+					{
+						log.error("Error writing to a channel " + channel, e);
+					}
+					finally
+					{
+						IOUtils.closeQuietly(channel);
+					}
+				}
+				else
+				{
+					log.warn(
+						"Cannot save page with id '{}' because the data file cannot be opened.",
+						pageId);
+				}
+			}
+		}
+
+		/**
+		 * Removes the page from pagemap file.
+		 * 
+		 * @param pageId
+		 */
+		public synchronized void removeData(int pageId)
+		{
+			if (sessionIdentifier == null)
+			{
+				return;
+			}
+			
+			getManager().removePage(pageId);
+		}
+
+		/**
+		 * Loads the part of pagemap file specified by the given PageWindow.
+		 * 
+		 * @param window
+		 * @return serialized page data
+		 */
+		public byte[] loadData(FileWindow window)
+		{
+			byte[] result = null;
+			FileChannel channel = getFileChannel(false);
+			if (channel != null)
+			{
+				ByteBuffer buffer = ByteBuffer.allocate(window.getFilePartSize());
+				try
+				{
+					channel.read(buffer, window.getFilePartOffset());
+					if (buffer.hasArray())
+					{
+						result = buffer.array();
+					}
+				}
+				catch (IOException e)
+				{
+					log.error("Error reading from file channel " + channel, e);
+				}
+				finally
+				{
+					IOUtils.closeQuietly(channel);
+				}
+			}
+			return result;
+		}
+
+		private FileChannel getFileChannel(boolean create)
+		{
+			FileChannel channel = null;
+			File file = new File(getFileName());
+			if (create || file.exists())
+			{
+				String mode = create ? "rw" : "r";
+				try
+				{
+					RandomAccessFile randomAccessFile = new RandomAccessFile(file, mode);
+					channel = randomAccessFile.getChannel();
+				}
+				catch (FileNotFoundException fnfx)
+				{
+					// can happen if the file is locked. WICKET-4176
+					log.error(fnfx.getMessage(), fnfx);
+				}
+			}
+			return channel;
+		}
+
+		/**
+		 * Loads the specified page data.
+		 * 
+		 * @param id
+		 * @return page data or null if the page is no longer in pagemap file
+		 */
+		public synchronized byte[] loadPage(int id)
+		{
+			if (sessionIdentifier == null)
+			{
+				return null;
+			}
+			
+			FileWindow window = getManager().getPageWindow(id);
+			if (window == null)
+			{
+				return null;
+			}
+
+			return loadData(window);
+		}
+
+		/**
+		 * Deletes all files for this session.
+		 */
+		public synchronized void unbind()
+		{
+			File sessionFolder = pageStore.getSessionFolder(sessionIdentifier, false);
+			if (sessionFolder.exists())
+			{
+				Files.removeFolder(sessionFolder);
+				cleanup(sessionFolder);
+			}
+			
+			sessionIdentifier = null;
+		}
+
+		/**
+		 * deletes the sessionFolder's parent and grandparent, if (and only if) they are empty.
+		 *
+		 * @see #createPathFrom(String sessionId)
+		 * @param sessionFolder
+		 *            must not be null
+		 */
+		private void cleanup(final File sessionFolder)
+		{
+			File high = sessionFolder.getParentFile();
+			if (high != null && high.list().length == 0)
+			{
+				if (Files.removeFolder(high))
+				{
+					File low = high.getParentFile();
+					if (low != null && low.list().length == 0)
+					{
+						Files.removeFolder(low);
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Returns the file name for specified session. If the session folder (folder that contains the
+	 * file) does not exist and createSessionFolder is true, the folder will be created.
+	 * 
+	 * @param sessionIdentifier
+	 * @param createSessionFolder
+	 * @return file name for pagemap
+	 */
+	private String getSessionFileName(String sessionIdentifier, boolean createSessionFolder)
+	{
+		File sessionFolder = getSessionFolder(sessionIdentifier, createSessionFolder);
+		return new File(sessionFolder, "data").getAbsolutePath();
+	}
+
+	/**
+	 * This folder contains sub-folders named as the session id for which they hold the data.
+	 * 
+	 * @return the folder where the pages are stored
+	 */
+	protected File getStoreFolder()
+	{
+		return new File(fileStoreFolder, applicationName + "-filestore");
+	}
+
+	/**
+	 * Returns the folder for the specified sessions. If the folder doesn't exist and the create
+	 * flag is set, the folder will be created.
+	 * 
+	 * @param sessionIdentifier
+	 * @param create
+	 * @return folder used to store session data
+	 */
+	protected File getSessionFolder(String sessionIdentifier, final boolean create)
+	{
+		File storeFolder = getStoreFolder();
+
+		sessionIdentifier = sessionIdentifier.replace('*', '_');
+		sessionIdentifier = sessionIdentifier.replace('/', '_');
+		sessionIdentifier = sessionIdentifier.replace(':', '_');
+
+		sessionIdentifier = createPathFrom(sessionIdentifier);
+
+		File sessionFolder = new File(storeFolder, sessionIdentifier);
+		if (create && sessionFolder.exists() == false)
+		{
+			Files.mkdirs(sessionFolder);
+		}
+		return sessionFolder;
+	}
+
+	/**
+	 * creates a three-level path from the sessionId in the format 0000/0000/<sessionId>. The two
+	 * prefixing directories are created from the sessionId's hashcode and thus, should be well
+	 * distributed.
+	 *
+	 * This is used to avoid problems with Filesystems allowing no more than 32k entries in a
+	 * directory.
+	 *
+	 * Note that the prefix paths are created from Integers and not guaranteed to be four chars
+	 * long.
+	 *
+	 * @param sessionId
+	 *      must not be null
+	 * @return path in the form 0000/0000/sessionId
+	 */
+	private String createPathFrom(final String sessionId)
+	{
+		int sessionIdHashCode = sessionId.hashCode();
+		if (sessionIdHashCode == Integer.MIN_VALUE) {
+			// Math.abs(MIN_VALUE) == MIN_VALUE, so avoid it
+			sessionIdHashCode += 1;
+		}
+		int hash = Math.abs(sessionIdHashCode);
+		String low = String.valueOf(hash % 9973);
+		String high = String.valueOf((hash / 9973) % 9973);
+		StringBuilder bs = new StringBuilder(sessionId.length() + 10);
+		bs.append(low);
+		bs.append(File.separator);
+		bs.append(high);
+		bs.append(File.separator);
+		bs.append(sessionId);
+
+		return bs.toString();
+	}
+	
+	/**
+	 * Attribute held in session.
+	 */
+	static class SessionAttribute implements Serializable, HttpSessionBindingListener {
+
+		private final String applicationName;
+		
+		/**
+		 * The identifier of the session, must not be equal to {@link Session#getId()}, e.g. when
+		 * the container changes the id after authorization.
+		 */
+		public final String identifier;
+		
+		public SessionAttribute(String applicationName, String sessionIdentifier) {
+			this.applicationName = Args.notNull(applicationName, "applicationName");
+			this.identifier = Args.notNull(sessionIdentifier, "sessionIdentifier");
+		}
+		
+		
+		@Override
+		public void valueBound(HttpSessionBindingEvent event)
+		{
+		}
+
+		@Override
+		public void valueUnbound(HttpSessionBindingEvent event)
+		{
+			DiskPageStore store = DISK_STORES.get(applicationName);
+			if (store == null) {
+				log.warn("Cannot remove data '{}' because disk store for application '{}' is no longer present.", identifier, applicationName);
+			} else {
+				DiskData diskData = store.getDiskData(identifier, false);
+				if (diskData != null) {
+					store.removeDiskData(diskData);
+				}
+			}
+		}
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/GroupingPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/GroupingPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/GroupingPageStore.java
new file mode 100644
index 0000000..1771763
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/GroupingPageStore.java
@@ -0,0 +1,289 @@
+/*
+ * 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.pageStore;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.wicket.MetaDataEntry;
+import org.apache.wicket.MetaDataKey;
+import org.apache.wicket.Session;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.page.IManageablePage;
+import org.apache.wicket.util.string.Strings;
+
+/**
+ * An {@link IPageStore} that groups pages.
+ * 
+ * @see #getGroup(IManageablePage)
+ */
+public abstract class GroupingPageStore extends DelegatingPageStore
+{
+
+	private static final String DEFAULT_GROUP = "default";
+
+	private static final MetaDataKey<SessionData> KEY = new MetaDataKey<SessionData>()
+	{
+		private static final long serialVersionUID = 1L;
+	};
+
+	private int maxGroups;
+	
+	/**
+	 * Is a group of a page stable.
+	 */
+	private boolean stableGroups = false;
+
+	/**
+	 * @param delegate
+	 *            store to delegate to
+	 * @param maxGroups
+	 *            maximum groups to keep
+	 */
+	public GroupingPageStore(IPageStore delegate, int maxGroups)
+	{
+		super(delegate);
+
+		this.maxGroups = maxGroups;
+	}
+
+	/**
+	 * Indicate that groups are stable, i.e. the group of a page never changes.
+	 */
+	public GroupingPageStore withStableGroups()
+	{
+		stableGroups = true;
+
+		return this;
+	}
+
+	/**
+	 * Get the group of a page, default is <code>"default"</code>
+	 * 
+	 * @return group of page, must not be empty
+	 */
+	protected String getGroup(IManageablePage page)
+	{
+		return DEFAULT_GROUP;
+	}
+
+	private String getGroupInternal(IManageablePage page)
+	{
+		String group = getGroup(page);
+
+		if (Strings.isEmpty(group))
+		{
+			throw new WicketRuntimeException("group must not be empy");
+		}
+
+		return group;
+	}
+
+	@Override
+	public void addPage(IPageContext context, IManageablePage page)
+	{
+		SessionData sessionData = getSessionData(context);
+
+		sessionData.addPage(context, page, getGroupInternal(page), maxGroups, stableGroups, getDelegate());
+	}
+
+	@Override
+	public void removePage(IPageContext context, IManageablePage page)
+	{
+		SessionData sessionData = getSessionData(context);
+		
+		sessionData.removePage(context, page, getDelegate());
+	}
+
+	@Override
+	public void removeAllPages(IPageContext context)
+	{
+		SessionData sessionData = getSessionData(context);
+
+		sessionData.removeAllPages(context, getDelegate());
+	}
+
+	@Override
+	public IManageablePage getPage(IPageContext context, int id)
+	{
+		SessionData sessionData = getSessionData(context);
+		
+		return sessionData.getPage(context, id, getDelegate());
+	}
+
+	private SessionData getSessionData(IPageContext context)
+	{
+		SessionData data = context.getSessionData(KEY);
+		if (data == null)
+		{
+			context.bind();
+			data = new SessionData();
+
+			context.setSessionData(KEY, data);
+		}
+
+		return data;
+	}
+
+	/**
+	 * Data kept in the {@link Session}.
+	 */
+	static class SessionData implements Serializable
+	{
+		Set<String> groups = new LinkedHashSet<>();
+
+		private Map<String, MetaDataEntry<?>[]> metaData = new HashMap<>();
+
+		public synchronized <T> void setMetaData(String group, MetaDataKey<T> key, T value)
+		{
+			metaData.put(group, key.set(metaData.get(group), value));
+		}
+
+		public synchronized <T> T getMetaData(String group, MetaDataKey<T> key)
+		{
+			return key.get(metaData.get(group));
+		}
+		
+		public synchronized void addPage(IPageContext context, IManageablePage page, String group, int maxGroups, boolean stableGroups, IPageStore delegate)
+		{
+			if (stableGroups == false)
+			{
+				// group might have changed, so remove page first from all groups
+				for (String other : groups)
+				{
+					delegate.removePage(new GroupContext(context, this, other), page);
+				}
+			}
+
+			groups.add(group);
+			delegate.addPage(new GroupContext(context, this, group), page);
+
+			Iterator<String> iterator = groups.iterator();
+			int size = groups.size();
+			while (size > maxGroups)
+			{
+				String other = iterator.next();
+				iterator.remove();
+				size--;
+				
+				delegate.removeAllPages(new GroupContext(context, this, other));
+			}
+		}
+		
+		public IManageablePage getPage(IPageContext context, int id, IPageStore delegate)
+		{
+			for (String group : groups)
+			{
+				IManageablePage page = delegate.getPage(new GroupContext(context, this, group), id);
+				if (page != null)
+				{
+					return page;
+				}
+			}
+			return null;
+		}
+
+		public synchronized void removePage(IPageContext context, IManageablePage page, IPageStore delegate)
+		{
+			for (String group : groups)
+			{
+				delegate.removePage(new GroupContext(context, this, group), page);
+			}
+		}
+
+		public synchronized void removeAllPages(IPageContext context, IPageStore delegate)
+		{
+			for (String group : groups)
+			{
+				delegate.removeAllPages(new GroupContext(context, this, group));
+			}
+		}
+	}
+
+	/**
+	 * Context passed to the delegate store to group data and attributes.
+	 */
+	static class GroupContext implements IPageContext
+	{
+
+		private final IPageContext context;
+
+		private final SessionData sessionData;
+
+		private final String group;
+
+		public GroupContext(IPageContext context, SessionData sessionData, String group)
+		{
+			this.context = context;
+			this.sessionData = sessionData;
+			this.group = group;
+		}
+
+		@Override
+		public String getSessionId()
+		{
+			return context.getSessionId() + "_" + group;
+		}
+
+		@Override
+		public <T extends Serializable> void setSessionData(MetaDataKey<T> key, T value)
+		{
+			sessionData.setMetaData(group, key, value);
+		}
+
+		@Override
+		public <T extends Serializable> T getSessionData(MetaDataKey<T> key)
+		{
+			return sessionData.getMetaData(group, key);
+		}
+
+		@Override
+		public <T extends Serializable> void setSessionAttribute(String key, T value)
+		{
+			context.setSessionAttribute(key + "_" + group, value);
+		}
+
+		@Override
+		public <T extends Serializable> T getSessionAttribute(String key)
+		{
+			return context.getSessionAttribute(key + "_" + group);
+		}
+
+		@Override
+		public <T> void setRequestData(MetaDataKey<T> key, T data)
+		{
+			throw new WicketRuntimeException("no request available for group");
+		}
+
+		@Override
+		public <T> T getRequestData(MetaDataKey<T> key)
+		{
+			throw new WicketRuntimeException("no request available for group");
+		}
+
+
+		@Override
+		public void bind()
+		{
+			context.bind();
+		}
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/IDataStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/IDataStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/IDataStore.java
deleted file mode 100644
index b59176a..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/IDataStore.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.pageStore;
-
-/**
- * Data stores are used to persist (read & write) Wicket page data to a persistent store like e.g.
- * files or databases.
- * 
- * @see IPageStore
- */
-public interface IDataStore
-{
-	/**
-	 * Get data associated with the session id and page id.
-	 * 
-	 * @param sessionId
-	 *            Session ID
-	 * @param id
-	 *            Page ID
-	 * @return All the page data persisted
-	 */
-	byte[] getData(String sessionId, int id);
-
-	/**
-	 * Remove all persisted data related to the session id and page id
-	 * 
-	 * @param sessionId
-	 *            Session ID
-	 * @param id
-	 *            Page ID
-	 */
-	void removeData(String sessionId, int id);
-
-	/**
-	 * Remove all page data for the session id
-	 * 
-	 * @param sessionId
-	 *            Session ID
-	 */
-	void removeData(String sessionId);
-
-	/**
-	 * Store the page data
-	 * 
-	 * @param sessionId
-	 *            Session ID
-	 * @param id
-	 *            Page ID
-	 * @param data
-	 *            Page data
-	 */
-	void storeData(String sessionId, int id, byte[] data);
-
-	/**
-	 * Properly close the data store and possibly open resource handles
-	 */
-	void destroy();
-
-	/**
-	 * 
-	 * @return whether the data store is replicated
-	 */
-	boolean isReplicated();
-
-	/**
-	 * @return whether the implementation can be wrapped in {@link AsynchronousDataStore}
-	 */
-	boolean canBeAsynchronous();
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/IPageContext.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/IPageContext.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/IPageContext.java
new file mode 100644
index 0000000..5b9fcfb
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/IPageContext.java
@@ -0,0 +1,102 @@
+/*
+ * 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.pageStore;
+
+import java.io.Serializable;
+
+import org.apache.wicket.MetaDataKey;
+import org.apache.wicket.page.IManageablePage;
+
+/**
+ * Context of a {@link IManageablePage} when it is store in a {@link IPageStore}, decoupling it from
+ * request cycle and session.
+ * 
+ * @author Matej Knopp
+ * @author svenmeier
+ */
+public interface IPageContext
+{
+	/**
+	 * Set data into the current request.
+	 * 
+	 * @param key
+	 *            key
+	 * @param value
+	 *            value
+	 */
+	<T> void setRequestData(MetaDataKey<T> key, T value);
+
+	/**
+	 * Get data from the current request.
+	 * 
+	 * @param key
+	 *            key
+	 * @return value
+	 */
+	<T> T getRequestData(MetaDataKey<T> key);
+
+	/**
+	 * Set an attribute in the session.
+	 * 
+	 * @param key
+	 *            key
+	 * @param value
+	 *            value
+	 */
+	<T extends Serializable> void setSessionAttribute(String key, T value);
+
+	/**
+	 * Get an attribute from the session.
+	 * 
+	 * @param key
+	 *            key
+	 * @param value
+	 *            value
+	 */
+	<T extends Serializable> T getSessionAttribute(String key);
+
+	/**
+	 * Set data into the session.
+	 * 
+	 * @param key
+	 *            key
+	 * @param value
+	 *            value
+	 */
+	<T extends Serializable> void setSessionData(MetaDataKey<T> key, T value);
+
+	/**
+	 * Get data from the session.
+	 * 
+	 * @param key
+	 *            key
+	 * @return value
+	 */
+	<T extends Serializable> T getSessionData(MetaDataKey<T> key);
+
+	/**
+	 * Bind the current session. This make a temporary session become persistent across requests.
+	 */
+	void bind();
+
+	/**
+	 * Get the identifier of the session.
+	 * 
+	 * @return session id
+	 */
+	String getSessionId();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/IPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/IPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/IPageStore.java
index ed1b9d3..8860b33 100644
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/IPageStore.java
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/IPageStore.java
@@ -16,96 +16,79 @@
  */
 package org.apache.wicket.pageStore;
 
-import java.io.Serializable;
-
 import org.apache.wicket.page.IManageablePage;
 
 /**
- * {@link IPageStore} role is to mediate the storing and loading of pages done by {@link IDataStore}
- * s. {@link IPageStore} may pre-process the pages before passing them to
- * {@link IDataStore#storeData(String, int, byte[])} and to post-process them after
- * {@link IDataStore#getData(String, int)}.
- * 
- * @see IDataStore
+ * A store of pages
  */
 public interface IPageStore
 {
 	/**
-	 * Destroy the store.
-	 */
-	void destroy();
-
-	/**
-	 * Restores a page from the persistent layer.
+	 * This method must be called before any attempt to call
+	 * {@link #addPage(IPageContext, IManageablePage)} asynchronously, as done by
+	 * {@link AsynchronousPageStore}.
 	 * 
-	 * @param sessionId
-	 *            The session of the page that must be removed
-	 * @param pageId
-	 *            The id of the page.
-	 * @return The page
+	 * @return whether {@link #addPage(IPageContext, IManageablePage)} may be called asynchronously,
+	 *         default is <code>false</code>
 	 */
-	IManageablePage getPage(String sessionId, int pageId);
+	default boolean canBeAsynchronous(IPageContext context)
+	{
+		return false;
+	}
 
 	/**
-	 * Removes a page from the persistent layer.
+	 * Stores the page-
 	 * 
-	 * @param sessionId
-	 *            The session of the page that must be removed
-	 * @param pageId
-	 *            The id of the page.
+	 * @param context
+	 *            the context of the page
+	 * @param id
+	 *            the id of the page.
 	 */
-	void removePage(String sessionId, int pageId);
+	void addPage(IPageContext context, IManageablePage page);
 
 	/**
-	 * Stores the page to a persistent layer. The page should be stored under the id and the version
-	 * number.
+	 * Removes a page from storage.
 	 * 
-	 * @param sessionId
-	 *            The session of the page that must be removed
-	 * @param page
-	 *            The page to store
+	 * @param context
+	 *            the context of the page
+	 * @param id
+	 *            the id of the page.
 	 */
-	void storePage(String sessionId, IManageablePage page);
+	void removePage(IPageContext context, IManageablePage page);
 
 	/**
-	 * The page store should cleanup all the pages for that sessionid.
+	 * All pages should be removed from storage for the given context.
 	 * 
-	 * @param sessionId
-	 *            The session of the page that must be removed
+	 * @param context
+	 *            the context of the pages
 	 */
-	void unbind(String sessionId);
+	void removeAllPages(IPageContext context);
 
 	/**
-	 * Process the page before the it gets serialized. The page can be either real page instance or
-	 * object returned by {@link #restoreAfterSerialization(Serializable)}.
+	 * Restores a page from storage.
 	 * 
-	 * @param sessionId
-	 *            The session of the page that must be removed
-	 * @param page
-	 * @return The Page itself or a SerializedContainer for that page
+	 * @param context
+	 *            the context of the page
+	 * @param id
+	 *            the id of the page.
+	 * @return the page
 	 */
-	Serializable prepareForSerialization(String sessionId, Serializable page);
+	IManageablePage getPage(IPageContext context, int id);
 
 	/**
-	 * This method should restore the serialized page to intermediate object that can be converted
-	 * to real page instance using {@link #convertToPage(Object)}.
+	 * Detach from the current context.
 	 * 
-	 * @param serializable
-	 * @return Page
+	 * @param context
+	 *            the context of the pages
 	 */
-	Object restoreAfterSerialization(Serializable serializable);
+	default void detach(IPageContext context)
+	{
+	}
 
 	/**
-	 * Converts a page representation to an instance of {@link IManageablePage}
-	 * 
-	 * @param page
-	 *            some kind of page representation
-	 * @return page
-	 */
-	IManageablePage convertToPage(Object page);
-	
-	/**
-	 * @return whether the implementation can be wrapped in {@link AsynchronousPageStore}
+	 * Destroy the store.
 	 */
-	boolean canBeAsynchronous();
+	default void destroy()
+	{
+	}
 }


[5/5] wicket git commit: WICKET-6563 new IPageStore implementation

Posted by sv...@apache.org.
WICKET-6563 new IPageStore implementation

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

Branch: refs/heads/WICKET-6563
Commit: bcf76f517310ac5d27a2092595c3a925c3973067
Parents: 175734d
Author: Sven Meier <sv...@apache.org>
Authored: Mon Jun 25 17:19:41 2018 +0200
Committer: Sven Meier <sv...@apache.org>
Committed: Mon Jul 2 23:14:42 2018 +0200

----------------------------------------------------------------------
 .../java/org/apache/wicket/Application.java     |  30 +-
 .../wicket/DefaultPageManagerProvider.java      | 132 ++--
 .../org/apache/wicket/IPageManagerProvider.java |   5 +-
 .../src/main/java/org/apache/wicket/Page.java   |  10 +-
 .../main/java/org/apache/wicket/Session.java    |   7 +-
 .../core/request/handler/PageProvider.java      |   1 -
 .../handler/RenderPageRequestHandler.java       |   2 +-
 .../org/apache/wicket/mock/MockApplication.java |   2 +-
 .../org/apache/wicket/mock/MockPageManager.java |  56 +-
 .../org/apache/wicket/mock/MockPageStore.java   |  60 ++
 .../apache/wicket/page/AbstractPageManager.java | 121 ----
 .../wicket/page/DefaultPageManagerContext.java  | 101 ---
 .../org/apache/wicket/page/IPageManager.java    |  80 +--
 .../apache/wicket/page/IPageManagerContext.java |  66 --
 .../wicket/page/PageAccessSynchronizer.java     |  42 +-
 .../org/apache/wicket/page/PageManager.java     |  88 +++
 .../wicket/page/PageManagerDecorator.java       | 100 ---
 .../apache/wicket/page/PageStoreManager.java    | 507 -------------
 .../org/apache/wicket/page/RequestAdapter.java  | 206 ------
 .../pageStore/AbstractCachingPageStore.java     | 105 ---
 .../wicket/pageStore/AbstractPageStore.java     | 154 ----
 .../wicket/pageStore/AsynchronousDataStore.java | 353 ---------
 .../wicket/pageStore/AsynchronousPageStore.java | 378 ++++++----
 .../wicket/pageStore/DefaultPageContext.java    | 103 +++
 .../wicket/pageStore/DefaultPageStore.java      | 469 ------------
 .../wicket/pageStore/DelegatingPageStore.java   |  67 ++
 .../apache/wicket/pageStore/DiskDataStore.java  | 589 ---------------
 .../apache/wicket/pageStore/DiskPageStore.java  | 719 +++++++++++++++++++
 .../wicket/pageStore/GroupingPageStore.java     | 289 ++++++++
 .../org/apache/wicket/pageStore/IDataStore.java |  83 ---
 .../apache/wicket/pageStore/IPageContext.java   | 102 +++
 .../org/apache/wicket/pageStore/IPageStore.java | 103 ++-
 .../apache/wicket/pageStore/IPersistedPage.java |  44 ++
 .../wicket/pageStore/IPersistentPageStore.java  |  51 ++
 .../wicket/pageStore/InMemoryPageStore.java     | 352 +++++++++
 .../wicket/pageStore/InSessionPageStore.java    | 268 +++++++
 .../apache/wicket/pageStore/NoopPageStore.java  |  53 ++
 .../wicket/pageStore/PageWindowManager.java     | 506 -------------
 .../wicket/pageStore/PerSessionPageStore.java   | 332 ---------
 .../wicket/pageStore/RequestPageStore.java      | 159 ++++
 .../wicket/pageStore/SecondLevelPageCache.java  |  42 --
 .../pageStore/disk/PageWindowManager.java       | 493 +++++++++++++
 .../pageStore/memory/HttpSessionDataStore.java  | 186 -----
 .../memory/IDataStoreEvictionStrategy.java      |  35 -
 .../memory/MemorySizeEvictionStrategy.java      |  64 --
 .../memory/PageNumberEvictionStrategy.java      |  62 --
 .../wicket/pageStore/memory/PageTable.java      | 128 ----
 .../pageStore/memory/PageTableCleaner.java      |  46 --
 .../apache/wicket/settings/StoreSettings.java   |  26 -
 .../wicket/util/tester/BaseWicketTester.java    |   8 +-
 .../core/request/mapper/TestMapperContext.java  |  47 +-
 .../DontStoreNotRenderedPageTestCase.java       |  23 +-
 .../html/TransparentWebMarkupContainerTest.java |   8 +-
 .../wicket/page/PageAccessSynchronizerTest.java |   2 +-
 .../wicket/page/PersistentPageManagerTest.java  |  75 +-
 .../persistent/disk/PageWindowManagerTest.java  | 301 --------
 .../wicket/pageStore/AbstractPageStoreTest.java |  61 +-
 .../pageStore/AsynchronousDataStoreTest.java    |  37 +-
 .../pageStore/AsynchronousPageStoreTest.java    | 135 ++--
 .../wicket/pageStore/DefaultPageStoreTest.java  |  31 -
 .../wicket/pageStore/DiskDataStoreTest.java     | 151 ++--
 .../wicket/pageStore/DummyPageContext.java      | 103 +++
 .../wicket/pageStore/GroupingPageStoreTest.java | 112 +++
 .../wicket/pageStore/InMemoryPageStoreTest.java |  31 +
 .../pageStore/InSessionPageStoreTest.java       |  32 +
 .../apache/wicket/pageStore/NoopDataStore.java  |  61 --
 .../pageStore/PerSessionPageStoreTest.java      |  53 --
 .../pageStore/disk/PageWindowManagerTest.java   | 303 ++++++++
 .../memory/DummyPageManagerContext.java         |  66 --
 .../memory/HttpSessionDataStoreTest.java        | 112 ---
 .../memory/MemorySizeEvictionStrategyTest.java  |  65 --
 .../memory/PageNumberEvictionStrategyTest.java  |  63 --
 .../wicket/pageStore/memory/PageTableTest.java  |  58 --
 .../request/handler/PageIdPoliticTest.java      |  25 +-
 .../request/handler/PageProviderTest.java       |  20 +-
 .../wicket/versioning/InMemoryPageStore.java    | 126 ----
 .../wicket/versioning/PageVersioningTest.java   |  28 +-
 .../devutils/debugbar/DebugBarInitializer.java  |   2 +-
 .../devutils/debugbar/InspectorDebugPanel.java  |  20 +-
 .../devutils/debugbar/PageSizeDebugPanel.java   | 104 ---
 .../devutils/debugbar/PageStoreDebugPanel.java  |  84 +++
 .../devutils/debugbar/StandardDebugPanel.java   |   4 +-
 .../devutils/diskstore/DebugDiskDataStore.java  | 100 ---
 .../diskstore/DebugPageManagerProvider.java     |  63 --
 .../diskstore/DiskStoreBrowserPage.html         |  31 -
 .../diskstore/DiskStoreBrowserPage.java         |  51 --
 .../devutils/diskstore/PageStorePage.html       |  31 +
 .../devutils/diskstore/PageStorePage.java       |  68 ++
 .../diskstore/browser/BrowserPanel.html         |   9 +-
 .../diskstore/browser/BrowserPanel.java         | 115 ++-
 .../diskstore/browser/BrowserTable.java         |  47 --
 .../diskstore/browser/DataStoreHelper.java      |  43 --
 .../diskstore/browser/PageWindowColumn.java     |  80 ---
 .../browser/PageWindowDescription.java          |  57 --
 .../diskstore/browser/PageWindowProvider.java   | 108 ---
 .../browser/PersistedPagesProvider.java         | 130 ++++
 .../browser/SessionIdentifiersModel.java        |  59 ++
 .../browser/SessionsProviderModel.java          |  57 --
 .../devutils/inspector/EnhancedPageView.java    |  37 +-
 .../devutils/inspector/InspectorPage.java       |  30 +-
 .../wicket/examples/frames/BodyFrame.java       |   6 +-
 .../apache/wicket/examples/StartExamples.java   |  13 +-
 .../ajax/markup/html/modal/ModalWindow.java     |   5 +-
 .../apache/wicket/jmx/StoreSettingsMBean.java   |  13 +-
 .../wicket/jmx/wrapper/StoreSettings.java       |   6 -
 .../ws/api/AbstractWebSocketProcessor.java      |   2 +-
 106 files changed, 4761 insertions(+), 6728 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/Application.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/Application.java b/wicket-core/src/main/java/org/apache/wicket/Application.java
index 9415afa..64bf7ac 100644
--- a/wicket-core/src/main/java/org/apache/wicket/Application.java
+++ b/wicket-core/src/main/java/org/apache/wicket/Application.java
@@ -55,10 +55,9 @@ import org.apache.wicket.markup.parser.filter.WicketMessageTagHandler;
 import org.apache.wicket.markup.resolver.HtmlHeaderResolver;
 import org.apache.wicket.markup.resolver.WicketContainerResolver;
 import org.apache.wicket.markup.resolver.WicketMessageResolver;
-import org.apache.wicket.page.DefaultPageManagerContext;
 import org.apache.wicket.page.IPageManager;
-import org.apache.wicket.page.IPageManagerContext;
-import org.apache.wicket.pageStore.IDataStore;
+import org.apache.wicket.pageStore.DefaultPageContext;
+import org.apache.wicket.pageStore.IPageContext;
 import org.apache.wicket.pageStore.IPageStore;
 import org.apache.wicket.protocol.http.IRequestLogger;
 import org.apache.wicket.protocol.http.RequestLogger;
@@ -1355,11 +1354,6 @@ public abstract class Application implements UnboundListener, IEventSink
 	}
 
 	/**
-	 * Context for PageManager to interact with rest of Wicket
-	 */
-	private final IPageManagerContext pageManagerContext = new DefaultPageManagerContext();
-
-	/**
 	 * Returns an unsynchronized version of page manager
 	 * 
 	 * @return the page manager
@@ -1372,22 +1366,13 @@ public abstract class Application implements UnboundListener, IEventSink
 			{
 				if (pageManager == null)
 				{
-					pageManager = pageManagerProvider.apply(getPageManagerContext());
+					pageManager = pageManagerProvider.get();
 				}
 			}
 		}
 		return pageManager;
 	}
 
-	/**
-	 * 
-	 * @return the page manager context
-	 */
-	protected IPageManagerContext getPageManagerContext()
-	{
-		return pageManagerContext;
-	}
-
 	// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 	// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 	//
@@ -1545,7 +1530,7 @@ public abstract class Application implements UnboundListener, IEventSink
 		{
 			session = newSession(requestCycle.getRequest(), requestCycle.getResponse());
 			ThreadContext.setSession(session);
-			internalGetPageManager().newSessionCreated();
+			internalGetPageManager().removeAllPages();
 			sessionListeners.onCreated(session);
 		}
 		else
@@ -1592,10 +1577,15 @@ public abstract class Application implements UnboundListener, IEventSink
 			@Override
 			public void onDetach(final RequestCycle requestCycle)
 			{
+				IPageManager pageManager;
+				
 				if (Session.exists())
 				{
-					Session.get().getPageManager().commitRequest();
+					pageManager = Session.get().getPageManager();
+				} else {
+					pageManager = internalGetPageManager();
 				}
+				pageManager.detach();
 
 				if (Application.exists())
 				{

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java b/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
index f109472..c800935 100644
--- a/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
+++ b/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
@@ -19,22 +19,44 @@ package org.apache.wicket;
 import java.io.File;
 
 import org.apache.wicket.page.IPageManager;
-import org.apache.wicket.page.IPageManagerContext;
-import org.apache.wicket.page.PageStoreManager;
-import org.apache.wicket.pageStore.AsynchronousDataStore;
+import org.apache.wicket.page.PageManager;
 import org.apache.wicket.pageStore.AsynchronousPageStore;
-import org.apache.wicket.pageStore.DefaultPageStore;
-import org.apache.wicket.pageStore.DiskDataStore;
-import org.apache.wicket.pageStore.IDataStore;
+import org.apache.wicket.pageStore.DiskPageStore;
+import org.apache.wicket.pageStore.GroupingPageStore;
 import org.apache.wicket.pageStore.IPageStore;
+import org.apache.wicket.pageStore.InMemoryPageStore;
+import org.apache.wicket.pageStore.InSessionPageStore;
+import org.apache.wicket.pageStore.NoopPageStore;
+import org.apache.wicket.pageStore.RequestPageStore;
 import org.apache.wicket.serialize.ISerializer;
 import org.apache.wicket.settings.StoreSettings;
 import org.apache.wicket.util.lang.Args;
 import org.apache.wicket.util.lang.Bytes;
 
 /**
- * {@link IPageManagerProvider} implementation that creates new instance of {@link IPageManager}
- * that persists the pages in {@link DiskDataStore}
+ * A provider of a {@link PageManager} with a default chain of page {@link IPageStore}s:
+ * <ul>
+ * <li>{@link RequestPageStore} caching pages until end of the request</li>
+ * <li>{@link InSessionPageStore} keeping the last accessed page in the session</li>
+ * <li>{@link AsynchronousPageStore} moving storage of pages to a worker thread (if enabled in {@link StoreSettings#isAsynchronous()})</li>
+ * <li>{@link DiskPageStore} keeping all pages, configured according to {@link StoreSettings}</li>
+ * </ul>
+ * An alternative chain with all pages held in-memory could be:
+ * <ul>
+ * <li>{@link RequestPageStore} caching pages until end of the request</li>
+ * <li>{@link InSessionPageStore} keeping the last accessed page in the session</li>
+ * <li>{@link InMemoryPageStore} keeping all pages</li>
+ * </ul>
+ * ... or if all pages should be kept in the session only:
+ * <ul>
+ * <li>{@link RequestPageStore} caching pages until end of the request</li>
+ * <li>{@link InSessionPageStore} keeping a limited count of pages in the session, e.g. 10</li>
+ * <li>{@link NoopPageStore} discarding all exceeding pages</li>
+ * </ul>
+ * Other stores be may inserted ad libitum, e.g.
+ * <ul>
+ * <li>{@link GroupingPageStore} groups pages with their own maximum page limit</li>
+ * </ul>
  */
 public class DefaultPageManagerProvider implements IPageManagerProvider
 {
@@ -44,7 +66,7 @@ public class DefaultPageManagerProvider implements IPageManagerProvider
 	 * Constructor.
 	 *
 	 * @param application
-	 *          The application instance
+	 *            The application instance
 	 */
 	public DefaultPageManagerProvider(Application application)
 	{
@@ -52,53 +74,81 @@ public class DefaultPageManagerProvider implements IPageManagerProvider
 	}
 
 	@Override
-	public IPageManager apply(IPageManagerContext pageManagerContext)
+	public IPageManager get()
 	{
-		IDataStore dataStore = newDataStore();
+		IPageStore store = newPersistentStore();
 
-		StoreSettings storeSettings = getStoreSettings();
+		store = newAsynchronousStore(store);
 
-		IPageStore pageStore;
+		store = newSessionStore(store);
 
-		if (dataStore.canBeAsynchronous() && storeSettings.isAsynchronous())
-		{
-			int capacity = storeSettings.getAsynchronousQueueCapacity();
-			dataStore = new AsynchronousDataStore(dataStore, capacity);
-
-			pageStore = newPageStore(dataStore);
-
-			if (pageStore.canBeAsynchronous())
-			{
-				pageStore = new AsynchronousPageStore(pageStore, capacity);
-			}
-		}
-		else
-		{
-			pageStore = newPageStore(dataStore);
-		}
+		store = newRequestStore(store);
+		
+		return new PageManager(store);
+	}
 
-		return new PageStoreManager(application.getName(), pageStore, pageManagerContext);
+	/**
+	 * Get the {@link ISerializer} to use for serializing of pages.
+	 * 
+	 * @return how to serialize pages if needed for any {@link IPageStore}
+	 */
+	protected ISerializer getSerializer()
+	{
+		return application.getFrameworkSettings().getSerializer();
+	}
 
+	/**
+	 * Keep pages in the request until it is finished.
+	 * 
+	 * @see RequestPageStore
+	 */
+	protected IPageStore newRequestStore(IPageStore pageStore)
+	{
+		return new RequestPageStore(pageStore);
 	}
 
-	protected IPageStore newPageStore(IDataStore dataStore)
+	/**
+	 * After requests keep pages in the session.
+	 * 
+	 * @see InSessionPageStore
+	 */
+	protected IPageStore newSessionStore(IPageStore pageStore)
 	{
-		int inmemoryCacheSize = getStoreSettings().getInmemoryCacheSize();
-		ISerializer pageSerializer = application.getFrameworkSettings().getSerializer();
-		return new DefaultPageStore(pageSerializer, dataStore, inmemoryCacheSize);
+		return new InSessionPageStore(pageStore, getSerializer(), 1);
 	}
 
-	protected IDataStore newDataStore()
+	/**
+	 * Store pages asynchronously into the persistent store.
+	 * 
+	 * @see AsynchronousPageStore
+	 * @see StoreSettings#isAsynchronous()
+	 */
+	protected IPageStore newAsynchronousStore(IPageStore pageStore)
 	{
-		StoreSettings storeSettings = getStoreSettings();
-		Bytes maxSizePerSession = storeSettings.getMaxSizePerSession();
-		File fileStoreFolder = storeSettings.getFileStoreFolder();
+		StoreSettings storeSettings = application.getStoreSettings();
 
-		return new DiskDataStore(application.getName(), fileStoreFolder, maxSizePerSession);
+		if (storeSettings.isAsynchronous())
+		{
+			int capacity = storeSettings.getAsynchronousQueueCapacity();
+			pageStore = new AsynchronousPageStore(pageStore, capacity);
+		}
+
+		return pageStore;
 	}
 
-	StoreSettings getStoreSettings()
+	/**
+	 * Keep pages persistent on disk.
+	 * 
+	 * @see DiskPageStore
+	 * @see StoreSettings#getMaxSizePerSession()
+	 * @see StoreSettings#getFileStoreFolder()
+	 */
+	protected IPageStore newPersistentStore()
 	{
-		return application.getStoreSettings();
+		StoreSettings storeSettings = application.getStoreSettings();
+		Bytes maxSizePerSession = storeSettings.getMaxSizePerSession();
+		File fileStoreFolder = storeSettings.getFileStoreFolder();
+
+		return new DiskPageStore(application.getName(), getSerializer(), fileStoreFolder, maxSizePerSession);
 	}
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/IPageManagerProvider.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/IPageManagerProvider.java b/wicket-core/src/main/java/org/apache/wicket/IPageManagerProvider.java
index 557a7aa..ce40fcc 100644
--- a/wicket-core/src/main/java/org/apache/wicket/IPageManagerProvider.java
+++ b/wicket-core/src/main/java/org/apache/wicket/IPageManagerProvider.java
@@ -16,11 +16,10 @@
  */
 package org.apache.wicket;
 
-import java.util.function.Function;
+import java.util.function.Supplier;
 
 import org.apache.wicket.page.IPageManager;
-import org.apache.wicket.page.IPageManagerContext;
 
-public interface IPageManagerProvider extends Function<IPageManagerContext, IPageManager>
+public interface IPageManagerProvider extends Supplier<IPageManager>
 {
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/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 a1f3b29..935e1f9 100644
--- a/wicket-core/src/main/java/org/apache/wicket/Page.java
+++ b/wicket-core/src/main/java/org/apache/wicket/Page.java
@@ -275,7 +275,7 @@ public abstract class Page extends MarkupContainer
 		}
 
 		final IPageManager pageManager = getSession().getPageManager();
-		if (!getFlag(FLAG_IS_DIRTY) && (isVersioned() && pageManager.supportsVersioning() ||
+		if (!getFlag(FLAG_IS_DIRTY) && (isVersioned() ||
 
 		// we need to get pageId for new page instances even when the page doesn't need
 		// versioning, otherwise pages override each other in the page store and back button
@@ -287,7 +287,7 @@ public abstract class Page extends MarkupContainer
 
 			if (isInitialization == false)
 			{
-				pageManager.touchPage(this);
+				pageManager.addPage(this);
 			}
 		}
 	}
@@ -298,7 +298,7 @@ public abstract class Page extends MarkupContainer
 		super.onInitialize();
 
 		final IPageManager pageManager = getSession().getPageManager();
-		pageManager.touchPage(this);
+		pageManager.addPage(this);
 	}
 
 	/**
@@ -825,7 +825,7 @@ public abstract class Page extends MarkupContainer
 			getSession().getSessionStore().getSessionId(RequestCycle.get().getRequest(), true);
 
 			// Add/touch the response page in the session.
-			getSession().getPageManager().touchPage(this);
+			getSession().getPageManager().addPage(this);
 		}
 
 		if (getApplication().getDebugSettings().isOutputMarkupContainerClassName())
@@ -936,7 +936,7 @@ public abstract class Page extends MarkupContainer
 		setStatelessHint(false);
 
 		// make sure the page will be available on following request
-		getSession().getPageManager().touchPage(this);
+		getSession().getPageManager().addPage(this);
 
 		return new PageReference(numericId);
 	}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/Session.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/Session.java b/wicket-core/src/main/java/org/apache/wicket/Session.java
index cb8f4a2..8df8577 100644
--- a/wicket-core/src/main/java/org/apache/wicket/Session.java
+++ b/wicket-core/src/main/java/org/apache/wicket/Session.java
@@ -37,6 +37,7 @@ import org.apache.wicket.feedback.FeedbackMessage;
 import org.apache.wicket.feedback.FeedbackMessages;
 import org.apache.wicket.page.IPageManager;
 import org.apache.wicket.page.PageAccessSynchronizer;
+import org.apache.wicket.pageStore.IPageStore;
 import org.apache.wicket.request.Request;
 import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.session.ISessionStore;
@@ -290,7 +291,7 @@ public abstract class Session implements IClusterable, IEventSink
 	{
 		if (isTemporary() == false)
 		{
-			getPageManager().clear();
+			getPageManager().removeAllPages();
 		}
 	}
 
@@ -907,8 +908,8 @@ public abstract class Session implements IClusterable, IEventSink
 	 */
 	public final IPageManager getPageManager()
 	{
-		IPageManager pageManager = Application.get().internalGetPageManager();
-		return pageAccessSynchronizer.get().adapt(pageManager);
+		IPageManager manager = Application.get().internalGetPageManager();
+		return pageAccessSynchronizer.get().adapt(manager);
 	}
 
 	/** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/core/request/handler/PageProvider.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/core/request/handler/PageProvider.java b/wicket-core/src/main/java/org/apache/wicket/core/request/handler/PageProvider.java
index 7bd473e..bd45693 100644
--- a/wicket-core/src/main/java/org/apache/wicket/core/request/handler/PageProvider.java
+++ b/wicket-core/src/main/java/org/apache/wicket/core/request/handler/PageProvider.java
@@ -19,7 +19,6 @@ package org.apache.wicket.core.request.handler;
 import org.apache.wicket.Application;
 import org.apache.wicket.core.request.mapper.IPageSource;
 import org.apache.wicket.core.request.mapper.StalePageException;
-import org.apache.wicket.page.IPageManager;
 import org.apache.wicket.protocol.http.PageExpiredException;
 import org.apache.wicket.request.IRequestHandler;
 import org.apache.wicket.request.IRequestMapper;

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/core/request/handler/RenderPageRequestHandler.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/core/request/handler/RenderPageRequestHandler.java b/wicket-core/src/main/java/org/apache/wicket/core/request/handler/RenderPageRequestHandler.java
index 156d420..642d40f 100644
--- a/wicket-core/src/main/java/org/apache/wicket/core/request/handler/RenderPageRequestHandler.java
+++ b/wicket-core/src/main/java/org/apache/wicket/core/request/handler/RenderPageRequestHandler.java
@@ -136,7 +136,7 @@ public class RenderPageRequestHandler
 			if (Session.exists())
 			{
 				// WICKET-5499
-				Session.get().getPageManager().touchPage(pageProvider.getPageInstance());
+				Session.get().getPageManager().addPage(pageProvider.getPageInstance());
 			}
 		}
 	}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/mock/MockApplication.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/mock/MockApplication.java b/wicket-core/src/main/java/org/apache/wicket/mock/MockApplication.java
index 1c0a99d..4ae6f41 100644
--- a/wicket-core/src/main/java/org/apache/wicket/mock/MockApplication.java
+++ b/wicket-core/src/main/java/org/apache/wicket/mock/MockApplication.java
@@ -67,7 +67,7 @@ public class MockApplication extends WebApplication
 
 		// set page and session store providers
 		setSessionStoreProvider(MockSessionStore::new);
-		setPageManagerProvider((pageManagerContext) -> new MockPageManager());
+		setPageManagerProvider(() -> new MockPageManager());
 
 		// for test cases we usually want stable resource names
 		getResourceSettings().setCachingStrategy(NoOpResourceCachingStrategy.INSTANCE);

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/mock/MockPageManager.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/mock/MockPageManager.java b/wicket-core/src/main/java/org/apache/wicket/mock/MockPageManager.java
index b27dc7e..05bb520 100644
--- a/wicket-core/src/main/java/org/apache/wicket/mock/MockPageManager.java
+++ b/wicket-core/src/main/java/org/apache/wicket/mock/MockPageManager.java
@@ -21,7 +21,7 @@ import java.util.Map;
 
 import org.apache.wicket.page.IManageablePage;
 import org.apache.wicket.page.IPageManager;
-import org.apache.wicket.page.IPageManagerContext;
+import org.apache.wicket.pageStore.IPageStore;
 
 /**
  * Simple {@link IPageManager} used for testing.
@@ -32,19 +32,6 @@ public class MockPageManager implements IPageManager
 {
 	private final Map<Integer, IManageablePage> pages = new HashMap<>();
 
-	/**
-	 * Construct.
-	 * 
-	 */
-	public MockPageManager()
-	{
-	}
-
-	@Override
-	public void commitRequest()
-	{
-	}
-
 	@Override
 	public void destroy()
 	{
@@ -59,54 +46,29 @@ public class MockPageManager implements IPageManager
 
 	@Override
 	public void removePage(final IManageablePage page) {
-		if (page != null) {
-			pages.remove(page.getPageId());
-		}
+		pages.remove(page.getPageId());
 	}
 
 	@Override
-	public void newSessionCreated()
+	public void addPage(IManageablePage page)
 	{
-		pages.clear();
+		pages.put(page.getPageId(), page);
 	}
 
 	@Override
-	public void clear()
+	public void removeAllPages()
 	{
 		pages.clear();
 	}
 
-	/**
-	 * @param context
-	 */
-	public void setContext(IPageManagerContext context)
-	{
-	}
-
-	@Override
-	public boolean supportsVersioning()
-	{
-		return true;
-	}
-
 	@Override
-	public void touchPage(IManageablePage page)
+	public void detach()
 	{
-		if (true || page.isPageStateless() == false)
-		{
-			pages.put(page.getPageId(), page);
-		}
-	}
-
-	@Override
-	public void untouchPage(IManageablePage page)
-	{
-		pages.remove(page.getPageId());
 	}
 
 	@Override
-	public IPageManagerContext getContext()
+	public IPageStore getPageStore()
 	{
-		return null;
+		throw new UnsupportedOperationException();
 	}
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/mock/MockPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/mock/MockPageStore.java b/wicket-core/src/main/java/org/apache/wicket/mock/MockPageStore.java
new file mode 100644
index 0000000..cb4eaec
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/mock/MockPageStore.java
@@ -0,0 +1,60 @@
+/*
+ * 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.mock;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.wicket.page.IManageablePage;
+import org.apache.wicket.pageStore.IPageContext;
+import org.apache.wicket.pageStore.IPageStore;
+
+public class MockPageStore implements IPageStore
+{
+	private final Map<Integer, IManageablePage> pages = new HashMap<>();
+
+	@Override
+	public void destroy()
+	{
+		pages.clear();
+	}
+
+	@Override
+	public IManageablePage getPage(IPageContext context, int id)
+	{
+		return pages.get(id);
+	}
+
+	@Override
+	public void removePage(IPageContext context, final IManageablePage page) {
+		if (page != null) {
+			pages.remove(page.getPageId());
+		}
+	}
+
+	@Override
+	public void removeAllPages(IPageContext context)
+	{
+		pages.clear();
+	}
+
+	@Override
+	public void addPage(IPageContext context, IManageablePage page)
+	{
+		pages.put(page.getPageId(), page);
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/page/AbstractPageManager.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/page/AbstractPageManager.java b/wicket-core/src/main/java/org/apache/wicket/page/AbstractPageManager.java
deleted file mode 100644
index 03b26aa..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/page/AbstractPageManager.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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.page;
-
-import org.apache.wicket.util.lang.Args;
-
-/**
- * Convenience class for {@link IPageManager} implementations. Subclass should extend
- * {@link RequestAdapter} and override {@link #newRequestAdapter(IPageManagerContext)} method to return it's
- * {@link RequestAdapter} implementation.
- * 
- * @author Matej Knopp
- */
-public abstract class AbstractPageManager implements IPageManager
-{
-	private final IPageManagerContext context;
-
-	/**
-	 * Construct.
-	 * 
-	 * @param context
-	 */
-	public AbstractPageManager(IPageManagerContext context)
-	{
-		this.context = Args.notNull(context, "context");
-	}
-
-	/**
-	 * 
-	 * @param context
-	 * @return a new request adapter
-	 */
-	protected abstract RequestAdapter newRequestAdapter(IPageManagerContext context);
-
-	/**
-	 * @return The page manager context
-	 */
-	@Override
-	public IPageManagerContext getContext()
-	{
-		return context;
-	}
-
-	/**
-	 * @see #newRequestAdapter(IPageManagerContext)
-	 * @return the request adapter
-	 */
-	protected RequestAdapter getRequestAdapter()
-	{
-		RequestAdapter adapter = (RequestAdapter)getContext().getRequestData();
-		if (adapter == null)
-		{
-			adapter = newRequestAdapter(getContext());
-			getContext().setRequestData(adapter);
-		}
-		return adapter;
-	}
-
-	@Override
-	public void commitRequest()
-	{
-		getRequestAdapter().commitRequest();
-	}
-
-	@Override
-	public IManageablePage getPage(int id)
-	{
-		IManageablePage page = getRequestAdapter().getPage(id);
-		if (page != null)
-		{
-			touchPage(page);
-		}
-		return page;
-	}
-
-	@Override
-	public void removePage(final IManageablePage page) {
-		if (page != null)
-		{
-			getRequestAdapter().removePage(page);
-		}
-	}
-
-	@Override
-	public void newSessionCreated()
-	{
-		getRequestAdapter().newSessionCreated();
-	}
-
-	@Override
-	public void touchPage(IManageablePage page)
-	{
-		if (page != null)
-		{
-			getRequestAdapter().touch(page);
-		}
-	}
-
-	@Override
-	public void untouchPage(IManageablePage page)
-	{
-		if (page != null)
-		{
-			getRequestAdapter().untouch(page);
-		}
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/page/DefaultPageManagerContext.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/page/DefaultPageManagerContext.java b/wicket-core/src/main/java/org/apache/wicket/page/DefaultPageManagerContext.java
deleted file mode 100644
index 78f4b49..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/page/DefaultPageManagerContext.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.page;
-
-import java.io.Serializable;
-
-import org.apache.wicket.MetaDataKey;
-import org.apache.wicket.Session;
-import org.apache.wicket.request.cycle.RequestCycle;
-
-/**
- * Wicket's default page manager context
- * 
- * @author Juergen Donnerstag
- */
-public class DefaultPageManagerContext implements IPageManagerContext
-{
-	private static final MetaDataKey<Object> requestCycleMetaDataKey = new MetaDataKey<Object>()
-	{
-		private static final long serialVersionUID = 1L;
-	};
-
-	/**
-	 * @see org.apache.wicket.page.IPageManagerContext#bind()
-	 */
-	@Override
-	public void bind()
-	{
-		Session.get().bind();
-	}
-
-	/**
-	 * @see org.apache.wicket.page.IPageManagerContext#getRequestData()
-	 */
-	@Override
-	public Object getRequestData()
-	{
-		RequestCycle requestCycle = RequestCycle.get();
-		if (requestCycle == null)
-		{
-			throw new IllegalStateException("Not a request thread.");
-		}
-		return requestCycle.getMetaData(requestCycleMetaDataKey);
-	}
-
-	/**
-	 * @see org.apache.wicket.page.IPageManagerContext#getSessionAttribute(java.lang.String)
-	 */
-	@Override
-	public Serializable getSessionAttribute(final String key)
-	{
-		return Session.get().getAttribute(key);
-	}
-
-	/**
-	 * @see org.apache.wicket.page.IPageManagerContext#getSessionId()
-	 */
-	@Override
-	public String getSessionId()
-	{
-		return Session.get().getId();
-	}
-
-	/**
-	 * @see org.apache.wicket.page.IPageManagerContext#setRequestData(Object)
-	 */
-	@Override
-	public void setRequestData(final Object data)
-	{
-		RequestCycle requestCycle = RequestCycle.get();
-		if (requestCycle == null)
-		{
-			throw new IllegalStateException("Not a request thread.");
-		}
-		requestCycle.setMetaData(requestCycleMetaDataKey, data);
-	}
-
-	/**
-	 * @see org.apache.wicket.page.IPageManagerContext#setSessionAttribute(java.lang.String,
-	 *      java.io.Serializable)
-	 */
-	@Override
-	public void setSessionAttribute(String key, Serializable value)
-	{
-		Session.get().setAttribute(key, value);
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/page/IPageManager.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/page/IPageManager.java b/wicket-core/src/main/java/org/apache/wicket/page/IPageManager.java
index a19a0e3..9d517df 100644
--- a/wicket-core/src/main/java/org/apache/wicket/page/IPageManager.java
+++ b/wicket-core/src/main/java/org/apache/wicket/page/IPageManager.java
@@ -16,89 +16,59 @@
  */
 package org.apache.wicket.page;
 
+import org.apache.wicket.Page;
+import org.apache.wicket.pageStore.IPageStore;
 
 /**
- * Page manager.
- * 
- * @author Matej Knopp
+ * A manager of pages - facade between {@link Page}s and {@link IPageStore}s they are stored in.
  */
 public interface IPageManager
 {
-	/**
-	 * 
-	 * @return the page manager context
-	 */
-	IPageManagerContext getContext();
 
 	/**
-	 * Retrieve page instance with given id.
+	 * Get a page
 	 * 
-	 * @param id
-	 *      the id of the page to load
-	 * @return page instance or <code>null</code>
-	 * @throws CouldNotLockPageException if the page is already locked by another thread
-	 * and the lock cannot be acquired for some timeout
-	 */
-	IManageablePage getPage(int id) throws CouldNotLockPageException;
-
-	/**
-	 * Removes a page from the {@link org.apache.wicket.pageStore.IPageStore} and
-	 * {@link org.apache.wicket.pageStore.IDataStore}. Any attempt to access it later
-	 * will lead to {@link org.apache.wicket.protocol.http.PageExpiredException}
-	 *
-	 * @param page The page instance to remove from the stores
+	 * @param pageId
+	 *            id of page
+	 * @return page, may be <code>null</code>
 	 */
-	void removePage(IManageablePage page);
+	IManageablePage getPage(int pageId);
 
 	/**
-	 * Marks page as changed.
-	 * <p><strong>Note:</strong>Only stateful pages are stored.</p>
+	 * Remove a page
 	 * 
 	 * @param page
-	 *      the page that should be stored in the page stores at the end of the request.
-	 * @throws CouldNotLockPageException if the page is already locked by another thread
-	 * and the lock cannot be acquired for some timeout
-	 */
-	void touchPage(IManageablePage page);
-
-	/**
-	 * Marks page as non-changed.
-	 * Could be used in Ajax requests to avoid storing the page if no changes have happened.
-	 *
-	 * @param page
-	 *      the page that should <strong>not</strong> be stored in the page stores at the end of the request.
-	 * @throws CouldNotLockPageException if the page is already locked by another thread
-	 * and the lock cannot be acquired for some timeout
+	 *            page to remove
 	 */
-	void untouchPage(IManageablePage page);
+	void removePage(IManageablePage page);
 
 	/**
-	 * Returns whether this manager supports versioning. Managers that support versioning must store
-	 * page snapshots.
+	 * Add a page.
 	 * 
-	 * @return whether this page manager supports versioning
+	 * @param page
+	 *            page to add
 	 */
-	boolean supportsVersioning();
+	void addPage(IManageablePage page);
 
 	/**
-	 * Commits the changes to external storage if the manager uses it.
-	 * 
-	 * Should also detach all pages that were touched during this request.
+	 * Remove all pages.
 	 */
-	void commitRequest();
+	void removeAllPages();
 
 	/**
-	 * Invoked when new session has been created.
+	 * Detach at end of request.
 	 */
-	void newSessionCreated();
+	void detach();
 
 	/**
-	 * Clears all data for the current session.
+	 * Destroy when application is destroyed.
 	 */
-	void clear();
+	void destroy();
 
 	/**
-	 * Destroy the page manager.
+	 * Get the storage of pages, optional.
+	 *  
+	 * @return store or <code>null</code>
 	 */
-	void destroy();
+	IPageStore getPageStore();
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/page/IPageManagerContext.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/page/IPageManagerContext.java b/wicket-core/src/main/java/org/apache/wicket/page/IPageManagerContext.java
deleted file mode 100644
index d928606..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/page/IPageManagerContext.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.page;
-
-import java.io.Serializable;
-
-/**
- * Context object for {@link IPageManager}. This decouples the {@link IPageManager} from request
- * cycle and session.
- * 
- * @author Matej Knopp
- */
-public interface IPageManagerContext
-{
-	/**
-	 * 
-	 * @param data
-	 */
-	void setRequestData(Object data);
-
-	/**
-	 * 
-	 * @return request data
-	 */
-	Object getRequestData();
-
-	/**
-	 * 
-	 * @param key
-	 * @param value
-	 */
-	void setSessionAttribute(String key, Serializable value);
-
-	/**
-	 * 
-	 * @param key
-	 * @return The session attribute associate with the key
-	 */
-	Serializable getSessionAttribute(String key);
-
-	/**
-	 * Tells Wicket to bind the current session. This make a temporary session become persistent
-	 * across requests.
-	 */
-	void bind();
-
-	/**
-	 * 
-	 * @return session id
-	 */
-	String getSessionId();
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/page/PageAccessSynchronizer.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/page/PageAccessSynchronizer.java b/wicket-core/src/main/java/org/apache/wicket/page/PageAccessSynchronizer.java
index d44b86a..c50fb20 100644
--- a/wicket-core/src/main/java/org/apache/wicket/page/PageAccessSynchronizer.java
+++ b/wicket-core/src/main/java/org/apache/wicket/page/PageAccessSynchronizer.java
@@ -23,6 +23,7 @@ import java.util.concurrent.ConcurrentMap;
 import java.util.function.Supplier;
 
 import org.apache.wicket.Application;
+import org.apache.wicket.pageStore.IPageStore;
 import org.apache.wicket.settings.ExceptionSettings.ThreadDumpStrategy;
 import org.apache.wicket.util.LazyInitializer;
 import org.apache.wicket.util.lang.Threads;
@@ -230,12 +231,12 @@ public class PageAccessSynchronizer implements Serializable
 	/**
 	 * Wraps a page manager with this synchronizer
 	 * 
-	 * @param pagemanager
+	 * @param manager
 	 * @return wrapped page manager
 	 */
-	public IPageManager adapt(IPageManager pagemanager)
+	public IPageManager adapt(final IPageManager manager)
 	{
-		return new PageManagerDecorator(pagemanager)
+		return new IPageManager()
 		{
 			@Override
 			public IManageablePage getPage(int pageId)
@@ -244,7 +245,7 @@ public class PageAccessSynchronizer implements Serializable
 				try
 				{
 					lockPage(pageId);
-					page = super.getPage(pageId);
+					page = manager.getPage(pageId);
 				}
 				finally
 				{
@@ -257,13 +258,13 @@ public class PageAccessSynchronizer implements Serializable
 			}
 
 			@Override
-			public void removePage(final IManageablePage page) {
+			public void removePage(IManageablePage page)
+			{
 				if (page != null)
 				{
 					try
 					{
-						super.removePage(page);
-						untouchPage(page);
+						manager.removePage(page);
 					}
 					finally
 					{
@@ -273,24 +274,43 @@ public class PageAccessSynchronizer implements Serializable
 			}
 
 			@Override
-			public void touchPage(IManageablePage page)
+			public void addPage(IManageablePage page)
 			{
 				lockPage(page.getPageId());
-				super.touchPage(page);
+				
+				manager.addPage(page);
 			}
 
 			@Override
-			public void commitRequest()
+			public void removeAllPages()
+			{
+				manager.removeAllPages();
+			}
+			
+			@Override
+			public void detach()
 			{
 				try
 				{
-					super.commitRequest();
+					manager.detach();
 				}
 				finally
 				{
 					unlockAllPages();
 				}
 			}
+			
+			@Override
+			public IPageStore getPageStore()
+			{
+				return manager.getPageStore();
+			}
+			
+			@Override
+			public void destroy()
+			{
+				manager.destroy();
+			}
 		};
 	}
 

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/page/PageManager.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/page/PageManager.java b/wicket-core/src/main/java/org/apache/wicket/page/PageManager.java
new file mode 100644
index 0000000..ab58c41
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/page/PageManager.java
@@ -0,0 +1,88 @@
+/*
+ * 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.page;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.Session;
+import org.apache.wicket.pageStore.DefaultPageContext;
+import org.apache.wicket.pageStore.IPageContext;
+import org.apache.wicket.pageStore.IPageStore;
+import org.apache.wicket.util.lang.Args;
+
+/**
+ * A manager of pages, i.e. a mediator between an {@link Application} and an {@link IPageStore}.
+ */
+public class PageManager implements IPageManager
+{
+	private final IPageStore store;
+	
+	public PageManager(IPageStore store) {
+		this.store = Args.notNull(store, "store");
+	}
+
+	@Override
+	public IManageablePage getPage(int pageId)
+	{
+		return store.getPage(createPageContext(), pageId);
+	}
+
+	@Override
+	public void removePage(IManageablePage page)
+	{
+		store.removePage(createPageContext(), page);
+	}
+
+	@Override
+	public void addPage(IManageablePage page)
+	{
+		store.addPage(createPageContext(), page);
+	}
+
+	@Override
+	public void removeAllPages()
+	{
+		store.removeAllPages(createPageContext());
+	}
+
+	@Override
+	public void detach()
+	{
+		store.detach(createPageContext());
+	}
+
+	/**
+	 * Factory method for an {@link IPageContext}, returns a {@link DefaultPageContext} by default.
+	 * 
+	 * @return
+	 */
+	protected IPageContext createPageContext()
+	{
+		return new DefaultPageContext(Session.get());
+	}
+
+	@Override
+	public void destroy()
+	{
+		store.destroy();
+	}
+	
+	@Override
+	public IPageStore getPageStore()
+	{
+		return store;
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/page/PageManagerDecorator.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/page/PageManagerDecorator.java b/wicket-core/src/main/java/org/apache/wicket/page/PageManagerDecorator.java
deleted file mode 100644
index 8f719f8..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/page/PageManagerDecorator.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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.page;
-
-import org.apache.wicket.util.lang.Args;
-
-/**
- * Decorator for {@link IPageManager}
- * 
- * @author igor
- */
-public class PageManagerDecorator implements IPageManager
-{
-	private final IPageManager delegate;
-
-	/**
-	 * Constructor
-	 * 
-	 * @param delegate
-	 */
-	public PageManagerDecorator(IPageManager delegate)
-	{
-		Args.notNull(delegate, "delegate");
-		this.delegate = delegate;
-	}
-
-	@Override
-	public IPageManagerContext getContext()
-	{
-		return delegate.getContext();
-	}
-
-	@Override
-	public IManageablePage getPage(int id)
-	{
-		return delegate.getPage(id);
-	}
-
-	@Override
-	public void removePage(final IManageablePage page) {
-		delegate.removePage(page);
-	}
-
-	@Override
-	public void touchPage(IManageablePage page)
-	{
-		delegate.touchPage(page);
-	}
-
-	@Override
-	public void untouchPage(IManageablePage page)
-	{
-		delegate.untouchPage(page);
-	}
-
-	@Override
-	public boolean supportsVersioning()
-	{
-		return delegate.supportsVersioning();
-	}
-
-	@Override
-	public void commitRequest()
-	{
-		delegate.commitRequest();
-	}
-
-	@Override
-	public void newSessionCreated()
-	{
-		delegate.newSessionCreated();
-	}
-
-	@Override
-	public void clear()
-	{
-		delegate.clear();
-	}
-
-	@Override
-	public void destroy()
-	{
-		delegate.destroy();
-	}
-
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/page/PageStoreManager.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/page/PageStoreManager.java b/wicket-core/src/main/java/org/apache/wicket/page/PageStoreManager.java
deleted file mode 100644
index 88c1645..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/page/PageStoreManager.java
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- * 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.page;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import javax.servlet.http.HttpSessionBindingEvent;
-import javax.servlet.http.HttpSessionBindingListener;
-
-import org.apache.wicket.pageStore.IPageStore;
-
-/**
- * 
- */
-public class PageStoreManager extends AbstractPageManager
-{
-	/**
-	 * A cache that holds all registered page managers. <br/>
-	 * applicationName -> page manager
-	 */
-	private static final ConcurrentMap<String, PageStoreManager> MANAGERS = new ConcurrentHashMap<>();
-
-	private static final String ATTRIBUTE_NAME = "wicket:persistentPageManagerData";
-
-	/**
-	 * A flag indicating whether this session entry is being re-set in the Session.
-	 * <p>
-	 * Web containers intercept
-	 * {@link javax.servlet.http.HttpSession#setAttribute(String, Object)} to detect changes and
-	 * replicate the session. If the attribute has been already bound in the session then
-	 * {@link #valueUnbound(HttpSessionBindingEvent)} might get called - this flag
-	 * helps us to ignore the invocation in that case.
-	 * 
-	 * @see #valueUnbound(HttpSessionBindingEvent)
-	 */
-	private static final ThreadLocal<Boolean> STORING_TOUCHED_PAGES = new ThreadLocal<Boolean>()
-	{
-		protected Boolean initialValue()
-		{
-			return Boolean.FALSE;
-		};
-	};
-
-	private final IPageStore pageStore;
-	
-	private final String applicationName;
-	
-	/**
-	 * Construct.
-	 * 
-	 * @param applicationName
-	 * @param pageStore
-	 * @param context
-	 */
-	public PageStoreManager(final String applicationName, final IPageStore pageStore,
-		final IPageManagerContext context)
-	{
-		super(context);
-
-		this.applicationName = applicationName;
-		this.pageStore = pageStore;
-
-		if (MANAGERS.containsKey(applicationName))
-		{
-			throw new IllegalStateException(
-				"Manager for application with key '" + applicationName + "' already exists.");
-		}
-		MANAGERS.put(applicationName, this);
-	}
-
-	/**
-	 * Represents entry for single session. This is stored as session attribute and caches pages
-	 * between requests.
-	 * 
-	 * @author Matej Knopp
-	 */
-	private static class SessionEntry implements Serializable, HttpSessionBindingListener
-	{
-		private static final long serialVersionUID = 1L;
-
-		private final String applicationName;
-
-		/**
-		 * The id handed to the {@link IPageStore} to identify the session.
-		 * <p>
-		 * Note: If the container changes a session's id, this field remains unchanged on its
-		 * initial value.
-		 */
-		private final String sessionId;
-
-		private transient List<IManageablePage> sessionCache;
-		private transient List<Object> afterReadObject;
-
-
-		/**
-		 * Construct.
-		 * 
-		 * @param applicationName
-		 * @param sessionId
-		 */
-		public SessionEntry(String applicationName, String sessionId)
-		{
-			this.applicationName = applicationName;
-			this.sessionId = sessionId;
-		}
-
-		/**
-		 * 
-		 * @return page store
-		 */
-		private IPageStore getPageStore()
-		{
-			PageStoreManager manager = MANAGERS.get(applicationName);
-
-			if (manager == null)
-			{
-				return null;
-			}
-
-			return manager.pageStore;
-		}
-
-		/**
-		 * 
-		 * @param id
-		 * @return null, if not found
-		 */
-		private IManageablePage findPage(int id)
-		{
-			for (IManageablePage p : sessionCache)
-			{
-				if (p.getPageId() == id)
-				{
-					return p;
-				}
-			}
-			return null;
-		}
-
-		/**
-		 * Add the page to cached pages if page with same id is not already there
-		 * 
-		 * @param page
-		 */
-		private void addPage(IManageablePage page)
-		{
-			if (page != null)
-			{
-				if (findPage(page.getPageId()) != null)
-				{
-					return;
-				}
-
-				sessionCache.add(page);
-			}
-		}
-
-		private synchronized void removePage(IManageablePage page)
-		{
-			if (page != null)
-			{
-				sessionCache.remove(page);
-				final IPageStore pageStore = getPageStore();
-				if (pageStore != null)
-				{
-					pageStore.removePage(sessionId, page.getPageId());
-				}
-			}
-		}
-
-		/**
-		 * If the pages are stored in temporary state (after deserialization) this method convert
-		 * them to list of "real" pages
-		 */
-		private void convertAfterReadObjects()
-		{
-			if (sessionCache == null)
-			{
-				sessionCache = new ArrayList<>();
-			}
-
-			final IPageStore pageStore = getPageStore();
-			if (pageStore != null)
-			{
-				for (Object o : afterReadObject)
-				{
-					IManageablePage page = pageStore.convertToPage(o);
-					addPage(page);
-				}
-			}
-
-			afterReadObject = null;
-		}
-
-		/**
-		 * 
-		 * @param id
-		 * @return manageable page
-		 */
-		public synchronized IManageablePage getPage(int id)
-		{
-			// check if pages are in deserialized state
-			if (afterReadObject != null && afterReadObject.isEmpty() == false)
-			{
-				convertAfterReadObjects();
-			}
-
-			IManageablePage page = null;
-			// try to find page with same id
-			if (sessionCache != null)
-			{
-				page = findPage(id);
-				if (page != null)
-				{
-					return page;
-				}
-			}
-
-			// not found, ask pagestore for the page
-			final IPageStore pageStore = getPageStore();
-			if (pageStore != null)
-			{
-				page = pageStore.getPage(sessionId, id);
-			}
-			return page;
-		}
-
-		/**
-		 * set the list of pages to remember after the request
-		 * 
-		 * @param pages
-		 */
-		public synchronized void setSessionCache(final List<IManageablePage> pages)
-		{
-			sessionCache = new ArrayList<>(pages);
-			afterReadObject = null;
-		}
-
-		/**
-		 * Serializes all pages in this {@link SessionEntry}. If this is http worker thread then
-		 * there is available {@link IPageStore} which will be asked to prepare the page for
-		 * serialization (see DefaultPageStore$SerializePage). If there is no {@link IPageStore}
-		 * available (session loading/persisting in application initialization/destruction thread)
-		 * then the pages are serialized without any pre-processing
-		 * 
-		 * @param s
-		 * @throws IOException
-		 */
-		private void writeObject(final ObjectOutputStream s) throws IOException
-		{
-			s.defaultWriteObject();
-
-			// prepare for serialization and store the pages
-			List<Serializable> serializedPages = new ArrayList<Serializable>();
-			if (sessionCache != null)
-			{
-				IPageStore pageStore = getPageStore();
-				for (IManageablePage p : sessionCache)
-				{
-					Serializable preparedPage;
-					if (pageStore != null)
-					{
-						preparedPage = pageStore.prepareForSerialization(sessionId, p);
-					}
-					else
-					{
-						preparedPage = p;
-					}
-
-					if (preparedPage != null)
-					{
-						serializedPages.add(preparedPage);
-					}
-				}
-			}
-			s.writeObject(serializedPages);
-		}
-
-		/**
-		 * Deserializes the pages in this {@link SessionEntry}. If this is http worker thread then
-		 * there is available {@link IPageStore} which will be asked to restore the page from its
-		 * optimized state (see DefaultPageStore$SerializePage). If there is no {@link IPageStore}
-		 * available (session loading/persisting in application initialization/destruction thread)
-		 * then the pages are deserialized without any post-processing
-		 * 
-		 * @param s
-		 * @throws IOException
-		 * @throws ClassNotFoundException
-		 */
-		@SuppressWarnings("unchecked")
-		private void readObject(final ObjectInputStream s)
-			throws IOException, ClassNotFoundException
-		{
-			s.defaultReadObject();
-
-			afterReadObject = new ArrayList<>();
-
-			List<Serializable> l = (List<Serializable>)s.readObject();
-
-			// convert to temporary state after deserialization (will need to be processed
-			// by convertAfterReadObject before the pages can be accessed)
-			IPageStore pageStore = getPageStore();
-			for (Serializable ser : l)
-			{
-				Object page;
-				if (pageStore != null)
-				{
-					page = pageStore.restoreAfterSerialization(ser);
-				}
-				else
-				{
-					page = ser;
-				}
-				afterReadObject.add(page);
-			}
-		}
-
-		@Override
-		public void valueBound(HttpSessionBindingEvent event)
-		{
-		}
-
-		@Override
-		public void valueUnbound(HttpSessionBindingEvent event)
-		{
-			if (STORING_TOUCHED_PAGES.get())
-			{
-				// triggered by #storeTouchedPages(), so do not remove the data
-				return;
-			}
-
-			// WICKET-5164 use the original sessionId
-			IPageStore store = getPageStore();
-			// store might be null if destroyed already
-			if (store != null)
-			{
-				store.unbind(sessionId);
-			}
-		}
-
-		@Override
-		public boolean equals(Object o)
-		{
-			// see https://issues.apache.org/jira/browse/WICKET-5390
-			return false;
-		}
-	}
-
-	private String getAttributeName()
-	{
-		return ATTRIBUTE_NAME + " - " + applicationName;
-	}
-
-	/**
-	 * {@link RequestAdapter} for {@link PageStoreManager}
-	 * 
-	 * @author Matej Knopp
-	 */
-	protected class PersistentRequestAdapter extends RequestAdapter
-	{
-		/**
-		 * Construct.
-		 * 
-		 * @param context
-		 */
-		public PersistentRequestAdapter(IPageManagerContext context)
-		{
-			super(context);
-		}
-
-		@Override
-		protected IManageablePage getPage(int id)
-		{
-			IManageablePage touchedPage = findPage(id);
-			if (touchedPage != null)
-			{
-				return touchedPage;
-			}
-
-			// try to get session entry for this session
-			SessionEntry entry = getSessionEntry(false);
-
-			if (entry != null)
-			{
-				return entry.getPage(id);
-			}
-			else
-			{
-				return null;
-			}
-		}
-
-		@Override
-		protected void removePage(final IManageablePage page)
-		{
-			final SessionEntry sessionEntry = getSessionEntry(false);
-			if (sessionEntry != null)
-			{
-				sessionEntry.removePage(page);
-			}
-		}
-
-		/**
-		 * 
-		 * @param create
-		 * @return Session Entry
-		 */
-		private SessionEntry getSessionEntry(boolean create)
-		{
-			SessionEntry entry = (SessionEntry)getSessionAttribute(getAttributeName());
-			if (entry == null && create)
-			{
-				bind();
-				entry = new SessionEntry(applicationName, getSessionId());
-			}
-			return entry;
-		}
-
-		@Override
-		protected void newSessionCreated()
-		{
-			// if the session is not temporary bind a session entry to it
-			if (getSessionId() != null)
-			{
-				getSessionEntry(true);
-			}
-		}
-
-		@Override
-		protected void storeTouchedPages(final List<IManageablePage> touchedPages)
-		{
-			if (!touchedPages.isEmpty())
-			{
-				SessionEntry entry = getSessionEntry(true);
-				entry.setSessionCache(touchedPages);
-				for (IManageablePage page : touchedPages)
-				{
-					// WICKET-5103 use the same sessionId as used in
-					// SessionEntry#getPage()
-					pageStore.storePage(entry.sessionId, page);
-				}
-
-				STORING_TOUCHED_PAGES.set(true);
-				try
-				{
-					setSessionAttribute(getAttributeName(), entry);
-				}
-				finally
-				{
-					STORING_TOUCHED_PAGES.remove();
-				}
-			}
-		}
-	}
-
-	@Override
-	protected RequestAdapter newRequestAdapter(IPageManagerContext context)
-	{
-		return new PersistentRequestAdapter(context);
-	}
-
-	@Override
-	public boolean supportsVersioning()
-	{
-		return true;
-	}
-
-	@Override
-	public void clear()
-	{
-		RequestAdapter requestAdapter = getRequestAdapter();
-		String sessionEntryAttributeName = getAttributeName();
-		Serializable sessionEntry = requestAdapter.getSessionAttribute(sessionEntryAttributeName);
-		if (sessionEntry instanceof SessionEntry)
-		{
-			((SessionEntry)sessionEntry).valueUnbound(null);
-		}
-	}
-
-	@Override
-	public void destroy()
-	{
-		MANAGERS.remove(applicationName);
-		pageStore.destroy();
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/page/RequestAdapter.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/page/RequestAdapter.java b/wicket-core/src/main/java/org/apache/wicket/page/RequestAdapter.java
deleted file mode 100644
index 7b8b7b9..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/page/RequestAdapter.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * 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.page;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Request scoped helper class for {@link IPageManager}.
- * 
- * @author Matej Knopp
- */
-public abstract class RequestAdapter
-{
-	private static final Logger log = LoggerFactory.getLogger(RequestAdapter.class);
-
-	private final IPageManagerContext context;
-
-	private final List<IManageablePage> touchedPages = new ArrayList<IManageablePage>();
-
-	/**
-	 * Construct.
-	 * 
-	 * @param context
-	 *            The page manager context
-	 */
-	public RequestAdapter(final IPageManagerContext context)
-	{
-		this.context = context;
-	}
-
-	/**
-	 * Returns the page with specified id. The page is then cached by {@link RequestAdapter} during
-	 * the rest of request processing.
-	 * 
-	 * @param id
-	 * @return page instance or <code>null</code> if the page does not exist.
-	 */
-	protected abstract IManageablePage getPage(int id);
-
-	/**
-	 * Removes a page from the cache and the stores ({@link org.apache.wicket.pageStore.IPageStore} and
-	 * {@link org.apache.wicket.pageStore.IDataStore}). Any attempt to access it later
-	 * will lead to {@link org.apache.wicket.protocol.http.PageExpiredException}
-	 *
-	 * @param page The page instance to remove
-	 */
-	protected abstract void removePage(final IManageablePage page);
-
-	/**
-	 * Store the list of stateful pages.
-	 * 
-	 * @param touchedPages
-	 */
-	protected abstract void storeTouchedPages(List<IManageablePage> touchedPages);
-
-	/**
-	 * Notification on new session being created.
-	 */
-	protected abstract void newSessionCreated();
-
-	/**
-	 * Bind the session
-	 * 
-	 * @see IPageManagerContext#bind()
-	 */
-	protected void bind()
-	{
-		context.bind();
-	}
-
-	/**
-	 * @see IPageManagerContext#setSessionAttribute(String, Serializable)
-	 * 
-	 * @param key
-	 * @param value
-	 */
-	public void setSessionAttribute(String key, Serializable value)
-	{
-		context.setSessionAttribute(key, value);
-	}
-
-	/**
-	 * @see IPageManagerContext#getSessionAttribute(String)
-	 * 
-	 * @param key
-	 * @return the session attribute
-	 */
-	public Serializable getSessionAttribute(final String key)
-	{
-		return context.getSessionAttribute(key);
-	}
-
-	/**
-	 * @see IPageManagerContext#getSessionId()
-	 * 
-	 * @return session id
-	 */
-	public String getSessionId()
-	{
-		return context.getSessionId();
-	}
-
-	/**
-	 * 
-	 * @param id
-	 * @return null, if not found
-	 */
-	protected IManageablePage findPage(final int id)
-	{
-		for (IManageablePage page : touchedPages)
-		{
-			if (page.getPageId() == id)
-			{
-				return page;
-			}
-		}
-		return null;
-	}
-
-	/**
-	 * Touches a page, so it will be stored in the page stores
-	 * at the end of the request cycle
-	 *
-	 * @param page The page to mark as dirty
-	 */
-	protected void touch(final IManageablePage page)
-	{
-		if (findPage(page.getPageId()) == null)
-		{
-			touchedPages.add(page);
-		}
-	}
-
-	/**
-	 * @param page The page to unmark as dirty, so it won't be stored
-	 *                at the end of the request cycle
-	 */
-	protected void untouch(final IManageablePage page)
-	{
-		Iterator<IManageablePage> iterator = touchedPages.iterator();
-		while (iterator.hasNext())
-		{
-			IManageablePage touchedPage = iterator.next();
-			if (touchedPage.getPageId() == page.getPageId())
-			{
-				iterator.remove();
-				break;
-			}
-		}
-	}
-
-	/**
-	 * 
-	 */
-	protected void commitRequest()
-	{
-		// store pages that are not stateless
-		if (touchedPages.isEmpty() == false)
-		{
-			List<IManageablePage> statefulPages = new ArrayList<IManageablePage>(
-				touchedPages.size());
-			for (IManageablePage page : touchedPages)
-			{
-				boolean isPageStateless;
-				try
-				{
-					isPageStateless = page.isPageStateless();
-				}
-				catch (Exception x)
-				{
-					log.warn("An error occurred while checking whether a page is stateless. Assuming it is stateful.", x);
-					isPageStateless = false;
-				}
-				if (isPageStateless == false)
-				{
-					statefulPages.add(page);
-				}
-			}
-
-			if (statefulPages.isEmpty() == false)
-			{
-				storeTouchedPages(statefulPages);
-			}
-		}
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractCachingPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractCachingPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractCachingPageStore.java
deleted file mode 100644
index 86b8d82..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractCachingPageStore.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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.pageStore;
-
-import org.apache.wicket.page.IManageablePage;
-import org.apache.wicket.serialize.ISerializer;
-import org.apache.wicket.util.lang.Args;
-
-/**
- * An abstract {@link org.apache.wicket.pageStore.IPageStore} that uses
- * {@link org.apache.wicket.pageStore.SecondLevelPageCache} to cache the stored pages in memory
- *
- * @param <P>
- *          The type of the page to be stored
- */
-public abstract class AbstractCachingPageStore<P> extends AbstractPageStore
-{
-	/**
-	 * The cache implementation
-	 */
-	protected final SecondLevelPageCache<String, Integer, P> pagesCache;
-
-	/**
-	 * Constructor.
-	 *
-	 * @param pageSerializer
-	 *          The serializer that will convert pages to/from byte[]
-	 * @param dataStore
-	 *          The third level page cache
-	 * @param pagesCache
-	 *          The cache to use as a second level store
-	 */
-	protected AbstractCachingPageStore(ISerializer pageSerializer, IDataStore dataStore,
-	                                   SecondLevelPageCache<String, Integer, P> pagesCache)
-	{
-		super(pageSerializer, dataStore);
-
-		this.pagesCache = Args.notNull(pagesCache, "pagesCache");
-	}
-
-	@Override
-	public IManageablePage getPage(final String sessionId, final int pageId)
-	{
-		P fromCache = pagesCache.getPage(sessionId, pageId);
-		if (fromCache != null)
-		{
-			return convertToPage(fromCache);
-		}
-
-		byte[] data = getPageData(sessionId, pageId);
-		if (data != null)
-		{
-			return deserializePage(data);
-		}
-		return null;
-	}
-
-	@Override
-	public void removePage(final String sessionId, final int pageId)
-	{
-		pagesCache.removePage(sessionId, pageId);
-		removePageData(sessionId, pageId);
-	}
-
-	@SuppressWarnings("unchecked")
-	@Override
-	public void storePage(final String sessionId, final IManageablePage page)
-	{
-		byte[] data = serializePage(page);
-		if (data != null)
-		{
-			int pageId = page.getPageId();
-			pagesCache.storePage(sessionId, pageId,  (P) page);
-			storePageData(sessionId, pageId, data);
-		}
-	}
-
-	@Override
-	public void unbind(final String sessionId)
-	{
-		removePageData(sessionId);
-		pagesCache.removePages(sessionId);
-	}
-
-	@Override
-	public void destroy()
-	{
-		super.destroy();
-		pagesCache.destroy();
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractPageStore.java
deleted file mode 100644
index 07f97d5..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractPageStore.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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.pageStore;
-
-import java.io.Serializable;
-
-import org.apache.wicket.page.IManageablePage;
-import org.apache.wicket.serialize.ISerializer;
-import org.apache.wicket.util.lang.Args;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- *
- */
-public abstract class AbstractPageStore implements IPageStore
-{
-	private static final Logger LOG = LoggerFactory.getLogger(AbstractPageStore.class);
-
-	protected final IDataStore dataStore;
-
-	/**
-	 * The {@link org.apache.wicket.serialize.ISerializer} that will be used to convert pages from/to byte arrays
-	 */
-	protected final ISerializer pageSerializer;
-
-	protected AbstractPageStore(final ISerializer pageSerializer, final IDataStore dataStore)
-	{
-		Args.notNull(pageSerializer, "pageSerializer");
-		Args.notNull(dataStore, "dataStore");
-
-		this.pageSerializer = pageSerializer;
-		this.dataStore = dataStore;
-	}
-
-	@Override
-	public void destroy()
-	{
-		dataStore.destroy();
-	}
-
-	@Override
-	public Serializable prepareForSerialization(final String sessionId, final Serializable page)
-	{
-		if (dataStore.isReplicated())
-		{
-			return null;
-		}
-
-		return page;
-	}
-
-	@Override
-	public Object restoreAfterSerialization(final Serializable serializable)
-	{
-		return serializable;
-	}
-
-	/**
-	 * @param sessionId
-	 *          The id of the http session
-	 * @param pageId
-	 *          The id of page which serialized data should be got
-	 * @return page data
-	 * @see org.apache.wicket.pageStore.IDataStore#getData(String, int)
-	 */
-	protected byte[] getPageData(final String sessionId, final int pageId)
-	{
-		return dataStore.getData(sessionId, pageId);
-	}
-
-	/**
-	 * @param sessionId
-	 *          The id of the http session
-	 * @param pageId
-	 *          The id of page which serialized data should be removed
-	 * @see org.apache.wicket.pageStore.IDataStore#removeData(String, int)
-	 */
-	protected void removePageData(final String sessionId, final int pageId)
-	{
-		dataStore.removeData(sessionId, pageId);
-	}
-
-	/**
-	 * @param sessionId
-	 *          The id of the http session for which all data should be removed
-	 * @see org.apache.wicket.pageStore.IDataStore#removeData(String)
-	 */
-	protected void removePageData(final String sessionId)
-	{
-		dataStore.removeData(sessionId);
-	}
-
-	/**
-	 * @param sessionId
-	 *          The id of the http session
-	 * @param pageId
-	 *          The id of the page to store
-	 * @param data
-	 *          The serialized view of the page
-	 * @see org.apache.wicket.pageStore.IDataStore#storeData(String, int, byte[])
-	 */
-	protected void storePageData(final String sessionId, final int pageId, final byte[] data)
-	{
-		dataStore.storeData(sessionId, pageId, data);
-	}
-
-	/**
-	 * Serializes the passed page to byte[]
-	 *
-	 * @param page
-	 *          The page to serialize
-	 * @return the serialized view of the passed page
-	 */
-	protected byte[] serializePage(final IManageablePage page)
-	{
-		Args.notNull(page, "page");
-
-		byte[] data = pageSerializer.serialize(page);
-
-		if (data == null && LOG.isWarnEnabled())
-		{
-			LOG.warn("Page {} cannot be serialized. See previous logs for possible reasons.", page);
-		}
-		return data;
-	}
-
-	/**
-	 *
-	 * @param data
-	 *          The serialized view of the page
-	 * @return page data deserialized
-	 */
-	protected IManageablePage deserializePage(final byte[] data)
-	{
-		Args.notNull(data, "data");
-
-		return (IManageablePage) pageSerializer.deserialize(data);
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/AsynchronousDataStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/AsynchronousDataStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/AsynchronousDataStore.java
deleted file mode 100644
index 7ccd689..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/AsynchronousDataStore.java
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * 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.pageStore;
-
-import java.util.Iterator;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.wicket.util.lang.Args;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Facade for {@link IDataStore} that does the actual saving in worker thread.
- * <p>
- * Creates an {@link Entry} for each triple (sessionId, pageId, data) and puts it in
- * {@link #entries} queue if there is room. Acts as producer.<br/>
- * Later {@link PageSavingRunnable} reads in blocking manner from {@link #entries} and saves each
- * entry. Acts as consumer.
- * </p>
- * It starts only one instance of {@link PageSavingRunnable} because all we need is to make the page
- * storing asynchronous. We don't want to write concurrently in the wrapped {@link IDataStore},
- * though it may happen in the extreme case when the queue is full. These cases should be avoided.
- * 
- * @author Matej Knopp
- */
-public class AsynchronousDataStore implements IDataStore
-{
-	/** Log for reporting. */
-	private static final Logger log = LoggerFactory.getLogger(AsynchronousDataStore.class);
-
-	/**
-	 * The time to wait when adding an {@link Entry} into the entries. In millis.
-	 */
-	private static final long OFFER_WAIT = 30L;
-
-	/**
-	 * The time to wait for an entry to save with the wrapped {@link IDataStore}. In millis.
-	 */
-	private static final long POLL_WAIT = 1000L;
-
-	/**
-	 * The page saving thread.
-	 */
-	private final Thread pageSavingThread;
-
-	/**
-	 * The wrapped {@link IDataStore} that actually stores that pages
-	 */
-	private final IDataStore dataStore;
-
-	/**
-	 * The queue where the entries which have to be saved are temporary stored
-	 */
-	private final BlockingQueue<Entry> entries;
-
-	/**
-	 * A map 'sessionId:::pageId' -> {@link Entry}. Used for fast retrieval of {@link Entry}s which
-	 * are not yet stored by the wrapped {@link IDataStore}
-	 */
-	private final ConcurrentMap<String, Entry> entryMap;
-
-	/**
-	 * Construct.
-	 * 
-	 * @param dataStore
-	 *            the wrapped {@link IDataStore} that actually saved the data
-	 * @param capacity
-	 *            the capacity of the queue that delays the saving
-	 */
-	public AsynchronousDataStore(final IDataStore dataStore, final int capacity)
-	{
-		this.dataStore = dataStore;
-		entries = new LinkedBlockingQueue<>(capacity);
-		entryMap = new ConcurrentHashMap<>();
-
-		PageSavingRunnable savingRunnable = new PageSavingRunnable(dataStore, entries, entryMap);
-		pageSavingThread = new Thread(savingRunnable, "Wicket-AsyncDataStore-PageSavingThread");
-		pageSavingThread.setDaemon(true);
-		pageSavingThread.start();
-	}
-
-	@Override
-	public void destroy()
-	{
-		if (pageSavingThread.isAlive())
-		{
-			pageSavingThread.interrupt();
-			try
-			{
-				pageSavingThread.join();
-			} catch (InterruptedException e)
-			{
-				log.error(e.getMessage(), e);
-			}
-		}
-
-		dataStore.destroy();
-	}
-
-	/**
-	 * Little helper
-	 * 
-	 * @param sessionId
-	 * @param id
-	 * @return Entry
-	 */
-	private Entry getEntry(final String sessionId, final int id)
-	{
-		return entryMap.get(getKey(sessionId, id));
-	}
-
-	@Override
-	public byte[] getData(final String sessionId, final int id)
-	{
-		Entry entry = getEntry(sessionId, id);
-		if (entry != null)
-		{
-			log.debug(
-				"Returning the data of a non-stored entry with sessionId '{}' and pageId '{}'",
-				sessionId, id);
-			return entry.data;
-		}
-		byte[] data = dataStore.getData(sessionId, id);
-
-		log.debug("Returning the data of a stored entry with sessionId '{}' and pageId '{}'",
-			sessionId, id);
-
-		return data;
-	}
-
-	@Override
-	public boolean isReplicated()
-	{
-		return dataStore.isReplicated();
-	}
-
-	@Override
-	public void removeData(final String sessionId, final int id)
-	{
-		String key = getKey(sessionId, id);
-		if (key != null)
-		{
-			Entry entry = entryMap.remove(key);
-			if (entry != null)
-			{
-				entries.remove(entry);
-			}
-		}
-
-		dataStore.removeData(sessionId, id);
-	}
-
-	@Override
-	public void removeData(final String sessionId)
-	{
-		for (Iterator<Entry> itor = entries.iterator(); itor.hasNext();)
-		{
-			Entry entry = itor.next();
-			if (entry != null) // this check is not needed in JDK6
-			{
-				String entrySessionId = entry.sessionId;
-
-				if (sessionId.equals(entrySessionId))
-				{
-					entryMap.remove(getKey(entry));
-					itor.remove();
-				}
-			}
-		}
-
-		dataStore.removeData(sessionId);
-	}
-
-	/**
-	 * Save the entry in the queue if there is a room or directly pass it to the wrapped
-	 * {@link IDataStore} if there is no such
-	 * 
-	 * @see org.apache.wicket.pageStore.IDataStore#storeData(java.lang.String, int, byte[])
-	 */
-	@Override
-	public void storeData(final String sessionId, final int id, final byte[] data)
-	{
-		Entry entry = new Entry(sessionId, id, data);
-		String key = getKey(entry);
-		entryMap.put(key, entry);
-
-		try
-		{
-			boolean added = entries.offer(entry, OFFER_WAIT, TimeUnit.MILLISECONDS);
-
-			if (added == false)
-			{
-				log.debug("Storing synchronously page with id '{}' in session '{}'", id, sessionId);
-				entryMap.remove(key);
-				dataStore.storeData(sessionId, id, data);
-			}
-		}
-		catch (InterruptedException e)
-		{
-			log.error(e.getMessage(), e);
-			entryMap.remove(key);
-			dataStore.storeData(sessionId, id, data);
-		}
-	}
-
-	/**
-	 * 
-	 * @param pageId
-	 * @param sessionId
-	 * @return generated key
-	 */
-	private static String getKey(final String sessionId, final int pageId)
-	{
-		return pageId + ":::" + sessionId;
-	}
-
-	/**
-	 * 
-	 * @param entry
-	 * @return generated key
-	 */
-	private static String getKey(final Entry entry)
-	{
-		return getKey(entry.sessionId, entry.pageId);
-	}
-
-	/**
-	 * The structure used for an entry in the queue
-	 */
-	private static class Entry
-	{
-		private final String sessionId;
-		private final int pageId;
-		private final byte data[];
-
-		public Entry(final String sessionId, final int pageId, final byte data[])
-		{
-			this.sessionId = Args.notNull(sessionId, "sessionId");
-			this.pageId = pageId;
-			this.data = Args.notNull(data, "data");
-		}
-
-		@Override
-		public int hashCode()
-		{
-			final int prime = 31;
-			int result = 1;
-			result = prime * result + pageId;
-			result = prime * result + ((sessionId == null) ? 0 : sessionId.hashCode());
-			return result;
-		}
-
-		@Override
-		public boolean equals(Object obj)
-		{
-			if (this == obj)
-				return true;
-			if (obj == null)
-				return false;
-			if (getClass() != obj.getClass())
-				return false;
-			Entry other = (Entry)obj;
-			if (pageId != other.pageId)
-				return false;
-			if (sessionId == null)
-			{
-				if (other.sessionId != null)
-					return false;
-			}
-			else if (!sessionId.equals(other.sessionId))
-				return false;
-			return true;
-		}
-
-		@Override
-		public String toString()
-		{
-			return "Entry [sessionId=" + sessionId + ", pageId=" + pageId + "]";
-		}
-
-	}
-
-	/**
-	 * The thread that acts as consumer of {@link Entry}ies
-	 */
-	private static class PageSavingRunnable implements Runnable
-	{
-		private static final Logger log = LoggerFactory.getLogger(PageSavingRunnable.class);
-
-		private final BlockingQueue<Entry> entries;
-
-		private final ConcurrentMap<String, Entry> entryMap;
-
-		private final IDataStore dataStore;
-
-		private PageSavingRunnable(IDataStore dataStore, BlockingQueue<Entry> entries,
-			ConcurrentMap<String, Entry> entryMap)
-		{
-			this.dataStore = dataStore;
-			this.entries = entries;
-			this.entryMap = entryMap;
-		}
-
-		@Override
-		public void run()
-		{
-			while (!Thread.interrupted())
-			{
-				Entry entry = null;
-				try
-				{
-					entry = entries.poll(POLL_WAIT, TimeUnit.MILLISECONDS);
-				}
-				catch (InterruptedException e)
-				{
-					Thread.currentThread().interrupt();
-				}
-
-				if (entry != null)
-				{
-					log.debug("Saving asynchronously: {}...", entry);
-					dataStore.storeData(entry.sessionId, entry.pageId, entry.data);
-					entryMap.remove(getKey(entry));
-				}
-			}
-		}
-	}
-
-	@Override
-	public final boolean canBeAsynchronous()
-	{
-		// should not wrap in another AsynchronousDataStore
-		return false;
-	}
-}


[3/5] wicket git commit: WICKET-6563 new IPageStore implementation

Posted by sv...@apache.org.
http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/IPersistedPage.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/IPersistedPage.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/IPersistedPage.java
new file mode 100644
index 0000000..8a03507
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/IPersistedPage.java
@@ -0,0 +1,44 @@
+/*
+ * 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.pageStore;
+
+import java.io.Serializable;
+
+import org.apache.wicket.util.lang.Bytes;
+
+/**
+ * Information about a persisted page in an {@link IPersistentPageStore}.
+ * 
+ * @see IPersistentPageStore#getPersistentPages(String, int)
+ */
+public interface IPersistedPage extends Serializable
+{
+	/**
+	 * Id of page.
+	 */
+	int getPageId();
+
+	/**
+	 * Size of page.
+	 */
+	Bytes getPageSize();
+
+	/**
+	 * Type of page.
+	 */
+	String getPageType();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/IPersistentPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/IPersistentPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/IPersistentPageStore.java
new file mode 100644
index 0000000..b42746d
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/IPersistentPageStore.java
@@ -0,0 +1,51 @@
+/*
+ * 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.pageStore;
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.wicket.util.lang.Bytes;
+
+/**
+ * A store that can provide information about stored pages.  
+ */
+public interface IPersistentPageStore extends IPageStore
+{
+
+	/**
+	 * Get the identifier for pages stored for the given context.
+	 */
+	String getSessionIdentifier(IPageContext context);
+
+	/**
+	 * Get the identifiers for all pages stored in all contexts.
+	 */
+	Set<String> getSessionIdentifiers();
+
+	/**
+	 * Get information about all persisted pages with the given session identifier.
+	 */
+	List<IPersistedPage> getPersistentPages(String sessionIdentifier);
+
+	/**
+	 * Get total size of all stored pages.
+	 *  
+	 * @return
+	 */
+	Bytes getTotalSize();
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/InMemoryPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/InMemoryPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/InMemoryPageStore.java
new file mode 100644
index 0000000..fc11901
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/InMemoryPageStore.java
@@ -0,0 +1,352 @@
+/*
+ * 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.pageStore;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.Session;
+import org.apache.wicket.core.util.lang.WicketObjects;
+import org.apache.wicket.page.IManageablePage;
+import org.apache.wicket.util.lang.Args;
+import org.apache.wicket.util.lang.Bytes;
+
+/**
+ * A storage of pages in memory.
+ */
+public class InMemoryPageStore implements IPersistentPageStore
+{
+
+	/**
+	 * A registry of all page instances.
+	 */
+	private static final ConcurrentMap<String, InMemoryPageStore> IN_MEMORY_STORES = new ConcurrentHashMap<>();
+
+	private static final String KEY = "wicket:InMemoryPageStore";
+
+	private final Map<String, MemoryData> datas = new ConcurrentHashMap<>();
+
+	private String applicationName;
+
+	private int maxPages;
+
+	/**
+	 * @param applicationName
+	 *            {@link Application#getName()}
+	 * @param maxPages
+	 *            max pages per session
+	 */
+	public InMemoryPageStore(String applicationName, int maxPages)
+	{
+		this.applicationName = Args.notNull(applicationName, "applicationName");
+		this.maxPages = maxPages;
+
+		IN_MEMORY_STORES.put(applicationName, this);
+	}
+
+	/**
+	 * 
+	 * 
+	 * @return <code>true</code> always
+	 */
+	@Override
+	public boolean canBeAsynchronous(IPageContext context)
+	{
+		// session attribute must be added here *before* any asynchronous calls
+		// when session is no longer available
+		getSessionAttribute(context, true);
+
+		return true;
+	}
+
+	protected SessionAttribute getSessionAttribute(IPageContext context, boolean create)
+	{
+		context.bind();
+		
+		SessionAttribute attribute = context.getSessionAttribute(KEY);
+		if (attribute == null && create)
+		{
+			attribute = new SessionAttribute(applicationName, context.getSessionId());
+			context.setSessionAttribute(KEY, attribute);
+		}
+		return attribute;
+	}
+
+	@Override
+	public void destroy()
+	{
+		datas.clear();
+
+		IN_MEMORY_STORES.remove(applicationName);
+	}
+
+	@Override
+	public IManageablePage getPage(IPageContext context, int id)
+	{
+		MemoryData data = getMemoryData(context, false);
+		if (data == null)
+		{
+			return null;
+		}
+
+		return data.get(id);
+	}
+
+	@Override
+	public void removePage(IPageContext context, final IManageablePage page)
+	{
+		MemoryData data = getMemoryData(context, false);
+
+		if (data != null)
+		{
+			synchronized (data)
+			{
+				data.remove(page);
+			}
+		}
+	}
+
+	@Override
+	public void removeAllPages(IPageContext context)
+	{
+		MemoryData data = getMemoryData(context, false);
+
+		if (data != null)
+		{
+			synchronized (data)
+			{
+				data.removeAll();
+			}
+		}
+	}
+
+	@Override
+	public void addPage(IPageContext context, IManageablePage page)
+	{
+		MemoryData data = getMemoryData(context, true);
+
+		data.add(page, maxPages);
+	}
+
+	@Override
+	public String getSessionIdentifier(IPageContext context)
+	{
+		return context.getSessionId();
+	}
+
+	@Override
+	public Set<String> getSessionIdentifiers()
+	{
+		return datas.keySet();
+	}
+
+	@Override
+	public List<IPersistedPage> getPersistentPages(String sessionIdentifier)
+	{
+		MemoryData data = datas.get(sessionIdentifier);
+		if (data == null)
+		{
+			return new ArrayList<>();
+		}
+
+		synchronized (data)
+		{
+			return StreamSupport.stream(data.spliterator(), false)
+				.map(page -> new MermoryPersistedPage(page, getSize(page)))
+				.collect(Collectors.toList());
+		}
+	}
+
+	@Override
+	public Bytes getTotalSize()
+	{
+		int size = 0;
+
+		for (MemoryData data : datas.values())
+		{
+			synchronized (data)
+			{
+				for (IManageablePage page : data)
+				{
+					size += getSize(page);
+				}
+			}
+		}
+
+		return Bytes.bytes(size);
+	}
+
+	protected long getSize(IManageablePage page)
+	{
+		return WicketObjects.sizeof(page);
+	}
+
+	private MemoryData getMemoryData(IPageContext context, boolean create)
+	{
+		SessionAttribute attribute = getSessionAttribute(context, create);
+
+		if (!create)
+		{
+			if (attribute == null) {
+				return null;
+			} else {
+				return datas.get(attribute.identifier);
+			}
+		}
+
+		MemoryData data = new MemoryData();
+		MemoryData existing = datas.putIfAbsent(attribute.identifier, data);
+		return existing != null ? existing : data;
+	}
+
+	private void removeMemoryData(String identifier)
+	{
+		datas.remove(identifier);
+	}
+	
+	/**
+	 * Data kept in memory.
+	 */
+	static class MemoryData implements Iterable<IManageablePage>
+	{
+		private LinkedHashMap<Integer, IManageablePage> pages = new LinkedHashMap<>();
+
+		@Override
+		public Iterator<IManageablePage> iterator()
+		{
+			return pages.values().iterator();
+		}
+
+		public synchronized void add(IManageablePage page, int maxPages)
+		{
+			pages.remove(page.getPageId());
+			pages.put(page.getPageId(), page);
+
+			Iterator<IManageablePage> iterator = pages.values().iterator();
+			int size = pages.size();
+			while (size > maxPages)
+			{
+				iterator.next();
+
+				iterator.remove();
+				size--;
+			}
+		}
+
+		public void remove(IManageablePage page)
+		{
+			pages.remove(page.getPageId());
+		}
+
+		public void removeAll()
+		{
+			pages.clear();
+		}
+
+		public IManageablePage get(int id)
+		{
+			IManageablePage page = pages.get(id);
+
+			return page;
+		}
+	}
+
+	/**
+	 * Attribute held in session.
+	 */
+	static class SessionAttribute implements Serializable, HttpSessionBindingListener
+	{
+
+		private final String applicationName;
+
+		/**
+		 * The identifier of the session, must not be equal to {@link Session#getId()}, e.g. when
+		 * the container changes the id after authorization.
+		 */
+		public final String identifier;
+
+		public SessionAttribute(String applicationName, String sessionIdentifier)
+		{
+			this.applicationName = Args.notNull(applicationName, "applicationName");
+			this.identifier = Args.notNull(sessionIdentifier, "sessionIdentifier");
+		}
+
+
+		@Override
+		public void valueBound(HttpSessionBindingEvent event)
+		{
+		}
+
+		@Override
+		public void valueUnbound(HttpSessionBindingEvent event)
+		{
+			InMemoryPageStore store = IN_MEMORY_STORES.get(applicationName);
+			if (store != null)
+			{
+				store.removeMemoryData(identifier);
+			}
+		}
+	}
+	
+	private static class MermoryPersistedPage implements IPersistedPage
+	{
+
+		private final int id;
+
+		private final String type;
+
+		private final long size;
+
+		public MermoryPersistedPage(IManageablePage page, long size)
+		{
+			this.id = page.getPageId();
+			this.type = page.getClass().getName();
+			this.size = size;
+		}
+
+		@Override
+		public int getPageId()
+		{
+			return id;
+		}
+
+		@Override
+		public String getPageType()
+		{
+			return type;
+		}
+
+		@Override
+		public Bytes getPageSize()
+		{
+			return Bytes.bytes(size);
+		}
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/InSessionPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/InSessionPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/InSessionPageStore.java
new file mode 100644
index 0000000..3914464
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/InSessionPageStore.java
@@ -0,0 +1,268 @@
+/*
+ * 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.pageStore;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+
+import javax.servlet.http.HttpSession;
+
+import org.apache.wicket.MetaDataKey;
+import org.apache.wicket.Session;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.page.IManageablePage;
+import org.apache.wicket.serialize.ISerializer;
+import org.apache.wicket.util.lang.Args;
+
+/**
+ * A store keeping a configurable maximum of pages in the session, delegating all or excessive pages
+ * to another store.
+ */
+public class InSessionPageStore extends DelegatingPageStore
+{
+
+	private static final MetaDataKey<SessionData> KEY = new MetaDataKey<SessionData>()
+	{
+		private static final long serialVersionUID = 1L;
+	};
+
+	private ISerializer serializer;
+
+	private int maxPages;
+
+	private boolean delegateAll = false;
+
+	/**
+	 * Use this constructor, if sessions are never serialized.
+	 * 
+	 * @param delegate
+	 *            store to delegate to
+	 * @param maxPages
+	 *            maximum pages to keep in session
+	 */
+	public InSessionPageStore(IPageStore delegate, int maxPages)
+	{
+		this(delegate, new ISerializer()
+		{
+			@Override
+			public byte[] serialize(Object object)
+			{
+				throw new WicketRuntimeException("InSessionPageStore not configured for serialization");
+			}
+			
+			@Override
+			public Object deserialize(byte[] data)
+			{
+				throw new WicketRuntimeException("InSessionPageStore not configured for serialization");
+			}
+		},  maxPages);
+	}
+	
+	/**
+	 * @param delegate
+	 *            store to delegate to
+	 * @param serializer
+	 *            serializer to use if session gets persisted
+	 * @param maxPages
+	 *            maximum pages to keep in session
+	 */
+	public InSessionPageStore(IPageStore delegate, ISerializer serializer, int maxPages)
+	{
+		super(delegate);
+
+		this.serializer = Args.notNull(serializer, "serializer");
+
+		this.maxPages = maxPages;
+	}
+
+	/**
+	 * Delegated all pages, even those that are still kept in the session.
+	 */
+	public InSessionPageStore delegateAll()
+	{
+		delegateAll = true;
+
+		return this;
+	}
+
+	@Override
+	public IManageablePage getPage(IPageContext context, int id)
+	{
+		IManageablePage page = getSessionData(context).get(id);
+		if (page != null)
+		{
+			return page;
+		}
+
+		return super.getPage(context, id);
+	}
+
+	@Override
+	public void addPage(IPageContext context, IManageablePage page)
+	{
+		SessionData data = getSessionData(context);
+
+		data.addAndDelegate(context, page, maxPages, delegateAll, getDelegate());
+	}
+
+	@Override
+	public void removePage(IPageContext context, IManageablePage page)
+	{
+		getSessionData(context).remove(page);
+
+		super.removePage(context, page);
+	}
+
+	@Override
+	public void removeAllPages(IPageContext context)
+	{
+		getSessionData(context).removeAll();
+
+		super.removeAllPages(context);
+	}
+
+	private SessionData getSessionData(IPageContext context)
+	{
+		SessionData data = context.getSessionData(KEY);
+		if (data == null)
+		{
+			context.bind();
+			data = new SessionData();
+
+			context.setSessionData(KEY, data);
+		}
+
+		// data might be deserialized so initialize again
+		data.init(serializer);
+
+		return data;
+	}
+
+	/**
+	 * Data kept in the {@link Session}, might get serialized along with its containing
+	 * {@link HttpSession}.
+	 */
+	static class SessionData implements Serializable
+	{
+
+		transient ISerializer serializer;
+
+		/**
+		 * Pages, may partly be serialized.
+		 */
+		private LinkedHashMap<Integer, Serializable> pages = new LinkedHashMap<>();
+
+		/**
+		 * This method <em>must</em> be called each time it is retrieved from the session: <br/>
+		 * After deserializing from persisted session the serializer is no longer referenced and all
+		 * contained pages are in a serialized state.
+		 */
+		public void init(ISerializer serializer)
+		{
+			this.serializer = Args.notNull(serializer, "serializer");
+		}
+
+		public synchronized void addAndDelegate(IPageContext context, IManageablePage page,
+			int maxPages, boolean delegateAll, IPageStore delegate)
+		{
+			pages.remove(page.getPageId());
+			pages.put(page.getPageId(), page);
+
+			Serializable expelled = null;
+			if (pages.size() > maxPages)
+			{
+				Iterator<Serializable> iterator = pages.values().iterator();
+				expelled = iterator.next();
+				iterator.remove();
+			}
+
+			if (delegateAll)
+			{
+				delegate.addPage(context, page);
+			}
+			else
+			{
+				// when pages are not delegated automatically, we have to catch up
+				// on an expelled page now
+				if (expelled != null)
+				{
+					if (expelled instanceof byte[])
+					{
+						// ... which results in this suboptimal case, when the session was persisted:
+						// in that case the expelled page is still in a serialized state, so we have
+						// to deserialize it first to be able to delegate it 
+						expelled = (IManageablePage)serializer.deserialize((byte[])expelled);
+					}
+					delegate.addPage(context, (IManageablePage)expelled);
+				}
+			}
+		}
+
+		public synchronized void remove(IManageablePage page)
+		{
+			pages.remove(page.getPageId());
+		}
+
+		public synchronized void removeAll()
+		{
+			pages.clear();
+		}
+
+		public synchronized IManageablePage get(int id)
+		{
+			Serializable serializable = pages.get(id);
+
+			if (serializable instanceof byte[])
+			{
+				if (serializer == null)
+				{
+					throw new IllegalStateException("SessionData#init() was not called");
+				}
+				serializable = (Serializable)serializer.deserialize((byte[])serializable);
+
+				pages.put(id, serializable);
+			}
+
+			return (IManageablePage)serializable;
+		}
+
+		/**
+		 * Serialize pages before writing to output.
+		 */
+		private void writeObject(final ObjectOutputStream output) throws IOException
+		{
+			// serialize pages if not already
+			for (Entry<Integer, Serializable> entry : pages.entrySet())
+			{
+				if (entry.getValue() instanceof IManageablePage)
+				{
+					if (serializer == null)
+					{
+						throw new IllegalStateException("SessionData#init() was not called");
+					}
+					entry.setValue(serializer.serialize(entry.getValue()));
+				}
+			}
+
+			output.defaultWriteObject();
+		}
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/NoopPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/NoopPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/NoopPageStore.java
new file mode 100644
index 0000000..846c858
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/NoopPageStore.java
@@ -0,0 +1,53 @@
+/*
+ * 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.pageStore;
+
+import org.apache.wicket.page.IManageablePage;
+
+/**
+ * A non-storage of pages.
+ */
+public class NoopPageStore implements IPageStore
+{
+
+	@Override
+	public boolean canBeAsynchronous(IPageContext context)
+	{
+		return true;
+	}
+	
+	@Override
+	public void addPage(IPageContext context, IManageablePage page)
+	{
+	}
+
+	@Override
+	public void removePage(IPageContext context, IManageablePage page)
+	{
+	}
+
+	@Override
+	public void removeAllPages(IPageContext context)
+	{
+	}
+
+	@Override
+	public IManageablePage getPage(IPageContext context, int id)
+	{
+		return null;
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/PageWindowManager.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/PageWindowManager.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/PageWindowManager.java
deleted file mode 100644
index f194710..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/PageWindowManager.java
+++ /dev/null
@@ -1,506 +0,0 @@
-/*
- * 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.pageStore;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.wicket.util.collections.IntHashMap;
-
-/**
- * Manages positions and size of serialized pages in the pagemap file.
- * <p>
- * The pages are stored inside the file in a cyclic way. Newer pages are placed after older ones,
- * until the maximum file size is reached. After that, the next page is stored in the beginning of
- * the file.
- * 
- * @author Matej Knopp
- */
-public class PageWindowManager implements Serializable
-{
-	private static final long serialVersionUID = 1L;
-
-	/**
-	 * Contains information about a page inside the file.
-	 * 
-	 * @author Matej Knopp
-	 */
-	private static class PageWindowInternal implements Serializable
-	{
-		private static final long serialVersionUID = 1L;
-
-		/** id of page or -1 if the window is empty */
-		private int pageId;
-
-		/** offset in the file where the serialized page data begins */
-		private int filePartOffset;
-
-		/** size of serialized page data */
-		private int filePartSize;
-	}
-
-	/** list of PageWindowInternal objects */
-	private final List<PageWindowInternal> windows = new ArrayList<PageWindowInternal>();
-
-	/**
-	 * map from page id to list of pagewindow indices (referring to the windows list) - to improve
-	 * searching speed the index must be cleaned when the instances in the windows list change their
-	 * indexes (e.g. items are shifted on page window removal)
-	 */
-	private IntHashMap<Integer> idToWindowIndex = null;
-
-	/**
-	 * Inversed index of #idToWindowIndex
-	 */
-	private IntHashMap<Integer> windowIndexToPageId = null;
-
-	/** index of last added page */
-	private int indexPointer = -1;
-
-	private int totalSize = 0;
-
-	/**
-	 * Maximum page size. After this size is exceeded, the pages will be saved starting at the
-	 * beginning of file.
-	 */
-	private final long maxSize;
-
-	/**
-	 * 
-	 * @param pageId
-	 * @param windowIndex
-	 */
-	private void putWindowIndex(int pageId, int windowIndex)
-	{
-		if (idToWindowIndex != null && pageId != -1 && windowIndex != -1)
-		{
-			Integer oldPageId = windowIndexToPageId.remove(windowIndex);
-			if (oldPageId != null)
-			{
-				idToWindowIndex.remove(oldPageId);
-			}
-			idToWindowIndex.put(pageId, windowIndex);
-			windowIndexToPageId.put(windowIndex, pageId);
-		}
-	}
-
-	/**
-	 * 
-	 * @param pageId
-	 */
-	private void removeWindowIndex(int pageId)
-	{
-		Integer windowIndex = idToWindowIndex.remove(pageId);
-		if (windowIndex != null)
-		{
-			windowIndexToPageId.remove(windowIndex);
-		}
-	}
-
-	/**
-	 * 
-	 */
-	private void rebuildIndices()
-	{
-		idToWindowIndex = null;
-		idToWindowIndex = new IntHashMap<Integer>();
-		windowIndexToPageId = null;
-		windowIndexToPageId = new IntHashMap<Integer>();
-		for (int i = 0; i < windows.size(); ++i)
-		{
-			PageWindowInternal window = windows.get(i);
-			putWindowIndex(window.pageId, i);
-		}
-	}
-
-	/**
-	 * Returns the index of the given page in the {@link #windows} list.
-	 * 
-	 * @param pageId
-	 * @return window index
-	 */
-	private int getWindowIndex(int pageId)
-	{
-		if (idToWindowIndex == null)
-		{
-			rebuildIndices();
-		}
-
-		Integer result = idToWindowIndex.get(pageId);
-		return result != null ? result : -1;
-	}
-
-	/**
-	 * Increments the {@link #indexPointer}. If the maximum file size has been reached, the
-	 * {@link #indexPointer} is set to 0.
-	 * 
-	 * @return new index pointer
-	 */
-	private int incrementIndexPointer()
-	{
-		if ((maxSize > 0) && (totalSize >= maxSize) && (indexPointer == windows.size() - 1))
-		{
-			indexPointer = 0;
-		}
-		else
-		{
-			++indexPointer;
-		}
-		return indexPointer;
-	}
-
-	/**
-	 * Returns the offset in file of the window on given index. The offset is counted by getting the
-	 * previous page offset and adding the previous page size to it.
-	 * 
-	 * @param index
-	 * @return window file offset
-	 */
-	private int getWindowFileOffset(int index)
-	{
-		if (index > 0)
-		{
-			PageWindowInternal window = windows.get(index - 1);
-			return window.filePartOffset + window.filePartSize;
-		}
-		return 0;
-	}
-
-	/**
-	 * Splits the window with given index to two windows. First of those will have size specified by
-	 * the argument, the other one will fill up the rest of the original window.
-	 * 
-	 * @param index
-	 * @param size
-	 */
-	private void splitWindow(int index, int size)
-	{
-		PageWindowInternal window = windows.get(index);
-		int delta = window.filePartSize - size;
-
-		if (index == windows.size() - 1)
-		{
-			// if this is last window
-			totalSize -= delta;
-			window.filePartSize = size;
-		}
-		else if (window.filePartSize != size)
-		{
-			PageWindowInternal newWindow = new PageWindowInternal();
-			newWindow.pageId = -1;
-			window.filePartSize = size;
-
-			windows.add(index + 1, newWindow);
-
-			newWindow.filePartOffset = getWindowFileOffset(index + 1);
-			newWindow.filePartSize = delta;
-		}
-
-		idToWindowIndex = null;
-		windowIndexToPageId = null;
-	}
-
-	/**
-	 * Merges the window with given index with the next window. The resulting window will have size
-	 * of the two windows summed together.
-	 * 
-	 * @param index
-	 */
-	private void mergeWindowWithNext(int index)
-	{
-		if (index < windows.size() - 1)
-		{
-			PageWindowInternal window = windows.get(index);
-			PageWindowInternal next = windows.get(index + 1);
-			window.filePartSize += next.filePartSize;
-
-			windows.remove(index + 1);
-			idToWindowIndex = null; // reset index
-			windowIndexToPageId = null;
-		}
-	}
-
-	/**
-	 * Adjusts the window on given index to the specified size. If the new size is smaller than the
-	 * window size, the window will be split. Otherwise the window will be merged with as many
-	 * subsequent window as necessary. In case the window is last window in the file, the size will
-	 * be adjusted without splitting or merging.
-	 * 
-	 * @param index
-	 * @param size
-	 */
-	private void adjustWindowSize(int index, int size)
-	{
-		PageWindowInternal window = windows.get(index);
-
-		// last window, just adjust size
-		if (index == windows.size() - 1)
-		{
-			int delta = size - window.filePartSize;
-			totalSize += delta;
-			window.filePartSize = size;
-		}
-		else
-		{
-			// merge as many times as necessary
-			while (window.filePartSize < size && index < windows.size() - 1)
-			{
-				mergeWindowWithNext(index);
-			}
-
-			// done merging - do we have enough room ?
-			if (window.filePartSize < size)
-			{
-				// no, this is the last window
-				int delta = size - window.filePartSize;
-				totalSize += delta;
-				window.filePartSize = size;
-			}
-			else
-			{
-				// yes, we might want to split the window, so that we don't lose
-				// space when the created window was too big
-				splitWindow(index, size);
-			}
-		}
-
-		window.pageId = -1;
-	}
-
-	/**
-	 * Allocates window on given index with to size. If the index is pointing to existing window,
-	 * the window size will be adjusted. Otherwise a new window with appropriated size will be
-	 * created.
-	 * 
-	 * @param index
-	 * @param size
-	 * @return page window
-	 */
-	private PageWindowInternal allocatePageWindow(int index, int size)
-	{
-		final PageWindowInternal window;
-
-		// new window
-		if (index == windows.size())
-		{
-			// new page window
-			window = new PageWindowInternal();
-			window.filePartOffset = getWindowFileOffset(index);
-			totalSize += size;
-			window.filePartSize = size;
-			windows.add(window);
-		}
-		else
-		{
-			// get the window
-			window = windows.get(index);
-
-			// adjust if necessary
-			if (window.filePartSize != size)
-			{
-				adjustWindowSize(index, size);
-			}
-		}
-
-		return window;
-	}
-
-	/**
-	 * Public (read only) version of page window.
-	 * 
-	 * @author Matej Knopp
-	 */
-	public static class PageWindow
-	{
-		private final PageWindowInternal pageWindowInternal;
-
-		/**
-		 * Construct.
-		 * 
-		 * @param pageWindowInternal
-		 */
-		private PageWindow(PageWindowInternal pageWindowInternal)
-		{
-			this.pageWindowInternal = pageWindowInternal;
-		}
-
-		/**
-		 * @return page Id
-		 */
-		public int getPageId()
-		{
-			return pageWindowInternal.pageId;
-		}
-
-		/**
-		 * @return offset in the pagemap file where the serialized page data starts
-		 */
-		public int getFilePartOffset()
-		{
-			return pageWindowInternal.filePartOffset;
-		}
-
-		/**
-		 * @return size of the serialized page data
-		 */
-		public int getFilePartSize()
-		{
-			return pageWindowInternal.filePartSize;
-		}
-	}
-
-	/**
-	 * Creates and returns a new page window for given page.
-	 * 
-	 * @param pageId
-	 * @param size
-	 * @return page window
-	 */
-	public synchronized PageWindow createPageWindow(int pageId, int size)
-	{
-		int index = getWindowIndex(pageId);
-
-		// if we found the page window, mark it as invalid
-		if (index != -1)
-		{
-			removeWindowIndex(pageId);
-			(windows.get(index)).pageId = -1;
-		}
-
-		// if we are not going to reuse a page window (because it's not on
-		// indexPointer position or because we didn't find it), increment the
-		// indexPointer
-		if (index == -1 || index != indexPointer)
-		{
-			index = incrementIndexPointer();
-		}
-
-		PageWindowInternal window = allocatePageWindow(index, size);
-		window.pageId = pageId;
-
-		putWindowIndex(pageId, index);
-		return new PageWindow(window);
-	}
-
-	/**
-	 * Returns the page window for given page or null if no window was found.
-	 * 
-	 * @param pageId
-	 * @return page window or null
-	 */
-	public synchronized PageWindow getPageWindow(int pageId)
-	{
-		int index = getWindowIndex(pageId);
-		if (index != -1)
-		{
-			return new PageWindow(windows.get(index));
-		}
-		return null;
-	}
-
-	/**
-	 * Removes the page window for given page.
-	 * 
-	 * @param pageId
-	 */
-	public synchronized void removePage(int pageId)
-	{
-		int index = getWindowIndex(pageId);
-		if (index != -1)
-		{
-			PageWindowInternal window = windows.get(index);
-			removeWindowIndex(pageId);
-			if (index == windows.size() - 1)
-			{
-				windows.remove(index);
-				totalSize -= window.filePartSize;
-				if (indexPointer == index)
-				{
-					--indexPointer;
-				}
-			}
-			else
-			{
-				window.pageId = -1;
-			}
-		}
-	}
-
-	/**
-	 * Returns last n saved page windows.
-	 * 
-	 * @param count
-	 * @return list of page windows
-	 */
-	public synchronized List<PageWindow> getLastPageWindows(int count)
-	{
-		List<PageWindow> result = new ArrayList<PageWindow>();
-
-		// start from current index to 0
-		int currentIndex = indexPointer;
-
-		do
-		{
-			if (currentIndex == -1)
-			{
-				break;
-			}
-
-			if (currentIndex < windows.size())
-			{
-				PageWindowInternal window = windows.get(currentIndex);
-				if (window.pageId != -1)
-				{
-					result.add(new PageWindow(window));
-				}
-			}
-
-			--currentIndex;
-			if (currentIndex == -1)
-			{
-				// rewind to the last entry and collect all entries until current index
-				currentIndex = windows.size() - 1;
-			}
-		}
-		while (result.size() < count && currentIndex != indexPointer);
-
-		return result;
-	}
-
-	/**
-	 * Creates a new PageWindowManager.
-	 * 
-	 * @param maxSize
-	 *            maximum page size. After this size is exceeded, the pages will be saved starting
-	 *            at the beginning of file
-	 */
-	public PageWindowManager(long maxSize)
-	{
-		this.maxSize = maxSize;
-	}
-
-	/**
-	 * Returns the size of all saved pages
-	 * 
-	 * @return total size
-	 */
-	public synchronized int getTotalSize()
-	{
-		return totalSize;
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/PerSessionPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/PerSessionPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/PerSessionPageStore.java
deleted file mode 100644
index 4a21f4c..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/PerSessionPageStore.java
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * 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.pageStore;
-
-import java.lang.ref.SoftReference;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ConcurrentSkipListMap;
-
-import org.apache.wicket.page.IManageablePage;
-import org.apache.wicket.serialize.ISerializer;
-import org.apache.wicket.util.lang.Args;
-
-/**
- * A page store that uses a SecondLevelPageCache with the last N used page instances
- * per session.
- *
- * <strong>Note</strong>: the size of the cache depends on the {@code cacheSize} constructor
- * parameter multiplied by the number of the active http sessions.
- *
- * It depends on the application use cases but usually a reasonable value of
- * {@code cacheSize} would be just a few pages (2-3). If the application don't expect many
- * active http sessions and the work flow involves usage of the browser/application history
- * then the {@code cacheSize} value may be increased to a bigger value.
- */
-public class PerSessionPageStore extends AbstractCachingPageStore<IManageablePage>
-{
-	/**
-	 * Constructor.
-	 *
-	 * @param pageSerializer
-	 *            the {@link org.apache.wicket.serialize.ISerializer} that will be used to convert pages from/to byte arrays
-	 * @param dataStore
-	 *            the {@link org.apache.wicket.pageStore.IDataStore} that actually stores the pages
-	 * @param cacheSize
-	 *            the number of pages to cache in memory before passing them to
-	 *            {@link org.apache.wicket.pageStore.IDataStore#storeData(String, int, byte[])}
-	 */
-	public PerSessionPageStore(final ISerializer pageSerializer, final IDataStore dataStore,
-	                           final int cacheSize)
-	{
-		super(pageSerializer, dataStore, new PagesCache(cacheSize));
-	}
-
-	@Override
-	public IManageablePage convertToPage(final Object object)
-	{
-		if (object == null)
-		{
-			return null;
-		}
-		else if (object instanceof IManageablePage)
-		{
-			return (IManageablePage)object;
-		}
-
-		String type = object.getClass().getName();
-		throw new IllegalArgumentException("Unknown object type: " + type);
-	}
-
-	/**
-	 * An implementation of SecondLevelPageCache that stores the last used N live page instances
-	 * per http session.
-	 */
-	protected static class PagesCache implements SecondLevelPageCache<String, Integer, IManageablePage>
-	{
-		/**
-		 * Helper class used to compare the page entries in the cache by their
-		 * access time
-		 */
-		private static class PageValue
-		{
-			/**
-			 * The id of the cached page
-			 */
-			private final int pageId;
-
-			/**
-			 * The last time this page has been used/accessed.
-			 */
-			private long accessTime;
-
-			private PageValue(IManageablePage page)
-			{
-				this(page.getPageId());
-			}
-
-			private PageValue(int pageId)
-			{
-				this.pageId = pageId;
-				touch();
-			}
-
-			/**
-			 * Updates the access time with the current time
-			 */
-			private void touch()
-			{
-				accessTime = System.nanoTime();
-			}
-
-			@Override
-			public boolean equals(Object o)
-			{
-				if (this == o) return true;
-				if (o == null || getClass() != o.getClass()) return false;
-
-				PageValue pageValue = (PageValue) o;
-
-				return pageId == pageValue.pageId;
-			}
-
-			@Override
-			public int hashCode()
-			{
-				return pageId;
-			}
-		}
-
-		private static class PageComparator implements Comparator<PageValue>
-		{
-			@Override
-			public int compare(PageValue p1, PageValue p2)
-			{
-				return Long.compare(p1.accessTime, p2.accessTime);
-			}
-		}
-
-		private final int maxEntriesPerSession;
-
-		private final ConcurrentMap<String, SoftReference<ConcurrentSkipListMap<PageValue, IManageablePage>>> cache;
-
-		/**
-		 * Constructor.
-		 *
-		 * @param maxEntriesPerSession
-		 *          The number of cache entries per session
-		 */
-		public PagesCache(final int maxEntriesPerSession)
-		{
-			this.maxEntriesPerSession = maxEntriesPerSession;
-			cache = new ConcurrentHashMap<>();
-		}
-
-		/**
-		 *
-		 * @param sessionId
-		 *          The id of the http session
-		 * @param pageId
-		 *          The id of the page to remove from the cache
-		 * @return the removed {@link org.apache.wicket.page.IManageablePage} or <code>null</code> - otherwise
-		 */
-		@Override
-		public IManageablePage removePage(final String sessionId, final Integer pageId)
-		{
-			IManageablePage result = null;
-
-			if (maxEntriesPerSession > 0)
-			{
-				Args.notNull(sessionId, "sessionId");
-				Args.notNull(pageId, "pageId");
-
-				SoftReference<ConcurrentSkipListMap<PageValue, IManageablePage>> pagesPerSession = cache.get(sessionId);
-				if (pagesPerSession != null)
-				{
-					ConcurrentMap<PageValue, IManageablePage> pages = pagesPerSession.get();
-					if (pages != null)
-					{
-						PageValue sample = new PageValue(pageId);
-						Iterator<Map.Entry<PageValue, IManageablePage>> iterator = pages.entrySet().iterator();
-						while (iterator.hasNext())
-						{
-							Map.Entry<PageValue, IManageablePage> entry = iterator.next();
-							if (sample.equals(entry.getKey()))
-							{
-								result = entry.getValue();
-								iterator.remove();
-								break;
-							}
-						}
-					}
-				}
-			}
-
-			return result;
-		}
-
-		/**
-		 * Removes all {@link org.apache.wicket.page.IManageablePage}s for the session
-		 * with <code>sessionId</code> from the cache.
-		 *
-		 * @param sessionId
-		 *          The id of the expired http session
-		 */
-		@Override
-		public void removePages(String sessionId)
-		{
-			Args.notNull(sessionId, "sessionId");
-
-			if (maxEntriesPerSession > 0)
-			{
-				cache.remove(sessionId);
-			}
-		}
-
-		/**
-		 * Returns a {@link org.apache.wicket.page.IManageablePage} by looking it up by <code>sessionId</code> and
-		 * <code>pageId</code>. If there is a match then it is <i>touched</i>, i.e. it is moved at
-		 * the top of the cache.
-		 * 
-		 * @param sessionId
-		 *          The id of the http session
-		 * @param pageId
-		 *          The id of the page to find
-		 * @return the found serialized page or <code>null</code> when not found
-		 */
-		@Override
-		public IManageablePage getPage(String sessionId, Integer pageId)
-		{
-			IManageablePage result = null;
-
-			if (maxEntriesPerSession > 0)
-			{
-				Args.notNull(sessionId, "sessionId");
-				Args.notNull(pageId, "pageId");
-
-				SoftReference<ConcurrentSkipListMap<PageValue, IManageablePage>> pagesPerSession = cache.get(sessionId);
-				if (pagesPerSession != null)
-				{
-					ConcurrentSkipListMap<PageValue, IManageablePage> pages = pagesPerSession.get();
-					if (pages != null)
-					{
-						PageValue sample = new PageValue(pageId);
-						for (Map.Entry<PageValue, IManageablePage> entry : pages.entrySet())
-						{
-							if (sample.equals(entry.getKey()))
-							{
-								// touch the entry
-								entry.getKey().touch();
-								result = entry.getValue();
-								break;
-							}
-						}
-					}
-				}
-			}
-			return result;
-		}
-
-		/**
-		 * Store the serialized page in cache
-		 * 
-		 * @param page
-		 *      the data to serialize (page id, session id, bytes)
-		 */
-		@Override
-		public void storePage(String sessionId, Integer pageId, IManageablePage page)
-		{
-			if (maxEntriesPerSession > 0)
-			{
-				Args.notNull(sessionId, "sessionId");
-				Args.notNull(pageId, "pageId");
-
-				SoftReference<ConcurrentSkipListMap<PageValue, IManageablePage>> pagesPerSession = cache.get(sessionId);
-				if (pagesPerSession == null)
-				{
-					ConcurrentSkipListMap<PageValue, IManageablePage> pages = new ConcurrentSkipListMap<>(new PageComparator());
-					pagesPerSession = new SoftReference<>(pages);
-					SoftReference<ConcurrentSkipListMap<PageValue, IManageablePage>> old = cache.putIfAbsent(sessionId, pagesPerSession);
-					if (old != null)
-					{
-						pagesPerSession = old;
-					}
-				}
-
-				ConcurrentSkipListMap<PageValue, IManageablePage> pages = pagesPerSession.get();
-				if (pages == null)
-				{
-					pages = new ConcurrentSkipListMap<>();
-					pagesPerSession = new SoftReference<>(pages);
-					SoftReference<ConcurrentSkipListMap<PageValue, IManageablePage>> old = cache.putIfAbsent(sessionId, pagesPerSession);
-					if (old != null)
-					{
-						pages = old.get();
-					}
-				}
-
-				if (pages != null)
-				{
-					removePage(sessionId, pageId);
-
-					PageValue pv = new PageValue(page);
-					pages.put(pv, page);
-
-					while (pages.size() > maxEntriesPerSession)
-					{
-						pages.pollFirstEntry();
-					}
-				}
-			}
-		}
-
-		@Override
-		public void destroy()
-		{
-			cache.clear();
-		}
-	}
-
-	@Override
-	public boolean canBeAsynchronous()
-	{
-		return false; // NOTE: not analyzed neither tested yet, this page store being wrapped by asynchronous one
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/RequestPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/RequestPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/RequestPageStore.java
new file mode 100644
index 0000000..2420768
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/RequestPageStore.java
@@ -0,0 +1,159 @@
+/*
+ * 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.pageStore;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.wicket.MetaDataKey;
+import org.apache.wicket.page.IManageablePage;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Buffer pages till the end of the request, when they are delegated to another store in
+ * the reverse order they where accessed.
+ */
+public class RequestPageStore extends DelegatingPageStore
+{
+
+	private static final Logger log = LoggerFactory.getLogger(RequestPageStore.class);
+
+	private static final MetaDataKey<RequestData> KEY = new MetaDataKey<>()
+	{
+		private static final long serialVersionUID = 1L;
+	};
+
+	public RequestPageStore(IPageStore delegate)
+	{
+		super(delegate);
+	}
+
+	@Override
+	public IManageablePage getPage(IPageContext context, int id)
+	{
+		IManageablePage page = getRequestData(context).get(id);
+		if (page != null)
+		{
+			return page;
+		}
+
+		return super.getPage(context, id);
+	}
+
+	@Override
+	public void addPage(IPageContext context, IManageablePage page)
+	{
+		getRequestData(context).add(page);
+	}
+
+	@Override
+	public void removePage(IPageContext context, IManageablePage page)
+	{
+		getRequestData(context).remove(page);
+
+		super.removePage(context, page);
+	}
+
+	@Override
+	public void removeAllPages(IPageContext context)
+	{
+		getRequestData(context).removeAll();
+
+		super.removeAllPages(context);
+	}
+
+	@Override
+	public void detach(IPageContext context)
+	{
+		RequestData requestData = getRequestData(context);
+		for (IManageablePage page : requestData.pages())
+		{
+			boolean isPageStateless;
+			try
+			{
+				isPageStateless = page.isPageStateless();
+			}
+			catch (Exception x)
+			{
+				log.warn("An error occurred while checking whether a page is stateless. Assuming it is stateful.", x);
+				isPageStateless = false;
+			}
+
+			if (isPageStateless == false)
+			{
+				super.addPage(context, page);
+			}
+		}
+		requestData.removeAll();
+
+		super.detach(context);
+	}
+
+	private RequestData getRequestData(IPageContext context)
+	{
+		RequestData requestData = context.getRequestData(KEY);
+		if (requestData == null)
+		{
+			requestData = new RequestData();
+
+			context.setRequestData(KEY, requestData);
+		}
+		return requestData;
+	}
+	
+	/**
+	 * Data kept in the {@link RequestCycle}.
+	 */
+	static class RequestData
+	{
+		private Map<Integer, IManageablePage> pages = new LinkedHashMap<>();
+		
+		public void add(IManageablePage page)
+		{
+			pages.remove(page.getPageId());
+			
+			pages.put(page.getPageId(), page);
+		}
+
+		public Iterable<IManageablePage> pages()
+		{
+			return pages.values();
+		}
+
+		public IManageablePage get(int id) {
+			IManageablePage page = pages.get(id);
+			
+			if (page != null) {
+				pages.put(id, page);
+			}
+			
+			return page;
+		}
+
+		public void remove(IManageablePage page)
+		{
+			pages.remove(page.getPageId());
+		}
+
+		public void removeAll()
+		{
+			pages.clear();
+		}		
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/SecondLevelPageCache.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/SecondLevelPageCache.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/SecondLevelPageCache.java
deleted file mode 100644
index 3d30857..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/SecondLevelPageCache.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.pageStore;
-
-/**
- * An application scoped cache that holds the last N used pages in the application.
- * Acts as a second level cache between the Http Session (first level) and the
- * disk (third level cache).
- *
- * @param <S>
- *          The type of the session identifier
- * @param <PI>
- *          The type of the page identifier
- * @param <P>
- *          The type of the stored page
- */
-public interface SecondLevelPageCache<S, PI, P>
-{
-	P removePage(S session, PI pageId);
-
-	void removePages(S session);
-
-	P getPage(S session, PI pageId);
-
-	void storePage(S session, PI pageId, P page);
-
-	void destroy();
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/disk/PageWindowManager.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/disk/PageWindowManager.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/disk/PageWindowManager.java
new file mode 100644
index 0000000..5f59d34
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/disk/PageWindowManager.java
@@ -0,0 +1,493 @@
+/*
+ * 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.pageStore.disk;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.wicket.pageStore.IPersistedPage;
+import org.apache.wicket.util.collections.IntHashMap;
+import org.apache.wicket.util.lang.Bytes;
+
+/**
+ * Manages positions and size of chunks of data in a file.
+ * <p>
+ * The data is stored inside the file in a cyclic way. Newer pages are placed after older ones,
+ * until the maximum file size is reached. After that, the next page is stored in the beginning of
+ * the file.
+ * 
+ * @author Matej Knopp
+ */
+public class PageWindowManager implements Serializable
+{
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Contains information about a page inside the file.
+	 * 
+	 * @author Matej Knopp
+	 */
+	public static class FileWindow implements IPersistedPage, Serializable
+	{
+		private static final long serialVersionUID = 1L;
+
+		/** id of data or -1 if the window is empty */
+		private int id;
+
+		private String type;
+
+		/** offset in the file where the serialized page data begins */
+		private int filePartOffset;
+
+		/** size of serialized page data */
+		private int filePartSize;
+
+		@Override
+		public int getPageId()
+		{
+			return id;
+		}
+
+		@Override
+		public String getPageType() {
+			return type;
+		}
+		
+		@Override
+		public Bytes getPageSize()
+		{
+			return Bytes.bytes(filePartSize);
+		}
+
+		public int getFilePartOffset()
+		{
+			return filePartOffset;
+		}
+		
+		public int getFilePartSize()
+		{
+			return filePartSize;
+		}
+	}
+
+	private final List<FileWindow> windows = new ArrayList<FileWindow>();
+
+	/**
+	 * map from page id to list of pagewindow indices (referring to the windows list) - to improve
+	 * searching speed the index must be cleaned when the instances in the windows list change their
+	 * indexes (e.g. items are shifted on page window removal)
+	 */
+	private IntHashMap<Integer> idToWindowIndex = null;
+
+	/**
+	 * Inversed index of #idToWindowIndex
+	 */
+	private IntHashMap<Integer> windowIndexToPageId = null;
+
+	/** index of last added page */
+	private int indexPointer = -1;
+
+	private int totalSize = 0;
+
+	/**
+	 * Maximum page size. After this size is exceeded, the pages will be saved starting at the
+	 * beginning of file.
+	 */
+	private final long maxSize;
+
+	/**
+	 * 
+	 * @param pageId
+	 * @param windowIndex
+	 */
+	private void putWindowIndex(int pageId, int windowIndex)
+	{
+		if (idToWindowIndex != null && pageId != -1 && windowIndex != -1)
+		{
+			Integer oldPageId = windowIndexToPageId.remove(windowIndex);
+			if (oldPageId != null)
+			{
+				idToWindowIndex.remove(oldPageId);
+			}
+			idToWindowIndex.put(pageId, windowIndex);
+			windowIndexToPageId.put(windowIndex, pageId);
+		}
+	}
+
+	/**
+	 * 
+	 * @param pageId
+	 */
+	private void removeWindowIndex(int pageId)
+	{
+		Integer windowIndex = idToWindowIndex.remove(pageId);
+		if (windowIndex != null)
+		{
+			windowIndexToPageId.remove(windowIndex);
+		}
+	}
+
+	/**
+	 * 
+	 */
+	private void rebuildIndices()
+	{
+		idToWindowIndex = null;
+		idToWindowIndex = new IntHashMap<Integer>();
+		windowIndexToPageId = null;
+		windowIndexToPageId = new IntHashMap<Integer>();
+		for (int i = 0; i < windows.size(); ++i)
+		{
+			FileWindow window = windows.get(i);
+			putWindowIndex(window.id, i);
+		}
+	}
+
+	/**
+	 * Returns the index of the given page in the {@link #windows} list.
+	 * 
+	 * @param pageId
+	 * @return window index
+	 */
+	private int getWindowIndex(int pageId)
+	{
+		if (idToWindowIndex == null)
+		{
+			rebuildIndices();
+		}
+
+		Integer result = idToWindowIndex.get(pageId);
+		return result != null ? result : -1;
+	}
+
+	/**
+	 * Increments the {@link #indexPointer}. If the maximum file size has been reached, the
+	 * {@link #indexPointer} is set to 0.
+	 * 
+	 * @return new index pointer
+	 */
+	private int incrementIndexPointer()
+	{
+		if ((maxSize > 0) && (totalSize >= maxSize) && (indexPointer == windows.size() - 1))
+		{
+			indexPointer = 0;
+		}
+		else
+		{
+			++indexPointer;
+		}
+		return indexPointer;
+	}
+
+	/**
+	 * Returns the offset in file of the window on given index. The offset is counted by getting the
+	 * previous page offset and adding the previous page size to it.
+	 * 
+	 * @param index
+	 * @return window file offset
+	 */
+	private int getWindowFileOffset(int index)
+	{
+		if (index > 0)
+		{
+			FileWindow window = windows.get(index - 1);
+			return window.filePartOffset + window.filePartSize;
+		}
+		return 0;
+	}
+
+	/**
+	 * Splits the window with given index to two windows. First of those will have size specified by
+	 * the argument, the other one will fill up the rest of the original window.
+	 * 
+	 * @param index
+	 * @param size
+	 */
+	private void splitWindow(int index, int size)
+	{
+		FileWindow window = windows.get(index);
+		int delta = window.filePartSize - size;
+
+		if (index == windows.size() - 1)
+		{
+			// if this is last window
+			totalSize -= delta;
+			window.filePartSize = size;
+		}
+		else if (window.filePartSize != size)
+		{
+			FileWindow newWindow = new FileWindow();
+			newWindow.id = -1;
+			window.filePartSize = size;
+
+			windows.add(index + 1, newWindow);
+
+			newWindow.filePartOffset = getWindowFileOffset(index + 1);
+			newWindow.filePartSize = delta;
+		}
+
+		idToWindowIndex = null;
+		windowIndexToPageId = null;
+	}
+
+	/**
+	 * Merges the window with given index with the next window. The resulting window will have size
+	 * of the two windows summed together.
+	 * 
+	 * @param index
+	 */
+	private void mergeWindowWithNext(int index)
+	{
+		if (index < windows.size() - 1)
+		{
+			FileWindow window = windows.get(index);
+			FileWindow next = windows.get(index + 1);
+			window.filePartSize += next.filePartSize;
+
+			windows.remove(index + 1);
+			idToWindowIndex = null; // reset index
+			windowIndexToPageId = null;
+		}
+	}
+
+	/**
+	 * Adjusts the window on given index to the specified size. If the new size is smaller than the
+	 * window size, the window will be split. Otherwise the window will be merged with as many
+	 * subsequent window as necessary. In case the window is last window in the file, the size will
+	 * be adjusted without splitting or merging.
+	 * 
+	 * @param index
+	 * @param size
+	 */
+	private void adjustWindowSize(int index, int size)
+	{
+		FileWindow window = windows.get(index);
+
+		// last window, just adjust size
+		if (index == windows.size() - 1)
+		{
+			int delta = size - window.filePartSize;
+			totalSize += delta;
+			window.filePartSize = size;
+		}
+		else
+		{
+			// merge as many times as necessary
+			while (window.filePartSize < size && index < windows.size() - 1)
+			{
+				mergeWindowWithNext(index);
+			}
+
+			// done merging - do we have enough room ?
+			if (window.filePartSize < size)
+			{
+				// no, this is the last window
+				int delta = size - window.filePartSize;
+				totalSize += delta;
+				window.filePartSize = size;
+			}
+			else
+			{
+				// yes, we might want to split the window, so that we don't lose
+				// space when the created window was too big
+				splitWindow(index, size);
+			}
+		}
+
+		window.id = -1;
+	}
+
+	/**
+	 * Allocates window on given index with to size. If the index is pointing to existing window,
+	 * the window size will be adjusted. Otherwise a new window with appropriated size will be
+	 * created.
+	 * 
+	 * @param index
+	 * @param size
+	 * @return page window
+	 */
+	private FileWindow allocatePageWindow(int index, int size)
+	{
+		final FileWindow window;
+
+		// new window
+		if (index == windows.size())
+		{
+			// new page window
+			window = new FileWindow();
+			window.filePartOffset = getWindowFileOffset(index);
+			totalSize += size;
+			window.filePartSize = size;
+			windows.add(window);
+		}
+		else
+		{
+			// get the window
+			window = windows.get(index);
+
+			// adjust if necessary
+			if (window.filePartSize != size)
+			{
+				adjustWindowSize(index, size);
+			}
+		}
+
+		return window;
+	}
+
+	/**
+	 * Creates and returns a new page window for given page.
+	 * 
+	 * @param pageId
+	 * @param type 
+	 * @param size
+	 * @return page window
+	 */
+	public synchronized FileWindow createPageWindow(int pageId, Class<?> type, int size)
+	{
+		int index = getWindowIndex(pageId);
+
+		// if we found the page window, mark it as invalid
+		if (index != -1)
+		{
+			removeWindowIndex(pageId);
+			(windows.get(index)).id = -1;
+		}
+
+		// if we are not going to reuse a page window (because it's not on
+		// indexPointer position or because we didn't find it), increment the
+		// indexPointer
+		if (index == -1 || index != indexPointer)
+		{
+			index = incrementIndexPointer();
+		}
+
+		FileWindow window = allocatePageWindow(index, size);
+		window.id = pageId;
+		window.type = type.getName();
+
+		putWindowIndex(pageId, index);
+		return window;
+	}
+
+	/**
+	 * Returns the page window for given page or null if no window was found.
+	 * 
+	 * @param pageId
+	 * @return page window or null
+	 */
+	public synchronized FileWindow getPageWindow(int pageId)
+	{
+		int index = getWindowIndex(pageId);
+		if (index != -1)
+		{
+			return windows.get(index);
+		}
+		return null;
+	}
+
+	/**
+	 * Removes the page window for given page.
+	 * 
+	 * @param pageId
+	 */
+	public synchronized void removePage(int pageId)
+	{
+		int index = getWindowIndex(pageId);
+		if (index != -1)
+		{
+			FileWindow window = windows.get(index);
+			removeWindowIndex(pageId);
+			if (index == windows.size() - 1)
+			{
+				windows.remove(index);
+				totalSize -= window.filePartSize;
+				if (indexPointer == index)
+				{
+					--indexPointer;
+				}
+			}
+			else
+			{
+				window.id = -1;
+			}
+		}
+	}
+
+	/**
+	 * Returns last n saved page windows.
+	 * 
+	 * @return list of page windows
+	 */
+	public synchronized List<FileWindow> getFileWindows()
+	{
+		List<FileWindow> result = new ArrayList<FileWindow>();
+
+		// start from current index to 0
+		int currentIndex = indexPointer;
+
+		do
+		{
+			if (currentIndex == -1)
+			{
+				break;
+			}
+
+			if (currentIndex < windows.size())
+			{
+				FileWindow window = windows.get(currentIndex);
+				if (window.id != -1)
+				{
+					result.add(window);
+				}
+			}
+
+			--currentIndex;
+			if (currentIndex == -1)
+			{
+				// rewind to the last entry and collect all entries until current index
+				currentIndex = windows.size() - 1;
+			}
+		}
+		while (currentIndex != indexPointer);
+
+		return result;
+	}
+
+	/**
+	 * Creates a new PageWindowManager.
+	 * 
+	 * @param maxSize
+	 *            maximum page size. After this size is exceeded, the pages will be saved starting
+	 *            at the beginning of file
+	 */
+	public PageWindowManager(long maxSize)
+	{
+		this.maxSize = maxSize;
+	}
+
+	/**
+	 * Returns the size of all saved pages
+	 * 
+	 * @return total size
+	 */
+	public synchronized int getTotalSize()
+	{
+		return totalSize;
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/HttpSessionDataStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/HttpSessionDataStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/HttpSessionDataStore.java
deleted file mode 100644
index fcc4f7a..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/HttpSessionDataStore.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * 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.pageStore.memory;
-
-import javax.servlet.http.HttpSession;
-
-import org.apache.wicket.Session;
-import org.apache.wicket.page.IPageManagerContext;
-import org.apache.wicket.pageStore.IDataStore;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A {@link IDataStore} which stores the pages in the {@link HttpSession}. Uses
- * {@link IDataStoreEvictionStrategy} to keep the memory footprint reasonable.
- *
- * <p>
- * Usage:
- *
- * <pre>
- * <!--@formatter:off-->
- * MyApp#init()
- * {
- *
- * 	setPageManagerProvider(new DefaultPageManagerProvider(this)
- * 	{
- * 		protected IDataStore newDataStore() 
- * 		{ 
- * 			return  new HttpSessionDataStore(pageManagerContext, new PageNumberEvictionStrategy(20));
- * 		}
- * 	}
- * }
- * <!--@formatter:on-->
- * </pre>
- */
-public class HttpSessionDataStore implements IDataStore
-{
-	private static final Logger LOG = LoggerFactory.getLogger(HttpSessionDataStore.class);
-
-	/** the session attribute key. auto-prefixed with application.getSessionAttributePrefix() */
-	private static final String PAGE_TABLE_KEY = "page:store:memory";
-
-	private final IPageManagerContext pageManagerContext;
-
-	private final IDataStoreEvictionStrategy evictionStrategy;
-
-	/**
-	 * Construct.
-	 * 
-	 * @param pageManagerContext
-	 * @param evictionStrategy
-	 */
-	public HttpSessionDataStore(IPageManagerContext pageManagerContext,
-		IDataStoreEvictionStrategy evictionStrategy)
-	{
-		this.pageManagerContext = pageManagerContext;
-		this.evictionStrategy = evictionStrategy;
-	}
-
-	/**
-	 * @param sessionId
-	 *            Ignored. Only pages from the current http session can be read
-	 * @see org.apache.wicket.pageStore.IDataStore#getData(java.lang.String, int)
-	 */
-	@Override
-	public byte[] getData(String sessionId, int pageId)
-	{
-		PageTable pageTable = getPageTable(false, false);
-		byte[] pageAsBytes = null;
-		if (pageTable != null)
-		{
-			pageAsBytes = pageTable.getPage(pageId);
-		}
-
-		if (LOG.isDebugEnabled())
-		{
-			int bytesLength = pageAsBytes != null ? pageAsBytes.length : -1;
-			LOG.debug("Loaded '{}' bytes for page with id '{}' in session '{}'",
-					bytesLength, pageId, sessionId);
-		}
-
-
-		return pageAsBytes;
-	}
-
-	@Override
-	public void removeData(String sessionId, int pageId)
-	{
-		PageTable pageTable = getPageTable(false, true);
-		if (pageTable != null)
-		{
-			byte[] bytes = pageTable.removePage(pageId);
-
-			if (LOG.isDebugEnabled() && bytes != null)
-			{
-				LOG.debug("Removed page '{}' in session '{}'", pageId, sessionId);
-			}
-		}
-	}
-
-	@Override
-	public void removeData(String sessionId)
-	{
-		PageTable pageTable = getPageTable(false, true);
-		if (pageTable != null)
-		{
-			pageTable.clear();
-			LOG.debug("Removed all pages in session '{}'", sessionId);
-		}
-	}
-
-	@Override
-	public void storeData(String sessionId, int pageId, byte[] pageAsBytes)
-	{
-		PageTable pageTable = getPageTable(true, true);
-		if (pageTable != null)
-		{
-			pageTable.storePage(pageId, pageAsBytes);
-			if (LOG.isDebugEnabled())
-			{
-				LOG.debug("Stored '{}' bytes for page '{}' in session '{}'",
-						pageAsBytes.length, pageId, sessionId);
-			}
-			evictionStrategy.evict(pageTable);
-		}
-		else
-		{
-			LOG.error("Cannot store the data for page with id '{}' in session with id '{}'",
-				pageId, sessionId);
-		}
-	}
-
-	@Override
-	public void destroy()
-	{
-		// do nothing
-		// this is application lifecycle thread (WicketFilter#destroy())
-		// so there is no reachable http session
-	}
-
-	@Override
-	public boolean isReplicated()
-	{
-		return true;
-	}
-
-	private PageTable getPageTable(boolean create, boolean rewriteToSession)
-	{
-		PageTable pageTable = null;
-		if (Session.exists())
-		{
-			pageTable = (PageTable)pageManagerContext.getSessionAttribute(PAGE_TABLE_KEY);
-			if (pageTable == null && create)
-			{
-				pageTable = new PageTable();
-				pageManagerContext.setSessionAttribute(PAGE_TABLE_KEY, pageTable);
-			} else if (rewriteToSession) {
-				pageManagerContext.setSessionAttribute(PAGE_TABLE_KEY, pageTable);
-			}
-		}
-		return pageTable;
-	}
-
-	@Override
-	public final boolean canBeAsynchronous()
-	{
-		// HttpSessionDataStore needs access to the current http session
-		// and this is not possible in AsychronousDataStore
-		return false;
-	}
-
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/IDataStoreEvictionStrategy.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/IDataStoreEvictionStrategy.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/IDataStoreEvictionStrategy.java
deleted file mode 100644
index 6b6e556..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/IDataStoreEvictionStrategy.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.pageStore.memory;
-
-
-/**
- * An eviction strategy that decides whether the in-memory data structure used as page store should
- * be compacted
- */
-@FunctionalInterface
-public interface IDataStoreEvictionStrategy
-{
-
-	/**
-	 * Called after each {@link org.apache.wicket.pageStore.IDataStore#storeData(String, int, byte[])} call.
-	 * 
-	 * @param pageTable
-	 *            the in-memory data store with <strong>all</strong> pages
-	 */
-	void evict(PageTable pageTable);
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/MemorySizeEvictionStrategy.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/MemorySizeEvictionStrategy.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/MemorySizeEvictionStrategy.java
deleted file mode 100644
index 92f985b..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/MemorySizeEvictionStrategy.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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.pageStore.memory;
-
-import org.apache.wicket.util.lang.Args;
-import org.apache.wicket.util.lang.Bytes;
-import org.apache.wicket.core.util.lang.WicketObjects;
-
-/**
- * An eviction strategy that keeps the data store size up to configured bytes
- */
-public class MemorySizeEvictionStrategy implements IDataStoreEvictionStrategy
-{
-
-	private final Bytes maxBytes;
-
-	/**
-	 * Construct.
-	 * 
-	 * @param maxBytes
-	 *            the maximum size of the data store
-	 */
-	public MemorySizeEvictionStrategy(Bytes maxBytes)
-	{
-		Args.notNull(maxBytes, "maxBytes");
-
-		this.maxBytes = maxBytes;
-	}
-
-	/**
-	 * 
-	 * @see IDataStoreEvictionStrategy#evict(org.apache.wicket.pageStore.memory.PageTable)
-	 */
-	@Override
-	public void evict(PageTable pageTable)
-	{
-
-		long storeCurrentSize = WicketObjects.sizeof(pageTable);
-
-		if (storeCurrentSize > maxBytes.bytes())
-		{
-			PageTableCleaner cleaner = new PageTableCleaner();
-			cleaner.drop(pageTable, 1);
-
-			// recurse until enough space is cleaned
-			evict(pageTable);
-		}
-	}
-
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageNumberEvictionStrategy.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageNumberEvictionStrategy.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageNumberEvictionStrategy.java
deleted file mode 100644
index 9857a6b..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageNumberEvictionStrategy.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.pageStore.memory;
-
-
-/**
- * An eviction strategy which decides whether to evict entries from the in-memory data store
- * depending on the number of stored paged per session
- */
-public class PageNumberEvictionStrategy implements IDataStoreEvictionStrategy
-{
-
-	private final int pagesNumber;
-
-	/**
-	 * Construct.
-	 * 
-	 * @param pagesNumber
-	 *            the maximum number of pages the data store can hold
-	 */
-	public PageNumberEvictionStrategy(int pagesNumber)
-	{
-		if (pagesNumber < 1)
-		{
-			throw new IllegalArgumentException("'pagesNumber' must be greater than 0.");
-		}
-
-		this.pagesNumber = pagesNumber;
-	}
-
-	/**
-	 * 
-	 * @see IDataStoreEvictionStrategy#evict(org.apache.wicket.pageStore.memory.PageTable)
-	 */
-	@Override
-	public void evict(PageTable pageTable)
-	{
-		int size = pageTable.size();
-		int pagesToDrop = size - pagesNumber;
-
-		if (pagesToDrop > 0)
-		{
-			PageTableCleaner cleaner = new PageTableCleaner();
-			cleaner.drop(pageTable, pagesToDrop);
-		}
-	}
-
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageTable.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageTable.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageTable.java
deleted file mode 100644
index dfad4af..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageTable.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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.pageStore.memory;
-
-import java.util.Iterator;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.ConcurrentMap;
-
-import org.apache.wicket.util.io.IClusterable;
-
-/**
- * A structure that holds page id => pageAsBytes.
- * 
- * <p>
- * Additionally it has an index of the least recently used pages
- */
-public class PageTable implements IClusterable
-{
-	private static final long serialVersionUID = 1L;
-
-	/**
-	 * Holds the index of last/least recently used page ids. The most recently used page id is in
-	 * the tail, the least recently used is in the head.
-	 */
-	/*
-	 * Can be replaced later with PriorityQueue to deal with lightweight (Ajax) and heavyweight
-	 * pages
-	 */
-	private final Queue<Integer> index;
-
-	/**
-	 * The actual container for the pages.
-	 * 
-	 * <p>
-	 * page id => page as bytes
-	 */
-	private final ConcurrentMap<Integer, byte[]> pages;
-
-	public PageTable()
-	{
-		pages = new ConcurrentHashMap<>();
-		index = new ConcurrentLinkedQueue<>();
-	}
-
-	public void storePage(Integer pageId, byte[] pageAsBytes)
-	{
-		synchronized (index)
-		{
-			pages.put(pageId, pageAsBytes);
-
-			updateIndex(pageId);
-		}
-	}
-
-	public byte[] getPage(final Integer pageId)
-	{
-		synchronized (index)
-		{
-			updateIndex(pageId);
-
-			return pages.get(pageId);
-		}
-	}
-
-	public byte[] removePage(Integer pageId)
-	{
-		synchronized (index)
-		{
-			index.remove(pageId);
-
-			return pages.remove(pageId);
-		}
-	}
-
-	public void clear()
-	{
-		synchronized (index)
-		{
-			index.clear();
-			pages.clear();
-		}
-	}
-
-	public int size()
-	{
-		return pages.size();
-	}
-
-	public Integer getOldest()
-	{
-		return index.peek();
-	}
-
-	public Iterator<Integer> indexIterator()
-	{
-		return index.iterator();
-	}
-
-	/**
-	 * Updates the index of last/least recently used pages by removing the page id from the index
-	 * (in case it is already in) and (re-)adding it at the head
-	 * 
-	 * @param pageId
-	 *            the id of a recently used page
-	 */
-	private void updateIndex(Integer pageId)
-	{
-		index.remove(pageId);
-		index.offer(pageId);
-	}
-
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageTableCleaner.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageTableCleaner.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageTableCleaner.java
deleted file mode 100644
index 2c9a9cb..0000000
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageTableCleaner.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.pageStore.memory;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Helper class that knows how to remove the nth oldest pages from {@link PageTable}
- */
-public class PageTableCleaner
-{
-	private static final Logger LOG = LoggerFactory.getLogger(PageTableCleaner.class);
-
-	/**
-	 * Removes {@code pagesNumber} of pages from the {@link PageTable pageTable}
-	 * 
-	 * @param pageTable
-	 *            the {@link PageTable} to clean
-	 * @param pagesNumber
-	 *            the number of pages to remove
-	 */
-	public void drop(final PageTable pageTable, final int pagesNumber)
-	{
-		for (int i = 0; i < pagesNumber; i++)
-		{
-			Integer pageIdOfTheOldest = pageTable.getOldest();
-			pageTable.removePage(pageIdOfTheOldest);
-			LOG.debug("Evicted page with id '{}' from the HttpSessionDataStore");
-		}
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/settings/StoreSettings.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/settings/StoreSettings.java b/wicket-core/src/main/java/org/apache/wicket/settings/StoreSettings.java
index 8852208..63a5499 100644
--- a/wicket-core/src/main/java/org/apache/wicket/settings/StoreSettings.java
+++ b/wicket-core/src/main/java/org/apache/wicket/settings/StoreSettings.java
@@ -50,8 +50,6 @@ public class StoreSettings
 
 	private static final int DEFAULT_ASYNCHRONOUS_QUEUE_CAPACITY = 100;
 
-	private int inmemoryCacheSize = DEFAULT_CACHE_SIZE;
-
 	private Bytes maxSizePerSession = DEFAULT_MAX_SIZE_PER_SESSION;
 
 	private File fileStoreFolder = null;
@@ -70,30 +68,6 @@ public class StoreSettings
 	}
 
 	/**
-	 * @return the number of page instances which will be stored in the application scoped cache for
-	 *         faster retrieval
-	 */
-	public int getInmemoryCacheSize()
-	{
-		return inmemoryCacheSize;
-	}
-
-	/**
-	 * Sets the maximum number of page instances which will be stored in the application scoped
-	 * second level cache for faster retrieval
-	 *
-	 * @param inmemoryCacheSize
-	 *            the maximum number of page instances which will be held in the application scoped
-	 *            cache
-	 * @return {@code this} object for chaining
-	 */
-	public StoreSettings setInmemoryCacheSize(int inmemoryCacheSize)
-	{
-		this.inmemoryCacheSize = inmemoryCacheSize;
-		return this;
-	}
-
-	/**
 	 * @return maximum page size. After this size is exceeded,
 	 * the {@link org.apache.wicket.pageStore.DiskDataStore} will start saving the
 	 * pages at the beginning of file.

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/util/tester/BaseWicketTester.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/util/tester/BaseWicketTester.java b/wicket-core/src/main/java/org/apache/wicket/util/tester/BaseWicketTester.java
index 54e7808..4012a5f 100644
--- a/wicket-core/src/main/java/org/apache/wicket/util/tester/BaseWicketTester.java
+++ b/wicket-core/src/main/java/org/apache/wicket/util/tester/BaseWicketTester.java
@@ -92,11 +92,11 @@ import org.apache.wicket.markup.html.list.ListView;
 import org.apache.wicket.markup.parser.XmlPullParser;
 import org.apache.wicket.markup.parser.XmlTag;
 import org.apache.wicket.mock.MockApplication;
-import org.apache.wicket.mock.MockPageManager;
+import org.apache.wicket.mock.MockPageStore;
 import org.apache.wicket.mock.MockRequestParameters;
 import org.apache.wicket.model.PropertyModel;
 import org.apache.wicket.page.IPageManager;
-import org.apache.wicket.page.IPageManagerContext;
+import org.apache.wicket.page.PageManager;
 import org.apache.wicket.protocol.http.AjaxEnclosureListener;
 import org.apache.wicket.protocol.http.IMetaDataBufferingWebResponse;
 import org.apache.wicket.protocol.http.WebApplication;
@@ -2926,9 +2926,9 @@ public class BaseWicketTester
 	private static class TestPageManagerProvider implements IPageManagerProvider
 	{
 		@Override
-		public IPageManager apply(IPageManagerContext pageManagerContext)
+		public IPageManager get()
 		{
-			return new MockPageManager();
+			return new PageManager(new MockPageStore());
 		}
 	}
 


[2/5] wicket git commit: WICKET-6563 new IPageStore implementation

Posted by sv...@apache.org.
http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/TestMapperContext.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/TestMapperContext.java b/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/TestMapperContext.java
index a1b960d..c1d7737 100644
--- a/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/TestMapperContext.java
+++ b/wicket-core/src/test/java/org/apache/wicket/core/request/mapper/TestMapperContext.java
@@ -20,19 +20,20 @@ import org.apache.wicket.MockPage;
 import org.apache.wicket.WicketRuntimeException;
 import org.apache.wicket.core.request.handler.PageProvider;
 import org.apache.wicket.markup.MarkupParser;
-import org.apache.wicket.page.IPageManagerContext;
-import org.apache.wicket.page.PageStoreManager;
-import org.apache.wicket.pageStore.DefaultPageStore;
-import org.apache.wicket.pageStore.IDataStore;
-import org.apache.wicket.pageStore.IPageStore;
-import org.apache.wicket.pageStore.memory.DummyPageManagerContext;
+import org.apache.wicket.mock.MockPageStore;
+import org.apache.wicket.page.IPageManager;
+import org.apache.wicket.page.PageManager;
+import org.apache.wicket.pageStore.DummyPageContext;
+import org.apache.wicket.pageStore.IPageContext;
+import org.apache.wicket.pageStore.InMemoryPageStore;
+import org.apache.wicket.pageStore.RequestPageStore;
+import org.apache.wicket.pageStore.InSessionPageStore;
 import org.apache.wicket.request.component.IRequestablePage;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.request.resource.ResourceReference;
 import org.apache.wicket.request.resource.ResourceReference.Key;
 import org.apache.wicket.request.resource.ResourceReferenceRegistry;
 import org.apache.wicket.serialize.java.JavaSerializer;
-import org.apache.wicket.versioning.InMemoryPageStore;
 
 /**
  * Simple {@link IMapperContext} implementation for testing purposes
@@ -43,10 +44,10 @@ public class TestMapperContext implements IMapperContext
 {
 	private static final String APP_NAME = "test_app";
 	private static int count;
-	IDataStore dataStore;
-	IPageStore pageStore;
-	IPageManagerContext pageManagerContext;
-	PageStoreManager pageManager;
+
+	InSessionPageStore pageStore;
+	DummyPageContext pageContext;
+	IPageManager pageManager;
 	private String appName;
 	private boolean createMockPageIfInstanceNotFound = true;
 
@@ -56,10 +57,18 @@ public class TestMapperContext implements IMapperContext
 	public TestMapperContext()
 	{
 		appName = APP_NAME + count++;
-		dataStore = new InMemoryPageStore();
-		pageStore = new DefaultPageStore(new JavaSerializer(appName), dataStore, 4);
-		pageManagerContext = new DummyPageManagerContext();
-		pageManager = new PageStoreManager(appName, pageStore, pageManagerContext);
+		
+		pageContext = new DummyPageContext();
+		
+		InMemoryPageStore inMemoryPageStore = new InMemoryPageStore(appName, Integer.MAX_VALUE);
+		pageStore = new InSessionPageStore(inMemoryPageStore, new JavaSerializer(appName), 4);
+		pageManager = new PageManager(new RequestPageStore(pageStore)) {
+			@Override
+			protected IPageContext createPageContext()
+			{
+				return pageContext;
+			}
+		};
 	}
 
 	/**
@@ -67,17 +76,17 @@ public class TestMapperContext implements IMapperContext
 	 */
 	public void cleanSessionCache()
 	{
-		getPageManager().getContext().setRequestData(null);
+		pageContext.clearRequest();
 		MockPage other = new MockPage();
 		other.setPageId(Integer.MAX_VALUE);
-		getPageManager().touchPage(other);
-		getPageManager().commitRequest();
+		getPageManager().addPage(other);
+		pageManager.detach();
 	}
 
 	/**
 	 * @return pageManager
 	 */
-	public PageStoreManager getPageManager()
+	public IPageManager getPageManager()
 	{
 		return pageManager;
 	}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/dontstoreunrendered/DontStoreNotRenderedPageTestCase.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/dontstoreunrendered/DontStoreNotRenderedPageTestCase.java b/wicket-core/src/test/java/org/apache/wicket/dontstoreunrendered/DontStoreNotRenderedPageTestCase.java
index fe1875b..f49a384 100644
--- a/wicket-core/src/test/java/org/apache/wicket/dontstoreunrendered/DontStoreNotRenderedPageTestCase.java
+++ b/wicket-core/src/test/java/org/apache/wicket/dontstoreunrendered/DontStoreNotRenderedPageTestCase.java
@@ -22,8 +22,6 @@ import org.apache.wicket.application.IComponentInstantiationListener;
 import org.apache.wicket.behavior.Behavior;
 import org.apache.wicket.mock.MockPageManager;
 import org.apache.wicket.page.IManageablePage;
-import org.apache.wicket.page.IPageManager;
-import org.apache.wicket.page.IPageManagerContext;
 import org.apache.wicket.protocol.http.WebApplication;
 import org.apache.wicket.util.tester.WicketTestCase;
 import org.apache.wicket.util.tester.WicketTester;
@@ -55,21 +53,16 @@ public abstract class DontStoreNotRenderedPageTestCase extends WicketTestCase
 			@Override
 			protected IPageManagerProvider newTestPageManagerProvider()
 			{
-				return new IPageManagerProvider()
-				{
-					@Override
-					public IPageManager apply(IPageManagerContext context)
+				return () -> {
+					return new MockPageManager()
 					{
-						return new MockPageManager()
+						@Override
+						public void addPage(IManageablePage page)
 						{
-							@Override
-							public void touchPage(IManageablePage page)
-							{
-								Assert.assertFalse("PageB should not be touched!", page instanceof PageB);
-								super.touchPage(page);
-							}
-						};
-					}
+							Assert.assertFalse("PageB should not be touched!", page instanceof PageB);
+							super.addPage(page);
+						}
+					};
 				};
 			}
 		};

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/markup/html/TransparentWebMarkupContainerTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/markup/html/TransparentWebMarkupContainerTest.java b/wicket-core/src/test/java/org/apache/wicket/markup/html/TransparentWebMarkupContainerTest.java
index 39586e0..6ec0066 100644
--- a/wicket-core/src/test/java/org/apache/wicket/markup/html/TransparentWebMarkupContainerTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/markup/html/TransparentWebMarkupContainerTest.java
@@ -34,7 +34,7 @@ import org.apache.wicket.markup.html.panel.Fragment;
 import org.apache.wicket.mock.MockPageManager;
 import org.apache.wicket.page.IManageablePage;
 import org.apache.wicket.page.IPageManager;
-import org.apache.wicket.page.IPageManagerContext;
+import org.apache.wicket.pageStore.IPageContext;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.util.resource.IResourceStream;
 import org.apache.wicket.util.resource.StringResourceStream;
@@ -117,15 +117,15 @@ public class TransparentWebMarkupContainerTest extends WicketTestCase
 				return new IPageManagerProvider()
 				{
 					@Override
-					public IPageManager apply(IPageManagerContext context)
+					public IPageManager get()
 					{
 						return new MockPageManager()
 						{
 							@Override
-							public void touchPage(IManageablePage page)
+							public void addPage(IManageablePage page)
 							{
 								page = WicketObjects.cloneObject(page);
-								super.touchPage(page);
+								super.addPage(page);
 							}
 						};
 					}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/page/PageAccessSynchronizerTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/page/PageAccessSynchronizerTest.java b/wicket-core/src/test/java/org/apache/wicket/page/PageAccessSynchronizerTest.java
index e2053df..eeb9ba3 100644
--- a/wicket-core/src/test/java/org/apache/wicket/page/PageAccessSynchronizerTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/page/PageAccessSynchronizerTest.java
@@ -324,7 +324,7 @@ public class PageAccessSynchronizerTest extends Assert
 
 		int pageId = 1;
 		IManageablePage page = new MockPage(pageId);
-		synchronizedPageManager.touchPage(page);
+		synchronizedPageManager.addPage(page);
 		synchronizedPageManager.getPage(pageId);
 		PageLock pageLock2 = locks.get(Integer.valueOf(pageId));
 		assertNotNull(pageLock2);

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/page/PersistentPageManagerTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/page/PersistentPageManagerTest.java b/wicket-core/src/test/java/org/apache/wicket/page/PersistentPageManagerTest.java
index 23fe264..cb39d94 100644
--- a/wicket-core/src/test/java/org/apache/wicket/page/PersistentPageManagerTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/page/PersistentPageManagerTest.java
@@ -19,19 +19,19 @@ package org.apache.wicket.page;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
-import java.io.ObjectInputStream;
 import java.io.Serializable;
+import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.wicket.Application;
+import org.apache.wicket.MetaDataKey;
 import org.apache.wicket.ThreadContext;
-import org.apache.wicket.pageStore.DefaultPageStore;
-import org.apache.wicket.pageStore.IDataStore;
+import org.apache.wicket.pageStore.DummyPageContext;
+import org.apache.wicket.pageStore.IPageContext;
 import org.apache.wicket.pageStore.IPageStore;
-import org.apache.wicket.pageStore.memory.DummyPageManagerContext;
+import org.apache.wicket.pageStore.InSessionPageStore;
+import org.apache.wicket.pageStore.NoopPageStore;
 import org.apache.wicket.serialize.java.JavaSerializer;
-import org.apache.wicket.versioning.InMemoryPageStore;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -58,44 +58,43 @@ public class PersistentPageManagerTest
 		ThreadContext.detach();
 
 		// create IPageManager (with IPageStore) and store a page instance
-		IPageManager pageManager = newPersistentPageManager(APP_NAME);
+		final AtomicReference<Object> sessionData = new AtomicReference<Object>(null);
+		
+		IPageManager pageManager = createPageManager(APP_NAME, sessionData);
+
+		// add a page
 		TestPage toSerializePage = new TestPage();
-		pageManager.touchPage(toSerializePage);
-		pageManager.commitRequest();
+		pageManager.addPage(toSerializePage);
+		pageManager.detach();
 
 		// get the stored SessionEntry
-		Serializable sessionEntry = pageManager.getContext().getSessionAttribute(null);
+		assertNotNull(sessionData.get());
 
 		// destroy the manager and the store
 		pageManager.destroy();
 
 		// simulate persisting of the http sessions initiated by the web container
-		byte[] serializedSessionEntry = new JavaSerializer(APP_NAME).serialize(sessionEntry);
+		byte[] serializedSessionData = new JavaSerializer(APP_NAME).serialize(sessionData.get());
 		assertNotNull("Wicket needs to be able to serialize the session entry",
-			serializedSessionEntry);
-
-		// simulate loading of the persisted http session initiated by the web container
-		// when starting an application
-		ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(
-			serializedSessionEntry));
+			serializedSessionData);
 
 		// WicketFilter is not initialized so there is no Application available yet
 		Assert.assertFalse("Worker thread should be unaware of Wicket application",
 			Application.exists());
 
-		assertEquals(APP_NAME, in.readObject());
+		// simulate loading of the persisted http session initiated by the web container
+		// when starting an application
+		sessionData.set(new JavaSerializer(APP_NAME).deserialize(serializedSessionData));
 
 		// without available IPageStore the read SessionEntry holds
 		// the IManageablePage itself, not SerializedPage
-		Serializable loadedSessionEntry = (Serializable)in.readObject();
 		assertNotNull(
 			"Wicket needs to be able to deserialize the session entry regardless the application availability",
-			loadedSessionEntry);
+			sessionData.get());
 
 		// provide new IPageStore which will read IManageablePage's or SerializedPage's
 		// from the SessionEntry's
-		IPageManager newPageManager = newPersistentPageManager(APP_NAME);
-		newPageManager.getContext().setSessionAttribute(null, loadedSessionEntry);
+		IPageManager newPageManager = createPageManager(APP_NAME, sessionData);
 
 		TestPage deserializedPage = (TestPage)newPageManager.getPage(toSerializePage.getPageId());
 		assertNotNull(deserializedPage);
@@ -104,12 +103,34 @@ public class PersistentPageManagerTest
 		newPageManager.destroy();
 	}
 
-	private PageStoreManager newPersistentPageManager(String appName)
+	/**
+	 * Create a manager that stores session data in the given atomic reference.
+	 */
+	private IPageManager createPageManager(String appName, AtomicReference<Object> sessionData)
 	{
-		IDataStore dataStore = new InMemoryPageStore();
-		IPageStore pageStore = new DefaultPageStore(new JavaSerializer(appName), dataStore, 4);
-		IPageManagerContext pageManagerContext = new DummyPageManagerContext();
-		return new PageStoreManager(appName, pageStore, pageManagerContext);
+		IPageStore store = new InSessionPageStore(new NoopPageStore(), new JavaSerializer(APP_NAME), Integer.MAX_VALUE);
+		
+		return new PageManager(store) {
+			@Override
+			protected IPageContext createPageContext()
+			{
+				return new DummyPageContext() {
+					@Override
+					public <T extends Serializable> void setSessionData(MetaDataKey<T> key, T value)
+					{
+						super.setSessionData(key, value);
+						
+						sessionData.set(value);
+					}
+					
+					@Override
+					public <T extends Serializable> T getSessionData(MetaDataKey<T> key)
+					{
+						return (T)sessionData.get();
+					}
+				};
+			}
+		};
 	}
 
 	private static class TestPage implements IManageablePage

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/page/persistent/disk/PageWindowManagerTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/page/persistent/disk/PageWindowManagerTest.java b/wicket-core/src/test/java/org/apache/wicket/page/persistent/disk/PageWindowManagerTest.java
deleted file mode 100644
index 1270800..0000000
--- a/wicket-core/src/test/java/org/apache/wicket/page/persistent/disk/PageWindowManagerTest.java
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * 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.page.persistent.disk;
-
-import java.security.SecureRandom;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-import org.apache.wicket.pageStore.PageWindowManager;
-import org.apache.wicket.pageStore.PageWindowManager.PageWindow;
-import org.junit.Assert;
-import org.junit.Test;
-
-/**
- * @author Matej Knopp
- */
-public class PageWindowManagerTest extends Assert
-{
-	/**
-	 * https://issues.apache.org/jira/browse/WICKET-4572
-	 */
-	@Test
-	public void removeObsoleteIndices()
-	{
-		int page0id = 0,
-			page1id = 1,
-			page2id = 2;
-		int maxSize = 10;
-
-		PageWindowManager manager = new PageWindowManager(maxSize);
-
-		// Add few pages.
-		// All of them fully occupy the max space in the pageWindowManager.
-		// So adding N+1st page removes the Nth page.
-		manager.createPageWindow(page0id, maxSize);
-		PageWindow page0Window = manager.getPageWindow(page0id);
-		assertWindow(page0Window, page0id, page0Window.getFilePartOffset(), page0Window.getFilePartSize());
-
-		manager.createPageWindow(page1id, maxSize);
-		PageWindow page1Window = manager.getPageWindow(page1id);
-		assertWindow(page1Window, page1id, page1Window.getFilePartOffset(), page1Window.getFilePartSize());
-
-		// Try to get a page which has been lost with the adding of page1
-		assertNull("Page0 must be lost when Page1 has been added.", manager.getPageWindow(page0id));
-
-		manager.createPageWindow(page2id, maxSize);
-		PageWindow page2Window = manager.getPageWindow(page2id);
-		assertWindow(page2Window, page2id, page2Window.getFilePartOffset(), page2Window.getFilePartSize());
-
-		// Try to get a page which has been lost with the adding of page2
-		assertNull("Page1 must be lost when Page2 has been added.", manager.getPageWindow(page1id));
-	}
-
-	/**
-	 * 
-	 */
-	@Test
-	public void addRemove()
-	{
-		PageWindowManager manager = new PageWindowManager(300);
-		PageWindow window;
-
-		window = manager.createPageWindow(1, 50);
-		assertWindow(window, 1, 0, 50);
-
-		window = manager.createPageWindow(2, 40);
-		assertWindow(window, 2, 50, 40);
-
-		assertEquals(manager.getTotalSize(), 90);
-
-		window = manager.createPageWindow(2, 30);
-		assertWindow(window, 2, 50, 30);
-		assertEquals(manager.getTotalSize(), 80);
-
-		manager.removePage(2);
-		assertEquals(manager.getTotalSize(), 50);
-
-		window = manager.createPageWindow(3, 30);
-		assertWindow(window, 3, 50, 30);
-		assertEquals(manager.getTotalSize(), 80);
-	}
-
-	/**
-	 * 
-	 */
-	@Test
-	public void pageWindowCycle()
-	{
-		PageWindowManager manager = new PageWindowManager(100);
-		PageWindow window;
-
-		window = manager.createPageWindow(1, 30);
-
-		window = manager.createPageWindow(2, 30);
-
-		window = manager.createPageWindow(3, 30);
-
-		assertWindow(window, 3, 60, 30);
-
-		window = manager.createPageWindow(4, 30);
-
-		assertWindow(window, 4, 90, 30);
-
-		// should start at the beginging
-
-		window = manager.createPageWindow(5, 20);
-
-		assertWindow(window, 5, 0, 20);
-
-		assertNull(manager.getPageWindow(1));
-
-		window = manager.getPageWindow(2);
-		assertWindow(window, 2, 30, 30);
-
-		window = manager.createPageWindow(6, 10);
-
-		assertWindow(window, 6, 20, 10);
-
-		window = manager.getPageWindow(2);
-		assertWindow(window, 2, 30, 30);
-
-		window = manager.createPageWindow(6, 30);
-		assertWindow(window, 6, 20, 30);
-
-		assertNull(manager.getPageWindow(2));
-		assertNotNull(manager.getPageWindow(3));
-
-		window = manager.createPageWindow(6, 60);
-		assertWindow(window, 6, 20, 60);
-
-		assertNull(manager.getPageWindow(3));
-
-		window = manager.createPageWindow(7, 20);
-		assertWindow(window, 7, 80, 20);
-
-		assertNotNull(manager.getPageWindow(7));
-
-		// should start at the beginning again
-
-		window = manager.createPageWindow(8, 10);
-		assertWindow(window, 8, 0, 10);
-
-		assertNull(manager.getPageWindow(5));
-		assertNotNull(manager.getPageWindow(6));
-
-		window = manager.createPageWindow(9, 20);
-		assertWindow(window, 9, 10, 20);
-
-		assertNull(manager.getPageWindow(6));
-		assertNotNull(manager.getPageWindow(7));
-
-		window = manager.createPageWindow(10, 20);
-		assertWindow(window, 10, 30, 20);
-
-		assertNull(manager.getPageWindow(6));
-		assertNotNull(manager.getPageWindow(7));
-
-		// make sure when replacing a page that's not last the old "instance" is
-		// not valid anymore
-
-		manager.createPageWindow(8, 10);
-
-		window = manager.getPageWindow(8);
-		assertWindow(window, 8, 50, 10);
-	}
-
-
-	private void assertWindow(PageWindow window, int pageId, int filePartOffset, int filePartSize)
-	{
-		assertTrue(window.getPageId() == pageId && window.getFilePartOffset() == filePartOffset &&
-			window.getFilePartSize() == filePartSize);
-	}
-
-	/** how many operations to execute */
-	private static final int EXECUTIONS = 10000;
-
-	/** used to wait the executions */
-	private static final CountDownLatch LATCH = new CountDownLatch(EXECUTIONS);
-
-	private final PageWindowManager pageWindowManager = new PageWindowManager(1000L);
-
-	/** the execution types */
-	private final Runnable[] TASKS = new Runnable[]
-	{
-		new CreatePageWindowTask(pageWindowManager),
-		new GetPageWindowTask(pageWindowManager),
-		new RemovePageInSessionTask(pageWindowManager)
-	};
-
-	private static final SecureRandom RND = new SecureRandom();
-
-	/**
-	 * Executes random mutator and accessor operations on {@link org.apache.wicket.pageStore.AsynchronousDataStore} validating
-	 * that the used data structures can be used simultaneously.
-	 *
-	 * @throws Exception
-	 */
-	@Test
-	public void randomOperations() throws Exception
-	{
-		ExecutorService executorService = Executors.newFixedThreadPool(50);
-
-		for (int i = 0; i < EXECUTIONS; i++)
-		{
-			Runnable task = TASKS[RND.nextInt(TASKS.length)];
-			executorService.submit(task);
-		}
-		LATCH.await();
-		executorService.shutdown();
-	}
-
-	private static abstract class AbstractTask implements Runnable
-	{
-		/** the ids for the stored/removed pages */
-		private static final int[] PAGE_IDS = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
-
-		protected final PageWindowManager pageWindowManager;
-
-		private AbstractTask(PageWindowManager pageWindowManager)
-		{
-			this.pageWindowManager = pageWindowManager;
-		}
-
-		protected abstract void r();
-
-		@Override
-		public void run()
-		{
-			try
-			{
-				r();
-			}
-			finally
-			{
-				LATCH.countDown();
-			}
-		}
-
-		protected int getPageId()
-		{
-			return PAGE_IDS[RND.nextInt(PAGE_IDS.length)];
-		}
-	}
-
-	private static class CreatePageWindowTask extends AbstractTask
-	{
-		private CreatePageWindowTask(PageWindowManager pageWindowManager)
-		{
-			super(pageWindowManager);
-		}
-
-		@Override
-		public void r()
-		{
-			pageWindowManager.createPageWindow(getPageId(), 1000);
-		}
-	}
-
-	private static class GetPageWindowTask extends AbstractTask
-	{
-		private GetPageWindowTask(PageWindowManager pageWindowManager)
-		{
-			super(pageWindowManager);
-		}
-
-		@Override
-		public void r()
-		{
-			pageWindowManager.getPageWindow(getPageId());
-		}
-	}
-
-	private static class RemovePageInSessionTask extends AbstractTask
-	{
-		private RemovePageInSessionTask(PageWindowManager pageWindowManager)
-		{
-			super(pageWindowManager);
-		}
-
-		@Override
-		public void r()
-		{
-			pageWindowManager.removePage(getPageId());
-		}
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/pageStore/AbstractPageStoreTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/pageStore/AbstractPageStoreTest.java b/wicket-core/src/test/java/org/apache/wicket/pageStore/AbstractPageStoreTest.java
index 0ab35fd..d662b5a 100644
--- a/wicket-core/src/test/java/org/apache/wicket/pageStore/AbstractPageStoreTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/pageStore/AbstractPageStoreTest.java
@@ -29,17 +29,16 @@ public abstract class AbstractPageStoreTest extends Assert
 	protected final String sessionId = "1234567890";
 	protected final int pageId = 123;
 	protected final ISerializer serializer = new JavaSerializer(getClass().getName());
-	protected final IDataStore dataStore = new NoopDataStore();
 	protected int maxEntries = 1;
 	protected IPageStore pageStore = null;
 
 	@Before
 	public void before()
 	{
-		pageStore = createPageStore(serializer, dataStore, maxEntries);
+		pageStore = createPageStore(serializer, maxEntries);
 	}
 
-	protected abstract IPageStore createPageStore(ISerializer serializer, IDataStore dataStore, int maxEntries);
+	protected abstract IPageStore createPageStore(ISerializer serializer, int maxEntries);
 
 	@After
 	public void after()
@@ -57,9 +56,11 @@ public abstract class AbstractPageStoreTest extends Assert
 	@Test
 	public void storePage()
 	{
-		pageStore.storePage(sessionId, new MockPage(pageId));
+		IPageContext context = new DummyPageContext(sessionId);
+		
+		pageStore.addPage(context, new MockPage(pageId));
 
-		assertNotNull(pageStore.getPage(sessionId, pageId));
+		assertNotNull(pageStore.getPage(context, pageId));
 	}
 
 	/**
@@ -68,46 +69,51 @@ public abstract class AbstractPageStoreTest extends Assert
 	@Test
 	public void storePage2()
 	{
+		IPageContext context = new DummyPageContext(sessionId);
 		int maxEntries = 10;
 
-		pageStore = createPageStore(serializer, dataStore, maxEntries);
+		pageStore = createPageStore(serializer, maxEntries);
 
-		pageStore.storePage(sessionId, new MockPage(pageId));
-		pageStore.storePage(sessionId, new MockPage(pageId));
+		pageStore.addPage(context, new MockPage(pageId));
+		pageStore.addPage(context, new MockPage(pageId));
 
-		assertNotNull(pageStore.getPage(sessionId, pageId));
+		assertNotNull(pageStore.getPage(context, pageId));
 
-		pageStore.removePage(sessionId, pageId);
+		pageStore.removePage(context, new MockPage(pageId));
 
-		assertNull(pageStore.getPage(sessionId, pageId));
+		assertNull(pageStore.getPage(context, pageId));
 	}
 
 	@Test
 	public void removePage()
 	{
-		pageStore.storePage(sessionId, new MockPage(pageId));
+		IPageContext context = new DummyPageContext(sessionId);
+		
+		pageStore.addPage(context, new MockPage(pageId));
 
-		assertNotNull(pageStore.getPage(sessionId, pageId));
+		assertNotNull(pageStore.getPage(context, pageId));
 
-		pageStore.removePage(sessionId, pageId);
+		pageStore.removePage(context, new MockPage(pageId));
 
-		assertNull(pageStore.getPage(sessionId, pageId));
+		assertNull(pageStore.getPage(context, pageId));
 	}
 
 	/**
-	 * Verify that at most {@code maxEntries} per session can be put in the cache
+	 * Verify that at most {@code maxEntries} per session can be put in the store
 	 */
 	@Test
 	public void maxSizeSameSession()
 	{
-		pageStore.storePage(sessionId, new MockPage(pageId));
+		IPageContext context = new DummyPageContext(sessionId);
+		
+		pageStore.addPage(context, new MockPage(pageId));
 
-		assertNotNull(pageStore.getPage(sessionId, pageId));
+		assertNotNull(pageStore.getPage(context, pageId));
 
 		int pageId2 = 234;
-		pageStore.storePage(sessionId, new MockPage(pageId2));
-		assertNull(pageStore.getPage(sessionId, pageId));
-		assertNotNull(pageStore.getPage(sessionId, pageId2));
+		pageStore.addPage(context, new MockPage(pageId2));
+		assertNull(pageStore.getPage(context, pageId));
+		assertNotNull(pageStore.getPage(context, pageId2));
 	}
 
 	/**
@@ -117,15 +123,16 @@ public abstract class AbstractPageStoreTest extends Assert
 	@Test
 	public void maxSizeDifferentSessions()
 	{
-		String sessionId2 = "0987654321";
+		IPageContext context = new DummyPageContext(sessionId);
+		IPageContext context2 = new DummyPageContext("0987654321");
 
-		pageStore.storePage(sessionId, new MockPage(pageId));
+		pageStore.addPage(context, new MockPage(pageId));
 
-		assertNotNull(pageStore.getPage(sessionId, pageId));
+		assertNotNull(pageStore.getPage(context, pageId));
 
-		pageStore.storePage(sessionId2, new MockPage(pageId));
+		pageStore.addPage(context2, new MockPage(pageId));
 
-		assertNull(pageStore.getPage(sessionId, pageId));
-		assertNotNull(pageStore.getPage(sessionId2, pageId));
+		assertNotNull(pageStore.getPage(context, pageId));
+		assertNotNull(pageStore.getPage(context2, pageId));
 	}
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/pageStore/AsynchronousDataStoreTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/pageStore/AsynchronousDataStoreTest.java b/wicket-core/src/test/java/org/apache/wicket/pageStore/AsynchronousDataStoreTest.java
index 7f5b2c1..7c0b0a7 100644
--- a/wicket-core/src/test/java/org/apache/wicket/pageStore/AsynchronousDataStoreTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/pageStore/AsynchronousDataStoreTest.java
@@ -21,7 +21,8 @@ import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
-import org.apache.wicket.versioning.InMemoryPageStore;
+import org.apache.wicket.MockPage;
+import org.apache.wicket.page.IManageablePage;
 import org.junit.Test;
 
 /**
@@ -29,17 +30,13 @@ import org.junit.Test;
  */
 public class AsynchronousDataStoreTest
 {
-//	private static final IDataStore WRAPPED_DATA_STORE = new DiskDataStore("asyncDataStoreApp", new StoreSettings(null).getFileStoreFolder(), Bytes.kilobytes(1));
-	private static final IDataStore WRAPPED_DATA_STORE = new InMemoryPageStore();
+	private static final IPageStore WRAPPED_PAGE_STORE = new InMemoryPageStore("test", Integer.MAX_VALUE);
 
 	/** the data store under test */
-	private static final IDataStore DATA_STORE = new AsynchronousDataStore(WRAPPED_DATA_STORE, 100);
-
-	/** the data for each page */
-	private static final byte[] DATA = new byte[] { 1, 2, 3 };
+	private static final IPageStore ASYNC_PAGE_STORE = new AsynchronousPageStore(WRAPPED_PAGE_STORE, 100);
 
 	/** the used jsessionid's */
-	private static final String[] SESSIONS = new String[] { "s1", "s2", "s3" };
+	private static final IPageContext[] CONTEXT = new IPageContext[] { createContext("s1"), createContext("s2"), createContext("s3")};
 
 	/** the ids for the stored/removed pages */
 	private static final int[] PAGE_IDS = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
@@ -74,7 +71,12 @@ public class AsynchronousDataStoreTest
 		}
 		LATCH.await();
 		executorService.shutdown();
-		DATA_STORE.destroy();
+		ASYNC_PAGE_STORE.destroy();
+	}
+
+	private static IPageContext createContext(String sessionId)
+	{
+		return new DummyPageContext(sessionId);
 	}
 
 	private static abstract class AbstractTask implements Runnable
@@ -94,15 +96,20 @@ public class AsynchronousDataStoreTest
 			}
 		}
 
-		protected String getSessionId()
+		protected IPageContext getPageContext()
 		{
-			return SESSIONS[RND.nextInt(SESSIONS.length)];
+			return CONTEXT[RND.nextInt(CONTEXT.length)];
 		}
 
 		protected int getPageId()
 		{
 			return PAGE_IDS[RND.nextInt(PAGE_IDS.length)];
 		}
+		
+		protected IManageablePage getPage()
+		{
+			return new MockPage(getPageId());
+		}
 	}
 
 	private static class StoreTask extends AbstractTask
@@ -110,7 +117,7 @@ public class AsynchronousDataStoreTest
 		@Override
 		public void r()
 		{
-			DATA_STORE.storeData(getSessionId(), getPageId(), DATA);
+			ASYNC_PAGE_STORE.addPage(getPageContext(), getPage());
 		}
 	}
 
@@ -119,7 +126,7 @@ public class AsynchronousDataStoreTest
 		@Override
 		public void r()
 		{
-			DATA_STORE.getData(getSessionId(), getPageId());
+			ASYNC_PAGE_STORE.getPage(getPageContext(), getPageId());
 		}
 	}
 
@@ -128,7 +135,7 @@ public class AsynchronousDataStoreTest
 		@Override
 		public void r()
 		{
-			DATA_STORE.removeData(getSessionId(), getPageId());
+			ASYNC_PAGE_STORE.removePage(getPageContext(), getPage());
 		}
 	}
 
@@ -137,7 +144,7 @@ public class AsynchronousDataStoreTest
 		@Override
 		public void r()
 		{
-			DATA_STORE.removeData(getSessionId());
+			ASYNC_PAGE_STORE.removeAllPages(getPageContext());
 		}
 	}
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/pageStore/AsynchronousPageStoreTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/pageStore/AsynchronousPageStoreTest.java b/wicket-core/src/test/java/org/apache/wicket/pageStore/AsynchronousPageStoreTest.java
index 4b17f69..1752e50 100644
--- a/wicket-core/src/test/java/org/apache/wicket/pageStore/AsynchronousPageStoreTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/pageStore/AsynchronousPageStoreTest.java
@@ -17,21 +17,19 @@
 package org.apache.wicket.pageStore;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
+import static org.junit.Assert.fail;
 
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
-import com.google.common.base.Stopwatch;
 import org.apache.commons.lang3.RandomUtils;
+import org.apache.wicket.mock.MockPageStore;
 import org.apache.wicket.page.IManageablePage;
 import org.apache.wicket.serialize.ISerializer;
 import org.apache.wicket.serialize.java.DeflatedJavaSerializer;
@@ -43,6 +41,8 @@ import org.junit.experimental.categories.Category;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Stopwatch;
+
 
 /**
  * AsynchronousPageStoreTest
@@ -155,31 +155,50 @@ public class AsynchronousPageStoreTest
 	@Test
 	public void storeReturnsSameInstanceOnClosePageRequest() throws InterruptedException
 	{
+		final Semaphore semaphore = new Semaphore(0);
+		
+		IPageStore store = new NoopPageStore() {
+			
+			@Override
+			public synchronized void addPage(IPageContext context, IManageablePage page)
+			{
+				try
+				{
+					semaphore.acquire();
+				}
+				catch (InterruptedException e)
+				{
+				}
+				
+				super.addPage(context, page);
+			}
+			
+			@Override
+			public IManageablePage getPage(IPageContext context, int id)
+			{
+				fail();
+				return null;
+			}
+		};
 
-		ISerializer serializer = new DeflatedJavaSerializer("applicationKey");
-		// ISerializer serializer = new DummySerializer();
-
-		IDataStore dataStore = new DiskDataStore("applicationName", new File("./target"),
-				Bytes.bytes(10000l));
-
-		// IPageStore pageStore = new DummyPageStore(new File("target/store"));
-		IPageStore pageStore = spy(new DefaultPageStore(serializer, dataStore, 0));
-
-		IPageStore asyncPageStore = new AsynchronousPageStore(pageStore, 100);
+		IPageStore asyncPageStore = new AsynchronousPageStore(store, 100);
 
 		int pageId = 0;
+		
 		String sessionId = "sessionId";
+		
+		IPageContext context = new DummyPageContext(sessionId);
 
 		DummyPage page = new DummyPage(pageId, 1000, 1000, sessionId);
-		asyncPageStore.storePage(sessionId, page);
-
-		Thread.sleep(500);
+		asyncPageStore.addPage(context, page);
 
-		IManageablePage pageBack = asyncPageStore.getPage(sessionId, pageId);
+		IManageablePage pageBack = asyncPageStore.getPage(context, pageId);
 
-		verify(pageStore, never()).getPage(sessionId, pageId);
+		semaphore.release();
 
 		assertEquals(page, pageBack);
+		
+		store.destroy();
 	}
 
 	/**
@@ -191,31 +210,55 @@ public class AsynchronousPageStoreTest
 	@Test
 	public void storeReturnsRestoredInstanceOnDistantPageRequest() throws InterruptedException
 	{
+		final Semaphore semaphore = new Semaphore(0);
+		
+		final AtomicBoolean got = new AtomicBoolean(false);
+		
+		IPageStore store = new MockPageStore() {
+			
+			@Override
+			public synchronized void addPage(IPageContext context, IManageablePage page)
+			{
+				super.addPage(context, page);
+				
+				semaphore.release();
+			}
+			
+			@Override
+			public IManageablePage getPage(IPageContext context, int id)
+			{
+				got.set(true);
+				
+				return super.getPage(context, id);
+			}
+		};
 
-		ISerializer serializer = new DeflatedJavaSerializer("applicationKey");
-		// ISerializer serializer = new DummySerializer();
-
-		IDataStore dataStore = new DiskDataStore("applicationName", new File("./target"),
-				Bytes.bytes(10000l));
-
-		// IPageStore pageStore = new DummyPageStore(new File("target/store"));
-		IPageStore pageStore = spy(new DefaultPageStore(serializer, dataStore, 0));
-
-		IPageStore asyncPageStore = new AsynchronousPageStore(pageStore, 100);
+		IPageStore asyncPageStore = new AsynchronousPageStore(store, 100);
 
 		int pageId = 0;
+		
 		String sessionId = "sessionId";
+		
+		IPageContext context = new DummyPageContext(sessionId);
 
 		DummyPage page = new DummyPage(pageId, 1000, 1000, sessionId);
-		asyncPageStore.storePage(sessionId, page);
+		asyncPageStore.addPage(context, page);
 
-		Thread.sleep(1500);
+		try
+		{
+			semaphore.acquire();
+		}
+		catch (InterruptedException e)
+		{
+		}
 
-		IManageablePage pageBack = asyncPageStore.getPage(sessionId, pageId);
+		IManageablePage pageBack = asyncPageStore.getPage(context, pageId);
 
-		verify(pageStore, times(1)).getPage(sessionId, pageId);
+		semaphore.release();
 
-		assertNotEquals(page, pageBack);
+		assertEquals(page, pageBack);
+		
+		store.destroy();
 	}
 
 	/**
@@ -315,18 +358,13 @@ public class AsynchronousPageStoreTest
 		// ISerializer serializer = new DummySerializer();
 		ISerializer serializer = new DeflatedJavaSerializer("applicationKey");
 
-		IDataStore dataStore = new DiskDataStore("applicationName", new File("./target"),
-				Bytes.bytes(10000l));
-
-		// IPageStore pageStore = new DummyPageStore(new File("target/store")) {
-		IPageStore pageStore = new DefaultPageStore(serializer, dataStore, 0)
-		{
-
+		IPageStore pageStore = new DiskPageStore("applicationName", serializer, new File("./target"),
+				Bytes.bytes(10000l)) {
 			@Override
-			public void storePage(String sessionId, IManageablePage page)
+			public void addPage(IPageContext context, IManageablePage page)
 			{
 
-				super.storePage(sessionId, page);
+				super.addPage(context, page);
 
 				lock.countDown();
 			}
@@ -341,20 +379,21 @@ public class AsynchronousPageStoreTest
 			for (int i = 1; i <= sessions; i++)
 			{
 				String sessionId = String.valueOf(i);
+				IPageContext context = new DummyPageContext(sessionId);
 				Metrics metrics = new Metrics();
 
 				stopwatch.reset();
 				DummyPage page = new DummyPage(pageId, around(writeMillis), around(readMillis),
 						sessionId);
 				stopwatch.start();
-				asyncPageStore.storePage(sessionId, page);
+				asyncPageStore.addPage(context, page);
 				metrics.storedPage = page;
 				metrics.storingMillis = stopwatch.elapsed(TimeUnit.MILLISECONDS);
 
 				stopwatch.reset();
 				stopwatch.start();
 				metrics.restoredPage = DummyPage.class
-						.cast(asyncPageStore.getPage(sessionId, pageId));
+						.cast(asyncPageStore.getPage(context, pageId));
 				metrics.restoringMillis = stopwatch.elapsed(TimeUnit.MILLISECONDS);
 
 				results.add(metrics);
@@ -363,6 +402,8 @@ public class AsynchronousPageStoreTest
 
 		lock.await(pages * sessions * (writeMillis + readMillis), TimeUnit.MILLISECONDS);
 
+		pageStore.destroy();
+		
 		return results;
 	}
 

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/pageStore/DefaultPageStoreTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/pageStore/DefaultPageStoreTest.java b/wicket-core/src/test/java/org/apache/wicket/pageStore/DefaultPageStoreTest.java
deleted file mode 100644
index 2ffa376..0000000
--- a/wicket-core/src/test/java/org/apache/wicket/pageStore/DefaultPageStoreTest.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.pageStore;
-
-import org.apache.wicket.serialize.ISerializer;
-
-/**
- * Tests for DefaultPageStore
- */
-public class DefaultPageStoreTest extends AbstractPageStoreTest
-{
-	@Override
-	protected IPageStore createPageStore(ISerializer serializer, IDataStore dataStore, int maxEntries)
-	{
-		return new DefaultPageStore(serializer, dataStore, maxEntries);
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/pageStore/DiskDataStoreTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/pageStore/DiskDataStoreTest.java b/wicket-core/src/test/java/org/apache/wicket/pageStore/DiskDataStoreTest.java
index e69ead6..933870e 100644
--- a/wicket-core/src/test/java/org/apache/wicket/pageStore/DiskDataStoreTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/pageStore/DiskDataStoreTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.wicket.pageStore;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -26,6 +27,8 @@ import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import org.apache.wicket.page.IManageablePage;
+import org.apache.wicket.serialize.java.JavaSerializer;
 import org.apache.wicket.settings.StoreSettings;
 import org.apache.wicket.util.SlowTests;
 import org.apache.wicket.util.lang.Bytes;
@@ -36,6 +39,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
+ * Test for {@link DiskDataStore}.
  */
 @Category(SlowTests.class)
 public class DiskDataStoreTest extends Assert
@@ -59,20 +63,33 @@ public class DiskDataStoreTest extends Assert
 	private static final int SLEEP_MAX = 10;
 	private static final int THREAD_COUNT = 20;
 	private static final int READ_MODULO = 100;
+	
+	private static final ConcurrentHashMap<String, IPageContext> contexts = new ConcurrentHashMap<>();
+	
+	private static IPageContext getContext(String sessionId) {
+		IPageContext context = new DummyPageContext();
+		
+		IPageContext existing = contexts.putIfAbsent(sessionId, context);
+		return existing != null ? existing : context;
+	}
 
-	private static class File
+	private static class TaskPage implements IManageablePage
 	{
 		private final String sessionId;
-		private final int id;
+		
+		private int id;
 
-		private byte first;
-		private byte last;
-		private int length;
+		private byte[] data;
 
-		public File(String sessionId, int id)
+		public TaskPage(String sessionId, int id)
 		{
 			this.sessionId = sessionId;
+			
 			this.id = id;
+
+			int length = FILE_SIZE_MIN + random.nextInt(FILE_SIZE_MAX - FILE_SIZE_MIN);
+			data = new byte[length];
+			random.nextBytes(data);
 		}
 
 		public String getSessionId()
@@ -80,51 +97,54 @@ public class DiskDataStoreTest extends Assert
 			return sessionId;
 		}
 
-		public int getId()
+		public boolean check(TaskPage other)
 		{
-			return id;
-		}
-
-		public byte[] generateData()
-		{
-			length = FILE_SIZE_MIN + random.nextInt(FILE_SIZE_MAX - FILE_SIZE_MIN);
-			byte data[] = new byte[length];
-			random.nextBytes(data);
-			first = data[0];
-			last = data[data.length - 1];
-			return data;
-		}
-
-		public boolean checkData(byte data[])
-		{
-			if (data == null)
-			{
-				log.error("data[] should never be null");
-				return false;
-			}
-			if (data.length != length)
+			if (other.data.length != other.data.length)
 			{
 				log.error("data.length != length");
 				return false;
 			}
-			if (first != data[0])
+			if (other.id != other.id)
 			{
-				log.error("first != data[0]");
+				log.error("data.id != id");
 				return false;
 			}
-			if (last != data[data.length - 1])
+			if (other.sessionId != other.sessionId)
 			{
-				log.error("last != data[data.length - 1]");
+				log.error("data.sessionId != sessionId");
 				return false;
 			}
 			return true;
 		}
+
+		@Override
+		public boolean isPageStateless()
+		{
+			return false;
+		}
+
+		@Override
+		public int getPageId()
+		{
+			return id;
+		}
+
+		@Override
+		public void detach()
+		{
+		}
+
+		@Override
+		public boolean setFreezePageId(boolean freeze)
+		{
+			return false;
+		}
 	}
 
 	private final Map<String, AtomicInteger> sessionCounter = new ConcurrentHashMap<String, AtomicInteger>();
-	private final ConcurrentLinkedQueue<File> filesToSave = new ConcurrentLinkedQueue<File>();
-	private final ConcurrentLinkedQueue<File> filesToRead1 = new ConcurrentLinkedQueue<File>();
-	private final ConcurrentLinkedQueue<File> filesToRead2 = new ConcurrentLinkedQueue<File>();
+	private final ConcurrentLinkedQueue<TaskPage> pagesToSave = new ConcurrentLinkedQueue<TaskPage>();
+	private final ConcurrentLinkedQueue<TaskPage> filesToRead1 = new ConcurrentLinkedQueue<TaskPage>();
+	private final ConcurrentLinkedQueue<TaskPage> filesToRead2 = new ConcurrentLinkedQueue<TaskPage>();
 
 	private final AtomicInteger read1Count = new AtomicInteger(0);
 	private final AtomicInteger read2Count = new AtomicInteger(0);
@@ -164,15 +184,15 @@ public class DiskDataStoreTest extends Assert
 		for (int i = 0; i < FILES_COUNT; ++i)
 		{
 			String session = randomSessionId();
-			File file = new File(session, nextSessionId(session));
+			TaskPage file = new TaskPage(session, nextSessionId(session));
 			long now = System.nanoTime();
-			filesToSave.add(file);
+			pagesToSave.add(file);
 			long duration = System.nanoTime() - now;
 			saveTime.addAndGet((int)duration);
 		}
 	}
 
-	private IDataStore dataStore;
+	private IPageStore pageStore;
 
 	/**
 	 * Stores RuntimeException into a field.
@@ -205,21 +225,20 @@ public class DiskDataStoreTest extends Assert
 		@Override
 		protected void doRun()
 		{
-			File file;
+			TaskPage page;
 
-			while ((file = filesToSave.poll()) != null || saveCount.get() < FILES_COUNT)
+			while ((page = pagesToSave.poll()) != null || saveCount.get() < FILES_COUNT)
 			{
-				if (file != null)
+				if (page != null)
 				{
-					byte data[] = file.generateData();
-					dataStore.storeData(file.getSessionId(), file.getId(), data);
+					pageStore.addPage(getContext(page.getSessionId()), page);
 
 					if (saveCount.get() % READ_MODULO == 0)
 					{
-						filesToRead1.add(file);
+						filesToRead1.add(page);
 					}
 					saveCount.incrementAndGet();
-					bytesWritten.addAndGet(data.length);
+					bytesWritten.addAndGet(page.data.length);
 				}
 
 				try
@@ -242,20 +261,20 @@ public class DiskDataStoreTest extends Assert
 		@Override
 		protected void doRun()
 		{
-			File file;
-			while ((file = filesToRead1.poll()) != null || !saveDone.get())
+			TaskPage page;
+			while ((page = filesToRead1.poll()) != null || !saveDone.get())
 			{
-				if (file != null)
+				if (page != null)
 				{
-					byte bytes[] = dataStore.getData(file.getSessionId(), file.getId());
-					if (file.checkData(bytes) == false)
+					TaskPage other = (TaskPage)pageStore.getPage(getContext(page.getSessionId()), page.getPageId());
+					if (page.check(other) == false)
 					{
 						failures.incrementAndGet();
 						log.error("Detected error number: " + failures.get());
 					}
-					filesToRead2.add(file);
+					filesToRead2.add(page);
 					read1Count.incrementAndGet();
-					bytesRead.addAndGet(bytes.length);
+					bytesRead.addAndGet(other.data.length);
 				}
 
 				try
@@ -277,19 +296,19 @@ public class DiskDataStoreTest extends Assert
 		@Override
 		protected void doRun()
 		{
-			File file;
-			while ((file = filesToRead2.poll()) != null || !read1Done.get())
+			TaskPage page;
+			while ((page = filesToRead2.poll()) != null || !read1Done.get())
 			{
-				if (file != null)
+				if (page != null)
 				{
-					byte bytes[] = dataStore.getData(file.getSessionId(), file.getId());
-					if (file.checkData(bytes) == false)
+					TaskPage other = (TaskPage)pageStore.getPage(getContext(page.getSessionId()), page.getPageId());
+					if (page.check(other) == false)
 					{
 						failures.incrementAndGet();
 						log.error("Detected error number: " + failures.get());
 					}
 					read2Count.incrementAndGet();
-					bytesRead.addAndGet(bytes.length);
+					bytesRead.addAndGet(other.data.length);
 				}
 
 				try
@@ -356,7 +375,7 @@ public class DiskDataStoreTest extends Assert
 
 		for (String s : sessionCounter.keySet())
 		{
-			dataStore.removeData(s);
+			pageStore.removeAllPages(getContext(s));
 		}
 	}
 
@@ -369,15 +388,15 @@ public class DiskDataStoreTest extends Assert
 		generateFiles();
 
 		StoreSettings storeSettings = new StoreSettings(null);
-		java.io.File fileStoreFolder = storeSettings.getFileStoreFolder();
-
-		dataStore = new DiskDataStore("app1", fileStoreFolder, MAX_SIZE_PER_SESSION);
+		File fileStoreFolder = storeSettings.getFileStoreFolder();
 		int asynchronousQueueCapacity = storeSettings.getAsynchronousQueueCapacity();
-		dataStore = new AsynchronousDataStore(dataStore, asynchronousQueueCapacity);
+
+		pageStore = new DiskPageStore("app1", new JavaSerializer("app1"), fileStoreFolder, MAX_SIZE_PER_SESSION);
+		pageStore = new AsynchronousPageStore(pageStore, asynchronousQueueCapacity);
 
 		doTestDataStore();
 
-		dataStore.destroy();
+		pageStore.destroy();
 	}
 
 	/**
@@ -391,7 +410,7 @@ public class DiskDataStoreTest extends Assert
 	{
 		StoreSettings storeSettings = new StoreSettings(null);
 		java.io.File fileStoreFolder = storeSettings.getFileStoreFolder();
-		DiskDataStore store = new DiskDataStore("sessionFolderName", fileStoreFolder, MAX_SIZE_PER_SESSION);
+		DiskPageStore store = new DiskPageStore("sessionFolderName", new JavaSerializer("sessionFolderName"), fileStoreFolder, MAX_SIZE_PER_SESSION);
 
 		String sessionId = "abcdefg";
 		java.io.File sessionFolder = store.getSessionFolder(sessionId, true);
@@ -401,7 +420,7 @@ public class DiskDataStoreTest extends Assert
 		assertTrue(absolutePath.contains("1279"));
 		assertTrue(absolutePath.contains("abcdefg"));
 
-		DiskDataStore.SessionEntry sessionEntry = new DiskDataStore.SessionEntry(store, sessionId);
+		DiskPageStore.DiskData sessionEntry = new DiskPageStore.DiskData(store, sessionId);
 		sessionEntry.unbind();
 		// assert that the 'sessionId' folder and the parents two levels up are removed
 		assertFalse(sessionFolder.getParentFile().getParentFile().exists());

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/pageStore/DummyPageContext.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/pageStore/DummyPageContext.java b/wicket-core/src/test/java/org/apache/wicket/pageStore/DummyPageContext.java
new file mode 100644
index 0000000..0cd0b08
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/pageStore/DummyPageContext.java
@@ -0,0 +1,103 @@
+/*
+ * 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.pageStore;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.wicket.MetaDataEntry;
+import org.apache.wicket.MetaDataKey;
+import org.apache.wicket.pageStore.IPageContext;
+
+/**
+ * Dummy implementation of a page context - suitable for a single session only.
+ */
+public class DummyPageContext implements IPageContext
+{
+
+	final String sessionId;
+
+	MetaDataEntry<?>[] requestData;
+	
+	MetaDataEntry<?>[] sessionData;
+
+	Map<String, Object> sessionAttributes = new HashMap<>();
+
+	public DummyPageContext()
+	{
+		this("dummy_id");
+	}
+
+	public DummyPageContext(String sessionId)
+	{
+		this.sessionId = sessionId;
+	}
+
+	@Override
+	public <T> void setRequestData(MetaDataKey<T> key, T value)
+	{
+		requestData = key.set(requestData, value);
+	}
+
+	@Override
+	public <T> T getRequestData(MetaDataKey<T> key)
+	{
+		return key.get(requestData);
+	}
+
+	@Override
+	public <T extends Serializable> void setSessionAttribute(String key, T value)
+	{
+		sessionAttributes.put(key, value);
+	}
+	
+	@SuppressWarnings("unchecked")
+	@Override
+	public <T extends Serializable> T getSessionAttribute(String key)
+	{
+		return (T)sessionAttributes.get(key);
+	}
+	
+	@Override
+	public <T extends Serializable> void setSessionData(MetaDataKey<T> key, T value)
+	{
+		sessionData = key.set(sessionData, value);
+	}
+
+	@Override
+	public <T extends Serializable> T getSessionData(MetaDataKey<T> key)
+	{
+		return key.get(sessionData);
+	}
+
+	@Override
+	public void bind()
+	{
+	}
+
+	@Override
+	public String getSessionId()
+	{
+		return sessionId;
+	}
+
+	public void clearRequest()
+	{
+		requestData = null;
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/pageStore/GroupingPageStoreTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/pageStore/GroupingPageStoreTest.java b/wicket-core/src/test/java/org/apache/wicket/pageStore/GroupingPageStoreTest.java
new file mode 100644
index 0000000..b863074
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/pageStore/GroupingPageStoreTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.pageStore;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.Serializable;
+
+import org.apache.wicket.MetaDataKey;
+import org.apache.wicket.MockPage;
+import org.apache.wicket.mock.MockPageStore;
+import org.apache.wicket.page.IManageablePage;
+import org.junit.Test;
+
+/**
+ * Test for {@link GroupingPageStore}.
+ * 
+ * @author svenmeier
+ */
+public class GroupingPageStoreTest
+{
+
+	private static MetaDataKey<Serializable> KEY = new MetaDataKey<Serializable>()
+	{
+	}; 
+	
+	private static Serializable VALUE = new Serializable()
+	{
+	};
+
+	@Test
+	public void test()
+	{
+		String sessionId = "foo";
+		
+		IPageStore store = new MockPageStore() {
+			
+			public void addPage(IPageContext context, IManageablePage page) {
+
+				context.setSessionAttribute("attribute", "value");
+				context.setSessionData(KEY, VALUE);
+
+				assertEquals(sessionId + "_" + group(page), context.getSessionId());
+				
+				super.addPage(context, page);
+			}
+			
+			@Override
+			public void removeAllPages(IPageContext context)
+			{
+				assertEquals(sessionId + "_group0", context.getSessionId());
+				
+				super.removeAllPages(context);
+			}
+		};
+		
+		IPageStore groupingStore = new GroupingPageStore(store, 2) {
+			@Override
+			protected String getGroup(IManageablePage page)
+			{
+				return group(page);
+			}
+		};
+		
+		DummyPageContext context = new DummyPageContext(sessionId) {
+			@Override
+			public <T extends Serializable> void setSessionData(MetaDataKey<T> key, T value)
+			{
+				assertFalse("session not set directly in session", value == VALUE);
+				
+				super.setSessionData(key, value);
+			}
+			
+			@Override
+			public <T extends Serializable> void setSessionAttribute(String key, T value)
+			{
+				assertTrue("key starts with group", key.startsWith("attribute_group"));
+				
+				super.setSessionAttribute(key, value);
+			}
+		};
+		
+		groupingStore.addPage(context, new MockPage(0));
+		groupingStore.addPage(context, new MockPage(1));
+		groupingStore.addPage(context, new MockPage(10));
+		groupingStore.addPage(context, new MockPage(11));
+		groupingStore.addPage(context, new MockPage(2));
+		groupingStore.addPage(context, new MockPage(21));
+		
+	}
+
+	protected String group(IManageablePage page)
+	{
+		return "group" + page.getPageId() / 10;
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/pageStore/InMemoryPageStoreTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/pageStore/InMemoryPageStoreTest.java b/wicket-core/src/test/java/org/apache/wicket/pageStore/InMemoryPageStoreTest.java
new file mode 100644
index 0000000..ab1dcec
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/pageStore/InMemoryPageStoreTest.java
@@ -0,0 +1,31 @@
+/*
+ * 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.pageStore;
+
+import org.apache.wicket.serialize.ISerializer;
+
+/**
+ * Tests for {@link InMemoryPageStore}
+ */
+public class InMemoryPageStoreTest extends AbstractPageStoreTest
+{
+	@Override
+	protected IPageStore createPageStore(ISerializer serializer, int maxEntries)
+	{
+		return new InMemoryPageStore("test", maxEntries);
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/pageStore/InSessionPageStoreTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/pageStore/InSessionPageStoreTest.java b/wicket-core/src/test/java/org/apache/wicket/pageStore/InSessionPageStoreTest.java
new file mode 100644
index 0000000..4fa2f4e
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/pageStore/InSessionPageStoreTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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.pageStore;
+
+import org.apache.wicket.serialize.ISerializer;
+
+/**
+ * Test for {@link InSessionPageStore}. 
+ */
+public class InSessionPageStoreTest extends AbstractPageStoreTest
+{
+	@Override
+	protected IPageStore createPageStore(ISerializer serializer, int maxEntries)
+	{
+		return new InSessionPageStore(new NoopPageStore(), serializer, maxEntries);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/pageStore/NoopDataStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/pageStore/NoopDataStore.java b/wicket-core/src/test/java/org/apache/wicket/pageStore/NoopDataStore.java
deleted file mode 100644
index 0cadaee..0000000
--- a/wicket-core/src/test/java/org/apache/wicket/pageStore/NoopDataStore.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.pageStore;
-
-/**
- * An implementation of IDataStore that does nothing
- */
-public class NoopDataStore implements IDataStore
-{
-	@Override
-	public byte[] getData(String sessionId, int id)
-	{
-		return null;
-	}
-
-	@Override
-	public void removeData(String sessionId, int id)
-	{
-	}
-
-	@Override
-	public void removeData(String sessionId)
-	{
-	}
-
-	@Override
-	public void storeData(String sessionId, int id, byte[] data)
-	{
-	}
-
-	@Override
-	public void destroy()
-	{
-	}
-
-	@Override
-	public boolean isReplicated()
-	{
-		return false;
-	}
-
-	@Override
-	public boolean canBeAsynchronous()
-	{
-		return false;
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/pageStore/PerSessionPageStoreTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/pageStore/PerSessionPageStoreTest.java b/wicket-core/src/test/java/org/apache/wicket/pageStore/PerSessionPageStoreTest.java
deleted file mode 100644
index 0cd4a2a..0000000
--- a/wicket-core/src/test/java/org/apache/wicket/pageStore/PerSessionPageStoreTest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.pageStore;
-
-import org.apache.wicket.MockPage;
-import org.apache.wicket.serialize.ISerializer;
-import org.junit.Test;
-
-/**
- * Tests for PerSessionPageStore
- */
-public class PerSessionPageStoreTest extends AbstractPageStoreTest
-{
-	@Override
-	protected IPageStore createPageStore(ISerializer serializer, IDataStore dataStore, int maxEntries)
-	{
-		return new PerSessionPageStore(serializer, dataStore, maxEntries);
-	}
-
-	/**
-	 * Verify that it is OK to store more pages than {@code maxEntries}
-	 * if they are in different sessions
-	 */
-	@Test
-	@Override
-	public void maxSizeDifferentSessions()
-	{
-		String sessionId2 = "0987654321";
-
-		pageStore.storePage(sessionId, new MockPage(pageId));
-
-		assertNotNull(pageStore.getPage(sessionId, pageId));
-
-		pageStore.storePage(sessionId2, new MockPage(pageId));
-
-		assertNotNull(pageStore.getPage(sessionId, pageId));
-		assertNotNull(pageStore.getPage(sessionId2, pageId));
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/pageStore/disk/PageWindowManagerTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/pageStore/disk/PageWindowManagerTest.java b/wicket-core/src/test/java/org/apache/wicket/pageStore/disk/PageWindowManagerTest.java
new file mode 100644
index 0000000..57ce54d
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/pageStore/disk/PageWindowManagerTest.java
@@ -0,0 +1,303 @@
+/*
+ * 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.pageStore.disk;
+
+import java.security.SecureRandom;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.pageStore.disk.PageWindowManager.FileWindow;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test for {@link PageWindowManager}.
+ * 
+ * @author Matej Knopp
+ */
+public class PageWindowManagerTest extends Assert
+{
+	/**
+	 * https://issues.apache.org/jira/browse/WICKET-4572
+	 */
+	@Test
+	public void removeObsoleteIndices()
+	{
+		int page0id = 0,
+			page1id = 1,
+			page2id = 2;
+		int maxSize = 10;
+
+		PageWindowManager manager = new PageWindowManager(maxSize);
+
+		// Add few pages.
+		// All of them fully occupy the max space in the pageWindowManager.
+		// So adding N+1st page removes the Nth page.
+		manager.createPageWindow(page0id, WebPage.class, maxSize);
+		FileWindow page0Window = manager.getPageWindow(page0id);
+		assertWindow(page0Window, page0id, page0Window.getFilePartOffset(), page0Window.getFilePartSize());
+
+		manager.createPageWindow(page1id, WebPage.class, maxSize);
+		FileWindow page1Window = manager.getPageWindow(page1id);
+		assertWindow(page1Window, page1id, page1Window.getFilePartOffset(), page1Window.getFilePartSize());
+
+		// Try to get a page which has been lost with the adding of page1
+		assertNull("Page0 must be lost when Page1 has been added.", manager.getPageWindow(page0id));
+
+		manager.createPageWindow(page2id, WebPage.class, maxSize);
+		FileWindow page2Window = manager.getPageWindow(page2id);
+		assertWindow(page2Window, page2id, page2Window.getFilePartOffset(), page2Window.getFilePartSize());
+
+		// Try to get a page which has been lost with the adding of page2
+		assertNull("Page1 must be lost when Page2 has been added.", manager.getPageWindow(page1id));
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void addRemove()
+	{
+		PageWindowManager manager = new PageWindowManager(300);
+		FileWindow window;
+
+		window = manager.createPageWindow(1, WebPage.class, 50);
+		assertWindow(window, 1, 0, 50);
+
+		window = manager.createPageWindow(2, WebPage.class, 40);
+		assertWindow(window, 2, 50, 40);
+
+		assertEquals(manager.getTotalSize(), 90);
+
+		window = manager.createPageWindow(2, WebPage.class, 30);
+		assertWindow(window, 2, 50, 30);
+		assertEquals(manager.getTotalSize(), 80);
+
+		manager.removePage(2);
+		assertEquals(manager.getTotalSize(), 50);
+
+		window = manager.createPageWindow(3, WebPage.class, 30);
+		assertWindow(window, 3, 50, 30);
+		assertEquals(manager.getTotalSize(), 80);
+	}
+
+	/**
+	 * 
+	 */
+	@Test
+	public void pageWindowCycle()
+	{
+		PageWindowManager manager = new PageWindowManager(100);
+		FileWindow window;
+
+		window = manager.createPageWindow(1, WebPage.class, 30);
+
+		window = manager.createPageWindow(2, WebPage.class, 30);
+
+		window = manager.createPageWindow(3, WebPage.class, 30);
+
+		assertWindow(window, 3, 60, 30);
+
+		window = manager.createPageWindow(4, WebPage.class, 30);
+
+		assertWindow(window, 4, 90, 30);
+
+		// should start at the beginging
+
+		window = manager.createPageWindow(5, WebPage.class, 20);
+
+		assertWindow(window, 5, 0, 20);
+
+		assertNull(manager.getPageWindow(1));
+
+		window = manager.getPageWindow(2);
+		assertWindow(window, 2, 30, 30);
+
+		window = manager.createPageWindow(6, WebPage.class, 10);
+
+		assertWindow(window, 6, 20, 10);
+
+		window = manager.getPageWindow(2);
+		assertWindow(window, 2, 30, 30);
+
+		window = manager.createPageWindow(6, WebPage.class, 30);
+		assertWindow(window, 6, 20, 30);
+
+		assertNull(manager.getPageWindow(2));
+		assertNotNull(manager.getPageWindow(3));
+
+		window = manager.createPageWindow(6, WebPage.class, 60);
+		assertWindow(window, 6, 20, 60);
+
+		assertNull(manager.getPageWindow(3));
+
+		window = manager.createPageWindow(7, WebPage.class, 20);
+		assertWindow(window, 7, 80, 20);
+
+		assertNotNull(manager.getPageWindow(7));
+
+		// should start at the beginning again
+
+		window = manager.createPageWindow(8, WebPage.class, 10);
+		assertWindow(window, 8, 0, 10);
+
+		assertNull(manager.getPageWindow(5));
+		assertNotNull(manager.getPageWindow(6));
+
+		window = manager.createPageWindow(9, WebPage.class, 20);
+		assertWindow(window, 9, 10, 20);
+
+		assertNull(manager.getPageWindow(6));
+		assertNotNull(manager.getPageWindow(7));
+
+		window = manager.createPageWindow(10, WebPage.class, 20);
+		assertWindow(window, 10, 30, 20);
+
+		assertNull(manager.getPageWindow(6));
+		assertNotNull(manager.getPageWindow(7));
+
+		// make sure when replacing a page that's not last the old "instance" is
+		// not valid anymore
+
+		manager.createPageWindow(8, WebPage.class, 10);
+
+		window = manager.getPageWindow(8);
+		assertWindow(window, 8, 50, 10);
+	}
+
+
+	private void assertWindow(FileWindow window, int pageId, int filePartOffset, int filePartSize)
+	{
+		assertTrue(window.getPageId() == pageId && window.getFilePartOffset() == filePartOffset &&
+			window.getFilePartSize() == filePartSize);
+	}
+
+	/** how many operations to execute */
+	private static final int EXECUTIONS = 10000;
+
+	/** used to wait the executions */
+	private static final CountDownLatch LATCH = new CountDownLatch(EXECUTIONS);
+
+	private final PageWindowManager pageWindowManager = new PageWindowManager(1000L);
+
+	/** the execution types */
+	private final Runnable[] TASKS = new Runnable[]
+	{
+		new CreatePageWindowTask(pageWindowManager),
+		new GetPageWindowTask(pageWindowManager),
+		new RemovePageInSessionTask(pageWindowManager)
+	};
+
+	private static final SecureRandom RND = new SecureRandom();
+
+	/**
+	 * Executes random mutator and accessor operations on {@link org.apache.wicket.pageStore.AsynchronousDataStore} validating
+	 * that the used data structures can be used simultaneously.
+	 *
+	 * @throws Exception
+	 */
+	@Test
+	public void randomOperations() throws Exception
+	{
+		ExecutorService executorService = Executors.newFixedThreadPool(50);
+
+		for (int i = 0; i < EXECUTIONS; i++)
+		{
+			Runnable task = TASKS[RND.nextInt(TASKS.length)];
+			executorService.submit(task);
+		}
+		LATCH.await();
+		executorService.shutdown();
+	}
+
+	private static abstract class AbstractTask implements Runnable
+	{
+		/** the ids for the stored/removed pages */
+		private static final int[] PAGE_IDS = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+
+		protected final PageWindowManager pageWindowManager;
+
+		private AbstractTask(PageWindowManager pageWindowManager)
+		{
+			this.pageWindowManager = pageWindowManager;
+		}
+
+		protected abstract void r();
+
+		@Override
+		public void run()
+		{
+			try
+			{
+				r();
+			}
+			finally
+			{
+				LATCH.countDown();
+			}
+		}
+
+		protected int getPageId()
+		{
+			return PAGE_IDS[RND.nextInt(PAGE_IDS.length)];
+		}
+	}
+
+	private static class CreatePageWindowTask extends AbstractTask
+	{
+		private CreatePageWindowTask(PageWindowManager pageWindowManager)
+		{
+			super(pageWindowManager);
+		}
+
+		@Override
+		public void r()
+		{
+			pageWindowManager.createPageWindow(getPageId(), WebPage.class, 1000);
+		}
+	}
+
+	private static class GetPageWindowTask extends AbstractTask
+	{
+		private GetPageWindowTask(PageWindowManager pageWindowManager)
+		{
+			super(pageWindowManager);
+		}
+
+		@Override
+		public void r()
+		{
+			pageWindowManager.getPageWindow(getPageId());
+		}
+	}
+
+	private static class RemovePageInSessionTask extends AbstractTask
+	{
+		private RemovePageInSessionTask(PageWindowManager pageWindowManager)
+		{
+			super(pageWindowManager);
+		}
+
+		@Override
+		public void r()
+		{
+			pageWindowManager.removePage(getPageId());
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/DummyPageManagerContext.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/DummyPageManagerContext.java b/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/DummyPageManagerContext.java
deleted file mode 100644
index 99d4c90..0000000
--- a/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/DummyPageManagerContext.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.pageStore.memory;
-
-import java.io.Serializable;
-
-import org.apache.wicket.page.IPageManagerContext;
-
-/**
- */
-public class DummyPageManagerContext implements IPageManagerContext
-{
-
-	Serializable attribute = null;
-	Object requestData;
-
-	@Override
-	public void setRequestData(Object data)
-	{
-		requestData = data;
-	}
-
-	@Override
-	public Object getRequestData()
-	{
-		return requestData;
-	}
-
-	@Override
-	public void setSessionAttribute(String key, Serializable value)
-	{
-		attribute = value;
-	}
-
-	@Override
-	public Serializable getSessionAttribute(String key)
-	{
-		return attribute;
-	}
-
-	@Override
-	public void bind()
-	{
-	}
-
-	@Override
-	public String getSessionId()
-	{
-		return "dummy_id";
-	}
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/HttpSessionDataStoreTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/HttpSessionDataStoreTest.java b/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/HttpSessionDataStoreTest.java
deleted file mode 100644
index 10b5268..0000000
--- a/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/HttpSessionDataStoreTest.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * 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.pageStore.memory;
-
-import static org.junit.Assert.assertArrayEquals;
-
-import org.apache.wicket.util.tester.WicketTestCase;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- */
-public class HttpSessionDataStoreTest extends WicketTestCase
-{
-	final String sessionId = "anything";
-
-	final int pageId = 1;
-
-	final byte[] PAGE1 = new byte[] { 1 };
-	final byte[] PAGE2 = new byte[] { 2 };
-
-	HttpSessionDataStore store;
-
-	/**
-	 * before()
-	 */
-	@Before
-	public void before()
-	{
-		store = new HttpSessionDataStore(new DummyPageManagerContext(), new NoopEvictionStrategy());
-	}
-
-	/**
-	 * after()
-	 */
-	@After
-	public void after()
-	{
-		store.destroy();
-	}
-
-	/**
-	 * storePage()
-	 */
-	@Test
-	public void storePage()
-	{
-		assertNull(store.getData(sessionId, pageId));
-
-		store.storeData(sessionId, pageId, PAGE1);
-		assertArrayEquals(PAGE1, store.getData(sessionId, pageId));
-	}
-
-	/**
-	 * removePage1()
-	 */
-	@Test
-	public void removePage1()
-	{
-		assertNull(store.getData(sessionId, pageId));
-
-		store.storeData(sessionId, pageId, PAGE1);
-
-		assertNotNull(store.getData(sessionId, pageId));
-
-		store.removeData(sessionId, pageId);
-
-		assertNull(store.getData(sessionId, pageId));
-	}
-
-	/**
-	 * removePage2()
-	 */
-	@Test
-	public void removePage2()
-	{
-		assertNull(store.getData(sessionId, pageId));
-
-		store.storeData(sessionId, pageId, PAGE1);
-
-		assertNotNull(store.getData(sessionId, pageId));
-
-		store.removeData(sessionId);
-
-		assertNull(store.getData(sessionId, pageId));
-	}
-
-
-	private static final class NoopEvictionStrategy implements IDataStoreEvictionStrategy
-	{
-
-		@Override
-		public void evict(PageTable pageTable)
-		{
-		}
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/MemorySizeEvictionStrategyTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/MemorySizeEvictionStrategyTest.java b/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/MemorySizeEvictionStrategyTest.java
deleted file mode 100644
index 50033fa..0000000
--- a/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/MemorySizeEvictionStrategyTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.pageStore.memory;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import org.apache.wicket.util.lang.Bytes;
-import org.apache.wicket.core.util.lang.WicketObjects;
-import org.junit.Test;
-
-/***/
-public class MemorySizeEvictionStrategyTest
-{
-	final byte[] PAGE1 = new byte[] { 1 };
-	final byte[] PAGE2 = new byte[] { 2, 3 };
-
-	/***/
-	@Test
-	public void evict()
-	{
-		PageTable pageTable = new PageTable();
-
-		long sizeOfEmptyPageTable = WicketObjects.sizeof(pageTable);
-
-		// evict to empty page table
-		MemorySizeEvictionStrategy strategy = new MemorySizeEvictionStrategy(
-			Bytes.bytes(sizeOfEmptyPageTable));
-		pageTable.storePage(PAGE1.length, PAGE1);
-		assertEquals(1, pageTable.size());
-		strategy.evict(pageTable);
-		assertEquals(0, pageTable.size());
-		long currentSize = WicketObjects.sizeof(pageTable);
-		assertTrue("Current size: |" + currentSize + "|, strategy size: |" + sizeOfEmptyPageTable +
-			"|", currentSize <= sizeOfEmptyPageTable);
-
-		// evict to page table with size: empty + PAGE2
-		pageTable.storePage(PAGE2.length, PAGE2);
-		long sizeOfWithPage2 = WicketObjects.sizeof(pageTable);
-		strategy = new MemorySizeEvictionStrategy(Bytes.bytes(sizeOfWithPage2));
-		pageTable.storePage(PAGE1.length, PAGE1);
-		assertEquals(2, pageTable.size());
-		strategy.evict(pageTable);
-		// the following assertion depends on the fact that PAGE2 has
-		// bigger size than PAGE1
-		assertEquals(1, pageTable.size());
-		currentSize = WicketObjects.sizeof(pageTable);
-		assertTrue("Current size: |" + currentSize + "|, strategy size: |" + sizeOfWithPage2 + "|",
-			currentSize <= sizeOfWithPage2);
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/PageNumberEvictionStrategyTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/PageNumberEvictionStrategyTest.java b/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/PageNumberEvictionStrategyTest.java
deleted file mode 100644
index d3bf35b..0000000
--- a/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/PageNumberEvictionStrategyTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.pageStore.memory;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import org.junit.Test;
-
-/***/
-public class PageNumberEvictionStrategyTest
-{
-
-	final byte[] PAGE1 = new byte[] { 1 };
-	final byte[] PAGE2 = new byte[] { 2, 3 };
-
-	/***/
-	@Test
-	public void evict()
-	{
-		// evict to page table with one page only
-		PageNumberEvictionStrategy strategy = new PageNumberEvictionStrategy(1);
-
-		PageTable pageTable = new PageTable();
-
-		pageTable.storePage(PAGE1.length, PAGE1);
-		assertEquals(1, pageTable.size());
-		strategy.evict(pageTable);
-		assertEquals(1, pageTable.size());
-		assertNotNull(pageTable.getPage(PAGE1.length));
-
-		pageTable.storePage(PAGE2.length, PAGE2);
-		assertEquals(2, pageTable.size());
-		strategy.evict(pageTable);
-		assertEquals(1, pageTable.size());
-		assertNotNull(pageTable.getPage(PAGE2.length));
-		assertNull(pageTable.getPage(PAGE1.length));
-	}
-
-	/**
-	 * The number of pages must be at least '1'
-	 */
-	@Test(expected = IllegalArgumentException.class)
-	public void greaterThanZero()
-	{
-		new PageNumberEvictionStrategy(0);
-	}
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/PageTableTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/PageTableTest.java b/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/PageTableTest.java
deleted file mode 100644
index 062068f..0000000
--- a/wicket-core/src/test/java/org/apache/wicket/pageStore/memory/PageTableTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.pageStore.memory;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-import org.junit.Test;
-
-/***/
-public class PageTableTest
-{
-
-	final byte[] data = new byte[] { 1 };
-
-	/***/
-	@Test
-	public void getOldest()
-	{
-		PageTable pageTable = new PageTable();
-
-		assertNull(pageTable.getOldest());
-
-		pageTable.storePage(1, data);
-		// index: 1
-		assertEquals(Integer.valueOf(1), pageTable.getOldest());
-
-		pageTable.storePage(2, data);
-		// index: 2, 1
-		assertEquals(Integer.valueOf(1), pageTable.getOldest());
-
-		pageTable.storePage(3, data);
-		// index: 3, 2, 1
-		assertEquals(Integer.valueOf(1), pageTable.getOldest());
-
-		pageTable.getPage(1);
-		// index: 1, 3, 2
-		assertEquals(Integer.valueOf(2), pageTable.getOldest());
-
-		pageTable.removePage(2);
-		// index: 1, 3
-		assertEquals(Integer.valueOf(3), pageTable.getOldest());
-	}
-}