| <!-- $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="intro"> |
| <title>Introduction</title> |
| |
| <para> |
| Tapestry is a component-based web application framework, written in Java. Tapestry |
| is more than a simple templating system; Tapestry builds on the Java Servlet API to |
| build a platform for creating dynamic, interactive web sites. More than |
| just another templating language, Tapestry is a real framework for building |
| complex applications from simple, reusable components. Tapestry offloads |
| much of the error-prone work in creating web applications into the framework itself, |
| taking over mundane tasks such as dispatching incoming requests, constructing and |
| interpretting URLs encoded with information, handling localization and internationalization |
| and much more besides. |
| </para> |
| |
| <para> |
| The "mantra" of Tapestry is "objects, methods and properties". That is, rather than have developers concerned |
| about the paraphanlia of the Servlet API: requests, responses, sessions, attributes, parameters, URLs and so on, |
| Tapestry focuses the developer on objects (including Tapestry pages and components, but |
| also including the domain objects of the application), methods on those objects, |
| and JavaBeans properties of those objects. That is, in a Tapestry application, the |
| actions of the user (clicking links and submitting forms) results in changes to object properties |
| combined with the invocation of user-supplied methods (containing application logic). Tapestry |
| takes care of the plumbing necessary to connect these user actions |
| with the objects. |
| </para> |
| |
| <para> |
| This can take some getting used to. You don't write servlets in Tapestry, you write |
| <emphasis>listener methods</emphasis>. You don't build URLs to servlets either -- you |
| use an existing component (such as &DirectLink;) and configure its <varname>listener</varname> parameter |
| to invoke your listener method. What does a listener method do? It interacts with backend systems (often, |
| stateless session EJBs) or does other bookkeeping related to the request and selects a new page to provide |
| a response to the user ... basically, the core code at the center of a servlet. In Tapestry, you write much less code |
| because all the boring, mechanical plumbing (creating URLs, dispatching incoming requests, managing |
| server-side state, and so forth) is the responsibility of the framework. |
| </para> |
| |
| <para> |
| This is not to say the Servlet API is inaccessible; it is simply not |
| <emphasis>relevant</emphasis> to a typical Tapestry user. |
| </para> |
| |
| <para> |
| This document describes many of the internals of Tapestry. It is not a tutorial, |
| that is available as a separate document. Instead, this is a guide to |
| some of the internals of Tapestry, and is intended for experienced developers |
| who wish to leverage Tapestry fully. |
| </para> |
| |
| <para> |
| Tapestry is currently in release 3.0, and has come a long way in the last couple |
| of years. Tapestry's focus is still on generating dynamic HTML pages, although there's plenty |
| of support for XHTML, WML and other types of markup as well. |
| </para> |
| |
| <para> |
| Nearly all of Tapestry's API is described in terms of interfaces, with |
| default implementations supplied. |
| By substituting new objects with the correct interfaces, the behavior of the framework |
| can be changed significantly. A common |
| example is to override where page and component specifications are stored |
| (perhaps in a database). |
| </para> |
| |
| <para> |
| Finally, Tapestry boasts extremely complete JavaDoc API documentation. This document |
| exists to supplement that documentation, to fill in gaps that may not be obvious. The JavaDoc |
| is often the best reference. |
| </para> |
| |
| <section id="intro.overview"> |
| <title>An overview of Tapestry</title> |
| |
| <para> |
| Perhaps the hardest part of understanding Tapestry is the fact that it is |
| <emphasis>component-centric</emphasis> not |
| <emphasis>operation-centric</emphasis>. Most web technologies (&Struts;, servlets, PHP, etc.) |
| are operation-centric. You create servlets (or <classname>Action</classname>s, or what have you) |
| that are invoked when a user clicks a link or submits a form. You are responsible for selecting an appropriate |
| URL, and the name and type of any query parameters, so that you can pass along the information you need |
| in the URL. |
| </para> |
| |
| <para> |
| You are also responsible for connecting your output pages (whether they are JSPs, &Velocity; templates, |
| or some other form of templating technology) to those operations. This requires you to construct those URLs and get them into the |
| <literal>href</literal> attribute of your <sgmltag class="starttag">a</sgmltag> tag, or |
| into the <literal>action</literal> attribute |
| of your <sgmltag class="starttag">form</sgmltag> tag. |
| </para> |
| |
| <para> |
| Everything is different inside Tapestry. Tapestry applications consist of pages; pages are constructed |
| from smaller components. Components may themselves be constructed from other components. Every page has a unique name, |
| and every component within a page has its own unique id ... this is a <emphasis>component object model</emphasis>. |
| Effectively, every component has an <emphasis>address</emphasis> that can easily be incorporated into a URL. |
| </para> |
| |
| <para> |
| In practical terms, your don't write a servlet for the <literal>add-item-to-shopping-cart</literal> operation. |
| In fact, you don't even write an <literal>add-item-to-shopping-cart</literal> component. What you do is take an existing component, such |
| as &DirectLink;, and configure it. When the component renders, it will create a callback URL. When you click that link, the callback URL (which |
| includes the name of the page and the id of the component within the page) will invoke a method on the component ... and <emphasis>that</emphasis> |
| method invokes your application-specific <emphasis>listener method</emphasis>. |
| <footnote> |
| <para> |
| Listener methods in Tapestry are very similar in intent to <emphasis>delegates</emphasis> in C#. In both cases, a method of a |
| particular object instance is represented as an object. Calling this a "listener" or a "listener method" is a bit of a naming |
| snafu; it should be called a "delegate" and a "delegate method" but the existing naming is too deeply entrenched to change any time soon. |
| </para> |
| </footnote> |
| You supply just the listener method ... not an entire servlet. |
| Tapestry takes care that your listener method is invoked at the right time, under the right conditions. You don't have to think about how to build that URL, |
| what data goes in the URL, or how to hook it up to your application-specific code--that's all handled by the framework. |
| </para> |
| |
| <figure> |
| <title>Tapestry request dispatch (high level)</title> |
| <mediaobject> |
| <imageobject> |
| <imagedata fileref="images/high-level-component-request.png" format="PNG"/> |
| </imageobject> |
| <caption> |
| <para> |
| Tapestry uses a component object model to dispatch incoming requests to the correct |
| page and component. |
| </para> |
| </caption> |
| </mediaobject> |
| </figure> |
| |
| |
| |
| </section> <!-- intro.overview --> |
| |
| <section id="intro.pages-and-components"> |
| <title>Pages and components</title> |
| |
| <para> |
| Tapestry divides an application into a set of pages. Each page is assembled from Tapestry components. |
| Components themselves may be assembled from other components ... there's no artificial depth limit. |
| </para> |
| |
| |
| <para> |
| Tapestry pages are themselves components, but are components with some special responsibilities. |
| </para> |
| |
| <para> |
| All Tapestry components can be containers of other components. Tapestry pages, and most |
| user-defined components, have a template, a special HTML file that defines the static |
| and dynamic portions of the component, with markers to indicate where embedded components are |
| active. Components do not have to have a template, most of the components provided with |
| Tapestry generate their portion of response in code, not using a template. |
| </para> |
| |
| <para> |
| Components may have one or more named parameters which may be set (or, more correctly, "bound") |
| by the page or component |
| which contains them. Unlike Java method parameters, Tapestry component parameters may be |
| bidirectional; a component may read a parameter to obtain a value, or write a parameter |
| to set a value. |
| </para> |
| |
| <para> |
| Most components are concerned only with generating HTML. A certain subset of components deal with the flip-side |
| of requests; handling of incoming requests. Link classes, such as &PageLink;, &DirectLink; and |
| &ActionLink;, create clickable links in the rendered page and are involved in dispatching to user-supplied |
| code when such a link is triggered by clicking it. |
| </para> |
| |
| <para> |
| Other components, &Form;, and the form control components (&TextField;, &PropertySelection;, &Checkbox;, etc.), |
| facilitate HTML forms. When such components render, they read properties from application objects so as |
| to provide default values. When the forms are submitted, the components |
| within the form read HTTP query parameters, convert the values to appropriate types |
| and then update properties of application objects. |
| </para> |
| |
| </section> <!-- intro.pages-and-components --> |
| |
| <section id="intro.engine-service-visit"> |
| <title>Engines, services and friends</title> |
| |
| <para> |
| Tapestry has evolved its own jargon over time. |
| </para> |
| |
| <para> |
| The Engine is a central object, it occupies the same semantic space in Tapestry that the |
| &HttpSession; does in the Servlet API. The Engine is ultimately responsible for |
| storing the persistent state of the application (properties that exist from one request to |
| the next), and this is accomplished by storing the Engine into the &HttpSession;. |
| This document will largely discuss the <emphasis>default</emphasis> implementation, |
| with notes about how the default implementation may be extended or overriden, where appropriate. |
| </para> |
| |
| <para> |
| Engine services are the bridge between servlets and URLs and the rest of Tapestry. Engine services |
| are responsible for encoding URLs, providing query parameters that identify, to the framework, |
| the exact operation that should occur when the generated URL is triggered (by the end user |
| clicking a link or submitting a form). Services are also responsible for dispatching |
| those incoming requests. This encapsulation of URL encoding and decoding inside a single object |
| is key to how Tapestry components can flexibily operate without concern for how they are contained and on which |
| page ... the services take into account page and location when formulating URLs. |
| </para> |
| |
| <para> |
| The <link linkend="state.visit">Visit</link> object |
| is an application-defined object that acts as a focal point |
| for all server-side state (not associated with any single page). Individual applications define for themselves |
| the class of the Visit object. The Visit is stored as a property of the Engine, and so is ultimately |
| stored persistently in the &HttpSession; |
| </para> |
| |
| <para> |
| The <link linkend="state.global">Global</link> object is also application-specific. It stores information |
| global to the entire application, independent of any particular user or session. A common use for the Global |
| object is to centralize logic that performs JNDI lookups of session EJBs. |
| </para> |
| |
| </section> <!-- intro.engine-service-visit --> |
| |
| <section id="intro.ognl"> |
| <title>Object Graph Navigation Language</title> |
| |
| <para> |
| Tapestry is tightly integrated with &OGNL;, the Object Graph Navigation Language. OGNL is a Java expression language, which is used |
| to peek into objects and read or update their properties. OGNL is similar to, and must more powerful than, |
| the expression language built into the JSP 2.0 standard tag library. OGNL not only support property access, |
| it can include mathematical expressions and method invocations. It can reference static fields of public classes. |
| It can create new objects, including lists and maps. |
| </para> |
| |
| <para> |
| The simplest OGNL expressions are property names, such as <literal>foo</literal>, which |
| is equivalent to method <code>getFoo()</code> (or <code>setFoo()</code> if the expression is |
| being used to update a property). The "Navigation" part comes |
| into play when the expression is a series of property names, such as <literal>foo.bar.baz</literal>, |
| which is equivalent to <code>getFoo().getBar().getBaz()</code> ... though care must always be taken that the intermediate |
| properties (<literal>foo</literal> and <literal>bar</literal> in this example) are not null. |
| </para> |
| |
| <para> |
| OGNL is primarily used to allow two different objects (such as a page and a component contained by that page) to |
| share information. |
| </para> |
| |
| </section> <!-- intro.ognl --> |
| |
| </chapter> |