WICKET-6563 new IPageStore implementation Project: http://git-wip-us.apache.org/repos/asf/wicket/repo Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/bcf76f51 Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/bcf76f51 Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/bcf76f51
Branch: refs/heads/WICKET-6563 Commit: bcf76f517310ac5d27a2092595c3a925c3973067 Parents: 175734d Author: Sven Meier <[email protected]> Authored: Mon Jun 25 17:19:41 2018 +0200 Committer: Sven Meier <[email protected]> Committed: Mon Jul 2 23:14:42 2018 +0200 ---------------------------------------------------------------------- .../java/org/apache/wicket/Application.java | 30 +- .../wicket/DefaultPageManagerProvider.java | 132 ++-- .../org/apache/wicket/IPageManagerProvider.java | 5 +- .../src/main/java/org/apache/wicket/Page.java | 10 +- .../main/java/org/apache/wicket/Session.java | 7 +- .../core/request/handler/PageProvider.java | 1 - .../handler/RenderPageRequestHandler.java | 2 +- .../org/apache/wicket/mock/MockApplication.java | 2 +- .../org/apache/wicket/mock/MockPageManager.java | 56 +- .../org/apache/wicket/mock/MockPageStore.java | 60 ++ .../apache/wicket/page/AbstractPageManager.java | 121 ---- .../wicket/page/DefaultPageManagerContext.java | 101 --- .../org/apache/wicket/page/IPageManager.java | 80 +-- .../apache/wicket/page/IPageManagerContext.java | 66 -- .../wicket/page/PageAccessSynchronizer.java | 42 +- .../org/apache/wicket/page/PageManager.java | 88 +++ .../wicket/page/PageManagerDecorator.java | 100 --- .../apache/wicket/page/PageStoreManager.java | 507 ------------- .../org/apache/wicket/page/RequestAdapter.java | 206 ------ .../pageStore/AbstractCachingPageStore.java | 105 --- .../wicket/pageStore/AbstractPageStore.java | 154 ---- .../wicket/pageStore/AsynchronousDataStore.java | 353 --------- .../wicket/pageStore/AsynchronousPageStore.java | 378 ++++++---- .../wicket/pageStore/DefaultPageContext.java | 103 +++ .../wicket/pageStore/DefaultPageStore.java | 469 ------------ .../wicket/pageStore/DelegatingPageStore.java | 67 ++ .../apache/wicket/pageStore/DiskDataStore.java | 589 --------------- .../apache/wicket/pageStore/DiskPageStore.java | 719 +++++++++++++++++++ .../wicket/pageStore/GroupingPageStore.java | 289 ++++++++ .../org/apache/wicket/pageStore/IDataStore.java | 83 --- .../apache/wicket/pageStore/IPageContext.java | 102 +++ .../org/apache/wicket/pageStore/IPageStore.java | 103 ++- .../apache/wicket/pageStore/IPersistedPage.java | 44 ++ .../wicket/pageStore/IPersistentPageStore.java | 51 ++ .../wicket/pageStore/InMemoryPageStore.java | 352 +++++++++ .../wicket/pageStore/InSessionPageStore.java | 268 +++++++ .../apache/wicket/pageStore/NoopPageStore.java | 53 ++ .../wicket/pageStore/PageWindowManager.java | 506 ------------- .../wicket/pageStore/PerSessionPageStore.java | 332 --------- .../wicket/pageStore/RequestPageStore.java | 159 ++++ .../wicket/pageStore/SecondLevelPageCache.java | 42 -- .../pageStore/disk/PageWindowManager.java | 493 +++++++++++++ .../pageStore/memory/HttpSessionDataStore.java | 186 ----- .../memory/IDataStoreEvictionStrategy.java | 35 - .../memory/MemorySizeEvictionStrategy.java | 64 -- .../memory/PageNumberEvictionStrategy.java | 62 -- .../wicket/pageStore/memory/PageTable.java | 128 ---- .../pageStore/memory/PageTableCleaner.java | 46 -- .../apache/wicket/settings/StoreSettings.java | 26 - .../wicket/util/tester/BaseWicketTester.java | 8 +- .../core/request/mapper/TestMapperContext.java | 47 +- .../DontStoreNotRenderedPageTestCase.java | 23 +- .../html/TransparentWebMarkupContainerTest.java | 8 +- .../wicket/page/PageAccessSynchronizerTest.java | 2 +- .../wicket/page/PersistentPageManagerTest.java | 75 +- .../persistent/disk/PageWindowManagerTest.java | 301 -------- .../wicket/pageStore/AbstractPageStoreTest.java | 61 +- .../pageStore/AsynchronousDataStoreTest.java | 37 +- .../pageStore/AsynchronousPageStoreTest.java | 135 ++-- .../wicket/pageStore/DefaultPageStoreTest.java | 31 - .../wicket/pageStore/DiskDataStoreTest.java | 151 ++-- .../wicket/pageStore/DummyPageContext.java | 103 +++ .../wicket/pageStore/GroupingPageStoreTest.java | 112 +++ .../wicket/pageStore/InMemoryPageStoreTest.java | 31 + .../pageStore/InSessionPageStoreTest.java | 32 + .../apache/wicket/pageStore/NoopDataStore.java | 61 -- .../pageStore/PerSessionPageStoreTest.java | 53 -- .../pageStore/disk/PageWindowManagerTest.java | 303 ++++++++ .../memory/DummyPageManagerContext.java | 66 -- .../memory/HttpSessionDataStoreTest.java | 112 --- .../memory/MemorySizeEvictionStrategyTest.java | 65 -- .../memory/PageNumberEvictionStrategyTest.java | 63 -- .../wicket/pageStore/memory/PageTableTest.java | 58 -- .../request/handler/PageIdPoliticTest.java | 25 +- .../request/handler/PageProviderTest.java | 20 +- .../wicket/versioning/InMemoryPageStore.java | 126 ---- .../wicket/versioning/PageVersioningTest.java | 28 +- .../devutils/debugbar/DebugBarInitializer.java | 2 +- .../devutils/debugbar/InspectorDebugPanel.java | 20 +- .../devutils/debugbar/PageSizeDebugPanel.java | 104 --- .../devutils/debugbar/PageStoreDebugPanel.java | 84 +++ .../devutils/debugbar/StandardDebugPanel.java | 4 +- .../devutils/diskstore/DebugDiskDataStore.java | 100 --- .../diskstore/DebugPageManagerProvider.java | 63 -- .../diskstore/DiskStoreBrowserPage.html | 31 - .../diskstore/DiskStoreBrowserPage.java | 51 -- .../devutils/diskstore/PageStorePage.html | 31 + .../devutils/diskstore/PageStorePage.java | 68 ++ .../diskstore/browser/BrowserPanel.html | 9 +- .../diskstore/browser/BrowserPanel.java | 115 ++- .../diskstore/browser/BrowserTable.java | 47 -- .../diskstore/browser/DataStoreHelper.java | 43 -- .../diskstore/browser/PageWindowColumn.java | 80 --- .../browser/PageWindowDescription.java | 57 -- .../diskstore/browser/PageWindowProvider.java | 108 --- .../browser/PersistedPagesProvider.java | 130 ++++ .../browser/SessionIdentifiersModel.java | 59 ++ .../browser/SessionsProviderModel.java | 57 -- .../devutils/inspector/EnhancedPageView.java | 37 +- .../devutils/inspector/InspectorPage.java | 30 +- .../wicket/examples/frames/BodyFrame.java | 6 +- .../apache/wicket/examples/StartExamples.java | 13 +- .../ajax/markup/html/modal/ModalWindow.java | 5 +- .../apache/wicket/jmx/StoreSettingsMBean.java | 13 +- .../wicket/jmx/wrapper/StoreSettings.java | 6 - .../ws/api/AbstractWebSocketProcessor.java | 2 +- 106 files changed, 4761 insertions(+), 6728 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/Application.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/Application.java b/wicket-core/src/main/java/org/apache/wicket/Application.java index 9415afa..64bf7ac 100644 --- a/wicket-core/src/main/java/org/apache/wicket/Application.java +++ b/wicket-core/src/main/java/org/apache/wicket/Application.java @@ -55,10 +55,9 @@ import org.apache.wicket.markup.parser.filter.WicketMessageTagHandler; import org.apache.wicket.markup.resolver.HtmlHeaderResolver; import org.apache.wicket.markup.resolver.WicketContainerResolver; import org.apache.wicket.markup.resolver.WicketMessageResolver; -import org.apache.wicket.page.DefaultPageManagerContext; import org.apache.wicket.page.IPageManager; -import org.apache.wicket.page.IPageManagerContext; -import org.apache.wicket.pageStore.IDataStore; +import org.apache.wicket.pageStore.DefaultPageContext; +import org.apache.wicket.pageStore.IPageContext; import org.apache.wicket.pageStore.IPageStore; import org.apache.wicket.protocol.http.IRequestLogger; import org.apache.wicket.protocol.http.RequestLogger; @@ -1355,11 +1354,6 @@ public abstract class Application implements UnboundListener, IEventSink } /** - * Context for PageManager to interact with rest of Wicket - */ - private final IPageManagerContext pageManagerContext = new DefaultPageManagerContext(); - - /** * Returns an unsynchronized version of page manager * * @return the page manager @@ -1372,22 +1366,13 @@ public abstract class Application implements UnboundListener, IEventSink { if (pageManager == null) { - pageManager = pageManagerProvider.apply(getPageManagerContext()); + pageManager = pageManagerProvider.get(); } } } return pageManager; } - /** - * - * @return the page manager context - */ - protected IPageManagerContext getPageManagerContext() - { - return pageManagerContext; - } - // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -1545,7 +1530,7 @@ public abstract class Application implements UnboundListener, IEventSink { session = newSession(requestCycle.getRequest(), requestCycle.getResponse()); ThreadContext.setSession(session); - internalGetPageManager().newSessionCreated(); + internalGetPageManager().removeAllPages(); sessionListeners.onCreated(session); } else @@ -1592,10 +1577,15 @@ public abstract class Application implements UnboundListener, IEventSink @Override public void onDetach(final RequestCycle requestCycle) { + IPageManager pageManager; + if (Session.exists()) { - Session.get().getPageManager().commitRequest(); + pageManager = Session.get().getPageManager(); + } else { + pageManager = internalGetPageManager(); } + pageManager.detach(); if (Application.exists()) { http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/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 f109472..c800935 100644 --- a/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java +++ b/wicket-core/src/main/java/org/apache/wicket/DefaultPageManagerProvider.java @@ -19,22 +19,44 @@ package org.apache.wicket; import java.io.File; import org.apache.wicket.page.IPageManager; -import org.apache.wicket.page.IPageManagerContext; -import org.apache.wicket.page.PageStoreManager; -import org.apache.wicket.pageStore.AsynchronousDataStore; +import org.apache.wicket.page.PageManager; import org.apache.wicket.pageStore.AsynchronousPageStore; -import org.apache.wicket.pageStore.DefaultPageStore; -import org.apache.wicket.pageStore.DiskDataStore; -import org.apache.wicket.pageStore.IDataStore; +import org.apache.wicket.pageStore.DiskPageStore; +import org.apache.wicket.pageStore.GroupingPageStore; import org.apache.wicket.pageStore.IPageStore; +import org.apache.wicket.pageStore.InMemoryPageStore; +import org.apache.wicket.pageStore.InSessionPageStore; +import org.apache.wicket.pageStore.NoopPageStore; +import org.apache.wicket.pageStore.RequestPageStore; 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; /** - * {@link IPageManagerProvider} implementation that creates new instance of {@link IPageManager} - * that persists the pages in {@link DiskDataStore} + * A provider of a {@link PageManager} with a default chain of page {@link IPageStore}s: + * <ul> + * <li>{@link RequestPageStore} caching pages until end of the request</li> + * <li>{@link InSessionPageStore} keeping the last accessed page in the session</li> + * <li>{@link AsynchronousPageStore} moving storage of pages to a worker thread (if enabled in {@link StoreSettings#isAsynchronous()})</li> + * <li>{@link DiskPageStore} keeping all pages, configured according to {@link StoreSettings}</li> + * </ul> + * An alternative chain with all pages held in-memory could be: + * <ul> + * <li>{@link RequestPageStore} caching pages until end of the request</li> + * <li>{@link InSessionPageStore} keeping the last accessed page in the session</li> + * <li>{@link InMemoryPageStore} keeping all pages</li> + * </ul> + * ... or if all pages should be kept in the session only: + * <ul> + * <li>{@link RequestPageStore} caching pages until end of the request</li> + * <li>{@link InSessionPageStore} keeping a limited count of pages in the session, e.g. 10</li> + * <li>{@link NoopPageStore} discarding all exceeding pages</li> + * </ul> + * Other stores be may inserted ad libitum, e.g. + * <ul> + * <li>{@link GroupingPageStore} groups pages with their own maximum page limit</li> + * </ul> */ public class DefaultPageManagerProvider implements IPageManagerProvider { @@ -44,7 +66,7 @@ public class DefaultPageManagerProvider implements IPageManagerProvider * Constructor. * * @param application - * The application instance + * The application instance */ public DefaultPageManagerProvider(Application application) { @@ -52,53 +74,81 @@ public class DefaultPageManagerProvider implements IPageManagerProvider } @Override - public IPageManager apply(IPageManagerContext pageManagerContext) + public IPageManager get() { - IDataStore dataStore = newDataStore(); + IPageStore store = newPersistentStore(); - StoreSettings storeSettings = getStoreSettings(); + store = newAsynchronousStore(store); - IPageStore pageStore; + store = newSessionStore(store); - if (dataStore.canBeAsynchronous() && storeSettings.isAsynchronous()) - { - int capacity = storeSettings.getAsynchronousQueueCapacity(); - dataStore = new AsynchronousDataStore(dataStore, capacity); - - pageStore = newPageStore(dataStore); - - if (pageStore.canBeAsynchronous()) - { - pageStore = new AsynchronousPageStore(pageStore, capacity); - } - } - else - { - pageStore = newPageStore(dataStore); - } + store = newRequestStore(store); + + return new PageManager(store); + } - return new PageStoreManager(application.getName(), pageStore, pageManagerContext); + /** + * Get the {@link ISerializer} to use for serializing of pages. + * + * @return how to serialize pages if needed for any {@link IPageStore} + */ + protected ISerializer getSerializer() + { + return application.getFrameworkSettings().getSerializer(); + } + /** + * Keep pages in the request until it is finished. + * + * @see RequestPageStore + */ + protected IPageStore newRequestStore(IPageStore pageStore) + { + return new RequestPageStore(pageStore); } - protected IPageStore newPageStore(IDataStore dataStore) + /** + * After requests keep pages in the session. + * + * @see InSessionPageStore + */ + protected IPageStore newSessionStore(IPageStore pageStore) { - int inmemoryCacheSize = getStoreSettings().getInmemoryCacheSize(); - ISerializer pageSerializer = application.getFrameworkSettings().getSerializer(); - return new DefaultPageStore(pageSerializer, dataStore, inmemoryCacheSize); + return new InSessionPageStore(pageStore, getSerializer(), 1); } - protected IDataStore newDataStore() + /** + * Store pages asynchronously into the persistent store. + * + * @see AsynchronousPageStore + * @see StoreSettings#isAsynchronous() + */ + protected IPageStore newAsynchronousStore(IPageStore pageStore) { - StoreSettings storeSettings = getStoreSettings(); - Bytes maxSizePerSession = storeSettings.getMaxSizePerSession(); - File fileStoreFolder = storeSettings.getFileStoreFolder(); + StoreSettings storeSettings = application.getStoreSettings(); - return new DiskDataStore(application.getName(), fileStoreFolder, maxSizePerSession); + if (storeSettings.isAsynchronous()) + { + int capacity = storeSettings.getAsynchronousQueueCapacity(); + pageStore = new AsynchronousPageStore(pageStore, capacity); + } + + return pageStore; } - StoreSettings getStoreSettings() + /** + * Keep pages persistent on disk. + * + * @see DiskPageStore + * @see StoreSettings#getMaxSizePerSession() + * @see StoreSettings#getFileStoreFolder() + */ + protected IPageStore newPersistentStore() { - return application.getStoreSettings(); + StoreSettings storeSettings = application.getStoreSettings(); + Bytes maxSizePerSession = storeSettings.getMaxSizePerSession(); + File fileStoreFolder = storeSettings.getFileStoreFolder(); + + return new DiskPageStore(application.getName(), getSerializer(), fileStoreFolder, maxSizePerSession); } } http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/IPageManagerProvider.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/IPageManagerProvider.java b/wicket-core/src/main/java/org/apache/wicket/IPageManagerProvider.java index 557a7aa..ce40fcc 100644 --- a/wicket-core/src/main/java/org/apache/wicket/IPageManagerProvider.java +++ b/wicket-core/src/main/java/org/apache/wicket/IPageManagerProvider.java @@ -16,11 +16,10 @@ */ package org.apache.wicket; -import java.util.function.Function; +import java.util.function.Supplier; import org.apache.wicket.page.IPageManager; -import org.apache.wicket.page.IPageManagerContext; -public interface IPageManagerProvider extends Function<IPageManagerContext, IPageManager> +public interface IPageManagerProvider extends Supplier<IPageManager> { } http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/Page.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/Page.java b/wicket-core/src/main/java/org/apache/wicket/Page.java index a1f3b29..935e1f9 100644 --- a/wicket-core/src/main/java/org/apache/wicket/Page.java +++ b/wicket-core/src/main/java/org/apache/wicket/Page.java @@ -275,7 +275,7 @@ public abstract class Page extends MarkupContainer } final IPageManager pageManager = getSession().getPageManager(); - if (!getFlag(FLAG_IS_DIRTY) && (isVersioned() && pageManager.supportsVersioning() || + if (!getFlag(FLAG_IS_DIRTY) && (isVersioned() || // we need to get pageId for new page instances even when the page doesn't need // versioning, otherwise pages override each other in the page store and back button @@ -287,7 +287,7 @@ public abstract class Page extends MarkupContainer if (isInitialization == false) { - pageManager.touchPage(this); + pageManager.addPage(this); } } } @@ -298,7 +298,7 @@ public abstract class Page extends MarkupContainer super.onInitialize(); final IPageManager pageManager = getSession().getPageManager(); - pageManager.touchPage(this); + pageManager.addPage(this); } /** @@ -825,7 +825,7 @@ public abstract class Page extends MarkupContainer getSession().getSessionStore().getSessionId(RequestCycle.get().getRequest(), true); // Add/touch the response page in the session. - getSession().getPageManager().touchPage(this); + getSession().getPageManager().addPage(this); } if (getApplication().getDebugSettings().isOutputMarkupContainerClassName()) @@ -936,7 +936,7 @@ public abstract class Page extends MarkupContainer setStatelessHint(false); // make sure the page will be available on following request - getSession().getPageManager().touchPage(this); + getSession().getPageManager().addPage(this); return new PageReference(numericId); } http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/Session.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/Session.java b/wicket-core/src/main/java/org/apache/wicket/Session.java index cb8f4a2..8df8577 100644 --- a/wicket-core/src/main/java/org/apache/wicket/Session.java +++ b/wicket-core/src/main/java/org/apache/wicket/Session.java @@ -37,6 +37,7 @@ import org.apache.wicket.feedback.FeedbackMessage; import org.apache.wicket.feedback.FeedbackMessages; import org.apache.wicket.page.IPageManager; import org.apache.wicket.page.PageAccessSynchronizer; +import org.apache.wicket.pageStore.IPageStore; import org.apache.wicket.request.Request; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.session.ISessionStore; @@ -290,7 +291,7 @@ public abstract class Session implements IClusterable, IEventSink { if (isTemporary() == false) { - getPageManager().clear(); + getPageManager().removeAllPages(); } } @@ -907,8 +908,8 @@ public abstract class Session implements IClusterable, IEventSink */ public final IPageManager getPageManager() { - IPageManager pageManager = Application.get().internalGetPageManager(); - return pageAccessSynchronizer.get().adapt(pageManager); + IPageManager manager = Application.get().internalGetPageManager(); + return pageAccessSynchronizer.get().adapt(manager); } /** {@inheritDoc} */ http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/core/request/handler/PageProvider.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/core/request/handler/PageProvider.java b/wicket-core/src/main/java/org/apache/wicket/core/request/handler/PageProvider.java index 7bd473e..bd45693 100644 --- a/wicket-core/src/main/java/org/apache/wicket/core/request/handler/PageProvider.java +++ b/wicket-core/src/main/java/org/apache/wicket/core/request/handler/PageProvider.java @@ -19,7 +19,6 @@ package org.apache.wicket.core.request.handler; import org.apache.wicket.Application; import org.apache.wicket.core.request.mapper.IPageSource; import org.apache.wicket.core.request.mapper.StalePageException; -import org.apache.wicket.page.IPageManager; import org.apache.wicket.protocol.http.PageExpiredException; import org.apache.wicket.request.IRequestHandler; import org.apache.wicket.request.IRequestMapper; http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/core/request/handler/RenderPageRequestHandler.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/core/request/handler/RenderPageRequestHandler.java b/wicket-core/src/main/java/org/apache/wicket/core/request/handler/RenderPageRequestHandler.java index 156d420..642d40f 100644 --- a/wicket-core/src/main/java/org/apache/wicket/core/request/handler/RenderPageRequestHandler.java +++ b/wicket-core/src/main/java/org/apache/wicket/core/request/handler/RenderPageRequestHandler.java @@ -136,7 +136,7 @@ public class RenderPageRequestHandler if (Session.exists()) { // WICKET-5499 - Session.get().getPageManager().touchPage(pageProvider.getPageInstance()); + Session.get().getPageManager().addPage(pageProvider.getPageInstance()); } } } http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/mock/MockApplication.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/mock/MockApplication.java b/wicket-core/src/main/java/org/apache/wicket/mock/MockApplication.java index 1c0a99d..4ae6f41 100644 --- a/wicket-core/src/main/java/org/apache/wicket/mock/MockApplication.java +++ b/wicket-core/src/main/java/org/apache/wicket/mock/MockApplication.java @@ -67,7 +67,7 @@ public class MockApplication extends WebApplication // set page and session store providers setSessionStoreProvider(MockSessionStore::new); - setPageManagerProvider((pageManagerContext) -> new MockPageManager()); + setPageManagerProvider(() -> new MockPageManager()); // for test cases we usually want stable resource names getResourceSettings().setCachingStrategy(NoOpResourceCachingStrategy.INSTANCE); http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/mock/MockPageManager.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/mock/MockPageManager.java b/wicket-core/src/main/java/org/apache/wicket/mock/MockPageManager.java index b27dc7e..05bb520 100644 --- a/wicket-core/src/main/java/org/apache/wicket/mock/MockPageManager.java +++ b/wicket-core/src/main/java/org/apache/wicket/mock/MockPageManager.java @@ -21,7 +21,7 @@ import java.util.Map; import org.apache.wicket.page.IManageablePage; import org.apache.wicket.page.IPageManager; -import org.apache.wicket.page.IPageManagerContext; +import org.apache.wicket.pageStore.IPageStore; /** * Simple {@link IPageManager} used for testing. @@ -32,19 +32,6 @@ public class MockPageManager implements IPageManager { private final Map<Integer, IManageablePage> pages = new HashMap<>(); - /** - * Construct. - * - */ - public MockPageManager() - { - } - - @Override - public void commitRequest() - { - } - @Override public void destroy() { @@ -59,54 +46,29 @@ public class MockPageManager implements IPageManager @Override public void removePage(final IManageablePage page) { - if (page != null) { - pages.remove(page.getPageId()); - } + pages.remove(page.getPageId()); } @Override - public void newSessionCreated() + public void addPage(IManageablePage page) { - pages.clear(); + pages.put(page.getPageId(), page); } @Override - public void clear() + public void removeAllPages() { pages.clear(); } - /** - * @param context - */ - public void setContext(IPageManagerContext context) - { - } - - @Override - public boolean supportsVersioning() - { - return true; - } - @Override - public void touchPage(IManageablePage page) + public void detach() { - if (true || page.isPageStateless() == false) - { - pages.put(page.getPageId(), page); - } - } - - @Override - public void untouchPage(IManageablePage page) - { - pages.remove(page.getPageId()); } @Override - public IPageManagerContext getContext() + public IPageStore getPageStore() { - return null; + throw new UnsupportedOperationException(); } -} +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/mock/MockPageStore.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/mock/MockPageStore.java b/wicket-core/src/main/java/org/apache/wicket/mock/MockPageStore.java new file mode 100644 index 0000000..cb4eaec --- /dev/null +++ b/wicket-core/src/main/java/org/apache/wicket/mock/MockPageStore.java @@ -0,0 +1,60 @@ +/* + * 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.mock; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.wicket.page.IManageablePage; +import org.apache.wicket.pageStore.IPageContext; +import org.apache.wicket.pageStore.IPageStore; + +public class MockPageStore implements IPageStore +{ + private final Map<Integer, IManageablePage> pages = new HashMap<>(); + + @Override + public void destroy() + { + pages.clear(); + } + + @Override + public IManageablePage getPage(IPageContext context, int id) + { + return pages.get(id); + } + + @Override + public void removePage(IPageContext context, final IManageablePage page) { + if (page != null) { + pages.remove(page.getPageId()); + } + } + + @Override + public void removeAllPages(IPageContext context) + { + pages.clear(); + } + + @Override + public void addPage(IPageContext context, IManageablePage page) + { + pages.put(page.getPageId(), page); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/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 deleted file mode 100644 index 03b26aa..0000000 --- a/wicket-core/src/main/java/org/apache/wicket/page/AbstractPageManager.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.wicket.page; - -import org.apache.wicket.util.lang.Args; - -/** - * Convenience class for {@link IPageManager} implementations. Subclass should extend - * {@link RequestAdapter} and override {@link #newRequestAdapter(IPageManagerContext)} method to return it's - * {@link RequestAdapter} implementation. - * - * @author Matej Knopp - */ -public abstract class AbstractPageManager implements IPageManager -{ - private final IPageManagerContext context; - - /** - * Construct. - * - * @param context - */ - public AbstractPageManager(IPageManagerContext context) - { - this.context = Args.notNull(context, "context"); - } - - /** - * - * @param context - * @return a new request adapter - */ - protected abstract RequestAdapter newRequestAdapter(IPageManagerContext context); - - /** - * @return The page manager context - */ - @Override - public IPageManagerContext getContext() - { - return context; - } - - /** - * @see #newRequestAdapter(IPageManagerContext) - * @return the request adapter - */ - protected RequestAdapter getRequestAdapter() - { - RequestAdapter adapter = (RequestAdapter)getContext().getRequestData(); - if (adapter == null) - { - adapter = newRequestAdapter(getContext()); - getContext().setRequestData(adapter); - } - return adapter; - } - - @Override - public void commitRequest() - { - getRequestAdapter().commitRequest(); - } - - @Override - public IManageablePage getPage(int id) - { - IManageablePage page = getRequestAdapter().getPage(id); - if (page != null) - { - touchPage(page); - } - return page; - } - - @Override - public void removePage(final IManageablePage page) { - if (page != null) - { - getRequestAdapter().removePage(page); - } - } - - @Override - public void newSessionCreated() - { - getRequestAdapter().newSessionCreated(); - } - - @Override - public void touchPage(IManageablePage page) - { - if (page != null) - { - getRequestAdapter().touch(page); - } - } - - @Override - public void untouchPage(IManageablePage page) - { - if (page != null) - { - getRequestAdapter().untouch(page); - } - } -} http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/page/DefaultPageManagerContext.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/page/DefaultPageManagerContext.java b/wicket-core/src/main/java/org/apache/wicket/page/DefaultPageManagerContext.java deleted file mode 100644 index 78f4b49..0000000 --- a/wicket-core/src/main/java/org/apache/wicket/page/DefaultPageManagerContext.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.wicket.page; - -import java.io.Serializable; - -import org.apache.wicket.MetaDataKey; -import org.apache.wicket.Session; -import org.apache.wicket.request.cycle.RequestCycle; - -/** - * Wicket's default page manager context - * - * @author Juergen Donnerstag - */ -public class DefaultPageManagerContext implements IPageManagerContext -{ - private static final MetaDataKey<Object> requestCycleMetaDataKey = new MetaDataKey<Object>() - { - private static final long serialVersionUID = 1L; - }; - - /** - * @see org.apache.wicket.page.IPageManagerContext#bind() - */ - @Override - public void bind() - { - Session.get().bind(); - } - - /** - * @see org.apache.wicket.page.IPageManagerContext#getRequestData() - */ - @Override - public Object getRequestData() - { - RequestCycle requestCycle = RequestCycle.get(); - if (requestCycle == null) - { - throw new IllegalStateException("Not a request thread."); - } - return requestCycle.getMetaData(requestCycleMetaDataKey); - } - - /** - * @see org.apache.wicket.page.IPageManagerContext#getSessionAttribute(java.lang.String) - */ - @Override - public Serializable getSessionAttribute(final String key) - { - return Session.get().getAttribute(key); - } - - /** - * @see org.apache.wicket.page.IPageManagerContext#getSessionId() - */ - @Override - public String getSessionId() - { - return Session.get().getId(); - } - - /** - * @see org.apache.wicket.page.IPageManagerContext#setRequestData(Object) - */ - @Override - public void setRequestData(final Object data) - { - RequestCycle requestCycle = RequestCycle.get(); - if (requestCycle == null) - { - throw new IllegalStateException("Not a request thread."); - } - requestCycle.setMetaData(requestCycleMetaDataKey, data); - } - - /** - * @see org.apache.wicket.page.IPageManagerContext#setSessionAttribute(java.lang.String, - * java.io.Serializable) - */ - @Override - public void setSessionAttribute(String key, Serializable value) - { - Session.get().setAttribute(key, value); - } -} http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/page/IPageManager.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/page/IPageManager.java b/wicket-core/src/main/java/org/apache/wicket/page/IPageManager.java index a19a0e3..9d517df 100644 --- a/wicket-core/src/main/java/org/apache/wicket/page/IPageManager.java +++ b/wicket-core/src/main/java/org/apache/wicket/page/IPageManager.java @@ -16,89 +16,59 @@ */ package org.apache.wicket.page; +import org.apache.wicket.Page; +import org.apache.wicket.pageStore.IPageStore; /** - * Page manager. - * - * @author Matej Knopp + * A manager of pages - facade between {@link Page}s and {@link IPageStore}s they are stored in. */ public interface IPageManager { - /** - * - * @return the page manager context - */ - IPageManagerContext getContext(); /** - * Retrieve page instance with given id. + * Get a page * - * @param id - * the id of the page to load - * @return page instance or <code>null</code> - * @throws CouldNotLockPageException if the page is already locked by another thread - * and the lock cannot be acquired for some timeout - */ - IManageablePage getPage(int id) throws CouldNotLockPageException; - - /** - * Removes a page from the {@link org.apache.wicket.pageStore.IPageStore} and - * {@link org.apache.wicket.pageStore.IDataStore}. Any attempt to access it later - * will lead to {@link org.apache.wicket.protocol.http.PageExpiredException} - * - * @param page The page instance to remove from the stores + * @param pageId + * id of page + * @return page, may be <code>null</code> */ - void removePage(IManageablePage page); + IManageablePage getPage(int pageId); /** - * Marks page as changed. - * <p><strong>Note:</strong>Only stateful pages are stored.</p> + * Remove a page * * @param page - * the page that should be stored in the page stores at the end of the request. - * @throws CouldNotLockPageException if the page is already locked by another thread - * and the lock cannot be acquired for some timeout - */ - void touchPage(IManageablePage page); - - /** - * Marks page as non-changed. - * Could be used in Ajax requests to avoid storing the page if no changes have happened. - * - * @param page - * the page that should <strong>not</strong> be stored in the page stores at the end of the request. - * @throws CouldNotLockPageException if the page is already locked by another thread - * and the lock cannot be acquired for some timeout + * page to remove */ - void untouchPage(IManageablePage page); + void removePage(IManageablePage page); /** - * Returns whether this manager supports versioning. Managers that support versioning must store - * page snapshots. + * Add a page. * - * @return whether this page manager supports versioning + * @param page + * page to add */ - boolean supportsVersioning(); + void addPage(IManageablePage page); /** - * Commits the changes to external storage if the manager uses it. - * - * Should also detach all pages that were touched during this request. + * Remove all pages. */ - void commitRequest(); + void removeAllPages(); /** - * Invoked when new session has been created. + * Detach at end of request. */ - void newSessionCreated(); + void detach(); /** - * Clears all data for the current session. + * Destroy when application is destroyed. */ - void clear(); + void destroy(); /** - * Destroy the page manager. + * Get the storage of pages, optional. + * + * @return store or <code>null</code> */ - void destroy(); + IPageStore getPageStore(); } http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/page/IPageManagerContext.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/page/IPageManagerContext.java b/wicket-core/src/main/java/org/apache/wicket/page/IPageManagerContext.java deleted file mode 100644 index d928606..0000000 --- a/wicket-core/src/main/java/org/apache/wicket/page/IPageManagerContext.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.wicket.page; - -import java.io.Serializable; - -/** - * Context object for {@link IPageManager}. This decouples the {@link IPageManager} from request - * cycle and session. - * - * @author Matej Knopp - */ -public interface IPageManagerContext -{ - /** - * - * @param data - */ - void setRequestData(Object data); - - /** - * - * @return request data - */ - Object getRequestData(); - - /** - * - * @param key - * @param value - */ - void setSessionAttribute(String key, Serializable value); - - /** - * - * @param key - * @return The session attribute associate with the key - */ - Serializable getSessionAttribute(String key); - - /** - * Tells Wicket to bind the current session. This make a temporary session become persistent - * across requests. - */ - void bind(); - - /** - * - * @return session id - */ - String getSessionId(); -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/page/PageAccessSynchronizer.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/page/PageAccessSynchronizer.java b/wicket-core/src/main/java/org/apache/wicket/page/PageAccessSynchronizer.java index d44b86a..c50fb20 100644 --- a/wicket-core/src/main/java/org/apache/wicket/page/PageAccessSynchronizer.java +++ b/wicket-core/src/main/java/org/apache/wicket/page/PageAccessSynchronizer.java @@ -23,6 +23,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.function.Supplier; import org.apache.wicket.Application; +import org.apache.wicket.pageStore.IPageStore; import org.apache.wicket.settings.ExceptionSettings.ThreadDumpStrategy; import org.apache.wicket.util.LazyInitializer; import org.apache.wicket.util.lang.Threads; @@ -230,12 +231,12 @@ public class PageAccessSynchronizer implements Serializable /** * Wraps a page manager with this synchronizer * - * @param pagemanager + * @param manager * @return wrapped page manager */ - public IPageManager adapt(IPageManager pagemanager) + public IPageManager adapt(final IPageManager manager) { - return new PageManagerDecorator(pagemanager) + return new IPageManager() { @Override public IManageablePage getPage(int pageId) @@ -244,7 +245,7 @@ public class PageAccessSynchronizer implements Serializable try { lockPage(pageId); - page = super.getPage(pageId); + page = manager.getPage(pageId); } finally { @@ -257,13 +258,13 @@ public class PageAccessSynchronizer implements Serializable } @Override - public void removePage(final IManageablePage page) { + public void removePage(IManageablePage page) + { if (page != null) { try { - super.removePage(page); - untouchPage(page); + manager.removePage(page); } finally { @@ -273,24 +274,43 @@ public class PageAccessSynchronizer implements Serializable } @Override - public void touchPage(IManageablePage page) + public void addPage(IManageablePage page) { lockPage(page.getPageId()); - super.touchPage(page); + + manager.addPage(page); } @Override - public void commitRequest() + public void removeAllPages() + { + manager.removeAllPages(); + } + + @Override + public void detach() { try { - super.commitRequest(); + manager.detach(); } finally { unlockAllPages(); } } + + @Override + public IPageStore getPageStore() + { + return manager.getPageStore(); + } + + @Override + public void destroy() + { + manager.destroy(); + } }; } http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/page/PageManager.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/page/PageManager.java b/wicket-core/src/main/java/org/apache/wicket/page/PageManager.java new file mode 100644 index 0000000..ab58c41 --- /dev/null +++ b/wicket-core/src/main/java/org/apache/wicket/page/PageManager.java @@ -0,0 +1,88 @@ +/* + * 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.page; + +import org.apache.wicket.Application; +import org.apache.wicket.Session; +import org.apache.wicket.pageStore.DefaultPageContext; +import org.apache.wicket.pageStore.IPageContext; +import org.apache.wicket.pageStore.IPageStore; +import org.apache.wicket.util.lang.Args; + +/** + * A manager of pages, i.e. a mediator between an {@link Application} and an {@link IPageStore}. + */ +public class PageManager implements IPageManager +{ + private final IPageStore store; + + public PageManager(IPageStore store) { + this.store = Args.notNull(store, "store"); + } + + @Override + public IManageablePage getPage(int pageId) + { + return store.getPage(createPageContext(), pageId); + } + + @Override + public void removePage(IManageablePage page) + { + store.removePage(createPageContext(), page); + } + + @Override + public void addPage(IManageablePage page) + { + store.addPage(createPageContext(), page); + } + + @Override + public void removeAllPages() + { + store.removeAllPages(createPageContext()); + } + + @Override + public void detach() + { + store.detach(createPageContext()); + } + + /** + * Factory method for an {@link IPageContext}, returns a {@link DefaultPageContext} by default. + * + * @return + */ + protected IPageContext createPageContext() + { + return new DefaultPageContext(Session.get()); + } + + @Override + public void destroy() + { + store.destroy(); + } + + @Override + public IPageStore getPageStore() + { + return store; + } +} http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/page/PageManagerDecorator.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/page/PageManagerDecorator.java b/wicket-core/src/main/java/org/apache/wicket/page/PageManagerDecorator.java deleted file mode 100644 index 8f719f8..0000000 --- a/wicket-core/src/main/java/org/apache/wicket/page/PageManagerDecorator.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.wicket.page; - -import org.apache.wicket.util.lang.Args; - -/** - * Decorator for {@link IPageManager} - * - * @author igor - */ -public class PageManagerDecorator implements IPageManager -{ - private final IPageManager delegate; - - /** - * Constructor - * - * @param delegate - */ - public PageManagerDecorator(IPageManager delegate) - { - Args.notNull(delegate, "delegate"); - this.delegate = delegate; - } - - @Override - public IPageManagerContext getContext() - { - return delegate.getContext(); - } - - @Override - public IManageablePage getPage(int id) - { - return delegate.getPage(id); - } - - @Override - public void removePage(final IManageablePage page) { - delegate.removePage(page); - } - - @Override - public void touchPage(IManageablePage page) - { - delegate.touchPage(page); - } - - @Override - public void untouchPage(IManageablePage page) - { - delegate.untouchPage(page); - } - - @Override - public boolean supportsVersioning() - { - return delegate.supportsVersioning(); - } - - @Override - public void commitRequest() - { - delegate.commitRequest(); - } - - @Override - public void newSessionCreated() - { - delegate.newSessionCreated(); - } - - @Override - public void clear() - { - delegate.clear(); - } - - @Override - public void destroy() - { - delegate.destroy(); - } - -} http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/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 deleted file mode 100644 index 88c1645..0000000 --- a/wicket-core/src/main/java/org/apache/wicket/page/PageStoreManager.java +++ /dev/null @@ -1,507 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.wicket.page; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import javax.servlet.http.HttpSessionBindingEvent; -import javax.servlet.http.HttpSessionBindingListener; - -import org.apache.wicket.pageStore.IPageStore; - -/** - * - */ -public class PageStoreManager extends AbstractPageManager -{ - /** - * A cache that holds all registered page managers. <br/> - * applicationName -> page manager - */ - private static final ConcurrentMap<String, PageStoreManager> MANAGERS = new ConcurrentHashMap<>(); - - private static final String ATTRIBUTE_NAME = "wicket:persistentPageManagerData"; - - /** - * A flag indicating whether this session entry is being re-set in the Session. - * <p> - * Web containers intercept - * {@link javax.servlet.http.HttpSession#setAttribute(String, Object)} to detect changes and - * replicate the session. If the attribute has been already bound in the session then - * {@link #valueUnbound(HttpSessionBindingEvent)} might get called - this flag - * helps us to ignore the invocation in that case. - * - * @see #valueUnbound(HttpSessionBindingEvent) - */ - private static final ThreadLocal<Boolean> STORING_TOUCHED_PAGES = new ThreadLocal<Boolean>() - { - protected Boolean initialValue() - { - return Boolean.FALSE; - }; - }; - - private final IPageStore pageStore; - - private final String applicationName; - - /** - * Construct. - * - * @param applicationName - * @param pageStore - * @param context - */ - public PageStoreManager(final String applicationName, final IPageStore pageStore, - final IPageManagerContext context) - { - super(context); - - this.applicationName = applicationName; - this.pageStore = pageStore; - - if (MANAGERS.containsKey(applicationName)) - { - throw new IllegalStateException( - "Manager for application with key '" + applicationName + "' already exists."); - } - MANAGERS.put(applicationName, this); - } - - /** - * Represents entry for single session. This is stored as session attribute and caches pages - * between requests. - * - * @author Matej Knopp - */ - private static class SessionEntry implements Serializable, HttpSessionBindingListener - { - private static final long serialVersionUID = 1L; - - private final String applicationName; - - /** - * The id handed to the {@link IPageStore} to identify the session. - * <p> - * Note: If the container changes a session's id, this field remains unchanged on its - * initial value. - */ - private final String sessionId; - - private transient List<IManageablePage> sessionCache; - private transient List<Object> afterReadObject; - - - /** - * Construct. - * - * @param applicationName - * @param sessionId - */ - public SessionEntry(String applicationName, String sessionId) - { - this.applicationName = applicationName; - this.sessionId = sessionId; - } - - /** - * - * @return page store - */ - private IPageStore getPageStore() - { - PageStoreManager manager = MANAGERS.get(applicationName); - - if (manager == null) - { - return null; - } - - return manager.pageStore; - } - - /** - * - * @param id - * @return null, if not found - */ - private IManageablePage findPage(int id) - { - for (IManageablePage p : sessionCache) - { - if (p.getPageId() == id) - { - return p; - } - } - return null; - } - - /** - * Add the page to cached pages if page with same id is not already there - * - * @param page - */ - private void addPage(IManageablePage page) - { - if (page != null) - { - if (findPage(page.getPageId()) != null) - { - return; - } - - sessionCache.add(page); - } - } - - private synchronized void removePage(IManageablePage page) - { - if (page != null) - { - sessionCache.remove(page); - final IPageStore pageStore = getPageStore(); - if (pageStore != null) - { - pageStore.removePage(sessionId, page.getPageId()); - } - } - } - - /** - * If the pages are stored in temporary state (after deserialization) this method convert - * them to list of "real" pages - */ - private void convertAfterReadObjects() - { - if (sessionCache == null) - { - sessionCache = new ArrayList<>(); - } - - final IPageStore pageStore = getPageStore(); - if (pageStore != null) - { - for (Object o : afterReadObject) - { - IManageablePage page = pageStore.convertToPage(o); - addPage(page); - } - } - - afterReadObject = null; - } - - /** - * - * @param id - * @return manageable page - */ - public synchronized IManageablePage getPage(int id) - { - // check if pages are in deserialized state - if (afterReadObject != null && afterReadObject.isEmpty() == false) - { - convertAfterReadObjects(); - } - - IManageablePage page = null; - // try to find page with same id - if (sessionCache != null) - { - page = findPage(id); - if (page != null) - { - return page; - } - } - - // not found, ask pagestore for the page - final IPageStore pageStore = getPageStore(); - if (pageStore != null) - { - page = pageStore.getPage(sessionId, id); - } - return page; - } - - /** - * set the list of pages to remember after the request - * - * @param pages - */ - public synchronized void setSessionCache(final List<IManageablePage> pages) - { - sessionCache = new ArrayList<>(pages); - afterReadObject = null; - } - - /** - * Serializes all pages in this {@link SessionEntry}. If this is http worker thread then - * there is available {@link IPageStore} which will be asked to prepare the page for - * serialization (see DefaultPageStore$SerializePage). If there is no {@link IPageStore} - * available (session loading/persisting in application initialization/destruction thread) - * then the pages are serialized without any pre-processing - * - * @param s - * @throws IOException - */ - private void writeObject(final ObjectOutputStream s) throws IOException - { - s.defaultWriteObject(); - - // prepare for serialization and store the pages - List<Serializable> serializedPages = new ArrayList<Serializable>(); - if (sessionCache != null) - { - IPageStore pageStore = getPageStore(); - for (IManageablePage p : sessionCache) - { - Serializable preparedPage; - if (pageStore != null) - { - preparedPage = pageStore.prepareForSerialization(sessionId, p); - } - else - { - preparedPage = p; - } - - if (preparedPage != null) - { - serializedPages.add(preparedPage); - } - } - } - s.writeObject(serializedPages); - } - - /** - * Deserializes the pages in this {@link SessionEntry}. If this is http worker thread then - * there is available {@link IPageStore} which will be asked to restore the page from its - * optimized state (see DefaultPageStore$SerializePage). If there is no {@link IPageStore} - * available (session loading/persisting in application initialization/destruction thread) - * then the pages are deserialized without any post-processing - * - * @param s - * @throws IOException - * @throws ClassNotFoundException - */ - @SuppressWarnings("unchecked") - private void readObject(final ObjectInputStream s) - throws IOException, ClassNotFoundException - { - s.defaultReadObject(); - - afterReadObject = new ArrayList<>(); - - List<Serializable> l = (List<Serializable>)s.readObject(); - - // convert to temporary state after deserialization (will need to be processed - // by convertAfterReadObject before the pages can be accessed) - IPageStore pageStore = getPageStore(); - for (Serializable ser : l) - { - Object page; - if (pageStore != null) - { - page = pageStore.restoreAfterSerialization(ser); - } - else - { - page = ser; - } - afterReadObject.add(page); - } - } - - @Override - public void valueBound(HttpSessionBindingEvent event) - { - } - - @Override - public void valueUnbound(HttpSessionBindingEvent event) - { - if (STORING_TOUCHED_PAGES.get()) - { - // triggered by #storeTouchedPages(), so do not remove the data - return; - } - - // WICKET-5164 use the original sessionId - IPageStore store = getPageStore(); - // store might be null if destroyed already - if (store != null) - { - store.unbind(sessionId); - } - } - - @Override - public boolean equals(Object o) - { - // see https://issues.apache.org/jira/browse/WICKET-5390 - return false; - } - } - - private String getAttributeName() - { - return ATTRIBUTE_NAME + " - " + applicationName; - } - - /** - * {@link RequestAdapter} for {@link PageStoreManager} - * - * @author Matej Knopp - */ - protected class PersistentRequestAdapter extends RequestAdapter - { - /** - * Construct. - * - * @param context - */ - public PersistentRequestAdapter(IPageManagerContext context) - { - super(context); - } - - @Override - protected IManageablePage getPage(int id) - { - IManageablePage touchedPage = findPage(id); - if (touchedPage != null) - { - return touchedPage; - } - - // try to get session entry for this session - SessionEntry entry = getSessionEntry(false); - - if (entry != null) - { - return entry.getPage(id); - } - else - { - return null; - } - } - - @Override - protected void removePage(final IManageablePage page) - { - final SessionEntry sessionEntry = getSessionEntry(false); - if (sessionEntry != null) - { - sessionEntry.removePage(page); - } - } - - /** - * - * @param create - * @return Session Entry - */ - private SessionEntry getSessionEntry(boolean create) - { - SessionEntry entry = (SessionEntry)getSessionAttribute(getAttributeName()); - if (entry == null && create) - { - bind(); - entry = new SessionEntry(applicationName, getSessionId()); - } - return entry; - } - - @Override - protected void newSessionCreated() - { - // if the session is not temporary bind a session entry to it - if (getSessionId() != null) - { - getSessionEntry(true); - } - } - - @Override - protected void storeTouchedPages(final List<IManageablePage> touchedPages) - { - if (!touchedPages.isEmpty()) - { - SessionEntry entry = getSessionEntry(true); - entry.setSessionCache(touchedPages); - for (IManageablePage page : touchedPages) - { - // WICKET-5103 use the same sessionId as used in - // SessionEntry#getPage() - pageStore.storePage(entry.sessionId, page); - } - - STORING_TOUCHED_PAGES.set(true); - try - { - setSessionAttribute(getAttributeName(), entry); - } - finally - { - STORING_TOUCHED_PAGES.remove(); - } - } - } - } - - @Override - protected RequestAdapter newRequestAdapter(IPageManagerContext context) - { - return new PersistentRequestAdapter(context); - } - - @Override - public boolean supportsVersioning() - { - return true; - } - - @Override - public void clear() - { - RequestAdapter requestAdapter = getRequestAdapter(); - String sessionEntryAttributeName = getAttributeName(); - Serializable sessionEntry = requestAdapter.getSessionAttribute(sessionEntryAttributeName); - if (sessionEntry instanceof SessionEntry) - { - ((SessionEntry)sessionEntry).valueUnbound(null); - } - } - - @Override - public void destroy() - { - MANAGERS.remove(applicationName); - pageStore.destroy(); - } -} http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/wicket-core/src/main/java/org/apache/wicket/page/RequestAdapter.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/page/RequestAdapter.java b/wicket-core/src/main/java/org/apache/wicket/page/RequestAdapter.java deleted file mode 100644 index 7b8b7b9..0000000 --- a/wicket-core/src/main/java/org/apache/wicket/page/RequestAdapter.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.wicket.page; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Request scoped helper class for {@link IPageManager}. - * - * @author Matej Knopp - */ -public abstract class RequestAdapter -{ - private static final Logger log = LoggerFactory.getLogger(RequestAdapter.class); - - private final IPageManagerContext context; - - private final List<IManageablePage> touchedPages = new ArrayList<IManageablePage>(); - - /** - * Construct. - * - * @param context - * The page manager context - */ - public RequestAdapter(final IPageManagerContext context) - { - this.context = context; - } - - /** - * Returns the page with specified id. The page is then cached by {@link RequestAdapter} during - * the rest of request processing. - * - * @param id - * @return page instance or <code>null</code> if the page does not exist. - */ - protected abstract IManageablePage getPage(int id); - - /** - * Removes a page from the cache and the stores ({@link org.apache.wicket.pageStore.IPageStore} and - * {@link org.apache.wicket.pageStore.IDataStore}). Any attempt to access it later - * will lead to {@link org.apache.wicket.protocol.http.PageExpiredException} - * - * @param page The page instance to remove - */ - protected abstract void removePage(final IManageablePage page); - - /** - * Store the list of stateful pages. - * - * @param touchedPages - */ - protected abstract void storeTouchedPages(List<IManageablePage> touchedPages); - - /** - * Notification on new session being created. - */ - protected abstract void newSessionCreated(); - - /** - * Bind the session - * - * @see IPageManagerContext#bind() - */ - protected void bind() - { - context.bind(); - } - - /** - * @see IPageManagerContext#setSessionAttribute(String, Serializable) - * - * @param key - * @param value - */ - public void setSessionAttribute(String key, Serializable value) - { - context.setSessionAttribute(key, value); - } - - /** - * @see IPageManagerContext#getSessionAttribute(String) - * - * @param key - * @return the session attribute - */ - public Serializable getSessionAttribute(final String key) - { - return context.getSessionAttribute(key); - } - - /** - * @see IPageManagerContext#getSessionId() - * - * @return session id - */ - public String getSessionId() - { - return context.getSessionId(); - } - - /** - * - * @param id - * @return null, if not found - */ - protected IManageablePage findPage(final int id) - { - for (IManageablePage page : touchedPages) - { - if (page.getPageId() == id) - { - return page; - } - } - return null; - } - - /** - * Touches a page, so it will be stored in the page stores - * at the end of the request cycle - * - * @param page The page to mark as dirty - */ - protected void touch(final IManageablePage page) - { - if (findPage(page.getPageId()) == null) - { - touchedPages.add(page); - } - } - - /** - * @param page The page to unmark as dirty, so it won't be stored - * at the end of the request cycle - */ - protected void untouch(final IManageablePage page) - { - Iterator<IManageablePage> iterator = touchedPages.iterator(); - while (iterator.hasNext()) - { - IManageablePage touchedPage = iterator.next(); - if (touchedPage.getPageId() == page.getPageId()) - { - iterator.remove(); - break; - } - } - } - - /** - * - */ - protected void commitRequest() - { - // store pages that are not stateless - if (touchedPages.isEmpty() == false) - { - List<IManageablePage> statefulPages = new ArrayList<IManageablePage>( - touchedPages.size()); - for (IManageablePage page : touchedPages) - { - boolean isPageStateless; - try - { - isPageStateless = page.isPageStateless(); - } - catch (Exception x) - { - log.warn("An error occurred while checking whether a page is stateless. Assuming it is stateful.", x); - isPageStateless = false; - } - if (isPageStateless == false) - { - statefulPages.add(page); - } - } - - if (statefulPages.isEmpty() == false) - { - storeTouchedPages(statefulPages); - } - } - } -} http://git-wip-us.apache.org/repos/asf/wicket/blob/bcf76f51/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 deleted file mode 100644 index 86b8d82..0000000 --- a/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractCachingPageStore.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.wicket.pageStore; - -import 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/bcf76f51/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 deleted file mode 100644 index 07f97d5..0000000 --- a/wicket-core/src/main/java/org/apache/wicket/pageStore/AbstractPageStore.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.wicket.pageStore; - -import java.io.Serializable; - -import 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/bcf76f51/wicket-core/src/main/java/org/apache/wicket/pageStore/AsynchronousDataStore.java ---------------------------------------------------------------------- diff --git a/wicket-core/src/main/java/org/apache/wicket/pageStore/AsynchronousDataStore.java b/wicket-core/src/main/java/org/apache/wicket/pageStore/AsynchronousDataStore.java deleted file mode 100644 index 7ccd689..0000000 --- a/wicket-core/src/main/java/org/apache/wicket/pageStore/AsynchronousDataStore.java +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.wicket.pageStore; - -import java.util.Iterator; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; - -import org.apache.wicket.util.lang.Args; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Facade for {@link IDataStore} that does the actual saving in worker thread. - * <p> - * Creates an {@link Entry} for each triple (sessionId, pageId, data) and puts it in - * {@link #entries} queue if there is room. Acts as producer.<br/> - * Later {@link PageSavingRunnable} reads in blocking manner from {@link #entries} and saves each - * entry. Acts as consumer. - * </p> - * It starts only one instance of {@link PageSavingRunnable} because all we need is to make the page - * storing asynchronous. We don't want to write concurrently in the wrapped {@link IDataStore}, - * though it may happen in the extreme case when the queue is full. These cases should be avoided. - * - * @author Matej Knopp - */ -public class AsynchronousDataStore implements IDataStore -{ - /** Log for reporting. */ - private static final Logger log = LoggerFactory.getLogger(AsynchronousDataStore.class); - - /** - * The time to wait when adding an {@link Entry} into the entries. In millis. - */ - private static final long OFFER_WAIT = 30L; - - /** - * The time to wait for an entry to save with the wrapped {@link IDataStore}. In millis. - */ - private static final long POLL_WAIT = 1000L; - - /** - * The page saving thread. - */ - private final Thread pageSavingThread; - - /** - * The wrapped {@link IDataStore} that actually stores that pages - */ - private final IDataStore dataStore; - - /** - * The queue where the entries which have to be saved are temporary stored - */ - private final BlockingQueue<Entry> entries; - - /** - * A map 'sessionId:::pageId' -> {@link Entry}. Used for fast retrieval of {@link Entry}s which - * are not yet stored by the wrapped {@link IDataStore} - */ - private final ConcurrentMap<String, Entry> entryMap; - - /** - * Construct. - * - * @param dataStore - * the wrapped {@link IDataStore} that actually saved the data - * @param capacity - * the capacity of the queue that delays the saving - */ - public AsynchronousDataStore(final IDataStore dataStore, final int capacity) - { - this.dataStore = dataStore; - entries = new LinkedBlockingQueue<>(capacity); - entryMap = new ConcurrentHashMap<>(); - - PageSavingRunnable savingRunnable = new PageSavingRunnable(dataStore, entries, entryMap); - pageSavingThread = new Thread(savingRunnable, "Wicket-AsyncDataStore-PageSavingThread"); - pageSavingThread.setDaemon(true); - pageSavingThread.start(); - } - - @Override - public void destroy() - { - if (pageSavingThread.isAlive()) - { - pageSavingThread.interrupt(); - try - { - pageSavingThread.join(); - } catch (InterruptedException e) - { - log.error(e.getMessage(), e); - } - } - - dataStore.destroy(); - } - - /** - * Little helper - * - * @param sessionId - * @param id - * @return Entry - */ - private Entry getEntry(final String sessionId, final int id) - { - return entryMap.get(getKey(sessionId, id)); - } - - @Override - public byte[] getData(final String sessionId, final int id) - { - Entry entry = getEntry(sessionId, id); - if (entry != null) - { - log.debug( - "Returning the data of a non-stored entry with sessionId '{}' and pageId '{}'", - sessionId, id); - return entry.data; - } - byte[] data = dataStore.getData(sessionId, id); - - log.debug("Returning the data of a stored entry with sessionId '{}' and pageId '{}'", - sessionId, id); - - return data; - } - - @Override - public boolean isReplicated() - { - return dataStore.isReplicated(); - } - - @Override - public void removeData(final String sessionId, final int id) - { - String key = getKey(sessionId, id); - if (key != null) - { - Entry entry = entryMap.remove(key); - if (entry != null) - { - entries.remove(entry); - } - } - - dataStore.removeData(sessionId, id); - } - - @Override - public void removeData(final String sessionId) - { - for (Iterator<Entry> itor = entries.iterator(); itor.hasNext();) - { - Entry entry = itor.next(); - if (entry != null) // this check is not needed in JDK6 - { - String entrySessionId = entry.sessionId; - - if (sessionId.equals(entrySessionId)) - { - entryMap.remove(getKey(entry)); - itor.remove(); - } - } - } - - dataStore.removeData(sessionId); - } - - /** - * Save the entry in the queue if there is a room or directly pass it to the wrapped - * {@link IDataStore} if there is no such - * - * @see org.apache.wicket.pageStore.IDataStore#storeData(java.lang.String, int, byte[]) - */ - @Override - public void storeData(final String sessionId, final int id, final byte[] data) - { - Entry entry = new Entry(sessionId, id, data); - String key = getKey(entry); - entryMap.put(key, entry); - - try - { - boolean added = entries.offer(entry, OFFER_WAIT, TimeUnit.MILLISECONDS); - - if (added == false) - { - log.debug("Storing synchronously page with id '{}' in session '{}'", id, sessionId); - entryMap.remove(key); - dataStore.storeData(sessionId, id, data); - } - } - catch (InterruptedException e) - { - log.error(e.getMessage(), e); - entryMap.remove(key); - dataStore.storeData(sessionId, id, data); - } - } - - /** - * - * @param pageId - * @param sessionId - * @return generated key - */ - private static String getKey(final String sessionId, final int pageId) - { - return pageId + ":::" + sessionId; - } - - /** - * - * @param entry - * @return generated key - */ - private static String getKey(final Entry entry) - { - return getKey(entry.sessionId, entry.pageId); - } - - /** - * The structure used for an entry in the queue - */ - private static class Entry - { - private final String sessionId; - private final int pageId; - private final byte data[]; - - public Entry(final String sessionId, final int pageId, final byte data[]) - { - this.sessionId = Args.notNull(sessionId, "sessionId"); - this.pageId = pageId; - this.data = Args.notNull(data, "data"); - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + pageId; - result = prime * result + ((sessionId == null) ? 0 : sessionId.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Entry other = (Entry)obj; - if (pageId != other.pageId) - return false; - if (sessionId == null) - { - if (other.sessionId != null) - return false; - } - else if (!sessionId.equals(other.sessionId)) - return false; - return true; - } - - @Override - public String toString() - { - return "Entry [sessionId=" + sessionId + ", pageId=" + pageId + "]"; - } - - } - - /** - * The thread that acts as consumer of {@link Entry}ies - */ - private static class PageSavingRunnable implements Runnable - { - private static final Logger log = LoggerFactory.getLogger(PageSavingRunnable.class); - - private final BlockingQueue<Entry> entries; - - private final ConcurrentMap<String, Entry> entryMap; - - private final IDataStore dataStore; - - private PageSavingRunnable(IDataStore dataStore, BlockingQueue<Entry> entries, - ConcurrentMap<String, Entry> entryMap) - { - this.dataStore = dataStore; - this.entries = entries; - this.entryMap = entryMap; - } - - @Override - public void run() - { - while (!Thread.interrupted()) - { - Entry entry = null; - try - { - entry = entries.poll(POLL_WAIT, TimeUnit.MILLISECONDS); - } - catch (InterruptedException e) - { - Thread.currentThread().interrupt(); - } - - if (entry != null) - { - log.debug("Saving asynchronously: {}...", entry); - dataStore.storeData(entry.sessionId, entry.pageId, entry.data); - entryMap.remove(getKey(entry)); - } - } - } - } - - @Override - public final boolean canBeAsynchronous() - { - // should not wrap in another AsynchronousDataStore - return false; - } -}
