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

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

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());
 		}
 	}