| <!-- $Id$ --> |
| <!-- |
| Copyright 2004 The Apache Software Foundation |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| |
| <chapter id="cycle"> |
| <title>Understanding the Request Cycle</title> |
| <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> |
| <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> |
| <para> |
| The Java Servlet API provides a base for managing the client - server interactions, |
| by providing the &HttpSession; object, which is used to store |
| server-side state information for a particular client. |
| </para> |
| <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> |
| <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> |
| <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> |
| <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. |
| </para> |
| <section id="cycle.URLs"> |
| <title>Service URLs and query parameters</title> |
| <para> |
| All URLs generated by the framework consist of the the path to the servlet, and up to three |
| query parameters. |
| |
| <itemizedlist> |
| <listitem> |
| <para> |
| <varname>service</varname>: the name of the service that will be used |
| to processes the request. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| <varname>context</varname>: contextual information needed by the service; typically |
| the name of the page or component involved. Often there are several pieces of |
| information, separated by slashes. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| <varname>sp</varname>: additional parameters that can be made available to |
| the component. This is used by a &DirectLink; component. If there is more than one |
| service parameter, then there will be multiple <varname>sp</varname> |
| parameters in the URL.</para> |
| </listitem> |
| </itemizedlist> |
| </para> |
| </section> |
| <section id="cycle.page"> |
| <title>Page service</title> |
| <para> |
| The page service is used for basic navigation between pages in the application. |
| The page service is tightly tied to the &PageLink; component. |
| </para> |
| <para> |
| A page service stores the name of the page as the single value in the service context. |
| </para> |
| <para> |
| The request cycle for the page service is relatively simple. |
| </para> |
| <figure> |
| <title>Page Service Sequence</title> |
| <mediaobject> |
| <imageobject> |
| <imagedata fileref="images/Page-Service-sequence.gif" format="GIF"/> |
| </imageobject> |
| </mediaobject> |
| </figure> |
| <para> |
| The URL contains the name of the page, and the corresponding page is aquired from |
| the request cycle. The page is given a chance to validate that the user can |
| access it, it can throw &PageRedirectException; to force a render of a different page. |
| Otherwise, <function>setPage()</function> tells the request cycle which page will |
| be used to render a response, and <function>renderPage()</function> peforms |
| the actual render. |
| </para> |
| </section> |
| <section id="cycle.listeners"> |
| <title>Action and Direct listeners</title> |
| <para> |
| The &ActionLink;, &DirectLink; and &Form; components (which make use of |
| the <link linkend="cycle.action">action</link> and <link linkend="cycle.direct">direct</link> |
| services) inform the application when they have been triggered using listeners. |
| </para> |
| <para> |
| A listener is an object that implements the &IActionListener; interface. |
| </para> |
| <para> |
| <informalexample> |
| <programlisting> |
| public void actionTriggered(IComponent component, IRequestCycle cycle) |
| throws RequestCycleException; |
| </programlisting> |
| </informalexample> |
| </para> |
| <para> |
| Prior to release 1.0.2, it was necessary to create an object to be notified |
| by the component; this was almost always an annonymous inner class: |
| |
| <informalexample> |
| <programlisting> |
| public IActionListener getFormListener() |
| { |
| return new IActionListener() |
| { |
| public void actionTriggered(IComponent component, IRequestCycle cycle) |
| throws RequestCycleException |
| { |
| // perform some operation ... |
| } |
| }; |
| } |
| </programlisting> |
| </informalexample> |
| </para> |
| <para> |
| Although elegant in theory, that's simply too much Java code for too little effect. |
| Starting with Tapestry 1.0.2, it is possible to create a |
| <emphasis>listener method</emphasis> instead. |
| </para> |
| <para> |
| A listener method takes the form: |
| |
| <informalexample> |
| <programlisting> |
| public void <replaceable>method-name</replaceable>(IRequestCycle cycle) |
| throws RequestCycleException; |
| </programlisting> |
| </informalexample> |
| </para> |
| <note> |
| <para>The throws clause is optional and may be omitted. However, no other |
| exception may be thrown. |
| </para> |
| </note> |
| |
| <para> |
| In reality, listener <emphasis>objects</emphasis> have not gone away. Instead, there's a mechanism |
| whereby a listener object is created automatically when needed. |
| Each component includes a property, <varname>listeners</varname>, that is a collection of |
| listener objects for the component, synthesized from the available public methods. The |
| listener objects are properties, with the names corresponding to the method names. |
| </para> |
| <tip> |
| <para> |
| The class <classname>AbstractEngine</classname> (the base class for &SimpleEngine;) |
| also implements a listeners property. This allows you to easily add listener methods |
| to your application engine. |
| </para> |
| </tip> |
| <para> |
| The earlier example is much simpler: |
| </para> |
| <informalexample> |
| <programlisting> |
| public void formSubmit(IRequestCycle cycle) |
| { |
| // perform some operation ... |
| } |
| </programlisting> |
| </informalexample> |
| <para> |
| However, the property path for the listener binding must be changed, from <varname>formListener</varname> to |
| <varname>listeners.formSubmit</varname>. |
| </para> |
| </section> |
| <section id="cycle.direct"> |
| <title>Direct service</title> |
| <para> |
| The direct service is used to trigger a particular action. This service is tied to the |
| &DirectLink; component. The service context identifies the page and component within the page. Any parameters |
| specified by the &DirectLink; component's <varname>context</varname> parameter are encoded as well. |
| </para> |
| <para> |
| The request cycle for the direct service is more complicated that the page service. |
| </para> |
| <figure> |
| <title>Direct Service Sequence</title> |
| <mediaobject> |
| <imageobject> |
| <imagedata fileref="images/Direct-Service-sequence.gif" format="GIF"/> |
| </imageobject> |
| </mediaobject> |
| </figure> |
| <para> |
| As with the page service, the page involved has a chance validate the request. |
| The component is located within the page, and the page is set as the default |
| response page. The listener is free to override this, and can load other pages, |
| change their properties, or otherwise affect the state of the application. |
| </para> |
| <para> |
| After the listener has its chance to respond to the request, a response page |
| is renderred. |
| </para> |
| <note> |
| <title>&IDirect; vs. &DirectLink;</title> |
| <para> |
| The sequence shown above is for the &DirectLink; component, which implements the &IDirect; |
| interface. In some rare cases, it is desirable to have a different component |
| implement the &IDirect; interface instead. It will still implement the |
| <function>trigger()</function> method, but will respond in its own way, likely without |
| a listener. |
| </para> |
| </note> |
| <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 |
| &IActionListener;. This is the hook that allows |
| pre-defined cotheirponents from the Tapestry framework to access application specific behavior. |
| The page or container of the |
| &DirectLink; component provides the necessary listener objects using dynamic bindings. |
| </para> |
| <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 |
| <link linkend="cycle.action">action service</link> |
| (and &ActionLink; or &Form; components) should be used. |
| </para> |
| |
| <para> |
| The &DirectLink; component has an optional parameter named <varname>parameters</varname>. |
| The value for this may be a single object, an array of objects, |
| or a &List;. Each object is converted into a string encoding, that is |
| included in the URL. |
| When the action is triggered, the array is reconstructed (from the URL) and stored |
| in the &IRequestCycle;, where it is available to the listener. The type is maintained, thus if |
| the third parameter is of type &Integer; when the URL is generated, then the third parameter |
| will still be an &Integer; when the listener method is invoked. |
| </para> |
| |
| <para> |
| This is a very powerful feature of Tapestry, as it allows the developer 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> |
| <para> |
| The most common use for these service parameters 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 service parameters |
| could identify which item |
| to remove in terms of a primary key, or line number within the order. |
| </para> |
| </section> |
| <section id="cycle.action"> |
| <title>Action service</title> |
| <para> |
| The action service is also used to trigger a particular application-specific |
| action using an &ActionLink; component, and its listener. |
| The action service may also be used by the &Form; component to process HTML form submissions. |
| </para> |
| <para> |
| An action service encodes the page name and component for the request. It also includes |
| an action id. |
| </para> |
| <para> |
| The request cycle for the action service is more complicated that the direct service. |
| This sequence assumes that the component is an |
| &ActionLink;, the details of handling form submissions are described in a later section. |
| </para> |
| <figure> |
| <title>Action Service Sequence</title> |
| <mediaobject> |
| <imageobject> |
| <imagedata fileref="images/Action-Service-Sequence.gif" format="GIF"/> |
| </imageobject> |
| </mediaobject> |
| </figure> |
| <para> |
| The point of the action service is to restore the <emphasis>dynamic state</emphasis> |
| of the page to how it was when the &ActionLink; component rendered the link. Only then is |
| the listener notified.</para> |
| <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 &ActionLink; component's listener is invoked. |
| </para> |
| <para> |
| Use of the action service is convenient, but not always appropriate. |
| Deeply nested &Foreach; components will result in a geometric increase in |
| processing time to respond to actions (as well as render the HTML). |
| </para> |
| <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 &DirectLink; component) instead, as |
| it allows the necessary context to be encoded into the URL, using service |
| parameters. 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> |
| <para> |
| For example, a product catalog could encode the primary key of the products listed as the |
| service parameters, |
| to create links to a product details page. |
| </para> |
| </section> |
| <section id="cycle.forms"> |
| <title>Services and forms</title> |
| <para> |
| Processing of requests for &Form; components is a little more complicated than |
| for ordinary &ActionLink; components. |
| This is because a &Form; will wrap a number of form-related components, such as |
| &TextField;, &Checkbox;, &PropertySelection; and others. |
| </para> |
| <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 &HttpServletRequest;, |
| interpreting it, and assigning a value through a parameter. |
| </para> |
| <para> |
| As with an &ActionLink; component, a full rewind must be done, |
| to account for conditional portions of the page and any &Foreach; components. |
| </para> |
| <note> |
| <para> |
| Starting with Tapestry release 1.0.2, &Form;s may now use the |
| <link linkend="cycle.direct">direct service</link> instead |
| of the |
| <link linkend="cycle.action">action service</link>; this is configurable. Using the direct service is the |
| default behavior unless specified. A rewind still occurs, it simply starts directly |
| with the &Form; component, rather than having to "work down" to it. This can be |
| a performance gain if a page contains many forms. |
| </para> |
| </note> |
| <para> |
| The &Form; component doesn't terminate the rewind cycle until <emphasis>after</emphasis> all of |
| its wrapped components have had a chance to render. It then notifies its own listener. |
| </para> |
| <para> |
| The basic components, &TextArea; and &TextField;, |
| are quite simple. They simply move text between the |
| application, the HTML and the submitted request. |
| </para> |
| <para> |
| Individual &Checkbox; components are also simple: they |
| set a boolean property. |
| A &RadioGroup; and some &Radio; components |
| allow a property to be set to a value |
| (dependent on which radio button is selected by the user). |
| |
| The &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> |
| <para> |
| Tapestry also includes the more involved component, |
| &ValidField;, |
| which is similar to the simple &TextField; component, |
| but provide greater validation and checking of input, and |
| provides the ability to visually mark fields that are required or in error. |
| </para> |
| <para> |
| Regardless of which service the &Form; uses, it encodes the query parameters |
| (which identify the service and context) |
| as hidden field elements, rather than encoding them into the URL. This is necessary because some |
| servlet containers ignore URL query parameters when using the HTTP POST request; therefore, it is necessary that all query parameters (including the ones related to the engine service), be part of the form posting ... and that means the use of hidden fields in the form. |
| </para> |
| </section> |
| </chapter> |