You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by mg...@apache.org on 2014/03/10 16:56:56 UTC

[1/3] git commit: WICKET-5527 Inefficient DefaultPageStore.SerializedPagesCache

Repository: wicket
Updated Branches:
  refs/heads/5527-inefficient-DefaultDataStore 8908b8f23 -> 25016d10b


WICKET-5527 Inefficient DefaultPageStore.SerializedPagesCache

Introduce PerSessionPageStore.
Extract the common logic between DefaultPageStore and PerSessionPageStore into AbstractPageStore.
Extract interface for SecondLevelPageCache implementations


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

Branch: refs/heads/5527-inefficient-DefaultDataStore
Commit: 65bd82fe9754d30ed103eba52aa1dc1e07f71bc0
Parents: 8908b8f2
Author: Martin Tzvetanov Grigorov <mg...@apache.org>
Authored: Mon Mar 10 15:20:36 2014 +0200
Committer: Martin Tzvetanov Grigorov <mg...@apache.org>
Committed: Mon Mar 10 15:20:36 2014 +0200

----------------------------------------------------------------------
 .../wicket/DefaultPageManagerProvider.java      |   2 +
 .../apache/wicket/page/AbstractPageManager.java |  23 +-
 .../apache/wicket/page/PageStoreManager.java    |   2 +-
 .../wicket/pageStore/AbstractPageStore.java     | 154 +++++++++
 .../wicket/pageStore/DefaultPageStore.java      | 144 +++-----
 .../org/apache/wicket/pageStore/IPageStore.java |   2 +-
 .../wicket/pageStore/PerSessionPageStore.java   | 337 +++++++++++++++++++
 .../wicket/pageStore/SecondLevelPageCache.java  |  40 +++
 .../wicket/protocol/http/WebApplication.java    |   1 -
 .../wicket/versioning/InMemoryPageStore.java    |   6 +-
 10 files changed, 580 insertions(+), 131 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/65bd82fe/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java b/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
index 4d8adcb..3ce5f8c 100644
--- a/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
+++ b/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
@@ -26,6 +26,7 @@ import org.apache.wicket.pageStore.DefaultPageStore;
 import org.apache.wicket.pageStore.DiskDataStore;
 import org.apache.wicket.pageStore.IDataStore;
 import org.apache.wicket.pageStore.IPageStore;
+import org.apache.wicket.pageStore.PerSessionPageStore;
 import org.apache.wicket.serialize.ISerializer;
 import org.apache.wicket.settings.StoreSettings;
 import org.apache.wicket.util.lang.Bytes;
@@ -71,6 +72,7 @@ public class DefaultPageManagerProvider implements IPageManagerProvider
 		int inmemoryCacheSize = getStoreSettings().getInmemoryCacheSize();
 		ISerializer pageSerializer = application.getFrameworkSettings().getSerializer();
 		return new DefaultPageStore(pageSerializer, dataStore, inmemoryCacheSize);
+//		return new PerSessionPageStore(pageSerializer, dataStore, inmemoryCacheSize);
 	}
 
 	protected IDataStore newDataStore()

http://git-wip-us.apache.org/repos/asf/wicket/blob/65bd82fe/wicket-core/src/main/java/org/apache/wicket/page/AbstractPageManager.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/page/AbstractPageManager.java b/wicket-core/src/main/java/org/apache/wicket/page/AbstractPageManager.java
index da259e4..21eb1f1 100644
--- a/wicket-core/src/main/java/org/apache/wicket/page/AbstractPageManager.java
+++ b/wicket-core/src/main/java/org/apache/wicket/page/AbstractPageManager.java
@@ -50,17 +50,9 @@ public abstract class AbstractPageManager implements IPageManager
 	 */
 	protected abstract RequestAdapter newRequestAdapter(IPageManagerContext context);
 
-	/**
-	 * 
-	 * @see org.apache.wicket.page.IPageManager#supportsVersioning()
-	 */
 	@Override
 	public abstract boolean supportsVersioning();
 
-	/**
-	 * 
-	 * @see org.apache.wicket.page.IPageManager#sessionExpired(java.lang.String)
-	 */
 	@Override
 	public abstract void sessionExpired(String sessionId);
 
@@ -75,7 +67,6 @@ public abstract class AbstractPageManager implements IPageManager
 
 	/**
 	 * @see #newRequestAdapter(IPageManagerContext)
-	 * 
 	 * @return the request adapter
 	 */
 	protected RequestAdapter getRequestAdapter()
@@ -89,41 +80,29 @@ public abstract class AbstractPageManager implements IPageManager
 		return adapter;
 	}
 
-	/**
-	 * @see org.apache.wicket.page.IPageManager#commitRequest()
-	 */
 	@Override
 	public void commitRequest()
 	{
 		getRequestAdapter().commitRequest();
 	}
 
-	/**
-	 * @see org.apache.wicket.page.IPageManager#getPage(int)
-	 */
 	@Override
 	public IManageablePage getPage(int id)
 	{
 		IManageablePage page = getRequestAdapter().getPage(id);
 		if (page != null)
 		{
-			getRequestAdapter().touch(page);
+			touchPage(page);
 		}
 		return page;
 	}
 
-	/**
-	 * @see org.apache.wicket.page.IPageManager#newSessionCreated()
-	 */
 	@Override
 	public void newSessionCreated()
 	{
 		getRequestAdapter().newSessionCreated();
 	}
 
-	/**
-	 * @see org.apache.wicket.page.IPageManager#touchPage(org.apache.wicket.page.IManageablePage)
-	 */
 	@Override
 	public void touchPage(IManageablePage page)
 	{

http://git-wip-us.apache.org/repos/asf/wicket/blob/65bd82fe/wicket-core/src/main/java/org/apache/wicket/page/PageStoreManager.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/page/PageStoreManager.java b/wicket-core/src/main/java/org/apache/wicket/page/PageStoreManager.java
index c13eaee..2149773 100644
--- a/wicket-core/src/main/java/org/apache/wicket/page/PageStoreManager.java
+++ b/wicket-core/src/main/java/org/apache/wicket/page/PageStoreManager.java
@@ -269,7 +269,7 @@ public class PageStoreManager extends AbstractPageManager
 		{
 			s.defaultReadObject();
 
-			afterReadObject = new ArrayList<Object>();
+			afterReadObject = new ArrayList<>();
 
 			List<Serializable> l = (List<Serializable>)s.readObject();
 

http://git-wip-us.apache.org/repos/asf/wicket/blob/65bd82fe/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractPageStore.java
new file mode 100644
index 0000000..16b1cbc
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractPageStore.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.pageStore;
+
+import java.io.Serializable;
+
+import org.apache.wicket.page.IManageablePage;
+import org.apache.wicket.serialize.ISerializer;
+import org.apache.wicket.util.lang.Args;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ */
+public abstract class AbstractPageStore implements IPageStore
+{
+	private static final Logger LOG = LoggerFactory.getLogger(AbstractPageStore.class);
+
+	protected final IDataStore dataStore;
+
+	/**
+	 * The {@link org.apache.wicket.serialize.ISerializer} that will be used to convert pages from/to byte arrays
+	 */
+	protected final ISerializer pageSerializer;
+
+	protected AbstractPageStore(final ISerializer pageSerializer, final IDataStore dataStore)
+	{
+		Args.notNull(pageSerializer, "pageSerializer");
+		Args.notNull(dataStore, "DataStore");
+
+		this.pageSerializer = pageSerializer;
+		this.dataStore = dataStore;
+	}
+
+	@Override
+	public void destroy()
+	{
+		dataStore.destroy();
+	}
+
+	@Override
+	public Serializable prepareForSerialization(final String sessionId, final Serializable page)
+	{
+		if (dataStore.isReplicated())
+		{
+			return null;
+		}
+
+		return page;
+	}
+
+	@Override
+	public Object restoreAfterSerialization(final Serializable serializable)
+	{
+		return serializable;
+	}
+
+	/**
+	 * @param sessionId
+	 *          The id of the http session
+	 * @param pageId
+	 *          The id of page which serialized data should be got
+	 * @return page data
+	 * @see org.apache.wicket.pageStore.IDataStore#getData(String, int)
+	 */
+	protected byte[] getPageData(final String sessionId, final int pageId)
+	{
+		return dataStore.getData(sessionId, pageId);
+	}
+
+	/**
+	 * @param sessionId
+	 *          The id of the http session
+	 * @param pageId
+	 *          The id of page which serialized data should be removed
+	 * @see org.apache.wicket.pageStore.IDataStore#removeData(String, int)
+	 */
+	protected void removePageData(final String sessionId, final int pageId)
+	{
+		dataStore.removeData(sessionId, pageId);
+	}
+
+	/**
+	 * @param sessionId
+	 *          The id of the http session for which all data should be removed
+	 * @see org.apache.wicket.pageStore.IDataStore#removeData(String)
+	 */
+	protected void removePageData(final String sessionId)
+	{
+		dataStore.removeData(sessionId);
+	}
+
+	/**
+	 * @param sessionId
+	 *          The id of the http session
+	 * @param pageId
+	 *          The id of the page to store
+	 * @param data
+	 *          The serialized view of the page
+	 * @see org.apache.wicket.pageStore.IDataStore#storeData(String, int, byte[])
+	 */
+	protected void storePageData(final String sessionId, final int pageId, final byte[] data)
+	{
+		dataStore.storeData(sessionId, pageId, data);
+	}
+
+	/**
+	 * Serializes the passed page to byte[]
+	 *
+	 * @param page
+	 *          The page to serialize
+	 * @return the serialized view of the passed page
+	 */
+	protected byte[] serializePage(final IManageablePage page)
+	{
+		Args.notNull(page, "page");
+
+		byte[] data = pageSerializer.serialize(page);
+
+		if (data == null && LOG.isWarnEnabled())
+		{
+			LOG.warn("Page {} cannot be serialized. See previous logs for possible reasons.", page);
+		}
+		return data;
+	}
+
+	/**
+	 *
+	 * @param data
+	 *          The serialized view of the page
+	 * @return page data deserialized
+	 */
+	protected IManageablePage deserializePage(final byte[] data)
+	{
+		Args.notNull(data, "data");
+
+		return (IManageablePage)pageSerializer.deserialize(data);
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/65bd82fe/wicket-core/src/main/java/org/apache/wicket/pageStore/DefaultPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/DefaultPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/DefaultPageStore.java
index 96d4ff0..b27e04f 100644
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/DefaultPageStore.java
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/DefaultPageStore.java
@@ -34,19 +34,12 @@ import org.slf4j.LoggerFactory;
  * direction when loading {@link SerializedPage} from the data store.
  * 
  */
-public class DefaultPageStore implements IPageStore
+public class DefaultPageStore extends AbstractPageStore
 {
 	private static final Logger LOG = LoggerFactory.getLogger(DefaultPageStore.class);
 
 	private final SerializedPagesCache serializedPagesCache;
 
-	private final IDataStore pageDataStore;
-
-	/**
-	 * The {@link ISerializer} that will be used to convert pages from/to byte arrays
-	 */
-	private final ISerializer pageSerializer;
-
 	/**
 	 * Construct.
 	 * 
@@ -61,64 +54,10 @@ public class DefaultPageStore implements IPageStore
 	public DefaultPageStore(final ISerializer pageSerializer, final IDataStore dataStore,
 		final int cacheSize)
 	{
-		Args.notNull(pageSerializer, "pageSerializer");
-		Args.notNull(dataStore, "DataStore");
-
-		this.pageSerializer = pageSerializer;
-		pageDataStore = dataStore;
+		super(pageSerializer, dataStore);
 		serializedPagesCache = new SerializedPagesCache(cacheSize);
 	}
 
-	/**
-	 * @see org.apache.wicket.pageStore.IPageStore#destroy()
-	 */
-	@Override
-	public void destroy()
-	{
-		pageDataStore.destroy();
-	}
-
-	/**
-	 * @param sessionId
-	 * @param pageId
-	 * @return page data
-	 * @see IDataStore#getData(String, int)
-	 */
-	protected byte[] getPageData(final String sessionId, final int pageId)
-	{
-		return pageDataStore.getData(sessionId, pageId);
-	}
-
-	/**
-	 * @param sessionId
-	 * @param pageId
-	 * @see IDataStore#removeData(String, int)
-	 */
-	protected void removePageData(final String sessionId, final int pageId)
-	{
-		pageDataStore.removeData(sessionId, pageId);
-	}
-
-	/**
-	 * @param sessionId
-	 * @see IDataStore#removeData(String)
-	 */
-	protected void removePageData(final String sessionId)
-	{
-		pageDataStore.removeData(sessionId);
-	}
-
-	/**
-	 * @param sessionId
-	 * @param pageId
-	 * @param data
-	 * @see IDataStore#storeData(String, int, byte[])
-	 */
-	protected void storePageData(final String sessionId, final int pageId, final byte[] data)
-	{
-		pageDataStore.storeData(sessionId, pageId, data);
-	}
-
 	@Override
 	public IManageablePage getPage(final String sessionId, final int id)
 	{
@@ -149,7 +88,7 @@ public class DefaultPageStore implements IPageStore
 		SerializedPage serialized = serializePage(sessionId, page);
 		if (serialized != null)
 		{
-			serializedPagesCache.storePage(serialized);
+			serializedPagesCache.storePage(sessionId, page.getPageId(), serialized);
 			storePageData(sessionId, serialized.getPageId(), serialized.getData());
 		}
 	}
@@ -213,38 +152,38 @@ public class DefaultPageStore implements IPageStore
 	}
 
 	@Override
-	public Serializable prepareForSerialization(final String sessionId, final Object object)
+	public Serializable prepareForSerialization(final String sessionId, final Serializable page)
 	{
-		if (pageDataStore.isReplicated())
+		if (dataStore.isReplicated())
 		{
 			return null;
 		}
 
 		SerializedPage result = null;
 
-		if (object instanceof IManageablePage)
+		if (page instanceof IManageablePage)
 		{
-			IManageablePage page = (IManageablePage)object;
-			result = serializedPagesCache.getPage(sessionId, page.getPageId());
+			IManageablePage _page = (IManageablePage)page;
+			result = serializedPagesCache.getPage(sessionId, _page.getPageId());
 			if (result == null)
 			{
-				result = serializePage(sessionId, page);
+				result = serializePage(sessionId, _page);
 				if (result != null)
 				{
-					serializedPagesCache.storePage(result);
+					serializedPagesCache.storePage(sessionId, _page.getPageId(), result);
 				}
 			}
 		}
-		else if (object instanceof SerializedPage)
+		else if (page instanceof SerializedPage)
 		{
-			SerializedPage page = (SerializedPage)object;
-			if (page.getData() == null)
+			SerializedPage _page = (SerializedPage)page;
+			if (_page.getData() == null)
 			{
-				result = restoreStrippedSerializedPage(page);
+				result = restoreStrippedSerializedPage(_page);
 			}
 			else
 			{
-				result = page;
+				result = _page;
 			}
 		}
 
@@ -252,7 +191,7 @@ public class DefaultPageStore implements IPageStore
 		{
 			return result;
 		}
-		return (Serializable)object;
+		return page;
 	}
 
 	/**
@@ -372,7 +311,7 @@ public class DefaultPageStore implements IPageStore
 
 		SerializedPage serializedPage = null;
 
-		byte[] data = pageSerializer.serialize(page);
+		byte[] data = serializePage(page);
 
 		if (data != null)
 		{
@@ -386,17 +325,6 @@ public class DefaultPageStore implements IPageStore
 	}
 
 	/**
-	 * 
-	 * @param data
-	 * @return page data deserialized
-	 */
-	protected IManageablePage deserializePage(final byte[] data)
-	{
-		IManageablePage page = (IManageablePage)pageSerializer.deserialize(data);
-		return page;
-	}
-
-	/**
 	 * Cache that stores serialized pages. This is important to make sure that a single page is not
 	 * serialized twice or more when not necessary.
 	 * <p>
@@ -406,7 +334,7 @@ public class DefaultPageStore implements IPageStore
 	 * 
 	 * @author Matej Knopp
 	 */
-	static class SerializedPagesCache
+	static class SerializedPagesCache implements SecondLevelPageCache<String, Integer, SerializedPage>
 	{
 		private final int size;
 
@@ -426,20 +354,22 @@ public class DefaultPageStore implements IPageStore
 		/**
 		 * 
 		 * @param sessionId
-		 * @param id
+		 * @param pageId
 		 * @return the removed {@link SerializedPage} or <code>null</code> - otherwise
 		 */
-		public SerializedPage removePage(final String sessionId, final int id)
+		@Override
+		public SerializedPage removePage(final String sessionId, final Integer pageId)
 		{
-			Args.notNull(sessionId, "sessionId");
-
 			if (size > 0)
 			{
+				Args.notNull(sessionId, "sessionId");
+				Args.notNull(pageId, "pageId");
+
 				for (Iterator<SoftReference<SerializedPage>> i = cache.iterator(); i.hasNext();)
 				{
 					SoftReference<SerializedPage> ref = i.next();
 					SerializedPage entry = ref.get();
-					if (entry != null && entry.getPageId() == id &&
+					if (entry != null && entry.getPageId() == pageId &&
 						entry.getSessionId().equals(sessionId))
 					{
 						i.remove();
@@ -456,12 +386,13 @@ public class DefaultPageStore implements IPageStore
 		 * 
 		 * @param sessionId
 		 */
+		@Override
 		public void removePages(String sessionId)
 		{
-			Args.notNull(sessionId, "sessionId");
-
 			if (size > 0)
 			{
+				Args.notNull(sessionId, "sessionId");
+
 				for (Iterator<SoftReference<SerializedPage>> i = cache.iterator(); i.hasNext();)
 				{
 					SoftReference<SerializedPage> ref = i.next();
@@ -483,13 +414,15 @@ public class DefaultPageStore implements IPageStore
 		 * @param pageId
 		 * @return the found serialized page or <code>null</code> when not found
 		 */
-		public SerializedPage getPage(String sessionId, int pageId)
+		@Override
+		public SerializedPage getPage(String sessionId, Integer pageId)
 		{
-			Args.notNull(sessionId, "sessionId");
-
 			SerializedPage result = null;
 			if (size > 0)
 			{
+				Args.notNull(sessionId, "sessionId");
+				Args.notNull(pageId, "pageId");
+
 				for (Iterator<SoftReference<SerializedPage>> i = cache.iterator(); i.hasNext();)
 				{
 					SoftReference<SerializedPage> ref = i.next();
@@ -506,7 +439,7 @@ public class DefaultPageStore implements IPageStore
 				if (result != null)
 				{
 					// move to top
-					storePage(result);
+					storePage(sessionId, pageId, result);
 				}
 			}
 			return result;
@@ -518,10 +451,15 @@ public class DefaultPageStore implements IPageStore
 		 * @param page
 		 *      the data to serialize (page id, session id, bytes)
 		 */
-		void storePage(SerializedPage page)
+		@Override
+		public void storePage(String sessionId, Integer pageId, SerializedPage page)
 		{
 			if (size > 0)
 			{
+				Args.notNull(sessionId, "sessionId");
+				Args.notNull(pageId, "pageId");
+				Args.notNull(page, "page");
+
 				for (Iterator<SoftReference<SerializedPage>> i = cache.iterator(); i.hasNext();)
 				{
 					SoftReference<SerializedPage> r = i.next();
@@ -534,7 +472,7 @@ public class DefaultPageStore implements IPageStore
 				}
 
 				cache.add(new SoftReference<>(page));
-				if (cache.size() > size)
+				while (cache.size() > size)
 				{
 					cache.remove(0);
 				}

http://git-wip-us.apache.org/repos/asf/wicket/blob/65bd82fe/wicket-core/src/main/java/org/apache/wicket/pageStore/IPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/IPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/IPageStore.java
index 5fe3cf6..470536a 100644
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/IPageStore.java
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/IPageStore.java
@@ -84,7 +84,7 @@ public interface IPageStore
 	 * @param page
 	 * @return The Page itself or a SerializedContainer for that page
 	 */
-	Serializable prepareForSerialization(String sessionId, Object page);
+	Serializable prepareForSerialization(String sessionId, Serializable page);
 
 	/**
 	 * This method should restore the serialized page to intermediate object that can be converted

http://git-wip-us.apache.org/repos/asf/wicket/blob/65bd82fe/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
new file mode 100644
index 0000000..ac57ab9
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/PerSessionPageStore.java
@@ -0,0 +1,337 @@
+/*
+ * 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.Map;
+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;
+import org.apache.wicket.util.lang.Objects;
+import org.apache.wicket.util.time.Time;
+
+/**
+ *
+ */
+public class PerSessionPageStore extends AbstractPageStore
+{
+	private final SecondLevelPageCache<String, Integer, IManageablePage> pagesCache;
+
+	/**
+	 * Construct.
+	 *
+	 * @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);
+		this.pagesCache = new PagesCache(cacheSize);
+	}
+
+	@Override
+	public IManageablePage getPage(final String sessionId, final int id)
+	{
+		IManageablePage fromCache = pagesCache.getPage(sessionId, id);
+		if (fromCache != null)
+		{
+			return fromCache;
+		}
+
+		byte[] data = getPageData(sessionId, id);
+		if (data != null)
+		{
+			return deserializePage(data);
+		}
+		return null;
+	}
+
+	@Override
+	public void removePage(final String sessionId, final int id)
+	{
+		pagesCache.removePage(sessionId, id);
+		removePageData(sessionId, id);
+	}
+
+	@Override
+	public void storePage(final String sessionId, final IManageablePage page)
+	{
+		byte[] data = serializePage(page);
+		if (data != null)
+		{
+			pagesCache.storePage(sessionId, page.getPageId(), page);
+			storePageData(sessionId, page.getPageId(), data);
+		}
+	}
+
+	@Override
+	public void unbind(final String sessionId)
+	{
+		removePageData(sessionId);
+		pagesCache.removePages(sessionId);
+	}
+
+	@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);
+	}
+
+	/**
+	 */
+	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 Time accessTime;
+
+			private PageValue(IManageablePage page)
+			{
+				this(page.getPageId());
+			}
+
+			private PageValue(int pageId)
+			{
+				this.pageId = pageId;
+				this.accessTime = Time.now();
+			}
+
+			@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 Objects.compareWithConversion(p1.accessTime, p2.accessTime);
+			}
+		}
+
+		private final int maxEntriesPerSession;
+
+		private final ConcurrentSkipListMap<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 ConcurrentSkipListMap<>();
+		}
+
+		/**
+		 *
+		 * @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 pv = new PageValue(pageId);
+						IManageablePage page = pages.remove(pv);
+						if (page != null)
+						{
+							result = page;
+						}
+					}
+				}
+			}
+
+			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 pv = new PageValue(pageId);
+						Map.Entry<PageValue, IManageablePage> entry = pages.ceilingEntry(pv);
+
+						if (entry != null)
+						{
+							// touch the entry
+							entry.getKey().accessTime = Time.now();
+							result = entry.getValue();
+						}
+					}
+				}
+			}
+			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)
+				{
+					while (pages.size() > maxEntriesPerSession)
+					{
+						pages.pollFirstEntry();
+					}
+
+					PageValue pv = new PageValue(page);
+					pages.put(pv, page);
+				}
+			}
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/65bd82fe/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
new file mode 100644
index 0000000..656827e
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/SecondLevelPageCache.java
@@ -0,0 +1,40 @@
+/*
+ * 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);
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/65bd82fe/wicket-core/src/main/java/org/apache/wicket/protocol/http/WebApplication.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/protocol/http/WebApplication.java b/wicket-core/src/main/java/org/apache/wicket/protocol/http/WebApplication.java
index 6c30bd1..988243a 100644
--- a/wicket-core/src/main/java/org/apache/wicket/protocol/http/WebApplication.java
+++ b/wicket-core/src/main/java/org/apache/wicket/protocol/http/WebApplication.java
@@ -66,7 +66,6 @@ import org.apache.wicket.request.resource.CssResourceReference;
 import org.apache.wicket.request.resource.JavaScriptResourceReference;
 import org.apache.wicket.request.resource.ResourceReference;
 import org.apache.wicket.resource.bundles.ReplacementResourceBundleReference;
-import org.apache.wicket.resource.bundles.ResourceBundleReference;
 import org.apache.wicket.session.HttpSessionStore;
 import org.apache.wicket.session.ISessionStore;
 import org.apache.wicket.util.IContextProvider;

http://git-wip-us.apache.org/repos/asf/wicket/blob/65bd82fe/wicket-core/src/test/java/org/apache/wicket/versioning/InMemoryPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/versioning/InMemoryPageStore.java b/wicket-core/src/test/java/org/apache/wicket/versioning/InMemoryPageStore.java
index ca1399a..f69a918 100644
--- a/wicket-core/src/test/java/org/apache/wicket/versioning/InMemoryPageStore.java
+++ b/wicket-core/src/test/java/org/apache/wicket/versioning/InMemoryPageStore.java
@@ -40,7 +40,7 @@ public class InMemoryPageStore implements IDataStore
 	 */
 	public InMemoryPageStore()
 	{
-		store = new ConcurrentHashMap<String, Map<Integer, byte[]>> ();
+		store = new ConcurrentHashMap<> ();
 	}
 
 	@Override
@@ -97,7 +97,7 @@ public class InMemoryPageStore implements IDataStore
 		Map<Integer, byte[]> sessionPages = store.get(sessionId);
 		if (sessionPages == null)
 		{
-			sessionPages = new ConcurrentHashMap<Integer, byte[]>();
+			sessionPages = new ConcurrentHashMap<>();
 			Map<Integer, byte[]> tmpSessionPages = store.putIfAbsent(sessionId, sessionPages);
 			if (tmpSessionPages != null)
 			{
@@ -123,4 +123,4 @@ public class InMemoryPageStore implements IDataStore
 		return false;
 	}
 
-}
\ No newline at end of file
+}


[3/3] git commit: WICKET-5527 Inefficient DefaultPageStore.SerializedPagesCache

Posted by mg...@apache.org.
WICKET-5527 Inefficient DefaultPageStore.SerializedPagesCache

Clean up and javadoc


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

Branch: refs/heads/5527-inefficient-DefaultDataStore
Commit: 25016d10b9762682d783252b0ded2697def9e870
Parents: 583e03c
Author: Martin Tzvetanov Grigorov <mg...@apache.org>
Authored: Mon Mar 10 17:55:05 2014 +0200
Committer: Martin Tzvetanov Grigorov <mg...@apache.org>
Committed: Mon Mar 10 17:55:05 2014 +0200

----------------------------------------------------------------------
 .../org/apache/wicket/DefaultPageManagerProvider.java    |  7 ++++---
 .../java/org/apache/wicket/page/AbstractPageManager.java |  4 ----
 .../org/apache/wicket/pageStore/PerSessionPageStore.java | 11 ++++++++++-
 3 files changed, 14 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/25016d10/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java b/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
index 3ce5f8c..bb1640e 100644
--- a/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
+++ b/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java
@@ -26,9 +26,9 @@ import org.apache.wicket.pageStore.DefaultPageStore;
 import org.apache.wicket.pageStore.DiskDataStore;
 import org.apache.wicket.pageStore.IDataStore;
 import org.apache.wicket.pageStore.IPageStore;
-import org.apache.wicket.pageStore.PerSessionPageStore;
 import org.apache.wicket.serialize.ISerializer;
 import org.apache.wicket.settings.StoreSettings;
+import org.apache.wicket.util.lang.Args;
 import org.apache.wicket.util.lang.Bytes;
 
 /**
@@ -40,13 +40,14 @@ public class DefaultPageManagerProvider implements IPageManagerProvider
 	protected final Application application;
 
 	/**
-	 * Construct.
+	 * Constructor.
 	 * 
 	 * @param application
+	 *          The application instance
 	 */
 	public DefaultPageManagerProvider(Application application)
 	{
-		this.application = application;
+		this.application = Args.notNull(application, "application");
 	}
 
 	@Override

http://git-wip-us.apache.org/repos/asf/wicket/blob/25016d10/wicket-core/src/main/java/org/apache/wicket/page/AbstractPageManager.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/page/AbstractPageManager.java b/wicket-core/src/main/java/org/apache/wicket/page/AbstractPageManager.java
index 21eb1f1..ca1cfe3 100644
--- a/wicket-core/src/main/java/org/apache/wicket/page/AbstractPageManager.java
+++ b/wicket-core/src/main/java/org/apache/wicket/page/AbstractPageManager.java
@@ -17,8 +17,6 @@
 package org.apache.wicket.page;
 
 import org.apache.wicket.util.lang.Args;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * Convenience class for {@link IPageManager} implementations. Subclass should extend
@@ -29,8 +27,6 @@ import org.slf4j.LoggerFactory;
  */
 public abstract class AbstractPageManager implements IPageManager
 {
-	private static final Logger log = LoggerFactory.getLogger(AbstractPageManager.class);
-
 	private final IPageManagerContext context;
 
 	/**

http://git-wip-us.apache.org/repos/asf/wicket/blob/25016d10/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
index f1d5d9a..8d0c631 100644
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/PerSessionPageStore.java
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/PerSessionPageStore.java
@@ -28,12 +28,21 @@ import org.apache.wicket.util.lang.Args;
 import org.apache.wicket.util.time.Time;
 
 /**
+ * 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>
 {
 	/**
-	 * Construct.
+	 * Constructor.
 	 *
 	 * @param pageSerializer
 	 *            the {@link org.apache.wicket.serialize.ISerializer} that will be used to convert pages from/to byte arrays


[2/3] git commit: WICKET-5527 Inefficient DefaultPageStore.SerializedPagesCache

Posted by mg...@apache.org.
WICKET-5527 Inefficient DefaultPageStore.SerializedPagesCache

Extract AbstractCachingPageStore - an AbstractPageStore that uses SecondLevelPageCache to manage the caching


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

Branch: refs/heads/5527-inefficient-DefaultDataStore
Commit: 583e03cf8f282c6c02d21c614594eaf1eecc15ae
Parents: 65bd82f
Author: Martin Tzvetanov Grigorov <mg...@apache.org>
Authored: Mon Mar 10 17:45:16 2014 +0200
Committer: Martin Tzvetanov Grigorov <mg...@apache.org>
Committed: Mon Mar 10 17:45:16 2014 +0200

----------------------------------------------------------------------
 .../pageStore/AbstractCachingPageStore.java     | 105 +++++++++++++++++++
 .../wicket/pageStore/AbstractPageStore.java     |   4 +-
 .../wicket/pageStore/DefaultPageStore.java      |  61 +++--------
 .../wicket/pageStore/PerSessionPageStore.java   |  58 ++--------
 .../wicket/pageStore/SecondLevelPageCache.java  |   2 +
 5 files changed, 135 insertions(+), 95 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/583e03cf/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractCachingPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractCachingPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractCachingPageStore.java
new file mode 100644
index 0000000..86b8d82
--- /dev/null
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractCachingPageStore.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.pageStore;
+
+import org.apache.wicket.page.IManageablePage;
+import org.apache.wicket.serialize.ISerializer;
+import org.apache.wicket.util.lang.Args;
+
+/**
+ * An abstract {@link org.apache.wicket.pageStore.IPageStore} that uses
+ * {@link org.apache.wicket.pageStore.SecondLevelPageCache} to cache the stored pages in memory
+ *
+ * @param <P>
+ *          The type of the page to be stored
+ */
+public abstract class AbstractCachingPageStore<P> extends AbstractPageStore
+{
+	/**
+	 * The cache implementation
+	 */
+	protected final SecondLevelPageCache<String, Integer, P> pagesCache;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param pageSerializer
+	 *          The serializer that will convert pages to/from byte[]
+	 * @param dataStore
+	 *          The third level page cache
+	 * @param pagesCache
+	 *          The cache to use as a second level store
+	 */
+	protected AbstractCachingPageStore(ISerializer pageSerializer, IDataStore dataStore,
+	                                   SecondLevelPageCache<String, Integer, P> pagesCache)
+	{
+		super(pageSerializer, dataStore);
+
+		this.pagesCache = Args.notNull(pagesCache, "pagesCache");
+	}
+
+	@Override
+	public IManageablePage getPage(final String sessionId, final int pageId)
+	{
+		P fromCache = pagesCache.getPage(sessionId, pageId);
+		if (fromCache != null)
+		{
+			return convertToPage(fromCache);
+		}
+
+		byte[] data = getPageData(sessionId, pageId);
+		if (data != null)
+		{
+			return deserializePage(data);
+		}
+		return null;
+	}
+
+	@Override
+	public void removePage(final String sessionId, final int pageId)
+	{
+		pagesCache.removePage(sessionId, pageId);
+		removePageData(sessionId, pageId);
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	public void storePage(final String sessionId, final IManageablePage page)
+	{
+		byte[] data = serializePage(page);
+		if (data != null)
+		{
+			int pageId = page.getPageId();
+			pagesCache.storePage(sessionId, pageId,  (P) page);
+			storePageData(sessionId, pageId, data);
+		}
+	}
+
+	@Override
+	public void unbind(final String sessionId)
+	{
+		removePageData(sessionId);
+		pagesCache.removePages(sessionId);
+	}
+
+	@Override
+	public void destroy()
+	{
+		super.destroy();
+		pagesCache.destroy();
+	}
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/583e03cf/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractPageStore.java
index 16b1cbc..07f97d5 100644
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractPageStore.java
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractPageStore.java
@@ -41,7 +41,7 @@ public abstract class AbstractPageStore implements IPageStore
 	protected AbstractPageStore(final ISerializer pageSerializer, final IDataStore dataStore)
 	{
 		Args.notNull(pageSerializer, "pageSerializer");
-		Args.notNull(dataStore, "DataStore");
+		Args.notNull(dataStore, "dataStore");
 
 		this.pageSerializer = pageSerializer;
 		this.dataStore = dataStore;
@@ -149,6 +149,6 @@ public abstract class AbstractPageStore implements IPageStore
 	{
 		Args.notNull(data, "data");
 
-		return (IManageablePage)pageSerializer.deserialize(data);
+		return (IManageablePage) pageSerializer.deserialize(data);
 	}
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/583e03cf/wicket-core/src/main/java/org/apache/wicket/pageStore/DefaultPageStore.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/DefaultPageStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/DefaultPageStore.java
index b27e04f..393fac3 100644
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/DefaultPageStore.java
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/DefaultPageStore.java
@@ -34,12 +34,10 @@ import org.slf4j.LoggerFactory;
  * direction when loading {@link SerializedPage} from the data store.
  * 
  */
-public class DefaultPageStore extends AbstractPageStore
+public class DefaultPageStore extends AbstractCachingPageStore<DefaultPageStore.SerializedPage>
 {
 	private static final Logger LOG = LoggerFactory.getLogger(DefaultPageStore.class);
 
-	private final SerializedPagesCache serializedPagesCache;
-
 	/**
 	 * Construct.
 	 * 
@@ -54,53 +52,22 @@ public class DefaultPageStore extends AbstractPageStore
 	public DefaultPageStore(final ISerializer pageSerializer, final IDataStore dataStore,
 		final int cacheSize)
 	{
-		super(pageSerializer, dataStore);
-		serializedPagesCache = new SerializedPagesCache(cacheSize);
-	}
-
-	@Override
-	public IManageablePage getPage(final String sessionId, final int id)
-	{
-		SerializedPage fromCache = serializedPagesCache.getPage(sessionId, id);
-		if (fromCache != null && fromCache.data != null)
-		{
-			return deserializePage(fromCache.data);
-		}
-
-		byte[] data = getPageData(sessionId, id);
-		if (data != null)
-		{
-			return deserializePage(data);
-		}
-		return null;
-	}
-
-	@Override
-	public void removePage(final String sessionId, final int id)
-	{
-		serializedPagesCache.removePage(sessionId, id);
-		removePageData(sessionId, id);
+		super(pageSerializer, dataStore, new SerializedPagesCache(cacheSize));
 	}
 
 	@Override
 	public void storePage(final String sessionId, final IManageablePage page)
 	{
-		SerializedPage serialized = serializePage(sessionId, page);
+		SerializedPage serialized = createSerializedPage(sessionId, page);
 		if (serialized != null)
 		{
-			serializedPagesCache.storePage(sessionId, page.getPageId(), serialized);
-			storePageData(sessionId, serialized.getPageId(), serialized.getData());
+			int pageId = page.getPageId();
+			pagesCache.storePage(sessionId, pageId, serialized);
+			storePageData(sessionId, pageId, serialized.getData());
 		}
 	}
 
 	@Override
-	public void unbind(final String sessionId)
-	{
-		removePageData(sessionId);
-		serializedPagesCache.removePages(sessionId);
-	}
-
-	@Override
 	public IManageablePage convertToPage(final Object object)
 	{
 		if (object == null)
@@ -140,7 +107,7 @@ public class DefaultPageStore extends AbstractPageStore
 	 */
 	private SerializedPage restoreStrippedSerializedPage(final SerializedPage serializedPage)
 	{
-		SerializedPage result = serializedPagesCache.getPage(serializedPage.getSessionId(),
+		SerializedPage result = pagesCache.getPage(serializedPage.getSessionId(),
 			serializedPage.getPageId());
 		if (result != null)
 		{
@@ -164,13 +131,13 @@ public class DefaultPageStore extends AbstractPageStore
 		if (page instanceof IManageablePage)
 		{
 			IManageablePage _page = (IManageablePage)page;
-			result = serializedPagesCache.getPage(sessionId, _page.getPageId());
+			result = pagesCache.getPage(sessionId, _page.getPageId());
 			if (result == null)
 			{
-				result = serializePage(sessionId, _page);
+				result = createSerializedPage(sessionId, _page);
 				if (result != null)
 				{
-					serializedPagesCache.storePage(sessionId, _page.getPageId(), result);
+					pagesCache.storePage(sessionId, _page.getPageId(), result);
 				}
 			}
 		}
@@ -304,7 +271,7 @@ public class DefaultPageStore extends AbstractPageStore
 	 * @param page
 	 * @return the serialized page information
 	 */
-	protected SerializedPage serializePage(final String sessionId, final IManageablePage page)
+	protected SerializedPage createSerializedPage(final String sessionId, final IManageablePage page)
 	{
 		Args.notNull(sessionId, "sessionId");
 		Args.notNull(page, "page");
@@ -478,5 +445,11 @@ public class DefaultPageStore extends AbstractPageStore
 				}
 			}
 		}
+
+		@Override
+		public void destroy()
+		{
+			cache.clear();
+		}
 	}
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/583e03cf/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
index ac57ab9..f1d5d9a 100644
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/PerSessionPageStore.java
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/PerSessionPageStore.java
@@ -25,16 +25,13 @@ import java.util.concurrent.ConcurrentSkipListMap;
 import org.apache.wicket.page.IManageablePage;
 import org.apache.wicket.serialize.ISerializer;
 import org.apache.wicket.util.lang.Args;
-import org.apache.wicket.util.lang.Objects;
 import org.apache.wicket.util.time.Time;
 
 /**
  *
  */
-public class PerSessionPageStore extends AbstractPageStore
+public class PerSessionPageStore extends AbstractCachingPageStore<IManageablePage>
 {
-	private final SecondLevelPageCache<String, Integer, IManageablePage> pagesCache;
-
 	/**
 	 * Construct.
 	 *
@@ -49,50 +46,7 @@ public class PerSessionPageStore extends AbstractPageStore
 	public PerSessionPageStore(final ISerializer pageSerializer, final IDataStore dataStore,
 	                           final int cacheSize)
 	{
-		super(pageSerializer, dataStore);
-		this.pagesCache = new PagesCache(cacheSize);
-	}
-
-	@Override
-	public IManageablePage getPage(final String sessionId, final int id)
-	{
-		IManageablePage fromCache = pagesCache.getPage(sessionId, id);
-		if (fromCache != null)
-		{
-			return fromCache;
-		}
-
-		byte[] data = getPageData(sessionId, id);
-		if (data != null)
-		{
-			return deserializePage(data);
-		}
-		return null;
-	}
-
-	@Override
-	public void removePage(final String sessionId, final int id)
-	{
-		pagesCache.removePage(sessionId, id);
-		removePageData(sessionId, id);
-	}
-
-	@Override
-	public void storePage(final String sessionId, final IManageablePage page)
-	{
-		byte[] data = serializePage(page);
-		if (data != null)
-		{
-			pagesCache.storePage(sessionId, page.getPageId(), page);
-			storePageData(sessionId, page.getPageId(), data);
-		}
-	}
-
-	@Override
-	public void unbind(final String sessionId)
-	{
-		removePageData(sessionId);
-		pagesCache.removePages(sessionId);
+		super(pageSerializer, dataStore, new PagesCache(cacheSize));
 	}
 
 	@Override
@@ -166,7 +120,7 @@ public class PerSessionPageStore extends AbstractPageStore
 			@Override
 			public int compare(PageValue p1, PageValue p2)
 			{
-				return Objects.compareWithConversion(p1.accessTime, p2.accessTime);
+				return p1.accessTime.compareTo(p2.accessTime);
 			}
 		}
 
@@ -333,5 +287,11 @@ public class PerSessionPageStore extends AbstractPageStore
 				}
 			}
 		}
+
+		@Override
+		public void destroy()
+		{
+			cache.clear();
+		}
 	}
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/583e03cf/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
index 656827e..3d30857 100644
--- a/wicket-core/src/main/java/org/apache/wicket/pageStore/SecondLevelPageCache.java
+++ b/wicket-core/src/main/java/org/apache/wicket/pageStore/SecondLevelPageCache.java
@@ -37,4 +37,6 @@ public interface SecondLevelPageCache<S, PI, P>
 	P getPage(S session, PI pageId);
 
 	void storePage(S session, PI pageId, P page);
+
+	void destroy();
 }