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/master
Commit: 65bd82fe9754d30ed103eba52aa1dc1e07f71bc0
Parents: 8908b8f2
Author: Martin Tzvetanov Grigorov <mgrigo...@apache.org>
Authored: Mon Mar 10 15:20:36 2014 +0200
Committer: Martin Tzvetanov Grigorov <mgrigo...@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
+}

Reply via email to