Understanding the Request Cycle</> <para> Web applications are significantly different in structure from other types of interactive applications. Because of the stateless nature of HTTP (the underlying communication protocol between web browsers and web servers), the server is constantly "picking up the pieces" of a conversation with the client. </> <para> This is complicated further in a high-volumes systems that utilizes load balancing and fail over. In these cases, the server is expected to pick up a conversation started by some other server. </> <para> The Java Servlet API provides a base for managing the client - server interactions, by providing the <classname>HttpSession</> object, which is used to store server-side state information for a particular client. </> <para> Tapestry picks up from there, allowing the application engine, pages and components to believe (with just a little bit of work) that they are in continuous contact with the client web browser. </> <para> At the center of this is the request cycle. This request cycle is so fundamental under Tapestry that a particular object represents it, and it is used throughout the process of responding to a client request and creating an HTML response. </> <para> Each application service makes use of the request cycle in its own way. We'll describe the three common application services (page, action and direct), in detail. </> <para> In most cases, it is necessary to think in terms of two consecutive request cycles. In the first request cycle, a particular page is rendered and, along the way, any number of URLs are generated and included in the HTML. The second request cycle is triggered by one of those service URLs. </> <section id="cycle.page"> <title>Page service</> <para> The page service is used for basic navigation between pages in the application. The page service is tightly tied to the <classname>Page</> component. </> <para> A page service URL is of the form: </> &start-listing;/<replaceable>servlet path</>/page/<replaceable>page name</>&end-listing; <para> The request cycle for the page service is relatively simple. </> <itemizedlist> <listitem><para>The service extracts the page name from the URL</></> <listitem><para>The named page it loaded and rolled back to its last recorded state</></> <listitem><para>The page is rendered to provide the HTML response sent back to the client</></> </itemizedlist> </section> <section id="cycle.direct"> <title>Direct service</> <para> The direct service is used to trigger a particular action. This service is tied to the <classname>Direct</> component. </> <para> A direct service URL is of the form: </> &start-listing;/<replaceable>servlet path</>/direct/<replaceable>page name</>/<replaceable>id path</>/<replaceable>context</>&end-listing; <para> This URL identifies a particular page (by name), and a component within that page (the id path). It also adds additional <emphasis>context</>, one or more strings that will be meaningful to the component when the URL is triggered <footnote> <para> Actually, the strings (which are optional) will be passed to the <classname>Direct</> component's listener. </> </>. </> <para> The request cycle for the direct service is more complicated that the page service. </> <itemizedlist> <listitem><para>The service extracts the page name from the URL</> </> <listitem><para>The page is loaded and its persistent state is rolled back to its last recorded state</> </> <listitem><para>The service extracts the id path from the URL</> </> <listitem><para>The component is located within the page, using its id path</> </> <listitem><para>The component is notified that it has been triggered, with the context passed as an array of Strings</> </> <listitem><para>The component locates its listener</> </> <listitem><para>The listener is informed that the component was triggered</> </> <listitem><para>The listener reacts in an application specific way, and may update the state of any number of pages</> </> <listitem><para>The listener may optionally select a new page to render a response</> </> <listitem><para>The state of any changed pages is recorded by the page recorders</> </> <listitem><para>The result page (the page specified in the URL, unless overridden by the listener) is rendered to provide the HTML response sent back to the client</> </> </> <para> This is the primary way (besides forms) in which applications respond to the user. What's key is the component's listener, of type <classname>com.primix.tapestry.IDirectListener</>. This is the hook that allows pre-defined components from the Tapestry framework to access application specific behavior. The page or container of the <classname>Direct</> component provides the necessary listener objects using dynamic bindings. </> <para> The direct service is useful in many cases, but does have its limitations. The state of the page when the listener is invoked is its state just prior to rendering (in the previous request cycle). This can cause a problem when the action to be performed is reliant on state that changes during the rendering of the page. In those cases, the action service (and <classname>Action</> or <classname>Form</> components) should be used. </> <para> The <classname>Direct</> component has an optional parameter named <varname>context</>. The value for this may be a single <classname>String</>, an array of <classname>String</>s, or a <classname>List</> of <classname>String</>s. These strings are added to the URL after the id path. When the action is triggered, the array is reconstructed (from the URL) and provided to the <classname>Direct</> component and its listener. This is primarily used to encode dynamic page state directly into the URL when doing so is not compatible with the action service (described in the next section). </> <para> The most common use for this context is to record an identifier for some object that is affected by the link. For example, if the link is designed to remove an item from the shopping cart (in an e-commerce example), the context could identify which item to remove in terms of a primary key, or line number within the order. </> </section> <section id="cycle.action"> <title>Action service <para> The action service is also used to trigger a particular application-specific action using an <classname>Action</> component, and its listener. The action service is also used by the <classname>Form</> component to process HTML form submissions. </> <para> An action service URL is of the form: </> &start-listing;/<replaceable>servlet prefix</>/action/<replaceable>page name</>/<replaceable>action id</>/<replaceable>component id path</>&end-listing; <para> The request cycle for the action service is more complicated that the direct service. This sequence assumes that the component is an <classname>Action</>, the details of handling form submissions are described in a later section. <itemizedlist> <listitem><para> The service extracts the page name from the URL </> </> <listitem> <para>The page is loaded and rolled back to its last recorded state</> </> <listitem> <para>The service extracts the action id and component id path from the URL</> </> <listitem> <para>A render of the page is started, with output discarded</> </> <listitem> <para>Each component "goes through the motions" of rendering</> </> <listitem> <para>Eventually, the <classname>Action</> component identifies that the page has been rewound (the action id in the URL matches the current action id) </> </> <listitem> <para> The <classname>Action</> component locates its listener </> </> <listitem> <para> The listener is informed that the action was triggered </> </> <listitem> <para> The listener reacts in an application specific way, and may update the state of any number of pages </> </> <listitem> <para> The listener may optionally select a new page to render a response </> </> <listitem> <para>The <classname>Action</> component terminates the rewind cycle </> </> <listitem> <para>The state of any changed pages is recorded by the page recorders </> </> <listitem> <para>The result page (the page specified in the URL, unless overridden by the listener) is rendered to provide the HTML response sent back to the client </> </> </> <para> The process of restoring the page's dynamic state is called rewinding. Rewinding is used to go beyond restoring a page's persistent state and actually restore the page's dynamic state. Whatever state the page was in when the action URL was rendered in the previous request cycle is restored before the <classname>Action</> component's listener is invoked. </> <para> Use of the action service is convenient, but not always appropriate. Deeply nested <classname>Foreach</> components will result in a geometric increase in processing time to respond to actions (as well as render the HTML). </> <para> If the data on the page is not easily accessible then the action service should be avoided. For example, if the page is generated from a long running database query. Alternate measures, such as storing the results of the query as persistent page state should be used. Another alternative is to use the direct service (and <classname>Direct</> component) instead, as it allows the necessary context to be encoded into the URL. This can be very useful when the dynamic state of the page is dependant on expensive or unpredictably changing data (such as a database query). </> <para> For example, a product catalog could encode the primary key of the products listed as the context to create links to a product details page. </> </section> <section id="cycle.forms"> <title>Action service and forms <para> Processing of requests for <classname>Form</> components is a little more complicated than for ordinary <classname>Action</> components. This is because a <classname>Form</> will wrap a number of form-related components, such as <classname>TextField</>, <classname>Checkbox</>, <classname>PropertySelection</> and others. </> <para> In order to accept the results posted in the HTML form, each of these components must be given a chance to respond to the request. A component responds to the request by extracting a request parameter from the <classname>HttpServletRequest</>, interpreting it, and assigning a value through a parameter. </> <para> As with an <classname>Action</> component, a full rewind must be done, to account for conditional portions of the page and any <classname>Foreach</> components. </> <para> The <classname>Form</> component doesn't terminate the rewind cycle until after all of its wrapped components have had a chance to render. It then notifies its own listener. </> <para> The basic components, <classname>Text</> and <classname>TextField</>, are quite simple. They simply move text between the application, the HTML and the submitted request. </> <para> Individual <classname>Checkbox</> components are also simple: they set a boolean property. A <classname>RadioGroup</> and some <classname>Radio</> components allow a property to be set to a value (dependent on which <classname>Radio</> button is selected by the user). The <classname>PropertySelection</> component is designed to more efficiently handle this and can produce HTML for either a popup list or a collection of radio buttons. </> <para> Tapestry also includes more complicated components, <classname>ValidatingTextField</> and some relatives. These are similar to the simple <classname>TextField</> component, but provide greater validation and checking of input, and provide the ability to visually mark fields that are required or in error. </section> </chapter>