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 +}