| <!-- $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="engine"> |
| <title>Application Engines and Services</title> |
| <para> |
| The application engine is a central object whose responsibility is to run the request cycle for each |
| request. To do this, it manages resources such as page loaders and page recorders and provides |
| services to the pages and components utilized during the request cycle. |
| </para> |
| <para> |
| Application engines are instantiated by the application's servlet (described in the next section). |
| They are stored into the &HttpSession; and are persistent between request cycles. |
| </para> |
| <para> |
| An important behavior of the engine is to provide named engine services, which are used to create and |
| respond to URLs. The application engine creates and manages the request cycle and provides |
| robust default behavior for catching and reporting exceptions. |
| </para> |
| <para> |
| The application engine provides the page recorder objects used by the request cycle. By doing so, |
| it sets the persistence strategy for the application as a whole. For example, applications which use |
| or subclass &SimpleEngine; will use the simple method of |
| storing persistent state: in memory. Such applications may still be distributed, since the page |
| recorders will be serialized with the application engine (which is stored within the |
| &HttpSession;). |
| </para> |
| <section id="engine.servlet"> |
| <title>Application Servlet</title> |
| <para> |
| Every Tapestry application has a single servlet, which acts |
| as a bridge between the servlet container and the |
| application engine. The application servlet is an instance of |
| &ApplicationServlet;. |
| </para> |
| <para> |
| The first thing a servlet does, upon initialization, is read the application specification. |
| To do this, it must know <emphasis>where</emphasis> the application specification is stored. |
| </para> |
| |
| <para> |
| Specifications are stored on the classpath, which means in a JAR file, or in the |
| <filename>WEB-INF/classes</filename> directory of the WAR. |
| </para> |
| |
| <para> |
| The servlet determines the location of the application specification from the |
| web deployment descriptor. A servlet initialization property, <varname>org.apache.tapestry.application-specification</varname> |
| provides the locations of the specificiation as a path. |
| </para> |
| |
| <example> |
| <title>Web Deployment Descriptor</title> |
| <programlisting> |
| <![CDATA[ |
| <?xml version="1.0"?> |
| <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" |
| "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> |
| <web-app> |
| <display-name>Tapestry Virtual Library Demo</display-name> |
| <servlet> |
| <servlet-name>vlib</servlet-name> |
| <servlet-class>org.apache.tapestry.ApplicationServlet</servlet-class> |
| <init-param> |
| <param-name>org.apache.tapestry.application-specification</param-name> |
| <param-value>/net/sf/tapestry/vlib/Vlib.application</param-value> |
| </init-param> |
| <load-on-startup>0</load-on-startup> |
| </servlet> |
| |
| <!-- The single mapping used for the Virtual Library application --> |
| |
| <servlet-mapping> |
| <servlet-name>vlib</servlet-name> |
| <url-pattern>/app</url-pattern> |
| </servlet-mapping> |
| |
| <session-config> |
| <session-timeout>15</session-timeout> |
| </session-config> |
| |
| <welcome-file-list> |
| <welcome-file>index.html</welcome-file> |
| </welcome-file-list> |
| </web-app> |
| ]]> |
| </programlisting> |
| </example> |
| |
| <para> |
| The servlet's main job is to find or create the &IEngine; instance. It then delegates all the behavior |
| for processing the request to the application engine. Encoded in the URL will be a particular |
| application service; the engine delegates to the service to perform the real work of handling |
| the request. |
| </para> |
| <figure> |
| <title>ApplicationServlet Sequence</title> |
| <mediaobject> |
| <imageobject> |
| <imagedata fileref="images/ApplicationServlet-sequence.gif" format="GIF"/> |
| </imageobject> |
| </mediaobject> |
| </figure> |
| |
| </section> |
| <section id="engine.req-pages"> |
| <title>Required Pages</title> |
| <para> |
| Each application is required to have a minimum of five pages with specific names. Tapestry |
| provides default implementations for four of the five, but a full-featured Tapestry application may |
| override any of the others to provide a consistent look-and-feel. |
| </para> |
| <table> |
| <title>Tapestry Pages</title> |
| <tgroup cols="3"> |
| <thead> |
| <row> |
| <entry>Page Name</entry> |
| <entry>Required</entry> |
| <entry>Description</entry> |
| </row> |
| </thead> |
| <tbody> |
| <row> |
| <entry>Exception</entry> |
| <entry>Default provided, may be overridden.</entry> |
| <entry> |
| Page used to present uncaught |
| exceptions to the user.</entry> |
| </row> |
| <row> |
| <entry>Home</entry> |
| <entry>Must be provided by developer.</entry> |
| <entry>The initial page displayed when the |
| application is started.</entry> |
| </row> |
| <row> |
| <entry>Inspector</entry> |
| <entry>Provided, never overriden.</entry> |
| <entry>Inspector that allows the Tapestry |
| application to be interrogated on its |
| structure.</entry> |
| </row> |
| <row> |
| <entry>StaleLink</entry> |
| <entry>Provided</entry> |
| <entry>Page displayed when a StaleLinkException is thrown during |
| the processing of a request.</entry> |
| </row> |
| <row> |
| <entry>StaleSession</entry> |
| <entry>Provided</entry> |
| <entry>Page displayed when a StaleSessionException is thrown |
| during the processing of a request.</entry> |
| </row> |
| </tbody> |
| </tgroup> |
| </table> |
| <para> |
| Tapestry only mandates the logical name of these four pages; the actual page component used is |
| defined in the application specification. |
| </para> |
| <para> |
| The <classname>Home</classname> page is the first page viewed by a client connecting to the application. |
| Other than that, |
| there is nothing special about the page. |
| </para> |
| <para> |
| The initial connection to the application, where nothing is specified in the URL but the path to |
| the servlet, causes the home service to be invoked, which makes use of the home page. |
| The restart service will also redirect the user to the home page. |
| </para> |
| <para> |
| No default is provided for the <classname>Home</classname> page; every Tapestry application must define its |
| own <classname>Home</classname> page. |
| </para> |
| <para> |
| The Exception page is invoked whenever an uncaught exception is thrown when processing a service. |
| </para> |
| <para> |
| The Tapestry framework catches the exception and discards any HTML output (this is why |
| output is buffered in memory). |
| </para> |
| <para> |
| The <classname>Exception</classname> page must implement a writable JavaBeans property of type |
| <classname>java.lang.Throwable</classname> named <varname>exception</varname>. |
| The framework will invoke the accessor method |
| before the page is rendered. |
| </para> |
| <para> |
| The class &ExceptionAnalyzer; and the |
| &ExceptionDisplay; component are typically used to present this information. |
| </para> |
| <para> |
| The <classname>StaleLink</classname> page is displayed when a &StaleLinkException; |
| is thrown, which may occur during the processing of the request. The exception is thrown when |
| Tapestry determines that the state of the page (on the server) is out of synch with the client's view |
| of the page ... this most often happens when the user makes use of the browser's back button. |
| <footnote> |
| <para> |
| If desired, the application engine can override the method |
| <function>handleStaleLinkException()</function>. The default implementation of |
| this method redirects to the <classname>StaleLink</classname> page, but a custom implementation |
| could set up an error message on the application's <classname>Home</classname> page and |
| redirect there instead. |
| </para> |
| </footnote> |
| </para> |
| <para> |
| The default implementation informs the user of the problem ("you really shouldn't use the back |
| button on your browser") and uses the home service to create a link back to the |
| <classname>Home</classname> page. |
| </para> |
| <para> |
| The <classname>StaleSession</classname> page is displayed when a |
| <classname>org.apache.tapestry.StaleSessionException</classname> |
| is thrown. This exception is thrown when the component |
| is configured to be stateful (which is the default) |
| and the <classname>HttpSession</classname> doesn't exist, or is |
| newly created - this indicates a fresh connection to the servlet container after the old session |
| timed out and was discarded. |
| <footnote> |
| <para> |
| Likewise, the default behavior can be changed by overriding the method |
| <function>handleStaleSessionException()</function>. |
| </para> |
| </footnote> |
| </para> |
| <para> |
| The <classname>Inspector</classname> page is provided by the framework; |
| it allows a developer to interrogate a running |
| Tapestry application to determine its structure. |
| </para> |
| </section> |
| <section id="engine.state"> |
| <title>Server-Side State</title> |
| <para> |
| There are two types of server side state that are supported by Tapestry: persistent page properties |
| and the visit object. The first (page properties) have already been discussed. |
| </para> |
| <para> |
| The visit object is a central repository for application state and presentation logic. The visit object |
| is accessible through the application engine (the engine implements a <varname>visit</varname> property). |
| The application engine doesn't care about the class of the visit object, or what properties it |
| implements. |
| </para> |
| <para> |
| The visit object holds central information that is needed by many pages. For example, an e- |
| commerce application may store the shopping cart as a property of the visit object. |
| </para> |
| <para> |
| When using Enterprise JavaBeans, the visit object is a good place to store remote object |
| references (centralizing the logic to look up home interfaces, instantiate references, etc.). |
| </para> |
| <para> |
| Every page implements a <varname>visit</varname> property that allows access to the visit object. |
| </para> |
| <para> |
| When using the &SimpleEngine; engine, |
| the visit object is |
| created the first time it is referenced. The class of the visit object is stored in the application |
| specification. |
| </para> |
| </section> |
| <section id="engine.stateless"> |
| <title>Stateful vs. Stateless</title> |
| <para> |
| Through Tapestry release 1.0.0, an &HttpSession; |
| was created on the very first request cycle, and an engine was created and |
| stored into it. |
| </para> |
| <para> |
| This comes at some cost, however. Creating the session is somewhat expensive if it is |
| not truly needed, and causes some overhead in a clustering or failover scenario. |
| In fact, until some real server-side state is created; that is, until a |
| persistent page property is recorded or the visit object created, it isn't really |
| necessary to store any server-side state for a particular client. |
| </para> |
| <para> |
| Starting with Tapestry release 1.0.1, the framework will operate statelessly |
| as long as possible. When triggered (by the creation of a visit, or by a |
| persistent page property) an &HttpSession; will be created and the engine |
| stored within it and the application will continue to operate pretty much |
| as it does in Tapestry release 1.0.0. |
| </para> |
| <para> |
| While the application continues statelessly, the framework makes use of a |
| pool of engine instances. This is more efficient, as it reduces the number of objects |
| that must be created during the request cycle. However, the major reason |
| for running statelessly is to bypass the overhead statefulness imposes |
| on the application server. |
| </para> |
| <para> |
| Of course, if rendering the <classname>Home</classname> page of your application triggers |
| the creation of the &HttpSession; |
| <footnote> |
| <para> |
| That is, changes a persistent page property, or forces the creation |
| of the visit object. |
| </para> |
| </footnote>, then nothing is gained. A well |
| designed application will attempt to defer creation of the session so that, at least, |
| the <classname>Home</classname> page can be displayed without creating a session. |
| </para> |
| </section> |
| <section id="engine.services"> |
| <title>Engine Services</title> |
| <para> |
| Engine services provide the structure for building a web application from individual pages and |
| components. |
| </para> |
| <para> |
| Each engine service has a unique name. Well known names exist for the basic services (page, |
| action, direct, etc., described in a later section). |
| </para> |
| <para> |
| Engine services are responsible for creating URLs (which are inserted into the response HTML) |
| and for later responding to those same URLs. This keeps the meaning of URLs localized. In a |
| typical servlet or JSP application, code in one place creates the URL for some servlet to interpret. |
| The servlet is in a completely different section of code. In situations where the servlet's behavior |
| is extended, it may be necessary to change the structure of the URL the servlet processes ... and |
| this requires finding every location such a URL is constructed and fixing it. This is the kind of |
| inflexible, ad-hoc, buggy solution Tapestry is designed to eliminate. |
| </para> |
| <para> |
| Most services have a relationship to a particular component. The basic services (action, direct, |
| page) each have a corresponding component (&ActionLink;, &DirectLink;, &PageLink;). |
| The following example |
| shows how the &PageLink; component is used to create a link between application pages. |
| </para> |
| <para> |
| First, an extract from the page's HTML template: |
| </para> |
| <informalexample> |
| <programlisting> |
| Click <a jwcid="login">here</a> to login. |
| </programlisting> |
| </informalexample> |
| <para> |
| This is combined with the a <sgmltag class="starttag">component</sgmltag> declaration in the the page's specification: |
| </para> |
| <informalexample> |
| <programlisting> |
| <component id="<emphasis>login</emphasis>" type="&PageLink;"> |
| <static-binding name="page">Login</static-binding> |
| </component> |
| </programlisting> |
| </informalexample> |
| <para> |
| The <varname>login</varname> component will locate the page service, and provide 'Login' |
| (the name of the target |
| page) as a parameter. The page service will build and return an appropriate URL, |
| which the <varname>login</varname> component will incorporate into the <sgmltag class="starttag">a</sgmltag> hyperlink it |
| generates. |
| </para> |
| <para> |
| The resulting HTML: |
| </para> |
| <informalexample> |
| <programlisting> |
| Click <a href="/<replaceable>servlet-path</replaceable>?service=page&context=Login">here</a> to login.</programlisting> |
| </informalexample> |
| <para> |
| If the user later clicks that link, the application will invoke the page service to handle the URL; |
| it |
| will extract the page name (<literal>Login</literal>) and render that page. |
| </para> |
| <para> |
| The other services are more or less complicated, but share the same basic trait: the service |
| provides the URL and later responds if the URL is triggered. |
| </para> |
| <para> |
| Links (&ActionLink;, &DirectLink;, etc.) and &Form;s use services in slightly different ways. Links encode |
| all the information directly into the URL whereas &Form;s encode most of the information as |
| hidden form fields. |
| </para> |
| <figure> |
| <title>Services and Gestures |
| </title> |
| <mediaobject> |
| <imageobject> |
| <imagedata fileref="images/Gestures.gif" format="GIF"/> |
| </imageobject> |
| </mediaobject> |
| </figure> |
| <para> |
| In the first part, a service generates a &Gesture; and then extracts the full URL from it, for use |
| as the <varname>href</varname> attribute of the <sgmltag class="starttag">a</sgmltag> tag. |
| </para> |
| <para> |
| In the second part, a service is used to access the servlet path (which becomes the |
| <varname>action</varname> attribute of the <sgmltag class="starttag">form</sgmltag> element). |
| The query parameters are individually extracted and encoded as hidden fields in the form. |
| </para> |
| </section> |
| <section id="engine.logging"> |
| <title>Logging</title> |
| <para> |
| Tapestry makes use of the Apache group's |
| &Log4J; package to perform logging. This is an easy, fast, |
| powerful framework for adding logging to any Java application. Using &Log4J;, any number of |
| <emphasis>loggers</emphasis> can be created, and a logging level for each logger assigned. |
| Tapestry uses the complete class name as the logger for each class. |
| </para> |
| <para> |
| The &ApplicationServlet; class includes a method, <function>setupLogging()</function>, |
| to help initialize &Log4J;, allowing the default configuration to be overridden using command line |
| parameters. |
| </para> |
| <para> |
| The <link linkend="inspector">Tapestry Inspector</link> includes a Logging tab that allows the logging configuration to be |
| dynamically changed. The logging level for any logger can be assigned, and new loggers |
| can be created. |
| </para> |
| <para> |
| What this means is that, using the Inspector, it is possible to control exactly what logging output is |
| produced, dynamically, while the application is still running. The Tapestry Inspector is easily |
| added to any Tapestry application. |
| </para> |
| </section> |
| <section id="engine.private-assets"> |
| <title>Private Assets</title> |
| <para> |
| The application engine is responsible for making private assets, assets that are stored |
| on the Java classpath, |
| visible when necessary to client web browser. |
| </para> |
| <para> |
| This takes two forms: |
| </para> |
| <itemizedlist> |
| <listitem> |
| <para>Dynamic download of asset data via the application servlet.</para> |
| </listitem> |
| <listitem> |
| <para>Dynamic copying of asset data into the web server's virtual file system. |
| </para> |
| </listitem> |
| </itemizedlist> |
| <para> |
| The first form is the default behavior; each private asset requires an additional round trip through the |
| application server and application engine to retrieve the stream of bytes which make up the asset. This is |
| fine during development, but less than ideal at deployment, since it places an extra burden on the |
| servlet container, stealing valuable cycles away from the main aspects of servicing end users. |
| </para> |
| <para> |
| The second form is better during deployment. The bytestreams are copied out of the classpath to a specific directory, |
| one that is mapped into the web server's virtual file system. Once it is so copied, the |
| access to the asset is completely static, as with any other image file or HTML page. |
| </para> |
| <para> |
| To enable dynamic copying, it is necessary to inform the framework about what file system directory |
| to copy the assets to, and what virtual file system directory that maps to. This is accomplished |
| using a pair of JVM system properties: |
| </para> |
| <variablelist> |
| <title>JVM System Properties</title> |
| <varlistentry> |
| <term> |
| <varname>org.apache.tapestry.asset.dir</varname> |
| </term> |
| <listitem> |
| <para> |
| The complete pathname of a directory to which private |
| assets may be copied by the asset externalizer. |
| </para> |
| </listitem> |
| </varlistentry> |
| <varlistentry> |
| <term> |
| <varname>org.apache.tapestry.asset.URL</varname> |
| </term> |
| <listitem> |
| <para> |
| The URL corresponding to the external asset directory. |
| </para> |
| </listitem> |
| </varlistentry> |
| </variablelist> |
| </section> |
| |
| </chapter> |