http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/IPersistedPage.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/IPersistedPage.java 
b/wicket-core/src/main/java/org/apache/wicket/pageStore/IPersistedPage.java
new file mode 100644
index 0000000..8a03507
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/IPersistedPage.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.pageStore;
+
+import java.io.Serializable;
+
+import org.apache.wicket.util.lang.Bytes;
+
+/**
+ * Information about a persisted page in an {@link IPersistentPageStore}.
+ * 
+ * @see IPersistentPageStore#getPersistentPages(String, int)
+ */
+public interface IPersistedPage extends Serializable
+{
+       /**
+        * Id of page.
+        */
+       int getPageId();
+
+       /**
+        * Size of page.
+        */
+       Bytes getPageSize();
+
+       /**
+        * Type of page.
+        */
+       String getPageType();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/IPersistentPageStore.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/IPersistentPageStore.java
 
b/wicket-core/src/main/java/org/apache/wicket/pageStore/IPersistentPageStore.java
new file mode 100644
index 0000000..b42746d
--- /dev/null
+++ 
b/wicket-core/src/main/java/org/apache/wicket/pageStore/IPersistentPageStore.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.pageStore;
+
+import java.util.List;
+import java.util.Set;
+
+import org.apache.wicket.util.lang.Bytes;
+
+/**
+ * A store that can provide information about stored pages.  
+ */
+public interface IPersistentPageStore extends IPageStore
+{
+
+       /**
+        * Get the identifier for pages stored for the given context.
+        */
+       String getSessionIdentifier(IPageContext context);
+
+       /**
+        * Get the identifiers for all pages stored in all contexts.
+        */
+       Set<String> getSessionIdentifiers();
+
+       /**
+        * Get information about all persisted pages with the given session 
identifier.
+        */
+       List<IPersistedPage> getPersistentPages(String sessionIdentifier);
+
+       /**
+        * Get total size of all stored pages.
+        *  
+        * @return
+        */
+       Bytes getTotalSize();
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/InMemoryPageStore.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/InMemoryPageStore.java 
b/wicket-core/src/main/java/org/apache/wicket/pageStore/InMemoryPageStore.java
new file mode 100644
index 0000000..fc11901
--- /dev/null
+++ 
b/wicket-core/src/main/java/org/apache/wicket/pageStore/InMemoryPageStore.java
@@ -0,0 +1,352 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.pageStore;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.Session;
+import org.apache.wicket.core.util.lang.WicketObjects;
+import org.apache.wicket.page.IManageablePage;
+import org.apache.wicket.util.lang.Args;
+import org.apache.wicket.util.lang.Bytes;
+
+/**
+ * A storage of pages in memory.
+ */
+public class InMemoryPageStore implements IPersistentPageStore
+{
+
+       /**
+        * A registry of all page instances.
+        */
+       private static final ConcurrentMap<String, InMemoryPageStore> 
IN_MEMORY_STORES = new ConcurrentHashMap<>();
+
+       private static final String KEY = "wicket:InMemoryPageStore";
+
+       private final Map<String, MemoryData> datas = new ConcurrentHashMap<>();
+
+       private String applicationName;
+
+       private int maxPages;
+
+       /**
+        * @param applicationName
+        *            {@link Application#getName()}
+        * @param maxPages
+        *            max pages per session
+        */
+       public InMemoryPageStore(String applicationName, int maxPages)
+       {
+               this.applicationName = Args.notNull(applicationName, 
"applicationName");
+               this.maxPages = maxPages;
+
+               IN_MEMORY_STORES.put(applicationName, this);
+       }
+
+       /**
+        * 
+        * 
+        * @return <code>true</code> always
+        */
+       @Override
+       public boolean canBeAsynchronous(IPageContext context)
+       {
+               // session attribute must be added here *before* any 
asynchronous calls
+               // when session is no longer available
+               getSessionAttribute(context, true);
+
+               return true;
+       }
+
+       protected SessionAttribute getSessionAttribute(IPageContext context, 
boolean create)
+       {
+               context.bind();
+               
+               SessionAttribute attribute = context.getSessionAttribute(KEY);
+               if (attribute == null && create)
+               {
+                       attribute = new SessionAttribute(applicationName, 
context.getSessionId());
+                       context.setSessionAttribute(KEY, attribute);
+               }
+               return attribute;
+       }
+
+       @Override
+       public void destroy()
+       {
+               datas.clear();
+
+               IN_MEMORY_STORES.remove(applicationName);
+       }
+
+       @Override
+       public IManageablePage getPage(IPageContext context, int id)
+       {
+               MemoryData data = getMemoryData(context, false);
+               if (data == null)
+               {
+                       return null;
+               }
+
+               return data.get(id);
+       }
+
+       @Override
+       public void removePage(IPageContext context, final IManageablePage page)
+       {
+               MemoryData data = getMemoryData(context, false);
+
+               if (data != null)
+               {
+                       synchronized (data)
+                       {
+                               data.remove(page);
+                       }
+               }
+       }
+
+       @Override
+       public void removeAllPages(IPageContext context)
+       {
+               MemoryData data = getMemoryData(context, false);
+
+               if (data != null)
+               {
+                       synchronized (data)
+                       {
+                               data.removeAll();
+                       }
+               }
+       }
+
+       @Override
+       public void addPage(IPageContext context, IManageablePage page)
+       {
+               MemoryData data = getMemoryData(context, true);
+
+               data.add(page, maxPages);
+       }
+
+       @Override
+       public String getSessionIdentifier(IPageContext context)
+       {
+               return context.getSessionId();
+       }
+
+       @Override
+       public Set<String> getSessionIdentifiers()
+       {
+               return datas.keySet();
+       }
+
+       @Override
+       public List<IPersistedPage> getPersistentPages(String sessionIdentifier)
+       {
+               MemoryData data = datas.get(sessionIdentifier);
+               if (data == null)
+               {
+                       return new ArrayList<>();
+               }
+
+               synchronized (data)
+               {
+                       return StreamSupport.stream(data.spliterator(), false)
+                               .map(page -> new MermoryPersistedPage(page, 
getSize(page)))
+                               .collect(Collectors.toList());
+               }
+       }
+
+       @Override
+       public Bytes getTotalSize()
+       {
+               int size = 0;
+
+               for (MemoryData data : datas.values())
+               {
+                       synchronized (data)
+                       {
+                               for (IManageablePage page : data)
+                               {
+                                       size += getSize(page);
+                               }
+                       }
+               }
+
+               return Bytes.bytes(size);
+       }
+
+       protected long getSize(IManageablePage page)
+       {
+               return WicketObjects.sizeof(page);
+       }
+
+       private MemoryData getMemoryData(IPageContext context, boolean create)
+       {
+               SessionAttribute attribute = getSessionAttribute(context, 
create);
+
+               if (!create)
+               {
+                       if (attribute == null) {
+                               return null;
+                       } else {
+                               return datas.get(attribute.identifier);
+                       }
+               }
+
+               MemoryData data = new MemoryData();
+               MemoryData existing = datas.putIfAbsent(attribute.identifier, 
data);
+               return existing != null ? existing : data;
+       }
+
+       private void removeMemoryData(String identifier)
+       {
+               datas.remove(identifier);
+       }
+       
+       /**
+        * Data kept in memory.
+        */
+       static class MemoryData implements Iterable<IManageablePage>
+       {
+               private LinkedHashMap<Integer, IManageablePage> pages = new 
LinkedHashMap<>();
+
+               @Override
+               public Iterator<IManageablePage> iterator()
+               {
+                       return pages.values().iterator();
+               }
+
+               public synchronized void add(IManageablePage page, int maxPages)
+               {
+                       pages.remove(page.getPageId());
+                       pages.put(page.getPageId(), page);
+
+                       Iterator<IManageablePage> iterator = 
pages.values().iterator();
+                       int size = pages.size();
+                       while (size > maxPages)
+                       {
+                               iterator.next();
+
+                               iterator.remove();
+                               size--;
+                       }
+               }
+
+               public void remove(IManageablePage page)
+               {
+                       pages.remove(page.getPageId());
+               }
+
+               public void removeAll()
+               {
+                       pages.clear();
+               }
+
+               public IManageablePage get(int id)
+               {
+                       IManageablePage page = pages.get(id);
+
+                       return page;
+               }
+       }
+
+       /**
+        * Attribute held in session.
+        */
+       static class SessionAttribute implements Serializable, 
HttpSessionBindingListener
+       {
+
+               private final String applicationName;
+
+               /**
+                * The identifier of the session, must not be equal to {@link 
Session#getId()}, e.g. when
+                * the container changes the id after authorization.
+                */
+               public final String identifier;
+
+               public SessionAttribute(String applicationName, String 
sessionIdentifier)
+               {
+                       this.applicationName = Args.notNull(applicationName, 
"applicationName");
+                       this.identifier = Args.notNull(sessionIdentifier, 
"sessionIdentifier");
+               }
+
+
+               @Override
+               public void valueBound(HttpSessionBindingEvent event)
+               {
+               }
+
+               @Override
+               public void valueUnbound(HttpSessionBindingEvent event)
+               {
+                       InMemoryPageStore store = 
IN_MEMORY_STORES.get(applicationName);
+                       if (store != null)
+                       {
+                               store.removeMemoryData(identifier);
+                       }
+               }
+       }
+       
+       private static class MermoryPersistedPage implements IPersistedPage
+       {
+
+               private final int id;
+
+               private final String type;
+
+               private final long size;
+
+               public MermoryPersistedPage(IManageablePage page, long size)
+               {
+                       this.id = page.getPageId();
+                       this.type = page.getClass().getName();
+                       this.size = size;
+               }
+
+               @Override
+               public int getPageId()
+               {
+                       return id;
+               }
+
+               @Override
+               public String getPageType()
+               {
+                       return type;
+               }
+
+               @Override
+               public Bytes getPageSize()
+               {
+                       return Bytes.bytes(size);
+               }
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/InSessionPageStore.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/InSessionPageStore.java 
b/wicket-core/src/main/java/org/apache/wicket/pageStore/InSessionPageStore.java
new file mode 100644
index 0000000..3914464
--- /dev/null
+++ 
b/wicket-core/src/main/java/org/apache/wicket/pageStore/InSessionPageStore.java
@@ -0,0 +1,268 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.pageStore;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+
+import javax.servlet.http.HttpSession;
+
+import org.apache.wicket.MetaDataKey;
+import org.apache.wicket.Session;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.page.IManageablePage;
+import org.apache.wicket.serialize.ISerializer;
+import org.apache.wicket.util.lang.Args;
+
+/**
+ * A store keeping a configurable maximum of pages in the session, delegating 
all or excessive pages
+ * to another store.
+ */
+public class InSessionPageStore extends DelegatingPageStore
+{
+
+       private static final MetaDataKey<SessionData> KEY = new 
MetaDataKey<SessionData>()
+       {
+               private static final long serialVersionUID = 1L;
+       };
+
+       private ISerializer serializer;
+
+       private int maxPages;
+
+       private boolean delegateAll = false;
+
+       /**
+        * Use this constructor, if sessions are never serialized.
+        * 
+        * @param delegate
+        *            store to delegate to
+        * @param maxPages
+        *            maximum pages to keep in session
+        */
+       public InSessionPageStore(IPageStore delegate, int maxPages)
+       {
+               this(delegate, new ISerializer()
+               {
+                       @Override
+                       public byte[] serialize(Object object)
+                       {
+                               throw new 
WicketRuntimeException("InSessionPageStore not configured for serialization");
+                       }
+                       
+                       @Override
+                       public Object deserialize(byte[] data)
+                       {
+                               throw new 
WicketRuntimeException("InSessionPageStore not configured for serialization");
+                       }
+               },  maxPages);
+       }
+       
+       /**
+        * @param delegate
+        *            store to delegate to
+        * @param serializer
+        *            serializer to use if session gets persisted
+        * @param maxPages
+        *            maximum pages to keep in session
+        */
+       public InSessionPageStore(IPageStore delegate, ISerializer serializer, 
int maxPages)
+       {
+               super(delegate);
+
+               this.serializer = Args.notNull(serializer, "serializer");
+
+               this.maxPages = maxPages;
+       }
+
+       /**
+        * Delegated all pages, even those that are still kept in the session.
+        */
+       public InSessionPageStore delegateAll()
+       {
+               delegateAll = true;
+
+               return this;
+       }
+
+       @Override
+       public IManageablePage getPage(IPageContext context, int id)
+       {
+               IManageablePage page = getSessionData(context).get(id);
+               if (page != null)
+               {
+                       return page;
+               }
+
+               return super.getPage(context, id);
+       }
+
+       @Override
+       public void addPage(IPageContext context, IManageablePage page)
+       {
+               SessionData data = getSessionData(context);
+
+               data.addAndDelegate(context, page, maxPages, delegateAll, 
getDelegate());
+       }
+
+       @Override
+       public void removePage(IPageContext context, IManageablePage page)
+       {
+               getSessionData(context).remove(page);
+
+               super.removePage(context, page);
+       }
+
+       @Override
+       public void removeAllPages(IPageContext context)
+       {
+               getSessionData(context).removeAll();
+
+               super.removeAllPages(context);
+       }
+
+       private SessionData getSessionData(IPageContext context)
+       {
+               SessionData data = context.getSessionData(KEY);
+               if (data == null)
+               {
+                       context.bind();
+                       data = new SessionData();
+
+                       context.setSessionData(KEY, data);
+               }
+
+               // data might be deserialized so initialize again
+               data.init(serializer);
+
+               return data;
+       }
+
+       /**
+        * Data kept in the {@link Session}, might get serialized along with 
its containing
+        * {@link HttpSession}.
+        */
+       static class SessionData implements Serializable
+       {
+
+               transient ISerializer serializer;
+
+               /**
+                * Pages, may partly be serialized.
+                */
+               private LinkedHashMap<Integer, Serializable> pages = new 
LinkedHashMap<>();
+
+               /**
+                * This method <em>must</em> be called each time it is 
retrieved from the session: <br/>
+                * After deserializing from persisted session the serializer is 
no longer referenced and all
+                * contained pages are in a serialized state.
+                */
+               public void init(ISerializer serializer)
+               {
+                       this.serializer = Args.notNull(serializer, 
"serializer");
+               }
+
+               public synchronized void addAndDelegate(IPageContext context, 
IManageablePage page,
+                       int maxPages, boolean delegateAll, IPageStore delegate)
+               {
+                       pages.remove(page.getPageId());
+                       pages.put(page.getPageId(), page);
+
+                       Serializable expelled = null;
+                       if (pages.size() > maxPages)
+                       {
+                               Iterator<Serializable> iterator = 
pages.values().iterator();
+                               expelled = iterator.next();
+                               iterator.remove();
+                       }
+
+                       if (delegateAll)
+                       {
+                               delegate.addPage(context, page);
+                       }
+                       else
+                       {
+                               // when pages are not delegated automatically, 
we have to catch up
+                               // on an expelled page now
+                               if (expelled != null)
+                               {
+                                       if (expelled instanceof byte[])
+                                       {
+                                               // ... which results in this 
suboptimal case, when the session was persisted:
+                                               // in that case the expelled 
page is still in a serialized state, so we have
+                                               // to deserialize it first to 
be able to delegate it 
+                                               expelled = 
(IManageablePage)serializer.deserialize((byte[])expelled);
+                                       }
+                                       delegate.addPage(context, 
(IManageablePage)expelled);
+                               }
+                       }
+               }
+
+               public synchronized void remove(IManageablePage page)
+               {
+                       pages.remove(page.getPageId());
+               }
+
+               public synchronized void removeAll()
+               {
+                       pages.clear();
+               }
+
+               public synchronized IManageablePage get(int id)
+               {
+                       Serializable serializable = pages.get(id);
+
+                       if (serializable instanceof byte[])
+                       {
+                               if (serializer == null)
+                               {
+                                       throw new 
IllegalStateException("SessionData#init() was not called");
+                               }
+                               serializable = 
(Serializable)serializer.deserialize((byte[])serializable);
+
+                               pages.put(id, serializable);
+                       }
+
+                       return (IManageablePage)serializable;
+               }
+
+               /**
+                * Serialize pages before writing to output.
+                */
+               private void writeObject(final ObjectOutputStream output) 
throws IOException
+               {
+                       // serialize pages if not already
+                       for (Entry<Integer, Serializable> entry : 
pages.entrySet())
+                       {
+                               if (entry.getValue() instanceof IManageablePage)
+                               {
+                                       if (serializer == null)
+                                       {
+                                               throw new 
IllegalStateException("SessionData#init() was not called");
+                                       }
+                                       
entry.setValue(serializer.serialize(entry.getValue()));
+                               }
+                       }
+
+                       output.defaultWriteObject();
+               }
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/NoopPageStore.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/NoopPageStore.java 
b/wicket-core/src/main/java/org/apache/wicket/pageStore/NoopPageStore.java
new file mode 100644
index 0000000..846c858
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/NoopPageStore.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.pageStore;
+
+import org.apache.wicket.page.IManageablePage;
+
+/**
+ * A non-storage of pages.
+ */
+public class NoopPageStore implements IPageStore
+{
+
+       @Override
+       public boolean canBeAsynchronous(IPageContext context)
+       {
+               return true;
+       }
+       
+       @Override
+       public void addPage(IPageContext context, IManageablePage page)
+       {
+       }
+
+       @Override
+       public void removePage(IPageContext context, IManageablePage page)
+       {
+       }
+
+       @Override
+       public void removeAllPages(IPageContext context)
+       {
+       }
+
+       @Override
+       public IManageablePage getPage(IPageContext context, int id)
+       {
+               return null;
+       }
+}
\ No newline at end of file

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

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

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/RequestPageStore.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/RequestPageStore.java 
b/wicket-core/src/main/java/org/apache/wicket/pageStore/RequestPageStore.java
new file mode 100644
index 0000000..2420768
--- /dev/null
+++ 
b/wicket-core/src/main/java/org/apache/wicket/pageStore/RequestPageStore.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.pageStore;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.wicket.MetaDataKey;
+import org.apache.wicket.page.IManageablePage;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Buffer pages till the end of the request, when they are delegated to 
another store in
+ * the reverse order they where accessed.
+ */
+public class RequestPageStore extends DelegatingPageStore
+{
+
+       private static final Logger log = 
LoggerFactory.getLogger(RequestPageStore.class);
+
+       private static final MetaDataKey<RequestData> KEY = new MetaDataKey<>()
+       {
+               private static final long serialVersionUID = 1L;
+       };
+
+       public RequestPageStore(IPageStore delegate)
+       {
+               super(delegate);
+       }
+
+       @Override
+       public IManageablePage getPage(IPageContext context, int id)
+       {
+               IManageablePage page = getRequestData(context).get(id);
+               if (page != null)
+               {
+                       return page;
+               }
+
+               return super.getPage(context, id);
+       }
+
+       @Override
+       public void addPage(IPageContext context, IManageablePage page)
+       {
+               getRequestData(context).add(page);
+       }
+
+       @Override
+       public void removePage(IPageContext context, IManageablePage page)
+       {
+               getRequestData(context).remove(page);
+
+               super.removePage(context, page);
+       }
+
+       @Override
+       public void removeAllPages(IPageContext context)
+       {
+               getRequestData(context).removeAll();
+
+               super.removeAllPages(context);
+       }
+
+       @Override
+       public void detach(IPageContext context)
+       {
+               RequestData requestData = getRequestData(context);
+               for (IManageablePage page : requestData.pages())
+               {
+                       boolean isPageStateless;
+                       try
+                       {
+                               isPageStateless = page.isPageStateless();
+                       }
+                       catch (Exception x)
+                       {
+                               log.warn("An error occurred while checking 
whether a page is stateless. Assuming it is stateful.", x);
+                               isPageStateless = false;
+                       }
+
+                       if (isPageStateless == false)
+                       {
+                               super.addPage(context, page);
+                       }
+               }
+               requestData.removeAll();
+
+               super.detach(context);
+       }
+
+       private RequestData getRequestData(IPageContext context)
+       {
+               RequestData requestData = context.getRequestData(KEY);
+               if (requestData == null)
+               {
+                       requestData = new RequestData();
+
+                       context.setRequestData(KEY, requestData);
+               }
+               return requestData;
+       }
+       
+       /**
+        * Data kept in the {@link RequestCycle}.
+        */
+       static class RequestData
+       {
+               private Map<Integer, IManageablePage> pages = new 
LinkedHashMap<>();
+               
+               public void add(IManageablePage page)
+               {
+                       pages.remove(page.getPageId());
+                       
+                       pages.put(page.getPageId(), page);
+               }
+
+               public Iterable<IManageablePage> pages()
+               {
+                       return pages.values();
+               }
+
+               public IManageablePage get(int id) {
+                       IManageablePage page = pages.get(id);
+                       
+                       if (page != null) {
+                               pages.put(id, page);
+                       }
+                       
+                       return page;
+               }
+
+               public void remove(IManageablePage page)
+               {
+                       pages.remove(page.getPageId());
+               }
+
+               public void removeAll()
+               {
+                       pages.clear();
+               }               
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/SecondLevelPageCache.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/SecondLevelPageCache.java
 
b/wicket-core/src/main/java/org/apache/wicket/pageStore/SecondLevelPageCache.java
deleted file mode 100644
index 3d30857..0000000
--- 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/SecondLevelPageCache.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.wicket.pageStore;
-
-/**
- * An application scoped cache that holds the last N used pages in the 
application.
- * Acts as a second level cache between the Http Session (first level) and the
- * disk (third level cache).
- *
- * @param <S>
- *          The type of the session identifier
- * @param <PI>
- *          The type of the page identifier
- * @param <P>
- *          The type of the stored page
- */
-public interface SecondLevelPageCache<S, PI, P>
-{
-       P removePage(S session, PI pageId);
-
-       void removePages(S session);
-
-       P getPage(S session, PI pageId);
-
-       void storePage(S session, PI pageId, P page);
-
-       void destroy();
-}

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

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/HttpSessionDataStore.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/HttpSessionDataStore.java
 
b/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/HttpSessionDataStore.java
deleted file mode 100644
index fcc4f7a..0000000
--- 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/HttpSessionDataStore.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.wicket.pageStore.memory;
-
-import javax.servlet.http.HttpSession;
-
-import org.apache.wicket.Session;
-import org.apache.wicket.page.IPageManagerContext;
-import org.apache.wicket.pageStore.IDataStore;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A {@link IDataStore} which stores the pages in the {@link HttpSession}. Uses
- * {@link IDataStoreEvictionStrategy} to keep the memory footprint reasonable.
- *
- * <p>
- * Usage:
- *
- * <pre>
- * <!--@formatter:off-->
- * MyApp#init()
- * {
- *
- *     setPageManagerProvider(new DefaultPageManagerProvider(this)
- *     {
- *             protected IDataStore newDataStore() 
- *             { 
- *                     return  new HttpSessionDataStore(pageManagerContext, 
new PageNumberEvictionStrategy(20));
- *             }
- *     }
- * }
- * <!--@formatter:on-->
- * </pre>
- */
-public class HttpSessionDataStore implements IDataStore
-{
-       private static final Logger LOG = 
LoggerFactory.getLogger(HttpSessionDataStore.class);
-
-       /** the session attribute key. auto-prefixed with 
application.getSessionAttributePrefix() */
-       private static final String PAGE_TABLE_KEY = "page:store:memory";
-
-       private final IPageManagerContext pageManagerContext;
-
-       private final IDataStoreEvictionStrategy evictionStrategy;
-
-       /**
-        * Construct.
-        * 
-        * @param pageManagerContext
-        * @param evictionStrategy
-        */
-       public HttpSessionDataStore(IPageManagerContext pageManagerContext,
-               IDataStoreEvictionStrategy evictionStrategy)
-       {
-               this.pageManagerContext = pageManagerContext;
-               this.evictionStrategy = evictionStrategy;
-       }
-
-       /**
-        * @param sessionId
-        *            Ignored. Only pages from the current http session can be 
read
-        * @see 
org.apache.wicket.pageStore.IDataStore#getData(java.lang.String, int)
-        */
-       @Override
-       public byte[] getData(String sessionId, int pageId)
-       {
-               PageTable pageTable = getPageTable(false, false);
-               byte[] pageAsBytes = null;
-               if (pageTable != null)
-               {
-                       pageAsBytes = pageTable.getPage(pageId);
-               }
-
-               if (LOG.isDebugEnabled())
-               {
-                       int bytesLength = pageAsBytes != null ? 
pageAsBytes.length : -1;
-                       LOG.debug("Loaded '{}' bytes for page with id '{}' in 
session '{}'",
-                                       bytesLength, pageId, sessionId);
-               }
-
-
-               return pageAsBytes;
-       }
-
-       @Override
-       public void removeData(String sessionId, int pageId)
-       {
-               PageTable pageTable = getPageTable(false, true);
-               if (pageTable != null)
-               {
-                       byte[] bytes = pageTable.removePage(pageId);
-
-                       if (LOG.isDebugEnabled() && bytes != null)
-                       {
-                               LOG.debug("Removed page '{}' in session '{}'", 
pageId, sessionId);
-                       }
-               }
-       }
-
-       @Override
-       public void removeData(String sessionId)
-       {
-               PageTable pageTable = getPageTable(false, true);
-               if (pageTable != null)
-               {
-                       pageTable.clear();
-                       LOG.debug("Removed all pages in session '{}'", 
sessionId);
-               }
-       }
-
-       @Override
-       public void storeData(String sessionId, int pageId, byte[] pageAsBytes)
-       {
-               PageTable pageTable = getPageTable(true, true);
-               if (pageTable != null)
-               {
-                       pageTable.storePage(pageId, pageAsBytes);
-                       if (LOG.isDebugEnabled())
-                       {
-                               LOG.debug("Stored '{}' bytes for page '{}' in 
session '{}'",
-                                               pageAsBytes.length, pageId, 
sessionId);
-                       }
-                       evictionStrategy.evict(pageTable);
-               }
-               else
-               {
-                       LOG.error("Cannot store the data for page with id '{}' 
in session with id '{}'",
-                               pageId, sessionId);
-               }
-       }
-
-       @Override
-       public void destroy()
-       {
-               // do nothing
-               // this is application lifecycle thread (WicketFilter#destroy())
-               // so there is no reachable http session
-       }
-
-       @Override
-       public boolean isReplicated()
-       {
-               return true;
-       }
-
-       private PageTable getPageTable(boolean create, boolean rewriteToSession)
-       {
-               PageTable pageTable = null;
-               if (Session.exists())
-               {
-                       pageTable = 
(PageTable)pageManagerContext.getSessionAttribute(PAGE_TABLE_KEY);
-                       if (pageTable == null && create)
-                       {
-                               pageTable = new PageTable();
-                               
pageManagerContext.setSessionAttribute(PAGE_TABLE_KEY, pageTable);
-                       } else if (rewriteToSession) {
-                               
pageManagerContext.setSessionAttribute(PAGE_TABLE_KEY, pageTable);
-                       }
-               }
-               return pageTable;
-       }
-
-       @Override
-       public final boolean canBeAsynchronous()
-       {
-               // HttpSessionDataStore needs access to the current http session
-               // and this is not possible in AsychronousDataStore
-               return false;
-       }
-
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/IDataStoreEvictionStrategy.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/IDataStoreEvictionStrategy.java
 
b/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/IDataStoreEvictionStrategy.java
deleted file mode 100644
index 6b6e556..0000000
--- 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/IDataStoreEvictionStrategy.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.wicket.pageStore.memory;
-
-
-/**
- * An eviction strategy that decides whether the in-memory data structure used 
as page store should
- * be compacted
- */
-@FunctionalInterface
-public interface IDataStoreEvictionStrategy
-{
-
-       /**
-        * Called after each {@link 
org.apache.wicket.pageStore.IDataStore#storeData(String, int, byte[])} call.
-        * 
-        * @param pageTable
-        *            the in-memory data store with <strong>all</strong> pages
-        */
-       void evict(PageTable pageTable);
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/MemorySizeEvictionStrategy.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/MemorySizeEvictionStrategy.java
 
b/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/MemorySizeEvictionStrategy.java
deleted file mode 100644
index 92f985b..0000000
--- 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/MemorySizeEvictionStrategy.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.wicket.pageStore.memory;
-
-import org.apache.wicket.util.lang.Args;
-import org.apache.wicket.util.lang.Bytes;
-import org.apache.wicket.core.util.lang.WicketObjects;
-
-/**
- * An eviction strategy that keeps the data store size up to configured bytes
- */
-public class MemorySizeEvictionStrategy implements IDataStoreEvictionStrategy
-{
-
-       private final Bytes maxBytes;
-
-       /**
-        * Construct.
-        * 
-        * @param maxBytes
-        *            the maximum size of the data store
-        */
-       public MemorySizeEvictionStrategy(Bytes maxBytes)
-       {
-               Args.notNull(maxBytes, "maxBytes");
-
-               this.maxBytes = maxBytes;
-       }
-
-       /**
-        * 
-        * @see 
IDataStoreEvictionStrategy#evict(org.apache.wicket.pageStore.memory.PageTable)
-        */
-       @Override
-       public void evict(PageTable pageTable)
-       {
-
-               long storeCurrentSize = WicketObjects.sizeof(pageTable);
-
-               if (storeCurrentSize > maxBytes.bytes())
-               {
-                       PageTableCleaner cleaner = new PageTableCleaner();
-                       cleaner.drop(pageTable, 1);
-
-                       // recurse until enough space is cleaned
-                       evict(pageTable);
-               }
-       }
-
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageNumberEvictionStrategy.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageNumberEvictionStrategy.java
 
b/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageNumberEvictionStrategy.java
deleted file mode 100644
index 9857a6b..0000000
--- 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageNumberEvictionStrategy.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.wicket.pageStore.memory;
-
-
-/**
- * An eviction strategy which decides whether to evict entries from the 
in-memory data store
- * depending on the number of stored paged per session
- */
-public class PageNumberEvictionStrategy implements IDataStoreEvictionStrategy
-{
-
-       private final int pagesNumber;
-
-       /**
-        * Construct.
-        * 
-        * @param pagesNumber
-        *            the maximum number of pages the data store can hold
-        */
-       public PageNumberEvictionStrategy(int pagesNumber)
-       {
-               if (pagesNumber < 1)
-               {
-                       throw new IllegalArgumentException("'pagesNumber' must 
be greater than 0.");
-               }
-
-               this.pagesNumber = pagesNumber;
-       }
-
-       /**
-        * 
-        * @see 
IDataStoreEvictionStrategy#evict(org.apache.wicket.pageStore.memory.PageTable)
-        */
-       @Override
-       public void evict(PageTable pageTable)
-       {
-               int size = pageTable.size();
-               int pagesToDrop = size - pagesNumber;
-
-               if (pagesToDrop > 0)
-               {
-                       PageTableCleaner cleaner = new PageTableCleaner();
-                       cleaner.drop(pageTable, pagesToDrop);
-               }
-       }
-
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageTable.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageTable.java 
b/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageTable.java
deleted file mode 100644
index dfad4af..0000000
--- 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageTable.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.wicket.pageStore.memory;
-
-import java.util.Iterator;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.ConcurrentMap;
-
-import org.apache.wicket.util.io.IClusterable;
-
-/**
- * A structure that holds page id => pageAsBytes.
- * 
- * <p>
- * Additionally it has an index of the least recently used pages
- */
-public class PageTable implements IClusterable
-{
-       private static final long serialVersionUID = 1L;
-
-       /**
-        * Holds the index of last/least recently used page ids. The most 
recently used page id is in
-        * the tail, the least recently used is in the head.
-        */
-       /*
-        * Can be replaced later with PriorityQueue to deal with lightweight 
(Ajax) and heavyweight
-        * pages
-        */
-       private final Queue<Integer> index;
-
-       /**
-        * The actual container for the pages.
-        * 
-        * <p>
-        * page id => page as bytes
-        */
-       private final ConcurrentMap<Integer, byte[]> pages;
-
-       public PageTable()
-       {
-               pages = new ConcurrentHashMap<>();
-               index = new ConcurrentLinkedQueue<>();
-       }
-
-       public void storePage(Integer pageId, byte[] pageAsBytes)
-       {
-               synchronized (index)
-               {
-                       pages.put(pageId, pageAsBytes);
-
-                       updateIndex(pageId);
-               }
-       }
-
-       public byte[] getPage(final Integer pageId)
-       {
-               synchronized (index)
-               {
-                       updateIndex(pageId);
-
-                       return pages.get(pageId);
-               }
-       }
-
-       public byte[] removePage(Integer pageId)
-       {
-               synchronized (index)
-               {
-                       index.remove(pageId);
-
-                       return pages.remove(pageId);
-               }
-       }
-
-       public void clear()
-       {
-               synchronized (index)
-               {
-                       index.clear();
-                       pages.clear();
-               }
-       }
-
-       public int size()
-       {
-               return pages.size();
-       }
-
-       public Integer getOldest()
-       {
-               return index.peek();
-       }
-
-       public Iterator<Integer> indexIterator()
-       {
-               return index.iterator();
-       }
-
-       /**
-        * Updates the index of last/least recently used pages by removing the 
page id from the index
-        * (in case it is already in) and (re-)adding it at the head
-        * 
-        * @param pageId
-        *            the id of a recently used page
-        */
-       private void updateIndex(Integer pageId)
-       {
-               index.remove(pageId);
-               index.offer(pageId);
-       }
-
-}

http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageTableCleaner.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageTableCleaner.java
 
b/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageTableCleaner.java
deleted file mode 100644
index 2c9a9cb..0000000
--- 
a/wicket-core/src/main/java/org/apache/wicket/pageStore/memory/PageTableCleaner.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.wicket.pageStore.memory;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Helper class that knows how to remove the nth oldest pages from {@link 
PageTable}
- */
-public class PageTableCleaner
-{
-       private static final Logger LOG = 
LoggerFactory.getLogger(PageTableCleaner.class);
-
-       /**
-        * Removes {@code pagesNumber} of pages from the {@link PageTable 
pageTable}
-        * 
-        * @param pageTable
-        *            the {@link PageTable} to clean
-        * @param pagesNumber
-        *            the number of pages to remove
-        */
-       public void drop(final PageTable pageTable, final int pagesNumber)
-       {
-               for (int i = 0; i < pagesNumber; i++)
-               {
-                       Integer pageIdOfTheOldest = pageTable.getOldest();
-                       pageTable.removePage(pageIdOfTheOldest);
-                       LOG.debug("Evicted page with id '{}' from the 
HttpSessionDataStore");
-               }
-       }
-}

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

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

Reply via email to