blob: be29fe7d089e1a5082ae9e2b6320ad6776d598f1 [file] [log] [blame]
<!-- $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>