"Request cycle" in this text means RequestCycle and its subclass WebRequestCycle as if they were merged. "Request cycle processor" means IRequestCycleProcessor and its implementations.
RequestCycle
The main responsibility of request cycle is instructing request cycle processor which steps of request handling to execute (see diagram of request cycle processor usage). Request cycle goes through several states and depending on the current state it sends different instruction to request cycle processor (see RequestCycle#step()). After sending all instructions request cycle reaches final state which denotes the end of request processing. Besides that request cycle stores list of request targets which are produced by request cycle processor (see RequestCycle#setResponsePage(), RequestCycle#setRequestTarget()).
From framework user's point of view, the request cycle has some other useful functionality:
- it contains instances of WebRequest, WebResponse (these classes basically wrap HttpServletRequest and HttpServletResponse). They can retrieved with WebRequestCycle#getWebRequest() and WebRequestCycle#getWebResponse() methods. See also how to get raw HttpServletRequest.
- it has a bunch of urlFor(...) methods which produce URLs for pages, components, etc.
- it has overridable method for exception handling. See #Exception handling.
- it has overridable methods which is called at the beginning and at the end of request processing. These methods are onBeginRequest() and onEndRequest(). See diagram below.
These calls happen in context of request processing; for bigger picture see diagram of request cycle processor usage. Request cycle calls prepare() method before sending any instructions to request cycle processor and detach() after sending all instructions or catching exception. Note that this detach() call initiates all other calls for pages, components and models.
New instance of RequestCycle is created on every request by WebApplication class. This is a pseudo code of how request cycle is created and used:
class WicketServlet ... public final void doGet(final HttpServletRequest servletRequest, final HttpServletResponse servletResponse) { ... // WebApplication instance is already created while initializing servlet/filter final WebRequest request = webApplication.newWebRequest(servletRequest); final WebResponse response = webApplication.newWebResponse(servletResponse); RequestCycle cycle = webApplication.newRequestCycle(request, response); cycle.request(); // <--- request processing starts ... }
Note that after creation request() method, which starts request processing, is called on RequestCycle. It's possible to customize creation of request cycle by overriding WebApplication#newRequestCycle().
To obtain request cycle anywhere in code RequestCycle#get() can be used which gets instance of RequestCycle from ThreadLocal variable.
IRequestCycleProcessor
Request cycle processor is responsible for handling instructions of a request cycle. It's used by RequestCycle which calls its methods in the pre-defined order:
- IRequestTarget resolve(RequestCycle, RequestParameters) is called to get the request target. A request might refer to e.g. a bookmarkable page, a component on a previously rendered page. This is one of the main responsibilities of request cycle processor. RequestParameters object consists of all possible optional parameters that can be translated from servlet request parameters and serves as a strongly typed variant of these (see IRequestCodingStrategy).
- void processEvents(RequestCycle) is meant to handle events like calls on components e.g. onClick(), onSubmit() event handlers.
- void respond(RequestCycle) is called to create a response, i.e. generate web page or do redirect.
- void respond(RuntimeException , RequestCycle) is called whenever an uncaught exception occurs during the event handling or response phase so that an appropriate exception response can be generated.
Note that RequestCycle delegates events processing and response generation to created request target.
TBD add more about resolving request targets
There is one instance of request cycle processor per Application. It is lazily created during the first request. Its creation can be customized by overriding WebApplication#newRequestCycleProcessor(). Request cycle processor instance can be obtained using WebApplication#getRequestCycleProcessor() method.
Exception handling
Wicket uses RuntimeExceptions with WicketRuntimeException, AbortException as superclasses. It means that Wicket classes do not throw checked exceptions and therefore user code that extends Wicket classes cannot do that either. Although it's possible to throw checked exception in component constructor and ultimately throw it in page constructor, this exception will be wrapped by Wicket with WicketRuntimeException (it will happen in one of DefaultPageFactory#newPage() methods).
All runtime exceptions thrown during request processing are propagated down to RequestCycle. After catching exception request cycle delegates its handling to AbstractRequestCycleProcessor as it's shown in diagram below.
Request cycle processor attempts to find on which page this exception happened and calls AbstractRequestCycleProcessor#onRuntimeException() which by default calls similar method in RequestCycle (see Exception handling in Wicket for details). If onRuntimeException() returns a page, request cycle processor redirects to it, otherwise it redirects to one of the default pages like "Page Expired" or "Internal Error" (these pages can be configured with IApplicationSettings#setAccessDenied/InternalError/PageExpiredErrorPage(); see also IExceptionSettings). "Redirecting" here means throwing AbstractRestartResponseException (subclass of AbortException) which makes request cycle to continue request processing from the state when it sends to request cycle processor processEvents() and respond() messages (see the diagram above). Unlike other exceptions handling AbortException doesn't involve calls of onRuntimeException().