http://git-wip-us.apache.org/repos/asf/wicket/blob/c43d3a33/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 04c4f19..046a1ec 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
@@ -20,19 +20,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.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.jupiter.api.Test;
 
 /**
@@ -58,42 +58,39 @@ 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);
-               assertNotNull(serializedSessionEntry,
-                       "Wicket needs to be able to serialize the session 
entry");
-
-               // simulate loading of the persisted http session initiated by 
the web container
-               // when starting an application
-               ObjectInputStream in = new ObjectInputStream(
-                       new ByteArrayInputStream(serializedSessionEntry));
+               byte[] serializedSessionData = new 
JavaSerializer(APP_NAME).serialize(sessionData.get());
+               assertNotNull(serializedSessionData, "Wicket needs to be able 
to serialize the session entry");
 
                // WicketFilter is not initialized so there is no Application 
available yet
                assertFalse(Application.exists(), "Worker thread should be 
unaware of Wicket application");
 
-               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(loadedSessionEntry,
-                       "Wicket needs to be able to deserialize the session 
entry regardless the application availability");
+               assertNotNull(sessionData.get(), "Wicket needs to be able to 
deserialize the session entry regardless the application availability");
 
                // 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);
@@ -102,12 +99,36 @@ 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(), 
Integer.MAX_VALUE, new JavaSerializer(APP_NAME));
+               
+               return new PageManager(store) {
+                       @Override
+                       protected IPageContext createPageContext()
+                       {
+                               return new DummyPageContext() {
+                                       @Override
+                                       public <T extends Serializable> T 
setSessionData(MetaDataKey<T> key, T value)
+                                       {
+                                               super.setSessionData(key, 
value);
+                                               
+                                               sessionData.set(value);
+                                               
+                                               return 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/c43d3a33/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 c4ae2fa..0000000
--- 
a/wicket-core/src/test/java/org/apache/wicket/page/persistent/disk/PageWindowManagerTest.java
+++ /dev/null
@@ -1,305 +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 static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-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.jupiter.api.Test;
-
-/**
- * @author Matej Knopp
- */
-public class PageWindowManagerTest
-{
-       /**
-        * https://issues.apache.org/jira/browse/WICKET-4572
-        */
-       @Test
-       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(manager.getPageWindow(page0id), "Page0 must be lost 
when Page1 has been added.");
-
-               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(manager.getPageWindow(page1id), "Page1 must be lost 
when Page2 has been added.");
-       }
-
-       /**
-        * 
-        */
-       @Test
-       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
-       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
-       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 };
-
-               final PageWindowManager pageWindowManager;
-
-               private AbstractTask(PageWindowManager pageWindowManager)
-               {
-                       this.pageWindowManager = pageWindowManager;
-               }
-
-               protected abstract void r();
-
-               @Override
-               public void run()
-               {
-                       try
-                       {
-                               r();
-                       }
-                       finally
-                       {
-                               LATCH.countDown();
-                       }
-               }
-
-               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/c43d3a33/wicket-core/src/test/java/org/apache/wicket/pageStore/AbstractConcurrentPageStoreTest.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/pageStore/AbstractConcurrentPageStoreTest.java
 
b/wicket-core/src/test/java/org/apache/wicket/pageStore/AbstractConcurrentPageStoreTest.java
new file mode 100644
index 0000000..3006313
--- /dev/null
+++ 
b/wicket-core/src/test/java/org/apache/wicket/pageStore/AbstractConcurrentPageStoreTest.java
@@ -0,0 +1,375 @@
+/*
+ * 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.jupiter.api.Assertions.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Concurrency tests for stores.
+ */
+public abstract class AbstractConcurrentPageStoreTest
+{
+       /** Log for reporting. */
+       private static final Logger log = LoggerFactory
+               .getLogger(AbstractConcurrentPageStoreTest.class);
+
+       private static final Random random = new Random();
+       private static final int FILE_SIZE_MIN = 1024 * 200;
+       private static final int FILE_SIZE_MAX = 1024 * 300;
+       private static final int SESSION_COUNT = 50;
+       private static final int FILES_COUNT = 1000;
+       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(sessionId);
+
+               IPageContext existing = contexts.putIfAbsent(sessionId, 
context);
+               return existing != null ? existing : context;
+       }
+
+       private static class TaskPage implements IManageablePage
+       {
+               private final String sessionId;
+
+               private int id;
+
+               private byte[] data;
+
+               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()
+               {
+                       return sessionId;
+               }
+
+               public boolean check(TaskPage other)
+               {
+                       if (other.data.length != other.data.length)
+                       {
+                               log.error("data.length != length");
+                               return false;
+                       }
+                       if (other.id != other.id)
+                       {
+                               log.error("data.id != id");
+                               return false;
+                       }
+                       if (other.sessionId != other.sessionId)
+                       {
+                               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<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);
+       private final AtomicInteger saveCount = new AtomicInteger(0);
+
+       private final AtomicBoolean saveDone = new AtomicBoolean(false);
+       private final AtomicBoolean read1Done = new AtomicBoolean(false);
+       private final AtomicBoolean read2Done = new AtomicBoolean(false);
+
+       private final AtomicInteger failures = new AtomicInteger();
+
+       private final AtomicInteger bytesWritten = new AtomicInteger(0);
+       private final AtomicInteger bytesRead = new AtomicInteger(0);
+
+       private final AtomicInteger saveTime = new AtomicInteger(0);
+
+       private RuntimeException exceptionThrownByThread;
+
+       private String randomSessionId()
+       {
+               List<String> s = new ArrayList<String>(sessionCounter.keySet());
+               return s.get(random.nextInt(s.size()));
+       }
+
+       private int nextSessionId(String sessionId)
+       {
+               AtomicInteger i = sessionCounter.get(sessionId);
+               return i.incrementAndGet();
+       }
+
+       private void generateSessionsAndPages()
+       {
+               for (int i = 0; i < SESSION_COUNT; ++i)
+               {
+                       sessionCounter.put(UUID.randomUUID().toString(), new 
AtomicInteger(0));
+               }
+               for (int i = 0; i < FILES_COUNT; ++i)
+               {
+                       String session = randomSessionId();
+                       TaskPage file = new TaskPage(session, 
nextSessionId(session));
+                       long now = System.nanoTime();
+                       pagesToSave.add(file);
+                       long duration = System.nanoTime() - now;
+                       saveTime.addAndGet((int)duration);
+               }
+       }
+
+       private IPageStore pageStore;
+
+       /**
+        * Stores RuntimeException into a field.
+        */
+       private abstract class ExceptionCapturingRunnable implements Runnable
+       {
+               @Override
+               public final void run()
+               {
+                       try
+                       {
+                               doRun();
+                       }
+                       catch (RuntimeException e)
+                       {
+                               exceptionThrownByThread = e;
+                       }
+               }
+
+               /**
+                * Called by {@link #run()}. Thrown RuntimeExceptions are 
stores into a field for later
+                * check.
+                */
+               protected abstract void doRun();
+       }
+
+       // Store/Save data in store
+       private class SaveRunnable extends ExceptionCapturingRunnable
+       {
+               @Override
+               protected void doRun()
+               {
+                       TaskPage page;
+
+                       while ((page = pagesToSave.poll()) != null || 
saveCount.get() < FILES_COUNT)
+                       {
+                               if (page != null)
+                               {
+                                       
pageStore.addPage(getContext(page.getSessionId()), page);
+
+                                       if (saveCount.get() % READ_MODULO == 0)
+                                       {
+                                               filesToRead1.add(page);
+                                       }
+                                       saveCount.incrementAndGet();
+                                       
bytesWritten.addAndGet(page.data.length);
+                               }
+
+                               try
+                               {
+                                       Thread.sleep(random.nextInt(SLEEP_MAX));
+                               }
+                               catch (InterruptedException e)
+                               {
+                                       log.error(e.getMessage(), e);
+                               }
+                       }
+
+                       saveDone.set(true);
+               }
+       };
+
+       // Read data from store
+       private class Read1Runnable extends ExceptionCapturingRunnable
+       {
+               @Override
+               protected void doRun()
+               {
+                       TaskPage page;
+                       while ((page = filesToRead1.poll()) != null || 
!saveDone.get())
+                       {
+                               if (page != null)
+                               {
+                                       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(page);
+                                       read1Count.incrementAndGet();
+                                       bytesRead.addAndGet(other.data.length);
+                               }
+
+                               try
+                               {
+                                       Thread.sleep(random.nextInt(SLEEP_MAX));
+                               }
+                               catch (InterruptedException e)
+                               {
+                                       log.error(e.getMessage(), e);
+                               }
+                       }
+
+                       read1Done.set(true);
+               }
+       };
+
+       private class Read2Runnable extends ExceptionCapturingRunnable
+       {
+               @Override
+               protected void doRun()
+               {
+                       TaskPage page;
+                       while ((page = filesToRead2.poll()) != null || 
!read1Done.get())
+                       {
+                               if (page != null)
+                               {
+                                       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(other.data.length);
+                               }
+
+                               try
+                               {
+                                       Thread.sleep(random.nextInt(SLEEP_MAX));
+                               }
+                               catch (InterruptedException e)
+                               {
+                                       log.error(e.getMessage(), e);
+                               }
+                       }
+
+                       read2Done.set(true);
+               }
+       }
+
+       protected void doTestStore(IPageStore pageStore)
+       {
+               this.pageStore = new AsynchronousPageStore(pageStore, 100);
+
+               generateSessionsAndPages();
+               
+               log.info("Starting...");
+               long start = System.currentTimeMillis();
+
+               for (int i = 0; i < THREAD_COUNT; ++i)
+               {
+                       new Thread(new Read1Runnable()).start();
+               }
+
+               for (int i = 0; i < THREAD_COUNT; ++i)
+               {
+                       new Thread(new Read2Runnable()).start();
+               }
+
+               for (int i = 0; i < THREAD_COUNT; ++i)
+               {
+                       new Thread(new SaveRunnable()).start();
+               }
+
+               while (!(read1Done.get() && read2Done.get() && saveDone.get()))
+               {
+                       try
+                       {
+                               Thread.sleep(50);
+                       }
+                       catch (InterruptedException e)
+                       {
+                               log.error(e.getMessage(), e);
+                       }
+               }
+
+               if (exceptionThrownByThread != null)
+               {
+                       throw new RuntimeException("One of the worker threads 
failed.",
+                               exceptionThrownByThread);
+               }
+
+               long duration = System.currentTimeMillis() - start;
+
+               log.info("Took: " + duration + " ms");
+               log.info("Save: " + saveCount.intValue() + " files, " + 
bytesWritten.get() + " bytes");
+               log.info("Read: " + (read1Count.get() + read2Count.get()) + " 
files, " + bytesRead.get()
+                       + " bytes");
+
+               log.info("Average save time (ns): " + (double)saveTime.get() / 
(double)saveCount.get());
+
+               assertEquals(0, failures.get());
+
+               for (String s : sessionCounter.keySet())
+               {
+                       pageStore.removeAllPages(getContext(s));
+               }
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/c43d3a33/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 17dd2c3..3fd83d3 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
@@ -26,25 +26,24 @@ import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-abstract class AbstractPageStoreTest
+public abstract class AbstractPageStoreTest
 {
-       final String sessionId = "1234567890";
-       final int pageId = 123;
-       private final ISerializer serializer = new 
JavaSerializer(getClass().getName());
-       private final IDataStore dataStore = new NoopDataStore();
-       private int maxEntries = 1;
-       IPageStore pageStore = null;
+       protected final String sessionId = "1234567890";
+       protected final int pageId = 123;
+       protected final ISerializer serializer = new 
JavaSerializer(getClass().getName());
+       protected int maxEntries = 1;
+       protected IPageStore pageStore = null;
 
        @BeforeEach
-       void before()
+       public void before()
        {
-               pageStore = createPageStore(serializer, dataStore, maxEntries);
+               pageStore = createPageStore(serializer, maxEntries);
        }
 
-       abstract IPageStore createPageStore(ISerializer serializer, IDataStore 
dataStore, int maxEntries);
+       protected abstract IPageStore createPageStore(ISerializer serializer, 
int maxEntries);
 
        @AfterEach
-       void after()
+       public void after()
        {
                if (pageStore != null)
                {
@@ -59,9 +58,11 @@ abstract class AbstractPageStoreTest
        @Test
        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));
        }
 
        /**
@@ -70,46 +71,65 @@ abstract class AbstractPageStoreTest
        @Test
        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
        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));
+       }
+
+       @Test
+       void removeAllPages()
+       {
+               IPageContext context = new DummyPageContext(sessionId);
+               
+               pageStore.addPage(context, new MockPage(pageId));
+
+               assertNotNull(pageStore.getPage(context, pageId));
+
+               pageStore.removeAllPages(context);
+
+               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
        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));
        }
 
        /**
@@ -119,15 +139,16 @@ abstract class AbstractPageStoreTest
        @Test
        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/c43d3a33/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 3c02261..40da190 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,25 +21,22 @@ 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.jupiter.api.Test;
 
 /**
- * Tests for {@link AsynchronousDataStore}
+ * Tests for {@link AsynchronousPageStore}
  */
 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 };
@@ -57,13 +54,13 @@ public class AsynchronousDataStoreTest
        private static final SecureRandom RND = new SecureRandom();
 
        /**
-        * Executes random mutator and accessor operations on {@link 
AsynchronousDataStore} validating
+        * Executes random mutator and accessor operations on {@link 
AsynchronousPageStore} validating
         * that the used data structures can be used simultaneously.
         * 
         * @throws Exception
         */
        @Test
-    void randomOperations() throws Exception
+       void randomOperations() throws Exception
        {
                ExecutorService executorService = 
Executors.newFixedThreadPool(50);
 
@@ -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
                        }
                }
 
-               String getSessionId()
+               protected IPageContext getPageContext()
                {
-                       return SESSIONS[RND.nextInt(SESSIONS.length)];
+                       return CONTEXT[RND.nextInt(CONTEXT.length)];
                }
 
-               int getPageId()
+               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/c43d3a33/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 b4fb451..0717230 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,20 +17,23 @@
 package org.apache.wicket.pageStore;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.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.jupiter.api.Assertions.fail;
 
 import java.io.IOException;
+import java.io.Serializable;
 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 org.apache.commons.lang3.RandomUtils;
+import org.apache.wicket.MetaDataKey;
+import org.apache.wicket.MockPage;
+import org.apache.wicket.WicketRuntimeException;
+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;
@@ -41,6 +44,7 @@ import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+
 import com.google.common.base.Stopwatch;
 
 
@@ -50,7 +54,7 @@ import com.google.common.base.Stopwatch;
  * @author manuelbarzi
  */
 @Tag(WicketTestTag.SLOW)
-class AsynchronousPageStoreTest
+public class AsynchronousPageStoreTest
 {
        /** Log for reporting. */
        private static final Logger log = 
LoggerFactory.getLogger(AsynchronousPageStoreTest.class);
@@ -155,31 +159,51 @@ class AsynchronousPageStoreTest
        @Test
        void storeReturnsSameInstanceOnClosePageRequest() throws 
InterruptedException
        {
+               final Semaphore semaphore = new Semaphore(0);
+               
+               IPageStore store = new NoopPageStore() {
+                       
+                       @Override
+                       public synchronized void addPage(IPageContext context, 
IManageablePage page)
+                       {
+                               try
+                               {
+                                       // wait until the page was get below
+                                       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);
+               asyncPageStore.addPage(context, page);
 
-               Thread.sleep(500);
+               IManageablePage pageBack = asyncPageStore.getPage(context, 
pageId);
 
-               IManageablePage pageBack = asyncPageStore.getPage(sessionId, 
pageId);
-
-               verify(pageStore, never()).getPage(sessionId, pageId);
+               semaphore.release();
 
                assertEquals(page, pageBack);
+               
+               store.destroy();
        }
 
        /**
@@ -191,31 +215,55 @@ class AsynchronousPageStoreTest
        @Test
        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();
        }
 
        /**
@@ -288,6 +336,121 @@ class AsynchronousPageStoreTest
                assertTrue(sync > 0);
        }
 
+       private MetaDataKey<Serializable> KEY1 = new MetaDataKey<Serializable>()
+       {
+       };
+       
+       private MetaDataKey<Serializable> KEY2 = new MetaDataKey<Serializable>()
+       {
+       };
+       
+       /**
+        * Store does not allow modifications when pages are added 
asynchronously.
+        */
+       @Test
+       public void storeAsynchronousContextClosed() throws InterruptedException
+       {
+               IPageStore store = new MockPageStore() {
+                       
+                       @Override
+                       public boolean canBeAsynchronous(IPageContext context)
+                       {
+                               // can bind and get session id
+                               context.bind();
+                               context.getSessionId();
+                               
+                               // can access request data
+                               context.getRequestData(KEY1);
+                               context.setRequestData(KEY1, "value1");
+
+                               // can access session data
+                               context.getSessionData(KEY1);
+                               context.setSessionData(KEY1, "value1");
+
+                               // can access session
+                               context.getSessionAttribute("key1");
+                               context.setSessionAttribute("key1", "value1");
+
+                               return true;
+                       }
+                       
+                       @Override
+                       public synchronized void addPage(IPageContext context, 
IManageablePage page)
+                       {
+                               // can bind and get session id
+                               context.bind();
+                               context.getSessionId();
+                               
+                               // cannot access request
+                               try {
+                                       context.getRequestData(KEY1);
+                                       fail();
+                               } catch (WicketRuntimeException expected) {
+                               }
+                               try {
+                                       context.setRequestData(KEY1, "value1");
+                                       fail();
+                               } catch (WicketRuntimeException expected) {
+                               }
+                               try {
+                                       context.getRequestData(KEY2);
+                                       fail();
+                               } catch (WicketRuntimeException expected) {
+                               }
+                               try {
+                                       context.setRequestData(KEY2, "value2");
+                                       fail();
+                               } catch (WicketRuntimeException expected) {
+                               }
+
+                               // can read session data 
+                               context.getSessionData(KEY1);
+                               context.getSessionData(KEY2);
+                               // .. but cannot set
+                               try {
+                                       context.setSessionData(KEY1, "value1");
+                                       fail();
+                               } catch (WicketRuntimeException expected) {
+                               }
+                               try {
+                                       context.setSessionData(KEY2, "value2");
+                                       fail();
+                               } catch (WicketRuntimeException expected) {
+                               }
+                               
+                               // can read session already read
+                               context.getSessionAttribute("key1");
+                               // .. but nothing new
+                               try {
+                                       context.getSessionAttribute("key2");
+                                       fail();
+                               } catch (WicketRuntimeException expected) {
+                               }
+                               // .. but cannot set
+                               try {
+                                       context.setSessionAttribute("key1", 
"value1");
+                                       fail();
+                               } catch (WicketRuntimeException expected) {
+                               }
+                               try {
+                                       context.setSessionAttribute("key2", 
"value2");
+                                       fail();
+                               } catch (WicketRuntimeException expected) {
+                               }
+                       }
+               };
+
+               IPageStore asyncPageStore = new AsynchronousPageStore(store, 
100);
+
+               MockPage page = new MockPage();
+               
+               IPageContext context = new DummyPageContext();
+               
+               asyncPageStore.addPage(context , page);
+               
+               store.destroy();
+       }
+       
        // test run
 
        private class Metrics
@@ -315,18 +478,13 @@ 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", new 
File("./target"),
+                               Bytes.bytes(10000l), serializer) {
                        @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 +499,21 @@ 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 +522,8 @@ class AsynchronousPageStoreTest
 
                lock.await(pages * sessions * (writeMillis + readMillis), 
TimeUnit.MILLISECONDS);
 
+               pageStore.destroy();
+               
                return results;
        }
 

http://git-wip-us.apache.org/repos/asf/wicket/blob/c43d3a33/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/c43d3a33/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
deleted file mode 100644
index 9187f79..0000000
--- 
a/wicket-core/src/test/java/org/apache/wicket/pageStore/DiskDataStoreTest.java
+++ /dev/null
@@ -1,413 +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 static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.wicket.settings.StoreSettings;
-import org.apache.wicket.util.WicketTestTag;
-import org.apache.wicket.util.lang.Bytes;
-import org.junit.jupiter.api.Tag;
-import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- */
-@Tag(WicketTestTag.SLOW)
-public class DiskDataStoreTest
-{
-       /** Log for reporting. */
-       private static final Logger log = 
LoggerFactory.getLogger(DiskDataStoreTest.class);
-
-       /**
-        * Construct.
-        */
-       DiskDataStoreTest()
-       {
-       }
-
-       private static final Random random = new Random();
-       private static final int FILE_SIZE_MIN = 1024 * 200;
-       private static final int FILE_SIZE_MAX = 1024 * 300;
-       private static final Bytes MAX_SIZE_PER_SESSION = Bytes.megabytes(10);
-       private static final int SESSION_COUNT = 50;
-       private static final int FILES_COUNT = 1000;
-       private static final int SLEEP_MAX = 10;
-       private static final int THREAD_COUNT = 20;
-       private static final int READ_MODULO = 100;
-
-       private static class File
-       {
-               private final String sessionId;
-               private final int id;
-
-               private byte first;
-               private byte last;
-               private int length;
-
-               File(String sessionId, int id)
-               {
-                       this.sessionId = sessionId;
-                       this.id = id;
-               }
-
-               String getSessionId()
-               {
-                       return sessionId;
-               }
-
-               int getId()
-               {
-                       return id;
-               }
-
-               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;
-               }
-
-               boolean checkData(byte data[])
-               {
-                       if (data == null)
-                       {
-                               log.error("data[] should never be null");
-                               return false;
-                       }
-                       if (data.length != length)
-                       {
-                               log.error("data.length != length");
-                               return false;
-                       }
-                       if (first != data[0])
-                       {
-                               log.error("first != data[0]");
-                               return false;
-                       }
-                       if (last != data[data.length - 1])
-                       {
-                               log.error("last != data[data.length - 1]");
-                               return false;
-                       }
-                       return true;
-               }
-       }
-
-       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 AtomicInteger read1Count = new AtomicInteger(0);
-       private final AtomicInteger read2Count = new AtomicInteger(0);
-       private final AtomicInteger saveCount = new AtomicInteger(0);
-
-       private final AtomicBoolean saveDone = new AtomicBoolean(false);
-       private final AtomicBoolean read1Done = new AtomicBoolean(false);
-       private final AtomicBoolean read2Done = new AtomicBoolean(false);
-
-       private final AtomicInteger failures = new AtomicInteger();
-
-       private final AtomicInteger bytesWritten = new AtomicInteger(0);
-       private final AtomicInteger bytesRead = new AtomicInteger(0);
-
-       private final AtomicInteger saveTime = new AtomicInteger(0);
-
-       private RuntimeException exceptionThrownByThread;
-
-       private String randomSessionId()
-       {
-               List<String> s = new ArrayList<String>(sessionCounter.keySet());
-               return s.get(random.nextInt(s.size()));
-       }
-
-       private int nextSessionId(String sessionId)
-       {
-               AtomicInteger i = sessionCounter.get(sessionId);
-               return i.incrementAndGet();
-       }
-
-       private void generateFiles()
-       {
-               for (int i = 0; i < SESSION_COUNT; ++i)
-               {
-                       sessionCounter.put(UUID.randomUUID().toString(), new 
AtomicInteger(0));
-               }
-               for (int i = 0; i < FILES_COUNT; ++i)
-               {
-                       String session = randomSessionId();
-                       File file = new File(session, nextSessionId(session));
-                       long now = System.nanoTime();
-                       filesToSave.add(file);
-                       long duration = System.nanoTime() - now;
-                       saveTime.addAndGet((int)duration);
-               }
-       }
-
-       private IDataStore dataStore;
-
-       /**
-        * Stores RuntimeException into a field.
-        */
-       private abstract class ExceptionCapturingRunnable implements Runnable
-       {
-               @Override
-               public final void run()
-               {
-                       try
-                       {
-                               doRun();
-                       }
-                       catch (RuntimeException e)
-                       {
-                               exceptionThrownByThread = e;
-                       }
-               }
-
-               /**
-                * Called by {@link #run()}. Thrown RuntimeExceptions are 
stores into a field for later
-                * check.
-                */
-               protected abstract void doRun();
-       }
-
-       // Store/Save data in DataStore
-       private class SaveRunnable extends ExceptionCapturingRunnable
-       {
-               @Override
-               protected void doRun()
-               {
-                       File file;
-
-                       while ((file = filesToSave.poll()) != null || 
saveCount.get() < FILES_COUNT)
-                       {
-                               if (file != null)
-                               {
-                                       byte data[] = file.generateData();
-                                       
dataStore.storeData(file.getSessionId(), file.getId(), data);
-
-                                       if (saveCount.get() % READ_MODULO == 0)
-                                       {
-                                               filesToRead1.add(file);
-                                       }
-                                       saveCount.incrementAndGet();
-                                       bytesWritten.addAndGet(data.length);
-                               }
-
-                               try
-                               {
-                                       Thread.sleep(random.nextInt(SLEEP_MAX));
-                               }
-                               catch (InterruptedException e)
-                               {
-                                       log.error(e.getMessage(), e);
-                               }
-                       }
-
-                       saveDone.set(true);
-               }
-       };
-
-       // Read data from DataStore
-       private class Read1Runnable extends ExceptionCapturingRunnable
-       {
-               @Override
-               protected void doRun()
-               {
-                       File file;
-                       while ((file = filesToRead1.poll()) != null || 
!saveDone.get())
-                       {
-                               if (file != null)
-                               {
-                                       byte bytes[] = 
dataStore.getData(file.getSessionId(), file.getId());
-                                       if (file.checkData(bytes) == false)
-                                       {
-                                               failures.incrementAndGet();
-                                               log.error("Detected error 
number: " + failures.get());
-                                       }
-                                       filesToRead2.add(file);
-                                       read1Count.incrementAndGet();
-                                       bytesRead.addAndGet(bytes.length);
-                               }
-
-                               try
-                               {
-                                       Thread.sleep(random.nextInt(SLEEP_MAX));
-                               }
-                               catch (InterruptedException e)
-                               {
-                                       log.error(e.getMessage(), e);
-                               }
-                       }
-
-                       read1Done.set(true);
-               }
-       };
-
-       private class Read2Runnable extends ExceptionCapturingRunnable
-       {
-               @Override
-               protected void doRun()
-               {
-                       File file;
-                       while ((file = filesToRead2.poll()) != null || 
!read1Done.get())
-                       {
-                               if (file != null)
-                               {
-                                       byte bytes[] = 
dataStore.getData(file.getSessionId(), file.getId());
-                                       if (file.checkData(bytes) == false)
-                                       {
-                                               failures.incrementAndGet();
-                                               log.error("Detected error 
number: " + failures.get());
-                                       }
-                                       read2Count.incrementAndGet();
-                                       bytesRead.addAndGet(bytes.length);
-                               }
-
-                               try
-                               {
-                                       Thread.sleep(random.nextInt(SLEEP_MAX));
-                               }
-                               catch (InterruptedException e)
-                               {
-                                       log.error(e.getMessage(), e);
-                               }
-                       }
-
-                       read2Done.set(true);
-               }
-       }
-
-       private void doTestDataStore()
-       {
-               log.info("Starting...");
-               long start = System.currentTimeMillis();
-
-               for (int i = 0; i < THREAD_COUNT; ++i)
-               {
-                       new Thread(new Read1Runnable()).start();
-               }
-
-               for (int i = 0; i < THREAD_COUNT; ++i)
-               {
-                       new Thread(new Read2Runnable()).start();
-               }
-
-               for (int i = 0; i < THREAD_COUNT; ++i)
-               {
-                       new Thread(new SaveRunnable()).start();
-               }
-
-               while (!(read1Done.get() && read2Done.get() && saveDone.get()))
-               {
-                       try
-                       {
-                               Thread.sleep(50);
-                       }
-                       catch (InterruptedException e)
-                       {
-                               log.error(e.getMessage(), e);
-                       }
-               }
-
-               if (exceptionThrownByThread != null)
-               {
-                       throw new RuntimeException("One of the worker threads 
failed.", exceptionThrownByThread);
-               }
-
-               long duration = System.currentTimeMillis() - start;
-
-               log.info("Took: " + duration + " ms");
-               log.info("Save: " + saveCount.intValue() + " files, " + 
bytesWritten.get() + " bytes");
-               log.info("Read: " + (read1Count.get() + read2Count.get()) + " 
files, " + bytesRead.get() +
-                       " bytes");
-
-               log.info("Average save time (ns): " + (double)saveTime.get() / 
(double)saveCount.get());
-
-               assertEquals(0, failures.get());
-
-               for (String s : sessionCounter.keySet())
-               {
-                       dataStore.removeData(s);
-               }
-       }
-
-       /**
-        * store()
-        */
-       @Test
-       void store()
-       {
-               generateFiles();
-
-               StoreSettings storeSettings = new StoreSettings(null);
-               java.io.File fileStoreFolder = 
storeSettings.getFileStoreFolder();
-
-               dataStore = new DiskDataStore("app1", fileStoreFolder, 
MAX_SIZE_PER_SESSION);
-               int asynchronousQueueCapacity = 
storeSettings.getAsynchronousQueueCapacity();
-               dataStore = new AsynchronousDataStore(dataStore, 
asynchronousQueueCapacity);
-
-               doTestDataStore();
-
-               dataStore.destroy();
-       }
-
-       /**
-        * https://issues.apache.org/jira/browse/WICKET-4478
-        *
-        * Tests that the folder where a session data is put is partitioned, 
i.e.
-        * it is put in folders which names are automatically calculated on the 
fly.
-        */
-       @Test
-       void sessionFolderName()
-       {
-               StoreSettings storeSettings = new StoreSettings(null);
-               java.io.File fileStoreFolder = 
storeSettings.getFileStoreFolder();
-               DiskDataStore store = new DiskDataStore("sessionFolderName", 
fileStoreFolder, MAX_SIZE_PER_SESSION);
-
-               String sessionId = "abcdefg";
-               java.io.File sessionFolder = store.getSessionFolder(sessionId, 
true);
-               String absolutePath = sessionFolder.getAbsolutePath();
-               
assertTrue(absolutePath.contains("sessionFolderName-filestore"));
-               assertTrue(absolutePath.contains("7141"));
-               assertTrue(absolutePath.contains("1279"));
-               assertTrue(absolutePath.contains("abcdefg"));
-
-               DiskDataStore.SessionEntry sessionEntry = new 
DiskDataStore.SessionEntry(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/c43d3a33/wicket-core/src/test/java/org/apache/wicket/pageStore/DiskPageStoreTest.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/pageStore/DiskPageStoreTest.java 
b/wicket-core/src/test/java/org/apache/wicket/pageStore/DiskPageStoreTest.java
new file mode 100644
index 0000000..dc9d534
--- /dev/null
+++ 
b/wicket-core/src/test/java/org/apache/wicket/pageStore/DiskPageStoreTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.IOException;
+import java.nio.file.Files;
+
+import org.apache.wicket.serialize.java.JavaSerializer;
+import org.apache.wicket.util.WicketTestTag;
+import org.apache.wicket.util.lang.Bytes;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test for {@link DiskPageStore}.
+ */
+@Tag(WicketTestTag.SLOW)
+public class DiskPageStoreTest extends AbstractConcurrentPageStoreTest
+{
+
+       private static final Bytes MAX_SIZE_PER_SESSION = Bytes.megabytes(10);
+
+       /**
+        * @throws IOException 
+        */
+       @Test
+       void store() throws IOException
+       {
+               File fileStoreFolder = Files.createTempDirectory(null).toFile();
+
+               IPageStore pageStore = new DiskPageStore("app1", 
fileStoreFolder, MAX_SIZE_PER_SESSION, new JavaSerializer("app1"));
+
+               doTestStore(pageStore);
+
+               pageStore.destroy();
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/c43d3a33/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..88fbb06
--- /dev/null
+++ 
b/wicket-core/src/test/java/org/apache/wicket/pageStore/DummyPageContext.java
@@ -0,0 +1,110 @@
+/*
+ * 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> T setSessionData(MetaDataKey<T> key, T 
value)
+       {
+               sessionData = key.set(sessionData, value);
+               
+               return 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;
+       }
+       
+       public void clearSession() {
+               sessionAttributes.clear();
+               sessionData = null;
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/c43d3a33/wicket-core/src/test/java/org/apache/wicket/pageStore/FilePageStoreTest.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/pageStore/FilePageStoreTest.java 
b/wicket-core/src/test/java/org/apache/wicket/pageStore/FilePageStoreTest.java
new file mode 100644
index 0000000..86f393a
--- /dev/null
+++ 
b/wicket-core/src/test/java/org/apache/wicket/pageStore/FilePageStoreTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.IOException;
+import java.nio.file.Files;
+
+import org.apache.wicket.serialize.java.JavaSerializer;
+import org.apache.wicket.util.WicketTestTag;
+import org.apache.wicket.util.lang.Bytes;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test for {@link FilePageStore}.
+ */
+@Tag(WicketTestTag.SLOW)
+public class FilePageStoreTest extends AbstractConcurrentPageStoreTest
+{
+       
+       private static final Bytes MAX_SIZE_PER_SESSION = Bytes.megabytes(10);
+       
+       /**
+        * @throws IOException 
+        */
+       @Test
+       void store() throws IOException
+       {
+               File folder = Files.createTempDirectory(null).toFile();
+
+               IPageStore pageStore = new FilePageStore("app1", folder, 
MAX_SIZE_PER_SESSION, new JavaSerializer("app1"));
+
+               doTestStore(pageStore);
+
+               pageStore.destroy();
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/c43d3a33/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..e96aa92
--- /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.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.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.jupiter.api.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
+       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 + "_group1", 
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> T 
setSessionData(MetaDataKey<T> key, T value)
+                       {
+                               assertFalse(value == VALUE, "group session data 
not set directly in session");
+                               
+                               return super.setSessionData(key, value);
+                       }
+                       
+                       @Override
+                       public <T extends Serializable> void 
setSessionAttribute(String key, T value)
+                       {
+                               assertTrue(key.startsWith("attribute_group"), 
"group session attribute starts with group");
+                               
+                               super.setSessionAttribute(key, value);
+                       }
+               };
+               
+               groupingStore.addPage(context, new MockPage(0)); // group 0
+               groupingStore.addPage(context, new MockPage(1)); // group 0
+               groupingStore.addPage(context, new MockPage(10)); // group 1
+               groupingStore.addPage(context, new MockPage(11)); // group 1
+               groupingStore.addPage(context, new MockPage(2)); // group 0 
+               groupingStore.addPage(context, new MockPage(21)); // group 2, 
expels oldest group 1
+               
+       }
+
+       protected String group(IManageablePage page)
+       {
+               return "group" + page.getPageId() / 10;
+       }
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/c43d3a33/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/c43d3a33/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..4904c9b
--- /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(), maxEntries, 
serializer);
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/c43d3a33/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 8242d4f..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
- */
-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/c43d3a33/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 7205659..0000000
--- 
a/wicket-core/src/test/java/org/apache/wicket/pageStore/PerSessionPageStoreTest.java
+++ /dev/null
@@ -1,55 +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 static org.junit.jupiter.api.Assertions.assertNotNull;
-
-import org.apache.wicket.MockPage;
-import org.apache.wicket.serialize.ISerializer;
-import org.junit.jupiter.api.Test;
-
-/**
- * Tests for PerSessionPageStore
- */
-public class PerSessionPageStoreTest extends AbstractPageStoreTest
-{
-       @Override
-       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
-       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/c43d3a33/wicket-core/src/test/java/org/apache/wicket/pageStore/RequestPageStoreTest.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/pageStore/RequestPageStoreTest.java
 
b/wicket-core/src/test/java/org/apache/wicket/pageStore/RequestPageStoreTest.java
new file mode 100644
index 0000000..b7fdf7a
--- /dev/null
+++ 
b/wicket-core/src/test/java/org/apache/wicket/pageStore/RequestPageStoreTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.wicket.MockPage;
+import org.apache.wicket.mock.MockPageStore;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test for {@link RequestPageStore}
+ * 
+ * @author svenmeier
+ */
+public class RequestPageStoreTest
+{
+
+       @Test
+       void test()
+       {
+               MockPageStore mockStore = new MockPageStore();
+               
+               DummyPageContext context = new DummyPageContext();
+
+               RequestPageStore store = new RequestPageStore(mockStore);
+               
+               MockPage page1 = new MockPage(2);
+               MockPage page2 = new MockPage(3);
+               MockPage page3 = new MockPage(4);
+               
+               store.addPage(context, page1);
+               store.addPage(context, page2);
+               store.addPage(context, page3);
+               
+               assertTrue(mockStore.getPages().isEmpty(), "no pages delegated 
before detach");
+               
+               store.detach(context);
+               
+               assertEquals(3, mockStore.getPages().size(), "pages delegated 
on detach");
+               
+               mockStore.getPages().clear();
+               
+               assertNull(store.getPage(context, 2), "no page in request 
store");
+               assertNull(store.getPage(context, 3), "no page in request 
store");
+               assertNull(store.getPage(context, 4), "no page in request 
store");
+       }
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/c43d3a33/wicket-core/src/test/java/org/apache/wicket/pageStore/SerializingPageStoreTest.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/pageStore/SerializingPageStoreTest.java
 
b/wicket-core/src/test/java/org/apache/wicket/pageStore/SerializingPageStoreTest.java
new file mode 100644
index 0000000..a72a514
--- /dev/null
+++ 
b/wicket-core/src/test/java/org/apache/wicket/pageStore/SerializingPageStoreTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.jupiter.api.Assertions.assertEquals;
+
+import org.apache.wicket.MockPage;
+import org.apache.wicket.mock.MockPageStore;
+import org.apache.wicket.page.IManageablePage;
+import org.apache.wicket.serialize.java.JavaSerializer;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test for {@link SerializingPageStore}.
+ */
+public class SerializingPageStoreTest
+{
+
+       @Test
+       void test()
+       {
+               DummyPageContext context = new DummyPageContext();
+               
+               MockPageStore mockStore = new MockPageStore();
+               
+               IManageablePage original = new MockPage(1);
+               
+               SerializingPageStore store = new 
SerializingPageStore(mockStore, new JavaSerializer("test"));
+               
+               store.addPage(context, original);
+               
+               SerializedPage serialized = 
(SerializedPage)mockStore.getPage(context, 1);
+               assertEquals(1, serialized.getPageId(), "page was serialized");
+               
+               MockPage deserialized = (MockPage)store.getPage(context, 1);
+               assertEquals(1, deserialized.getPageId(), "page was 
deserialized");
+       }
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/c43d3a33/wicket-core/src/test/java/org/apache/wicket/pageStore/disk/NestedFoldersTest.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/test/java/org/apache/wicket/pageStore/disk/NestedFoldersTest.java
 
b/wicket-core/src/test/java/org/apache/wicket/pageStore/disk/NestedFoldersTest.java
new file mode 100644
index 0000000..5d65390
--- /dev/null
+++ 
b/wicket-core/src/test/java/org/apache/wicket/pageStore/disk/NestedFoldersTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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 static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test for {@link NestedFolders}.
+ */
+public class NestedFoldersTest
+{
+
+       @Test
+       public void escapedName() throws IOException
+       {
+               NestedFolders folders = new 
NestedFolders(Files.createTempDirectory(null).toFile());
+               
+               File sessionFolder = folders.get("x*x/x:x", false);
+               
+               assertEquals("x_x_x_x", sessionFolder.getName());
+       }
+
+       /**
+        * https://issues.apache.org/jira/browse/WICKET-4478
+        *
+        * Tests that the folder where a session data is put is partitioned, 
i.e. it is put in folders
+        * which names are automatically calculated on the fly.
+        * 
+        * @throws IOException
+        */
+       @Test
+       public void parentFoldersAreRemovedIfEmpty() throws IOException
+       {
+               NestedFolders folders = new 
NestedFolders(Files.createTempDirectory(null).toFile());
+
+               String sessionId = "abcdefg";
+               File sessionFolder = folders.get(sessionId, true);
+
+               assertEquals("abcdefg", sessionFolder.getName());
+               assertEquals("1279", sessionFolder.getParentFile().getName());
+               assertEquals("7141", 
sessionFolder.getParentFile().getParentFile().getName());
+               assertEquals(folders.getBase(), 
sessionFolder.getParentFile().getParentFile().getParentFile());
+
+               folders.remove(sessionId);
+
+               // assert that the 'sessionId' folder and the parents two 
levels up are removed
+               
assertFalse(sessionFolder.getParentFile().getParentFile().exists());
+       }
+
+       @Test
+       public void parentFoldersAreKeptWhenNotEmpty() throws IOException
+       {
+               NestedFolders folders = new 
NestedFolders(Files.createTempDirectory(null).toFile());
+
+               String sessionId = "hijklmn";
+               File sessionFolder = folders.get(sessionId, true);
+
+               assertEquals("hijklmn", sessionFolder.getName());
+               assertEquals("2326", sessionFolder.getParentFile().getName());
+               assertEquals("7548", 
sessionFolder.getParentFile().getParentFile().getName());
+               assertEquals(folders.getBase(), 
sessionFolder.getParentFile().getParentFile().getParentFile());
+
+               // create additional folder inside the nested folders
+               new File(sessionFolder.getParentFile().getParentFile(), 
"2345").mkdirs();
+               
+               folders.remove(sessionId);
+
+               // assert that the 'sessionId' folder and the parent one level 
up are removed
+               assertFalse(sessionFolder.getParentFile().exists());
+               // ... but the parent two levels up still exists
+               
assertTrue(sessionFolder.getParentFile().getParentFile().exists());
+
+       }
+}

Reply via email to