001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket;
018
019import java.io.File;
020
021import org.apache.wicket.page.IPageManager;
022import org.apache.wicket.page.PageManager;
023import org.apache.wicket.pageStore.AsynchronousPageStore;
024import org.apache.wicket.pageStore.CachingPageStore;
025import org.apache.wicket.pageStore.CryptingPageStore;
026import org.apache.wicket.pageStore.DiskPageStore;
027import org.apache.wicket.pageStore.FilePageStore;
028import org.apache.wicket.pageStore.GroupingPageStore;
029import org.apache.wicket.pageStore.IPageStore;
030import org.apache.wicket.pageStore.InMemoryPageStore;
031import org.apache.wicket.pageStore.InSessionPageStore;
032import org.apache.wicket.pageStore.NoopPageStore;
033import org.apache.wicket.pageStore.RequestPageStore;
034import org.apache.wicket.pageStore.SerializedPage;
035import org.apache.wicket.pageStore.SerializingPageStore;
036import org.apache.wicket.serialize.ISerializer;
037import org.apache.wicket.settings.FrameworkSettings;
038import org.apache.wicket.settings.StoreSettings;
039import org.apache.wicket.util.lang.Args;
040import org.apache.wicket.util.lang.Bytes;
041
042/**
043 * A provider of a {@link PageManager} managing @link IManageablePage}s with a default chain of {@link IPageStore}s:
044 * <ol>
045 * <li>{@link RequestPageStore} keeping pages until end of the request</li>
046 * <li>{@link CachingPageStore} caching with an ...</li>
047 * <li>{@link InSessionPageStore} keeping the last accessed page in the session</li>
048 * <li>{@link SerializingPageStore} serializing all pages (so they are available for back-button)</li>
049 * <li>{@link AsynchronousPageStore} moving storage of pages to an asynchronous worker thread (enabled by default with {@link StoreSettings#isAsynchronous()})</li>
050 * <li>{@link CryptingPageStore} encrypting all pages (disabled by default in {@link StoreSettings#isEncrypted()})</li>
051 * <li>{@link DiskPageStore} persisting all pages, configured according to {@link StoreSettings}</li>
052 * </ol>
053 * An alternative chain with all pages held in-memory could be:
054 * <ol>
055 * <li>{@link RequestPageStore} keeping pages until end of the request</li>
056 * <li>{@link CachingPageStore} caching with an ...</li>
057 * <li>{@link InSessionPageStore} keeping the last accessed page in the session</li>
058 * <li>{@link SerializingPageStore} serializing all pages (so they are available for back-button)</li>
059 * <li>{@link AsynchronousPageStore} moving storage of pages to a worker thread</li>
060 * <li>{@link InMemoryPageStore} keeping all pages in memory</li>
061 * </ol>
062 * ... or if all pages should be kept in the session only, without any serialization (no back-button
063 * support though):
064 * <ul>
065 * <li>{@link RequestPageStore} caching pages until end of the request</li>
066 * <li>{@link InSessionPageStore} keeping a limited count of pages in the session, e.g. 10</li>
067 * </ul>
068 * The chain's initial store should always be a {@link RequestPageStore}, buffering all adding of pages until the end of the request.
069 * Several stores accept {@link SerializedPage} only, these have to be preceded by a {@link SerializingPageStore}.
070 * <p> 
071 * For back-button support <em>at least one</em> store in the chain must create copies of stored
072 * pages (usually through serialization), otherwise any following request will work on an identical
073 * page instance and the previous state of page is no longer accessible.
074 * <p>
075 * Other stores be may inserted ad libitum, e.g.
076 * <ul>
077 * <li>{@link NoopPageStore} discards all pages</li>
078 * <li>{@link GroupingPageStore} groups pages, e.g. to limit storage size on a per-group basis</li>
079 * <li>{@link FilePageStore} as an alternative to the trusted {@link DiskPageStore}</li>
080 * <li>other implementations from <a href="https://github.com/wicketstuff/core/tree/master/datastores-parent">wicketstuff-datastores</a></li>
081 * </ul>
082 */
083public class DefaultPageManagerProvider implements IPageManagerProvider
084{
085        protected final Application application;
086
087        /**
088         * Constructor.
089         *
090         * @param application
091         *            The application instance
092         */
093        public DefaultPageManagerProvider(Application application)
094        {
095                this.application = Args.notNull(application, "application");
096        }
097
098        @Override
099        public IPageManager get()
100        {
101                IPageStore store = newPersistentStore();
102                
103                store = newCryptingStore(store);
104
105                store = newAsynchronousStore(store);
106                
107                store = newSerializingStore(store);
108
109                store = newCachingStore(store);
110
111                store = newRequestStore(store);
112
113                return new PageManager(store);
114        }
115
116        /**
117         * Get the {@link ISerializer} to use for serializing of pages.
118         * <p>
119         * By default the serializer of the applications {@link FrameworkSettings}.
120         * 
121         * @return how to serialize pages if needed for any {@link IPageStore}
122         * 
123         * @see FrameworkSettings#getSerializer()
124         */
125        protected ISerializer getSerializer()
126        {
127                return application.getFrameworkSettings().getSerializer();
128        }
129
130        /**
131         * Keep pages in the request until it is finished.
132         * 
133         * @see RequestPageStore
134         */
135        protected IPageStore newRequestStore(IPageStore pageStore)
136        {
137                return new RequestPageStore(pageStore);
138        }
139
140        /**
141         * Cache last page non-serialized in the session for fast access.
142         * <p>
143         * On session serialization the cached page will be dropped and re-acquired from
144         * a persistent store. 
145         * 
146         * @see InSessionPageStore
147         */
148        protected IPageStore newCachingStore(IPageStore pageStore)
149        {
150                return new CachingPageStore(pageStore, new InSessionCache());
151        }
152
153        /**
154         * Store pages asynchronously into the persistent store, if enabled in {@link StoreSettings#isAsynchronous()}.
155         * 
156         * @see AsynchronousPageStore
157         */
158        protected IPageStore newAsynchronousStore(IPageStore pageStore)
159        {
160                StoreSettings storeSettings = application.getStoreSettings();
161
162                if (storeSettings.isAsynchronous())
163                {
164                        int capacity = storeSettings.getAsynchronousQueueCapacity();
165                        pageStore = new AsynchronousPageStore(pageStore, capacity);
166                }
167
168                return pageStore;
169        }
170
171        /**
172         * Serialize pages.
173         * 
174         * @see SerializingPageStore
175         */
176        protected IPageStore newSerializingStore(IPageStore pageStore)
177        {
178                return new SerializingPageStore(pageStore, getSerializer());
179        }
180
181        /**
182         * Crypt all pages, if enabled in {@link StoreSettings#isEncrypted()}.
183         * 
184         * @see CryptingPageStore
185         */
186        protected IPageStore newCryptingStore(IPageStore pageStore)
187        {
188                StoreSettings storeSettings = application.getStoreSettings();
189                
190                if (storeSettings.isEncrypted())
191                {
192                        pageStore = new CryptingPageStore(pageStore, application);
193                }
194
195                return pageStore;
196        }
197
198        /**
199         * Keep persistent copies of all pages on disk.
200         * 
201         * @see DiskPageStore
202         * @see StoreSettings#getMaxSizePerSession()
203         * @see StoreSettings#getFileStoreFolder()
204         */
205        protected IPageStore newPersistentStore()
206        {
207                StoreSettings storeSettings = application.getStoreSettings();
208                Bytes maxSizePerSession = storeSettings.getMaxSizePerSession();
209                File fileStoreFolder = storeSettings.getFileStoreFolder();
210
211                return new DiskPageStore(application.getName(), fileStoreFolder, maxSizePerSession);
212        }
213        
214        private static class InSessionCache extends InSessionPageStore {
215
216                private static final MetaDataKey<SessionData> KEY = new MetaDataKey<>()
217                {
218                        private static final long serialVersionUID = 1L;
219                };
220                
221                InSessionCache()
222                {
223                        super(1);
224                }
225
226                /**
227                 * Use a separate key, so this store does not interfere with any additional {@link InSessionPageStore}
228                 * the application might set up.
229                 */
230                @Override
231                protected MetaDataKey<SessionData> getKey()
232                {
233                        return KEY;
234                }
235        }
236}