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}