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.settings;
018
019import java.time.Duration;
020import java.util.ArrayList;
021import java.util.Collections;
022import java.util.List;
023import org.apache.wicket.response.filter.IResponseFilter;
024import org.apache.wicket.util.lang.Args;
025
026/**
027 * Class for request related settings
028 * <p>
029 * <i>bufferResponse </i> (defaults to true) - True if the application should buffer responses. This
030 * does require some additional memory, but helps keep exception displays accurate because the whole
031 * rendering process completes before the page is sent to the user, thus aRequestCycleSettingsing the possibility of
032 * a partially rendered page.
033 * <p>
034 * <i>renderStrategy </i>- Sets in what way the render part of a request is handled. Basically,
035 * there are two different options:
036 * <ul>
037 * <li>Direct, IRequestCycleSettings.RenderStrategy.ONE_PASS_RENDER. Everything is handled in one
038 * physical request. This is efficient, and is the best option if you want to do sophisticated
039 * clustering. It does not however, shield you from what is commonly known as the <i>Double submit
040 * problem </i></li>
041 * <li>Using a redirect. This follows the pattern <a
042 * href="http://www.theserverside.com/articles/article.tss?l=RedirectAfterPost" >as described at the
043 * serverside </a> and that is commonly known as Redirect after post. Wicket takes it one step
044 * further to do any rendering after a redirect, so that not only form submits are shielded from the
045 * double submit problem, but also the IRequestListener handlers (that could be e.g. a link that
046 * deletes a row). With this pattern, you have two options to choose from:
047 * <ul>
048 * <li>IRequestCycleSettings.RenderStrategy.REDIRECT_TO_RENDER. This option first handles the
049 * 'action' part of the request, which is either page construction (bookmarkable pages or the home
050 * page) or calling a IRequestListener handler, such as Link.onClick. When that part is done, a
051 * redirect is issued to the render part, which does all the rendering of the page and its
052 * components. <strong>Be aware </strong> that this may mean, depending on whether you access any
053 * models in the action part of the request, that attachment and detachment of some models is done
054 * twice for a request.</li>
055 * <li>IRequestCycleSettings.RenderStrategy.REDIRECT_TO_BUFFER. This option handles both the action-
056 * and the render part of the request in one physical request, but instead of streaming the result
057 * to the browser directly, it is kept in memory, and a redirect is issued to get this buffered
058 * result (after which it is immediately removed). This option currently is the default render
059 * strategy, as it shields you from the double submit problem, while being more efficient and less
060 * error prone regarding to detachable models.</li>
061 * </ul>
062 * Note: In rare cases the strategies involving redirect may lose session data! For example: if
063 * after the first phase of the strategy the server node fails without having the chance to
064 * replicate the session then the second phase will be executed on another node and the whole
065 * process will be restarted and thus anything stored in the first phase will be lost with the
066 * failure of the server node. For similar reasons it is recommended to use sticky sessions when
067 * using redirect strategies.</li>
068 * </ul>
069 *
070 * <p>
071 * More documentation is available about each setting in the setter method for the property.
072 *
073 * @author Jonathan Locke
074 * @author Chris Turner
075 * @author Eelco Hillenius
076 * @author Juergen Donnerstag
077 * @author Johan Compagner
078 * @author Igor Vaynberg (ivaynberg)
079 * @author Martijn Dashorst
080 * @author James Carman
081 */
082public class RequestCycleSettings
083{
084        /**
085         * Enum type for different render strategies
086         */
087        public enum RenderStrategy {
088                /**
089                 * All logical parts of a request (the action and render part) are handled within the same
090                 * request.
091                 * <p>
092                 * This strategy is more efficient than the 'REDIRECT_TO_RENDER' strategy, and doesn't have
093                 * some of the potential problems of it, it also does not solve the double submit problem.
094                 * It is however the best option to use when you want to do sophisticated (non-sticky
095                 * session) clustering.
096                 * </p>
097                 */
098                ONE_PASS_RENDER,
099
100                /**
101                 * All logical parts of a request (the action and render part) are handled within the same
102                 * request, but instead of streaming the render result to the browser directly, the result
103                 * is cached on the server. A client side redirect command is issued to the browser
104                 * specifically to render this request.
105                 */
106                REDIRECT_TO_BUFFER,
107
108                /**
109                 * The render part of a request (opposed to the 'action part' which is either the
110                 * construction of a bookmarkable page or the execution of a IRequestListener handler) is
111                 * handled by a separate request by issuing a redirect request to the browser. This is
112                 * commonly known as the 'redirect after submit' pattern, though in our case, we use it for
113                 * GET and POST requests instead of just the POST requests.
114                 * <p>
115                 * This pattern solves the 'refresh' problem. While it is a common feature of browsers to
116                 * refresh/ reload a web page, this results in problems in many dynamic web applications.
117                 * For example, when you have a link with an event handler that e.g. deletes a row from a
118                 * list, you usually want to ignore refresh requests after that link is clicked on. By using
119                 * this strategy, the refresh request only results in the re-rendering of the page without
120                 * executing the event handler again.
121                 * </p>
122                 * <p>
123                 * Though it solves the refresh problem, it introduces potential problems, as the request
124                 * that is logically one, are actually two separate request. Not only is this less
125                 * efficient, but this also can mean that within the same request attachment/ detachment of
126                 * models is done twice (in case you use models in the bookmarkable page constructors and
127                 * IRequestListener handlers). If you use this strategy, you should be aware of this
128                 * possibility, and should also be aware that for one logical request, actually two
129                 * instances of RequestCycle are created and processed.
130                 * </p>
131                 * <p>
132                 * Also, even with this strategy set, it is ignored for instances of
133                 * {@link org.apache.wicket.core.request.handler.BookmarkableListenerRequestHandler},
134                 * because otherwise they wouldn't be bookmarkable.
135                 * </p>
136                 */
137                REDIRECT_TO_RENDER
138        }
139
140        /** True if the response should be buffered */
141        private boolean bufferResponse = true;
142
143        /**
144         * Whether Wicket should try to get extensive client info by redirecting to
145         * {@link org.apache.wicket.markup.html.pages.BrowserInfoPage a page that polls for client
146         * capabilities}. False by default.
147         */
148        private boolean gatherExtendedBrowserInfo = false;
149
150        /**
151         * The render strategy, defaults to 'REDIRECT_TO_BUFFER'. This property influences the default
152         * way in how a logical request that consists of an 'action' and a 'render' part is handled, and
153         * is mainly used to have a means to circumvent the 'refresh' problem.
154         */
155        private RequestCycleSettings.RenderStrategy renderStrategy = RenderStrategy.REDIRECT_TO_BUFFER;
156
157        /** List of {@link IResponseFilter}s. */
158        private List<IResponseFilter> responseFilters;
159
160        /**
161         * In order to do proper form parameter decoding it is important that the response and the
162         * following request have the same encoding. see
163         * http://www.crazysquirrel.com/computing/general/form-encoding.jspx for additional information.
164         */
165        private String responseRequestEncoding = "UTF-8";
166
167        /**
168         * The time that a request will by default be waiting for the previous request to be handled
169         * before giving up. Defaults to one minute.
170         */
171        private Duration timeout = Duration.ofMinutes(1);
172
173        private int exceptionRetryCount = 10;
174
175// ****************************************************************************
176// IRequestCycleSettings Implementation
177// ****************************************************************************
178
179        /**
180         * Adds a response filter to the list. Filters are evaluated in the order they have been added.
181         *
182         * @param responseFilter
183         *            The {@link IResponseFilter} that is added
184         * @return {@code this} object for chaining
185         */
186        public RequestCycleSettings addResponseFilter(IResponseFilter responseFilter)
187        {
188                if (responseFilters == null)
189                {
190                        responseFilters = new ArrayList<>(3);
191                }
192                responseFilters.add(responseFilter);
193                return this;
194        }
195
196        /**
197         * Decides whether to buffer the response's headers until the end of the request processing.
198         * The buffering is needed if the application makes use of
199         * {@link org.apache.wicket.Component#setResponsePage(org.apache.wicket.request.component.IRequestablePage)} or
200         * {@link org.apache.wicket.request.flow.ResetResponseException}
201         *
202         * @return {@code true} if the application should buffer the response's headers.
203         */
204        public boolean getBufferResponse()
205        {
206                return bufferResponse;
207        }
208
209        /**
210         * Gets whether Wicket should try to get extensive client info by redirecting to
211         * {@link org.apache.wicket.markup.html.pages.BrowserInfoPage a page that polls for client capabilities}. This method is used by the
212         * default implementation of {@link org.apache.wicket.Session#getClientInfo()}, so if that method is
213         * overridden, there is no guarantee this method will be taken into account.
214         *
215         * @return Whether to gather extensive client info
216         */
217        public boolean getGatherExtendedBrowserInfo()
218        {
219                return gatherExtendedBrowserInfo;
220        }
221
222        /**
223         * Gets in what way the render part of a request is handled.
224         *
225         * @return the render strategy
226         */
227        public RequestCycleSettings.RenderStrategy getRenderStrategy()
228        {
229                return renderStrategy;
230        }
231
232        /**
233         * @return an unmodifiable list of added response filters, null if none
234         */
235        public List<IResponseFilter> getResponseFilters()
236        {
237                if (responseFilters == null)
238                {
239                        return null;
240                }
241                else
242                {
243                        return Collections.unmodifiableList(responseFilters);
244                }
245        }
246
247
248        /**
249         * In order to do proper form parameter encoding it is important that the response and the
250         * subsequent request stipulate a common character encoding.
251         *
252         * possible form encodings and their problems:
253         *
254         * <ul>
255         * <li><a
256         * href="http://www.crazysquirrel.com/computing/general/form-encoding.jspx">application/x-
257         * www-form-urlencoded</a></li>
258         * <li><a href=
259         * "http://stackoverflow.com/questions/546365/utf-8-text-is-garbled-when-form-is-posted-as-multipart-form-data"
260         * >multipart/form-data</a></li>
261         * </ul>
262         *
263         * wicket now uses multipart/form-data for it's forms.
264         *
265         * @return The request and response encoding
266         */
267        public String getResponseRequestEncoding()
268        {
269                return responseRequestEncoding;
270        }
271
272        /**
273         * Gets the time that a request will by default be waiting for the previous request to be
274         * handled before giving up.
275         *
276         * @return The time out
277         */
278        public Duration getTimeout()
279        {
280                return timeout;
281        }
282
283        /**
284         * Sets a flag whether the application should buffer the response's headers until the end
285         * of the request processing. The buffering is needed if the application makes use of
286         * {@link org.apache.wicket.Component#setResponsePage(org.apache.wicket.request.component.IRequestablePage)}
287         * or {@link org.apache.wicket.request.flow.ResetResponseException}
288         *
289         * @param bufferResponse
290         *            {@code true} if the application should buffer response's headers.
291         * @return {@code this} object for chaining
292         */
293        public RequestCycleSettings setBufferResponse(boolean bufferResponse)
294        {
295                this.bufferResponse = bufferResponse;
296                return this;
297        }
298
299        /**
300         * Sets whether Wicket should try to get extensive client info by redirecting to
301         * {@link org.apache.wicket.markup.html.pages.BrowserInfoPage a page that polls for client capabilities}. This method is used by the
302         * default implementation of {@link org.apache.wicket.Session#getClientInfo()}, so if that method is
303         * overridden, there is no guarantee this method will be taken into account.
304         *
305         * <p>
306         * <strong>WARNING: </strong> though this facility should work transparently in most cases, it
307         * is recommended that you trigger the roundtrip to get the browser info somewhere where it
308         * hurts the least. The roundtrip will be triggered the first time you call
309         * {@link org.apache.wicket.Session#getClientInfo()} for a session, and after the roundtrip a new request with the
310         * same info (url, post parameters) is handled. So rather than calling this in the middle of an
311         * implementation of a form submit method, which would result in the code of that method before
312         * the call to {@link org.apache.wicket.Session#getClientInfo()} to be executed twice, you best call
313         * {@link org.apache.wicket.Session#getClientInfo()} e.g. in a page constructor or somewhere else where you didn't
314         * do a lot of processing first.
315         * </p>
316         *
317         * @param gatherExtendedBrowserInfo
318         *            Whether to gather extensive client info
319         * @return {@code this} object for chaining
320         */
321        public RequestCycleSettings setGatherExtendedBrowserInfo(boolean gatherExtendedBrowserInfo)
322        {
323                this.gatherExtendedBrowserInfo = gatherExtendedBrowserInfo;
324                return this;
325        }
326
327        /**
328         * Sets in what way the render part of a request is handled. Basically, there are two different
329         * options:
330         * <ul>
331         * <li>Direct, ApplicationSettings.ONE_PASS_RENDER. Everything is handled in one physical
332         * request. This is efficient, and is the best option if you want to do sophisticated
333         * clustering. It does not however, shield you from what is commonly known as the <i>Double
334         * submit problem </i></li>
335         * <li>Using a redirect. This follows the pattern <a
336         * href="http://www.theserverside.com/articles/article.tss?l=RedirectAfterPost" >as described at
337         * the serverside </a> and that is commonly known as Redirect after post. Wicket takes it one
338         * step further to do any rendering after a redirect, so that not only form submits are shielded
339         * from the double submit problem, but also the IRequestListener handlers (that could be e.g. a
340         * link that deletes a row). With this pattern, you have two options to choose from:
341         * <ul>
342         * <li>ApplicationSettings.REDIRECT_TO_RENDER. This option first handles the 'action' part of
343         * the request, which is either page construction (bookmarkable pages or the home page) or
344         * calling a IRequestListener handler, such as Link.onClick. When that part is done, a redirect
345         * is issued to the render part, which does all the rendering of the page and its components.
346         * <strong>Be aware </strong> that this may mean, depending on whether you access any models in
347         * the action part of the request, that attachment and detachment of some models is done twice
348         * for a request.</li>
349         * <li>ApplicationSettings.REDIRECT_TO_BUFFER. This option handles both the action- and the
350         * render part of the request in one physical request, but instead of streaming the result to
351         * the browser directly, it is kept in memory, and a redirect is issue to get this buffered
352         * result (after which it is immediately removed). This option currently is the default render
353         * strategy, as it shields you from the double submit problem, while being more efficient and
354         * less error prone regarding to detachable models.</li>
355         * </ul>
356         *
357         * @param renderStrategy
358         *            the render strategy that should be used by default.
359         * @return {@code this} object for chaining
360         */
361        public RequestCycleSettings setRenderStrategy(RequestCycleSettings.RenderStrategy renderStrategy)
362        {
363                this.renderStrategy = renderStrategy;
364                return this;
365        }
366
367        /**
368         * In order to do proper form parameter decoding it is important that the response and the
369         * following request have the same encoding. see
370         * http://www.crazysquirrel.com/computing/general/form-encoding.jspx for additional information.
371         *
372         * Default encoding: UTF-8
373         *
374         * @param encoding
375         *            The request and response encoding to be used.
376         * @return {@code this} object for chaining
377         */
378        public RequestCycleSettings setResponseRequestEncoding(final String encoding)
379        {
380                Args.notNull(encoding, "encoding");
381                this.responseRequestEncoding = encoding;
382                return this;
383        }
384
385        /**
386         * Sets the time that a request will by default be waiting for the previous request to be
387         * handled before giving up.
388         *
389         * @param timeout
390         * @return {@code this} object for chaining
391         */
392        public RequestCycleSettings setTimeout(Duration timeout)
393        {
394                Args.notNull(timeout, "timeout");
395                this.timeout = timeout;
396                return this;
397        }
398
399        /**
400         * Sets how many attempts Wicket will make to render the exception request handler before
401         *         giving up.
402         * @param retries
403         *      the number of attempts
404         * @return {@code this} object for chaining
405         */
406        public RequestCycleSettings setExceptionRetryCount(int retries)
407        {
408                this.exceptionRetryCount = retries;
409                return this;
410        }
411
412        /**
413         * @return How many times will Wicket attempt to render the exception request handler before
414         *         giving up.
415         */
416        public int getExceptionRetryCount()
417        {
418                return exceptionRetryCount;
419        }
420}