blob: e99f9c4bcddf1e0dcba3a4addae8a98297b79590 [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 comprehensive web application framework, written in Java.</para>
<para>Tapestry is not an application server.
Tapestry is a framework designed to be used inside an application server.</para>
<para>Tapestry is not an application. Tapestry is a framework for creating web applications.</para>
<para>Tapestry is not a way of using JavaServer Pages.
Tapestry is an alternative to using JavaServer Pages.</para>
<para>Tapestry is not a scripting environment. Tapestry uses a component object model,
not simple scripting, to create highly dynamic, interactive web pages.</para>
<para>Tapestry is based on the Java Servlet API version 2.2 It is compatible with JDK 1.2 and above.
Tapestry uses a sophisticated component model to divide a web application into a hierarchy of components.
Each component
has specific responsibilities for rendering web pages (that is, generating a portion of an HTML page)
and responding to HTML queries (such as clicking on a link, or submitting a form).</para>
<para>The Tapestry framework takes on virtually all of the responsibilities
for managing application flow and server-side client state.
This allows developers to concentrate on the business and presentation aspects of the application.</para>
<para>
Tapestry reconceptualizes web application development
in terms of objects, methods and properties
instead of URLs and query parameters.
</para>
<section id="intro.scripting">
<title>Scripting vs. Components</title>
<para>Most leading web application frameworks are based on some form of
scripting. These frameworks (often bundled into a web or application server) include:
<itemizedlist mark="opencircle">
<listitem>
<para>
<ulink url="http://java.sun.com/products/jsp/">Sun JavaServer Pages</ulink>
</para>
</listitem>
<listitem>
<para>Microsoft Active Server Pages</para>
</listitem>
<listitem>
<para>
<ulink url="http://www.macromedia.com/software/coldfusion/">Allaire ColdFusion</ulink>
</para>
</listitem>
<listitem>
<para>
<ulink url="http://www.php.net/">PHP</ulink>
</para>
</listitem>
<listitem>
<para>
<ulink url="http://www.webmacro.org/">WebMacro</ulink>
</para>
</listitem>
<listitem>
<para>
<ulink url="http://freemarker.sourceforge.net/">FreeMarker</ulink>
</para>
</listitem>
<listitem>
<para>
<ulink url="http://jakarta.apache.org/velocity/index.html">Velocity</ulink>
</para>
</listitem>
</itemizedlist>
</para>
<para>All of these systems are based on reading an HTML template file
and performing some kind of processing on it. The processing is identified by directives ...
special tags in the HTML template that indicate dynamic behavior.</para>
<para>Each framework has a scripting language. For JavaServer Pages it is Java itself.
For ASP it is Visual Basic. Most often, the directives are snippets of
the scripting language inserted into the HTML.</para>
<para>For example, here's a snippet from a hypothetical
JavaServer Page that displays part of a shopping cart.
</para>
<informalexample>
<programlisting>
<emphasis>&lt;%
String userName = (String)session.getAttribute("userName");
%&gt;</emphasis>
&lt;h1&gt;Contents of shopping cart for
<emphasis>&lt;%= userName %&gt;</emphasis>:&lt;/h1&gt;
</programlisting>
</informalexample>
<para>Most of the text is static HTML that is sent directly back to the client web browser.
The <emphasis>emphasised</emphasis> text identifies scripting code.</para>
<para>The first large block is used to extract the user name from the &HttpSession;,
a sort of per-client scratch pad (it is part of the Java Servlet API;
other systems have some similar construct).
The second block is used to insert the value of an expression into the HTML.
Here, the expression is simply the value of the userName variable.
It could be more complex, including the result of invoking a method on a Java object.</para>
<para>This kind of example is often touted as showing how useful and powerful scripting solutions are.
In fact, it shows the very weaknesses of scripting.</para>
<para>First off, we have a good bit of Java code in an HTML file.
This is a problem ... no HTML editor is going to understand the JavaServer Pages syntax,
or be able to validate that the Java code in the scripting sections is correct, or that it even compiles.
Validation will be deferred until the page is viewed within the application.
Any errors in the page will be shown as runtime errors.
Having Java code here is unnatural ... Java code should be developed exclusively inside an IDE.</para>
<para>In a real JavaServer Pages application I've worked on, each JSP file was 30% - 50% Java.
Very little of the Java was simple presentation logic like
<sgmltag class="starttag">%= userName %</sgmltag>,
most of it was larger blocks needed to 'set up' the presentation logic.
Another good chunk was concerned with looping through lists of results.</para>
<para>In an environment with separate creative and technical teams,
nobody is very happy. The creative team is unlikely to know JSP or Java syntax.
The technical team will have difficulty "instrumenting" the HTML files provided by creative team.
Likewise, the two teams don't have a good common language
to describe their requirements for each page.</para>
<para>One design goal for Tapestry is minimal impact on the HTML.
Many template-oriented systems add several different directives
for inserting values into the HTML, marking blocks as conditional,
performing repetitions and other operations.
Tapestry works quite differently; it allows existing tags to be marked
as dynamic in a completely unobtrusive way.</para>
<para>A Tapestry component is any HTML tag with a <varname>jwcid</varname>
attribute ("jwc" stands for "Java Web Component").
For comparison, an equivalent Tapestry template to the previous JSP example:
</para>
<informalexample>
<programlisting><![CDATA[
<h1>Contents of shopping cart for
<span jwcid="insertUserName">John Doe</span>:</h1>
]]></programlisting>
</informalexample>
<para>This defines a component named <varname>insertUserName</varname> on the page.
To assist HTML development, a sample value, "<literal>John Doe</literal>" is included, but
this is automatically editted out when the HTML template is used by the framework.
</para>
<para>
The <sgmltag class="starttag">span</sgmltag> tag simply indicated where the Tapestry component
will go ... it doesn't identify any of its behavior. That is provided elsewhere, in
a <link linkend="spec.component-specification">component specification</link>.
</para>
<para>A portion of the page's specification file defines what
the <varname>insertUserName</varname> component is and what it does:
</para>
<informalexample id="intro.ex">
<programlisting>
&lt;component id="insertUserName" type="&Insert;"&gt; <co id="intro.ex.co.id-and-type"/>
&lt;binding name="value" expression="visit.userName"/&gt; <co id="intro.ex.co.value-binding"/>
&lt;/component&gt;
</programlisting>
<calloutlist>
<callout arearefs="intro.ex.co.id-and-type">
<para>
The <varname>id</varname> attribute gives the component a unique identifier, that matches against the
HTML template. The <varname>type</varname> attribute is used to specify which kind of component
is to be used.
</para>
</callout>
<callout arearefs="intro.ex.co.value-binding">
<para>
Bindings identify how the component gets the data it needs. In this example,
the &Insert; component requires a binding for its <varname>value</varname>
parameter, which is what will be inserted into the response HTML page. This
type of binding (there are others), extracts the userName property from the
visit object (a central, application-defined object used to store most
server-side state in a Tapestry application).
</para>
</callout>
</calloutlist>
</informalexample>
<para>Tapestry really excels when it is doing something
more complicated than simply producing output.
For example, let's assume that there's a checkout button that should only
be enabled when the user has items in their shopping cart.</para>
<para>In the JSP world, this would look something like:
</para>
<informalexample>
<programlisting><emphasis>&lt;%
boolean showLink;
String imageURL;
showLink = applicationObject.getHasCheckoutItems();
if (showLink)
imageURL = "/images/Checkout.gif";
else
imageURL = "/images/Checkout-disabled.gif";
if (showLink)
{
String linkURL;
linkURL = response.encodeURL("/servlet/checkout"); %&gt;</emphasis>
&lt;a href="<emphasis>&lt;%= linkURL %&gt;</emphasis>"&gt;
<emphasis>&lt;% } %&gt;</emphasis>
&lt;img border=0 src="<emphasis>&lt;%= imageURL %&gt;</emphasis>" alt="Checkout"&gt;<emphasis>&lt;%
if (showLink)
out.println("&lt;/a&gt;");
%&gt;</emphasis>
</programlisting>
</informalexample>
<para>This assumes that <varname>applicationObject</varname> exists to
determine whether the user has entered any checkout items.
Presumably, this object was provided by a controlling servlet, or placed into the
&HttpSession;.
</para>
<para>
The corresponding Tapestry HTML template is much simpler:
</para>
<informalexample>
<programlisting><emphasis><![CDATA[
<a jwcid="checkoutLink"><img jwcid="checkoutButton" alt="Checkout"/></a>]]></emphasis>
</programlisting>
</informalexample>
<para>
A bit more goes into the page's specification :
</para>
<informalexample id="intro.ex2">
<programlisting>&lt;component id="checkoutLink" type="&PageLink;"&gt; <co id="intro.ex2.co.checkoutLink"/>
&lt;static-binding name="page"&gt;Checkout&lt;/static-binding&gt;
&lt;binding name="disabled" expression="visit.cartEmpty"/&gt; <co id="intro.ex2.co.link-disabled"/>
&lt;/component&gt;
&lt;component id="checkoutButton" type="&Rollover;"&gt; <co id="intro.ex2.co.Rollover"/>
&lt;binding name="image" expression="assets.checkout"/&gt;
&lt;binding name="disabled" expression="assets.checkoutDisabled"/&gt;
&lt;/component&gt;
&lt;external-asset name="checkout" URL="/images/Checkout.gif"/&gt; <co id="intro.ex2.co.checkout-asset"/>
&lt;external-asset name="checkoutDisabled" URL="/images/Checkout-disabled.gif"/&gt;
</programlisting>
<calloutlist>
<callout arearefs="intro.ex2.co.checkoutLink">
<para>
Component <varname>checkoutLink</varname> is a &PageLink;, a component that creates
a link to another page in the application. Tapestry takes care of generating the
appropriate URL.
</para>
</callout>
<callout arearefs="intro.ex2.co.link-disabled">
<para>
The <varname>disabled</varname> parameter allows the link to be "turned off"; here it is turned
off when the shopping cart is empty.
</para>
</callout>
<callout arearefs="intro.ex2.co.Rollover">
<para>
A &Rollover; component inserts an image; it must be inside some kind of
link component (such as the &PageLink;) and is sensitive to whether the link
is enabled or disabled; inserting a different image when disabled. Not shown here
is the ability of the &Rollover; component to
generate dynamic mouse-over effects as well.
</para>
</callout>
<callout arearefs="intro.ex2.co.checkout-asset">
<para>
Tapestry uses an abstraction, <link linkend="components.assets">assets</link>,
to identify images, stylesheets
and other resources. The &Rollover; component wants a reference to
an asset, not a URL.
</para>
</callout>
</calloutlist>
</informalexample>
<para>
The point of this example is that the JSP developer had to worry about character-by-character
production of HTML. Further, the ratio of Java code to HTML is quickly getting out of hand.
</para>
<para>
By contrast, the Tapestry developer is concerned with the behavior of
components and has an elegant way of specifying that behavior dynamically.
</para>
</section>
<section id="intro.interaction">
<title>Interaction</title>
<para>
Let's continue with a portion of the JSP that would allow an
item to be deleted from the shopping cart.
For simplicity, we'll assume that there's an object of class <classname>LineItem</classname>
named <varname>item</varname> and that there's a servlet used for making changes to the shopping cart.
</para>
<informalexample>
<programlisting><![CDATA[
<tr>
<td> ]]><emphasis>&lt;%= item.getProductName() %&gt;</emphasis><![CDATA[ </td>
<td> ]]><emphasis>&lt;%= item.getQuantity() %&gt;</emphasis><![CDATA[ </td>
<td> ]]><emphasis>&lt;% String URL = response.encodeURL("/servlet/update-cart?action=remove" +
"&amp;item=" + item.getId());
%&gt;</emphasis><![CDATA[
<a href="]]><emphasis>&lt;%= URL %&gt;</emphasis><![CDATA[">Remove</a> </td>
</tr>]]></programlisting>
</informalexample>
<para>
This clearly shows that in a JSP application, the designer is responsible for
"knitting together" the pages, servlets and other elements at a very low level.
By contrast, Tapestry takes care of nearly all these issues automatically:
</para>
<informalexample>
<programlisting><![CDATA[
<tr>
<td> <span jwcid="insertName">Sample Product</span> </td>
<td> <span jwcid="insertQuantity">10</span> </td>
<td> <a jwcid="remove">Remove</a> </td>
</tr>]]></programlisting>
</informalexample>
<para>
Because of the component object model used by Tapestry, the framework knows exactly
"where on the page" the <varname>remove</varname> component is.
It uses this information to build an appropriate URL that references the <varname>remove</varname> component.
If the user clicks the link, the framework will inform the component to perform the desired action.
The <varname>remove</varname> component can then remove the item from the shopping cart.
</para>
<para>
In fact, under Tapestry, no user code ever has to either encode or decode a URL.
This removes an entire class of errors from a web application
(those URLs can be harder to assemble and parse than you might think!)
</para>
<para>
Tapestry isn't merely building the URL to a servlet for you;
the whole concept of 'servlets' drops out of the web application.
Tapestry is building a URL that will invoke a method on a component.
</para>
<para>
Tapestry applications act like a 'super-servlet'.
There's only one servlet to configure and deploy.
By contrast, even a simple JavaServer Pages application developed using
Sun's Model 2 (where servlets provide control logic and JSPs are used for presenting results)
can easily have dozens of servlets.
</para>
</section>
<section id="intro.security">
<title>Security</title>
<para>
Developing applications using Tapestry provides some modest security benefits.
</para>
<para>
Tapestry applications are built on top of the Java Servlet API,
and so inherits all the sercurity benefits of servlets.
Most security intrusions against CGI programs
(such as those written in Perl or other scripting languages)
rely on sloppy code that evaluates portions of the URL in a system shell;
this never happens when using the Java Servlet API.
</para>
<para>
Because the URLs created by Tapestry for processing client interaction
are more strongly structured than the URLs
in traditional solutions, there are fewer weaknesses
to exploit. Improperly formatted URLs result in an exception response
being presented to the user.
</para>
<para>
Where the Java Servlet API suffers is in client identification,
since a session identifier is stored on the client either as an
HTTP Cookie or encoded into each URL. Malicious software could acquire
such an identifier and "assume" the identity of a user who has recently logged
into the application. Again, this is a common limitation of servlet applications
in general.
</para>
<para>
Finally, Tapestry applications have a single flow of control:
all incoming requests flow through a few specific methods of particular classes.
This makes it easier to add additional security measures that are specific to the application.
</para>
</section>
<section id="intro.web-app">
<title>Web Applications</title>
<para>
Tapestry has a very strong sense of what an application is, derived from an
XML specification file. This file identifies and gives names to
all the pages in the application, and identifies certain other key classes as well.
It also gives a human-readable name to the entire application.
</para>
<para>
In other systems, there is no application per-se. There is some kind of
'home page' (or servlet), which is the first page seen when a client
connects to the web application. There are many pages, servlets (or equivalent,
in other frameworks) and interrelations between them. There is also some amount
of state stored on the server, such as the user name and a shopping
cart (in a typical e-commerce application). The sum total of these
elements is the web application.
</para>
<para>
Tapestry imposes a small set of constraints on the developer, chiefly, that the application
be organized in terms of pages and components. These constraints are
intended to be of minimal impact to the developer, imposing an acceptible amount of
structure. They create a common language that can be used between members
of a team, and even between the technical and creative groups within a team.
</para>
<para>
Under Tapestry, a page is also very well defined: It consists of a component
specification, a corresponding Java class, an HTML template, and a
set of contained components.
</para>
<para>
By contrast, when using JavaServer Pages there are one or more servlets, embedded JavaBeans,
a JSP file and the Java class created from the JSP file. There isn't
a standard naming scheme or other way of cleanly identifying the
various elements.
</para>
<para>
Interactivity in Tapestry is component based. If a component is interactive, such as
an image button with a hyperlink (<sgmltag class="starttag">a</sgmltag>), clicking on the link
invokes a method on the component. All interactivity on a
page is implemented by components on the page.
</para>
<para>
JavaServer Pages bases its interactivity on servlets. Interactive portions of a page must
build URLs that reference these servlets. The servlets use a variety of ad-hoc
methods to identify what operation is to take place when a link is clicked. Since there
is no standard for any of this, different developers, even on the same project,
may take widely varying approaches to implementing similar constructs.
</para>
<para>
Because pages are components, they have a well-defined interface, which describes to
both the framework and the developer how the page fits into the overall application.
</para>
<para>
At the core of any Tapestry application are two objects: the engine and the visit.
The engine is created when the first request from a client arrives at the server.
The engine is responsible for all the mundane tasks in Tapestry, such as managing the request cycle.
It is sort of a dispatcher, that handles the incoming request and runs the
process of responding to the request with a new HTML page.
</para>
<para>
The visit is a second object that contains application-specific data and logic.
Its type is completely defined by the application.
In an e-commerce application, the visit might store a shopping cart
and information about the user (once logged in).
</para>
<para>
Both the engine and the visit are stored persistently between request cycles, inside the
&HttpSession; object.
</para>
<para>
The engine also provides services. Services are the bridge between URLs and components.
Services are used to generate the URLs used by hyperlinks and form submissions.
They are also responsible for interpreting the same URLs when they are later
triggered from the client web browser.
</para>
</section> <!-- intro.web-app -->
<section id="intro.features">
<title>Features</title>
<para>
The framework, based on the component object model, provides a significant number of other features,
including:
<itemizedlist>
<listitem>
<para>Easy localization of applications</para>
</listitem>
<listitem>
<para>Extremely robust error handling and reporting</para>
</listitem>
<listitem>
<para>Highly re-usable components</para>
</listitem>
<listitem>
<para>Automatic persistence of server-side client state between request cycles</para>
</listitem>
<listitem>
<para>Powerful processing of HTML forms</para>
</listitem>
<listitem>
<para>Strong support for load balancing and fail over </para>
</listitem>
<listitem>
<para>Zero code generation
<footnote>
<para>That is, Tapestry templates and specifications are interpreted as is.
Unlike JSPs, they are not translated into Java source code
and compiled into Java classes. This greatly simplifies debugging
since no code is generated at runtime.
</para>
</footnote>
</para>
</listitem>
<listitem>
<para>Easy deployment</para>
</listitem>
<listitem>
<para>The <link linkend="inspector">Inspector</link>,
which allows developers to debug a running Tapestry application</para>
</listitem>
</itemizedlist>
</para>
<para>
The point of Tapestry is to free the web application developer from the most tedious tasks.
In many cases, the "raw plumbing" of a web application can be completely mechanized by
the framework, leaving the developer to deal with more interesting challenges, such
as business and presentation logic.
</para>
<para>
As Tapestry continues to develop, new features will be added. On the drawing board are:
<itemizedlist>
<listitem>
<para>Support for easy cross-browser DHTML</para>
</listitem>
<listitem>
<para>XML / XHTML support</para>
</listitem>
<listitem>
<para>Improved WAP / WML support</para>
</listitem>
<listitem>
<para>A real-time performance "Dashboard"</para>
</listitem>
</itemizedlist>
</para>
</section> <!-- intro.features -->
</chapter>