blob: d66b90621c50a35dcc6400f3ced7f146bb55b17f [file] [log] [blame]
<?xml version="1.0" encoding="utf-8"?>
<!-- $Id$ -->
<!DOCTYPE book PUBLIC
"-//OASIS//DTD DocBook XML V4.1.2//EN"
"http://www.oasis-open.org/docbook/xml/4.0/docbookx.dtd" [
<!ENTITY % TapestryLinks SYSTEM "../common/TapestryLinks.xml">
%TapestryLinks;
]>
<!-- Conventions:
Component ids are <varname>
Java packages and class names are <classname>
Tapestry component aliases are <classname>
In-line code snippets use <function>
Property paths and JavaBeans property names used <varname>
-->
<book>
<title>Tapestry Tutorial</title>
<bookinfo>
<author>
<firstname>Howard</firstname>
<surname>Lewis Ship</surname>
</author>
<copyright>
<year>2000</year>
<year>2001</year>
<year>2002</year>
<year>2003</year>
<holder>The Apache Software Foundation</holder>
</copyright>
</bookinfo>
<chapter id="intro">
<title>Introduction</title>
<warning>
<para>
This Tutorial is extremely out of date. A new tutorial should be ready
before 3.0 reaches GA.
</para>
</warning>
<para>
Tapestry is a new application framework for developing web applications. It uses
a component object model to represent the pages of a web application. This is
similar to spirit to using the Java Swing component object model to build GUIs.
</para>
<para>
Just like using a GUI toolkit, there's some preparation and some basic ideas that must be cleared
before going to more ambitious things. Nobody writes a word processor off the top of their head
as their first GUI project; nobody should attempt a full-featured e-commerce site as their first
attempt using Tapestry.
</para>
<para>
The goal of Tapestry is to eliminate most of the coding in a web application. Under Tapestry,
nearly all code is directly related to application functionality, with very little "plumbing". If you
have previously developed a web application using Microsoft Active Server Pages, JavaServer
Pages or Java Servlets, you may take for granted all the plumbing: writing servlets, assembling
URLs, parsing URLs, managing objects inside the &HttpSession;, etc.
</para>
<para>
Tapestry takes care of nearly all of that, for free. It allows for the development of rich, highly
interactive applications.
</para>
<para>
This tutorial will start with basic concepts, such as the "Hello World" application, and will
gradually build up to more sophisticated examples.
</para>
<para>
The tutorial uses &Jetty;,
a freely available servlet engine, which is packaged with
the Tapestry
distribution.
</para>
<para>
The format of this tutorial is to describe (visually and with text) an application within the tutorial,
then describe how it is constructed, using code excerpts. The reader is best served by having an
IDE open so that they can look at the code in detail, as well as run the applications.
</para>
</chapter>
<chapter id="setup">
<title>Setting up the Tutorial</title>
<para>
This document expects that you will have extracted the full Tapestry distribution to your
<filename class="directory">C:</filename> drive
<footnote>
<para>
If you are using Solaris or another non-Windows operating system, you're expected
to be savvy enough to translate to a sensibly constructed file system.
</para>
</footnote>
</para>
<para>
This will have created a directory <filename class="directory">C:\Tapestry-<replaceable>x.x</replaceable>
</filename>
and, beneath it, several more directories.
<footnote>
<para>
The three numbers are the release number. At the time of this writing, the release
was 2.2, but this is constantly changing. Simply adjust the actual pathname to
reflect the release of Tapestry you downloaded.
</para>
</footnote>
</para>
<para>
The source code for the Tutorial is distributed as a JAR file,
<filename>src/examples-src.jar</filename>. A precompiled WAR file,
<filename>lib/tutorial.war</filename> is included in the distribution.
</para>
<para>
The Tapestry distribution includes an &Ant; build file that allows the
Tutorial to be directly executed. Ant release 1.5 is required.
</para>
<para>
From the Tapestry root directory,
execute the command <command>ant -emacs run-tutorial</command>, which will launch the
Jetty server for the Tutorial.
</para>
<para>
Once Jetty is running, you can access the Tutorials using the URL
<ulink url="http://localhost:8080/tutorial">
<filename>http://localhost:8080/tutorial</filename>
</ulink>.
</para>
<figure>
<title>Tutorial Index Page</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/tutorial-index.png" format="PNG"/>
</imageobject>
</mediaobject>
</figure>
</chapter>
<chapter id="hello">
<title>Hello World</title>
<para>
In this first example, we'll create a very simple "Hello World" kind of application. It won't have
any real functionality but it'll demonstrate the simplest possible variation of a number of key
aspects of the framework.
</para>
<para>
We'll define our application, define the lone page of our application, configure everything and
launch it.
</para>
<para>
The code for this section of the tutorial is in the Java package <classname>tutorial.hello</classname>, i.e.,
<filename class="directory">C:\Tapestry-<replaceable>x.x.x</replaceable>\examples\Tutorial\src\tutorial\hello</filename>.
</para>
<section id="hello.engine">
<title>Application Engine</title>
<para>
As each new client connects to the application, an instance of the application engine is created for
them. The application engine is used to track that client's activity within the application.
</para>
<para>
The application engine is an instance, or subclass of, the Tapestry class
&SimpleEngine;.
</para>
<para>
In these first few examples, we have no additional behavior to add to the provided base class, so
we simply use &SimpleEngine; directly.
</para>
</section>
<section id="hello.descriptor">
<title>Web Deployment Descriptor</title>
<para>
The application servlet is a "bridge" between the servlet container and the application engine. Its
job is simply to create (on the first request) or locate (on subsequent requests) the application
engine.
</para>
<para>
All Tapestry applications use the same servlet class, however its configuration is different. Part
of the configuration is to identify the location of the <emphasis>application specification</emphasis>
which is like a master index of all the pages in the application.
</para>
<para>
The tutorial is a rare case; it is a single WAR that contains multiple Tapestry applications. This
isn't a problem ... each Tapestry application has its own servlet and has its own configuration.
The following figure shows the deployment descriptor for the Tapestry Tutorial (but excludes
the additional sections for the other applications within the WAR).
</para>
<figure>
<title>Tutorial Deployment Descriptor (partial)</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 Tutorial</display-name>
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>org.apache.tapestry.ApplicationServlet</servlet-class>
<init-param>
<param-name>org.apache.tapestry.application-specification</param-name>
<param-value>/tutorial/hello/HelloWorld.application</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</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>
</figure>
</section>
<section id="hello.appspec">
<title>Application Specification</title>
<para>
The application specification is used to describe the application to the Tapestry framework. It
provides the application with a name, an engine class, and a list of pages.
</para>
<para>
This specification is a file that is located on the Java class path. In a deployed Tapestry
application, the specification lives with the application's class files, in the
<filename class="directory">WEB-INF/classes</filename> directory of a War file.
</para>
<figure>
<title>HelloWorld.application</title>
<programlisting>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE application PUBLIC
"-//Howard Lewis Ship//Tapestry Specification 1.3//EN"
"http://tapestry.sf.net/dtd/Tapestry_1_3.dtd"&gt;
&lt;application
name="Hello World Tutorial"
engine-class="org.apache.tapestry.engine.&SimpleEngine;"&gt;
&lt;page name="Home"
specification-path="/tutorial/hello/Home.page"/&gt;
&lt;/application&gt;
</programlisting>
</figure>
<para>
Our application is very simple; we give the application a name, use the standard engine, and define
a single page, named "Home". In Tapestry, pages and components are specified with the path to their
specification file (a file that end with '.page' for page specifications or '.jwc'
for component specifications).
</para>
<para>
Page "Home" has a special meaning to Tapestry: when you first launch a Tapestry application, it
loads and displays the "Home" page. All Tapestry applications are required to have such a home
page.
</para>
</section>
<section id="hello.home-page-spec">
<title>Home Page Specification</title>
<para>
The page specification defines the Tapestry component responsible for the page. In this first
example, our component is very simple.
</para>
<figure>
<title>Home.page</title>
<programlisting>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE page-specification PUBLIC
"-//Howard Lewis Ship//Tapestry Specification 1.3//EN"
"http://tapestry.sf.net/dtd/Tapestry_1_3.dtd"&gt;
&lt;page-specification class="org.apache.tapestry.html.&BasePage;"/&gt;
</programlisting>
</figure>
<para>
This simply says that <classname>Home</classname> is a kind of page. We use the supplied Tapestry class
&BasePage; since we aren't adding any behavior to the page.
</para>
</section>
<section id="hello.home-page-template">
<title>Home Page Template</title>
<para>
Finally, we get to the content of our application. This file is also a Java resource;
it isn't directly
visible to the web server. It has the same location and name as the component specification,
except that it ends in "html".
</para>
<figure>
<title>Home.html</title>
<programlisting><![CDATA[<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Hello World</title>
</head>
<body>
Welcome to your first <b>Tapestry Application</b>.
</body>
</html>]]></programlisting>
</figure>
</section>
<section id="hello.run">
<title>Run the Application</title>
<para>
You should already be running the Jetty server in a window, and have a browser running the
tutorials page. Select the first option, Hello World, from the list. You will be presented with the
first (and only) page generated by Tapestry for this application:
</para>
<figure>
<title>Hello World Application</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/hello-world.jpg" format="JPEG"/>
</imageobject>
</mediaobject>
</figure>
<para>
Not much of an application ... there's no interactivity. It might as well be a static web page, but it's
a start. Remember, there was no JavaServer page here, and no HTML directly visible to the web
server. We used the Tapestry framework to assembly an application consisting of a single component.
</para>
<para>
In the following chapters, we'll see how to add dynamic content and then true interactivity.
</para>
</section>
</chapter>
<chapter id="dynamic">
<title>Dynamic Content</title>
<para>
In this chapter, we'll create a new web application that will show some dynamic content. We'll also
begin to show some interactivity by adding a link to the page.
Our dynamic content will simply be to show the current date and time. The interactivity will be a
link to refresh the page. It all looks like this:
</para>
<figure>
<title>Dynamic Application</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/dynamic.jpg" format="JPEG"/>
</imageobject>
</mediaobject>
</figure>
<para>
Clicking the word "here" will update the page showing the new data and time. Not incredibly
interactive, but it's a start.
</para>
<para>
The code for this section of the tutorial is in the package
<classname>tutorial.simple</classname>.
</para>
<para>
The application specification is almost identical to the Hello World example:
</para>
<figure>
<title>Simple.application</title>
<programlisting>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE application PUBLIC
"-//Howard Lewis Ship//Tapestry Specification 1.3//EN"
"http://tapestry.sf.net/dtd/Tapestry_1_3.dtd"&gt;
&lt;application name="Simple Tutorial" engine-class="org.apache.tapestry.engine.&SimpleEngine;"&gt;
&lt;page name="Home" specification-path="/tutorial/simple/Home.page"/&gt;
&lt;/application&gt;
</programlisting>
</figure>
<para>
Things only begin to get more interesting when we look at the HTML template for the home
page:
</para>
<figure>
<title>Home.html</title>
<programlisting><![CDATA[<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Simple</title>
</head>
<body>
This application demonstrates some dynamic behavior using Tapestry components.
<p>The current date and time is: <b><span jwcid="insertDate">Current Date</span></b>
<p>Click <a jwcid="refresh">here</a> to refresh.
</body>
</html>]]></programlisting>
</figure>
<para>
This looks like ordinary HTML, except for the special <varname>jwcid</varname>
attribute. "jwc" is short for "Java Web Component"; these attributes
identify the tag as a placeholder for a dynamic Tapestry component.
</para>
<para>
We have two components. The first inserts the current date and time into the HTML response.
The second component
creates a hyperlink that refreshes the page when clicked.</para>
<para>
One of the goals of Tapestry is that the HTML should have the minimum amount of special
markup. This is demonstrated here ... the dynamic component tags blend into the standard HTML of the
template. We also don't confuse the HTML by explaining exactly what an <varname>insertDate</varname>
or <varname>refresh</varname>
is; that comes out of the specification (described shortly). The ids used here are meaningful only
to the developer
<footnote>
<para>Of course, good and consistent naming is important.
</para>
</footnote>, the particular type and configuration of each component is defined in the
component specification.
</para>
<para>Tapestry doesn't really care what HTML tag you use, as long as you balance the tag correctly. In fact, it ignores the tag entirely: the
<varname>refresh</varname> component above could just has easily been identified with a &lt;span&gt; tag, or any other tag for that matter. Tapestry is only interested in the <emphasis>structure</emphasis> of the HTML template. The fact that you can use meaningful tags is a convienience; it allows a Tapestry HTML template to be previewed in a <acronym>WYSIWYG</acronym> HTML editor, such as HomeSite. Additionally, Tapestry edits out the content of tags for components that don't wrap around other content: the <varname>insertDate</varname> component in this example. This allows a preview
values to be kept in the template.
</para>
<para>
Very significant is the fact that a Tapestry component can
<emphasis>wrap</emphasis> around other elements of the
template. The <varname>refresh</varname> component wraps around the word "here".
What this means is that the
<varname>refresh</varname> component will get a chance to emit some HTML (an
<sgmltag class="starttag">a</sgmltag> hyperlink tag), then emit the
HTML it wraps (the word "here"), then get a chance to emit more HTML (the
<sgmltag class="endtag">a</sgmltag> closing tag).
</para>
<para>
What's more important is that the component can not only wrap static HTML from the template
(as shown in this example), but may wrap around other Tapestry components and those
components may themselves wrap text and components, to whatever depth is required.
</para>
<para>
And, as we'll see in later chapters, a Tapestry component itself may have a template and more
components inside of it. In a real application, the single page of HTML produced by the
framework may be the product of dozens of components, effectively "woven" from dozens of
HTML templates.
</para>
<para>
Again, the HTML template doesn't define what the components are, it is simply a mix of static
HTML that will be passed directly back to the client web browser, with a few placeholders (the
tags with the <varname>jwcid</varname> attribute) for where dynamic content will be plugged in.
</para>
<para>
The page's component specification defines what types of components are used and how data
moves between the application, page and any components.
</para>
<figure>
<title>Home.page</title>
<programlisting>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE page-specification PUBLIC
"-//Howard Lewis Ship//Tapestry Specification 1.3//EN"
"http://tapestry.sf.net/dtd/Tapestry_1_3.dtd"&gt;
&lt;page-specification class="tutorial.simple.Home"&gt;
&lt;component id="insertDate" type="&Insert;"&gt;
&lt;binding name="value" expression="currentDate"/&gt;
&lt;/component&gt;
&lt;component id="refresh" type="&PageLink;"&gt;
&lt;static-binding name="page"&gt;Home&lt;/static-binding&gt;
&lt;/component&gt;
&lt;/page-specification&gt;
</programlisting>
</figure>
<para>
Here's what all that means: The <varname>Home</varname> page is implemented with a custom class,
<classname>tutorial.simple.Home</classname>. It contains two components,
<varname>insertDate</varname> and <varname>refresh</varname>.
</para>
<para>
The two components used within this page are provided by the Tapestry framework.
</para>
<para>
The <varname>insertDate</varname> component is type &Insert;.
&Insert; components have a <varname>value</varname> parameter used to
specify what should be inserted into the HTML produced by the page. The
<varname>insertDate</varname>
component has its <varname>value</varname> parameter bound to a JavaBeans property of its container
(the page), the <varname>currentDate</varname> property.
</para>
<para>
The <varname>refresh</varname> component is type &PageLink;, meaning it creates a
link to some other page in the
application. &PageLink; components have a parameter, named <varname>page</varname>,
which defines the name of the
page to navigate to. The name is matched against a page named in the application specification.
</para>
<para>
In this case, we only have one page in our application (named "Home"), so we can use a static
binding for the page parameter. A static binding provides a value for the component parameter
statically, the same value every time. The value is defined right in the specification.
</para>
<para>
That just leaves the implementation of the Home page component:
</para>
<figure>
<title>Home.java</title>
<programlisting>
package tutorial.simple;
import java.util.Date;
import org.apache.tapestry.html.BasePage;
public class Home extends &BasePage;
{
public Date getCurrentDate()
{
return new Date();
}
}
</programlisting>
</figure>
<para>
<classname>Home</classname> implements a read-only JavaBeans property, <varname>currentDate</varname>.
This is the same <varname>currentDate</varname>
that the <varname>insertDate</varname> component needs. When asked for the current date, the
<varname>Home</varname> object returns
a new instance of the <classname>java.util.Date</classname> object.
</para>
<para>
The <varname>insertDate</varname> component converts objects into strings by invoking
<function>toString()</function> on the object.
</para>
<para>
Now all the bits and pieces are working together.
</para>
<para>
Run the application, and use the View Source command to examine the HTML generated by
by framework.
</para>
<figure>
<title>HTML generated for Home page</title>
<programlisting><![CDATA[
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Simple</title>
</head>
<body>
This application demonstrates some dynamic behavior using Tapestry components.
<p>The current date and time is: <b>Fri Nov 23 17:05:53 PST 2001</b>
<p>Click <a href="/tutorial/simple...">here</a> to refresh.
</body>
</html>
]]></programlisting>
</figure>
<para>
This should look very familiar, in that it is mostly the same as the HTML template for the page.
Tapestry not only inserted simple text (the current date and time,
obtained from an <classname>java.util.Date</classname> object), but the
<varname>refresh</varname> component inserted the <sgmltag class="starttag">a</sgmltag> and
<sgmltag class="endtag">a</sgmltag> tags, and created an appropriate URL for the href attribute.
</para>
</chapter>
<chapter id="hangman">
<title>Hangman</title>
<para>
So far, these examples have been a little bit cut-and-dried. Lets do a meatier example that uses a
few more interesting components. Let's play Hangman!
</para>
<para>
Our Hangman application consists of four pages. The Home page allows a new game to be
started, which includes selecting the difficulty of the game (how many wrong guesses you are
allowed).
</para>
<figure>
<title>Hangman Home Page</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/hangman-home.jpg" format="JPEG"/>
</imageobject>
</mediaobject>
</figure>
<para>
The main page is the Guess page, where the partially filled out word is displayed, and the user can
make guesses (from a shrinking list of possible letters):
</para>
<figure>
<title>Hangman Guess Page</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/hangman-guess.jpg" format="JPEG"/>
</imageobject>
</mediaobject>
</figure>
<para>
After you give up, or when you make too many mistakes, you end up on the the Failed page:
</para>
<figure>
<title>Hangman Failed Page</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/hangman-lose.jpg" format="JPEG"/>
</imageobject>
</mediaobject>
</figure>
<para>
But, if you guess all the letters, you are sent to the Success page:
</para>
<figure>
<title>Hangman Success Page</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/hangman-win.jpg" format="JPEG"/>
</imageobject>
</mediaobject>
</figure>
<section id="hangman.visit">
<title>The Visit Object</title>
<para>
The center of this application is an object that represents game, an object of class
<classname>HangmanGame</classname>. This object is used to track the word being guessed,
the letters that have been
used, the number of misses and the letters that have been correctly guessed.
</para>
<para>
This object is a property of the <emphasis>visit</emphasis> object. What's the visit object?
The visit object is a holder of
all information about a single client's visit to your web application. It contains data and methods
that are needed by the pages and components of your application.
</para>
<para>
The visit object is owned and created by the engine object. It is serialized and de-serialized with
the engine.
</para>
<para>
The application specification includes a little extra segment at the bottom to specify the class of
the visit object.
</para>
<figure>
<title>Hangman.application</title>
<programlisting>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE application PUBLIC
"-//Howard Lewis Ship//Tapestry Specification 1.3//EN"
"http://tapestry.sf.net/dtd/Tapestry_1_3.dtd"&gt;
&lt;application name="Tapestry Hangman" engine-class="org.apache.tapestry.engine.&SimpleEngine;"&gt;
&lt;property name="org.apache.tapestry.visit-class"&gt;tutorial.hangman.Visit&lt;/property&gt; <co id="hangman.app.visit"/>
&lt;page name="Home" specification-path="/tutorial/hangman/Home.page"/&gt;
&lt;page name="Guess" specification-path="/tutorial/hangman/Guess.page"/&gt;
&lt;page name="Failed" specification-path="/tutorial/hangman/Failed.page"/&gt;
&lt;page name="Success" specification-path="/tutorial/hangman/Success.page"/&gt;
&lt;/application&gt;
</programlisting>
</figure>
<para>
<calloutlist>
<callout arearefs="hangman.app.visit">
<para>
This property specifies that the engine should instantiate an instance of
<classname>tutorial.hangman.Visit</classname> when a visit object is first required.
This is the default way in which the visit object is specified, though if
the visit object doesn't have an empty constructor method, the engine method
<function>createVisit()</function> must be implemented instead.
</para>
</callout>
</calloutlist>
</para>
<para>
So, returning from that distraction, the game object is a property of the visit object, which is
accessible from any page (via the page's visit property).
</para>
</section>
<section id="hangman.home-page">
<title>The Home Page</title>
<para>
The Home page's job is to collect the difficulty and initiate a game:
</para>
<figure>
<title>Home.java</title>
<programlisting>
public class Home extends &BasePage;
{
public static final int EASY = 10;
public static final int MEDIUM = 5;
public static final int HARD = 3;
private int misses;
private String error;
public void detach()
{
misses = 0;
error = null;
super.detach();
}
public int getMisses()
{
return misses;
}
public void setMisses(int value)
{
misses = value;
fireObservedChange("misses", value);
}
public String getError()
{
return error;
}
public void formSubmit(IRequestCycle cycle)
{
if (misses == 0)
{
error = "Please select a game difficulty.";
return;
}
Visit visit = (Visit) getVisit();
visit.start(misses);
cycle.setPage("Guess");
}
}
</programlisting>
</figure>
<para>
We're seeing all the familiar ideas: The
<varname>misses</varname> property is a persistent page property (which means
the page will "remember" the value previously selected by the user).
</para>
<para>
We use a common trick for simple pages: the page contains a single
&Form; component, so we use
the page itself as the form's listener, and have the page implement the
&IActionListener; interface.
</para>
<para>
This saves a bit of code for creating an inner class as the form listener.
</para>
<para>
Initially, we don't select a difficulty level, and the user can click "Play!" without selecting a value
from the list, so we check that.
</para>
<para>
Otherwise, we get the visit object and ask it to start a new game with the selected number of
misses. We then jump to the Guess page to start accepting guesses from the user.
</para>
<para>
The interesting part of the Home page HTML template is the form:
</para>
<figure>
<title>Home.html (excerpt)</title>
<programlisting><![CDATA[
<form jwcid="form">
<span jwcid="group">
<span jwcid="ifError">
<font size=+2 color=red><span jwcid="insertError"/></font>
</span>
<table>
<tr>
<td><input jwcid="inputEasy"/></td>
<td>Easy game; you are allowed ten misses.</td>
</tr>
<tr>
<td><input jwcid="inputMedium"/></td>
<td>Medium game; you are allowed five misses.</td>
</tr>
<tr>
<td><input jwcid="inputHard"/></td>
<td>Hard game; you are only allowed three misses.</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Play!"></td>
</tr>
</table>
</span>
</form>
]]></programlisting>
</figure>
<para>
Here, the interesting components are <varname>group</varname>,
<varname>inputEasy</varname>, <varname>inputMedium</varname> and <varname>inputHard</varname>.
<varname>group</varname> is
type &RadioGroup;, a wrapper that must go around the &Radio;
components (the other three). The
&RadioGroup; determines what property of the page is to be read and updated
(its bound to the
<varname>misses</varname> property). Each &Radio; button is associated with
a particular value to be assigned to the
property, when that radio button is selected by the user.
</para>
<para>
This comes together in the Home page specification:
</para>
<figure>
<title>Home.page (excerpt)</title>
<programlisting>
&lt;component id="group" type="&RadioGroup;"&gt;
&lt;binding name="selected" expression="misses"/&gt;
&lt;/component&gt;
&lt;component id="inputEasy" type="&Radio;"&gt;
&lt;field-binding name="value" field-name="tutorial.hangman.Home.EASY"/&gt; <co id="hangman.home.spec.field-binding"/>
&lt;/component&gt;
&lt;component id="inputMedium" type="&Radio;"&gt;
&lt;field-binding name="value" field-name="tutorial.hangman.Home.MEDIUM"/&gt;
&lt;/component&gt;
&lt;component id="inputHard" type="&Radio;"&gt;
&lt;field-binding name="value" field-name="tutorial.hangman.Home.HARD"/&gt;
&lt;/component&gt;
</programlisting>
</figure>
<para>
<calloutlist>
<callout arearefs="hangman.home.spec.field-binding">
<para>
A <sgmltag class="starttag">field-binding</sgmltag> is like a
<sgmltag class="starttag">static-binding</sgmltag>, except that
the static value is taken from a public static field of
some class. This makes it easy to coordinate behaviors between
the specification and the class.
</para>
<para>
This is a good thing, since if you decide to make a <varname>HARD</varname>
game only allow two mistakes, you can
make the change in exactly one place .. your Java code.
</para>
</callout>
</calloutlist>
</para>
<para>
So the end result is: when the user clicks the radio button for a Hard game, the static constant
<varname>HARD</varname> is assigned to the page's <varname>misses</varname> property.
</para>
</section>
<section id="hangman.guess-page">
<title>The Guess Page</title>
<para>
This is the page where uses make letter guesses. The page has four sections:
</para>
<itemizedlist>
<listitem>
<para>
A display of the word, with underscores replacing unguessed letters.
</para>
</listitem>
<listitem>
<para>
A status area, showing the number of bad guesses and an optional error message after an
invalid guess.
</para>
</listitem>
<listitem>
<para>
A list of letters that may be guessed. Letters disappear after they are used.
</para>
</listitem>
<listitem>
<para>
An option to give up and see the word, terminating the game.
</para>
</listitem>
</itemizedlist>
<para>
Let's start with the HTML template this time:
</para>
<figure>
<title>Guess.html (excerpt)</title>
<programlisting><![CDATA[
<h1>Make a Guess</h1>
<font size=+3>
<span jwcid="insertGuess"/>
</font>
<p>
You have made <span jwcid="insertMissed"/> bad guesses,
out of a maximum of <span jwcid="insertMaxMisses"/>.
<span jwcid="ifError">
<p>
<font size=+3 color=red><span jwcid="insertError"/></font>
</span>
<p>Guess:
<font size=+1>
<span jwcid="e">
<a jwcid="guess"><span jwcid="insertLetter"/></a>
</span>
</font>
<p><a jwcid="giveUp">Give up?</a>
]]></programlisting>
</figure>
<para>
Most of these components should be fairly obvious by now; let's focus on the components that
allow the user to guess a letter. This could have been implemented in a number of ways using
more radio buttons, a drop down list or a text field the user could type into. In this example, we
chose to simply create a series of links, one for each letter the user may still guess.
</para>
<para>
Let's look at the specification for those three components (<varname>e</varname>,
<varname>guess</varname> and <varname>insertLetter</varname>).
</para>
<figure>
<title>Guess.jwc (excerpt)</title>
<programlisting>
&lt;component id="e" type="&Foreach;"&gt;
&lt;binding name="source" expression="unused"/&gt;
&lt;/component&gt;
&lt;component id="guess" type="&DirectLink;"&gt;
&lt;binding name="listener" expression="listeners.makeGuess"/&gt;
&lt;binding name="parameters" expression="components.e.value"/&gt;
&lt;/component&gt;
&lt;component id="insertLetter" type="&Insert;"&gt;
&lt;binding name="value" expression="components.e.value"/&gt;
&lt;/component&gt;
</programlisting>
</figure>
<para>
Component <varname>e</varname> is simply a &Foreach;,
the <varname>source</varname> is the <varname>unused</varname> property of the page (we'll see in a
moment how the page gets this list of unused letters from the game object).
</para>
<para>
Component <varname>insertLetter</varname> inserts the current letter from the list of unused letters.
It gets this current letter directly from the <varname>e</varname> component.
On successive iterations, a &Foreach; component's
<varname>value</varname> property is the value for the iteration.
</para>
<para>
Component <varname>guess</varname> is type &DirectLink;, which creates a
hyperlink on the page and notifies its listener
when the user clicks the link. Just knowing that the component was clicked isn't very helpful
though; the application needs to know which letter was actually clicked.
</para>
<para>
Passing that kind of information along is accomplished by setting the
<varname>parameters</varname> parameter for the
component. The <varname>parameters</varname> parameter is an object, or
array or objects, that will be encoded into the
URL for the hyperlink. When the component's listener is notified, it
can obtain the array of objects from the &IRequestCycle;
<footnote>
<para>
Tapestry takes care of converting objects into strings when constructing the URL, then
converts those strings back into objects when the link is clicked. Your listener method
will be able to get <emphasis>copies</emphasis> of the original parameters.
</para>
</footnote>.
</para>
<para>
These <emphasis>service parameters</emphasis>
are often used to encode primary keys of objects, names of columns or other
information specific to the application.
</para>
<para>
In this case, the service parameters consist of a single value,
the letter to be guessed.
</para>
<para>
All of this comes together in the Java code for the Guess page.
</para>
<figure>
<title>Guess.java (excerpt)</title>
<programlisting>
public void makeGuess(&IRequestCycle; cycle)
{
Object[] parameters = cycle.getServiceParameters();
char letter = ((Character)parameters[0]).charValue();
HangmanGame game = getGame();
try
{
game.guess(letter);
}
catch (GameException ex)
{
error = ex.getMessage();
if (game.getFailed())
cycle.setPage("Failed");
return;
}
// A good guess.
if (game.getDone())
cycle.setPage("Success");
}
</programlisting>
</figure>
<para>
The component specification showed how data was encoded into the URL as the
service parameters; here we see how the
<function>makeGuess()</function> listener
method has access to the service parameters and uses them.
The listener method extracts the letter and informs the game object, which throws an exception
if the letter is not in the word being guessed.
</para>
<para>
The method <function>HangmanGame.getFailed()</function> returns
<literal>true</literal> when all the missed guesses are used up, at
which point we go to the <varname>Failed</varname> page to tell the user what the word was.
</para>
<para>
On the other hand, if an exception isn't thrown, then the guess was good.
<function>getDone()</function> returns <literal>true</literal>
if all letters have been guessed, in which go to the <varname>Success</varname> page.
</para>
<para>
If all letters weren't guessed, we stay on the <varname>Guess</varname> page,
which will display the word with the
guessed letter filled in, and with fewer options in the list of possible guesses.
</para>
</section>
<section id="hangman.limitations">
<title>Limitations</title>
<para>
This is a very, very simple implementation of the game. For example, it's easy to cheat; you can
give up, then use your browser's back button to return to the <varname>Guess</varname>
page and keep guessing (with accuracy, if your memory is any good).
</para>
</section>
</chapter>
<chapter id="reuse">
<title>Creating Reusable Components</title>
<para>
In this tutorial, we'll show how to create a reusable component. One common use of components
it to create a common "border" for the application that includes basic navigation. We'll be
creating a simple, three page application with a navigation bar down the left side.
</para>
<figure>
<title>Border Home Page</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/border-home.jpg" format="JPEG"/>
</imageobject>
</mediaobject>
</figure>
<para>
Navigating to another page results in a similar display:
</para>
<figure>
<title>Border Credo Page</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/border-credo.jpg" format="JPEG"/>
</imageobject>
</mediaobject>
</figure>
<para>
Each page's content is confined to the silver area in the center. Note that the border adapts itself
to each page: the title "Home" or "Credo" is specific to the page, and the current page doesn't
have an active link (in the above page, "Credo" is the current page, so only "Home" and "Legal"
are usable as navigation links).
</para>
<para>
The "i" in the gear is the Show Inspector link. It will be described in the next chapter.
</para>
<para>
Because this tutorial is somewhat large, we'll only be showing excerpts from some of the files.
The complete source of the tutorial examples is available seperately, in the
<classname>tutorial.border</classname> package.
</para>
<para>
Each of the three pages has a similar HTML template:
</para>
<figure>
<title>Home.html</title>
<programlisting>
&lt;span jwcid="border"&gt;
Nothing much doing here on the &lt;b&gt;home&lt;/b&gt; page. Visit one of our other
fine
pages.
&lt;/span&gt;
</programlisting>
</figure>
<para>
Remember that Tapestry components can wrap around other HTML elements or components.
For the border, we have an HTML template where everything on the page is wrapped by the
<varname>border</varname> component.
</para>
<para>
Note that we don't specify any
<sgmltag class="starttag">html</sgmltag> or
<sgmltag class="starttag">body</sgmltag> tags; those are provided by the <classname>Border</classname>
component (as well as the matching close tags).
</para>
<para>
This illustrates a key concept within Tapestry: embedding vs. wrapping. The
<classname>Home</classname> page embeds
the <varname>border</varname> component (as we'll see in the <classname>Home</classname> page's specification).
This means that the <classname>Home</classname>
page is implemented using the <varname>border</varname> component.
</para>
<para>
However, the <varname>border</varname> component wraps the content of the <classname>Home</classname> page,
the <classname>Home</classname> page
HTML template indicates the <emphasis>order</emphasis> in which components (and static HTML elements) are
renderred. On the <classname>Home</classname> page, the <varname>border</varname> component 'bats' first and cleanup.
</para>
<para>
The construction of the <classname>Border</classname> component is driven by how it differs from page to page.
You'll
see that on each page, the title (in the upper left corner) changes. The names of all three pages are
displayed, but only two of the three will have links (the third, the current page, is just text).
Lastly, each page contains the specific content from its own HTML template.
</para>
<figure>
<title>Border.html</title>
<programlisting>
&lt;span jwcid="shell"&gt; <co id="reuse.border.shell"/>
&lt;body jwcid="body"&gt; <co id="reuse.border.body"/>
&lt;table border=0 bgcolor=gray cellspacing=0 cellpadding=4&gt;
&lt;tr valign=top&gt;
&lt;td colspan=3 align=left&gt;
&lt;font size=5 color="White"&gt;&lt;jwc id="insertPageTitle"/&gt;&lt;/font&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign=top&gt;
&lt;td align=right&gt;
&lt;font color=white&gt;
&lt;span jwcid="e"&gt; <co id="reuse.border.e"/>
&lt;br&gt;&lt;a jwcid="link"&gt;&lt;span jwcid="insertName"/&gt;&lt;/a&gt; <co id="reuse.border.insertName"/>
&lt;/span&gt;
&lt;/font&gt;
&lt;/td&gt;
&lt;td rowspan=2 valign=top bgcolor=silver&gt;
&lt;span jwcid="renderBody"/&gt; <co id="reuse.border.renderBody"/>
&lt;/td&gt;
&lt;td rowspan=2 width=4&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span jwcid="inspector"/&gt;&lt;/td&gt; <co id="reuse.border.showInspector"/>
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan=3 height=4&gt;&nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/body&gt;
&lt;/span&gt;
</programlisting>
</figure>
<para>
<calloutlist>
<callout arearefs="reuse.border.shell">
<para>
The <varname>shell</varname> component provides the <sgmltag class="starttag">html</sgmltag> and
<sgmltag class="starttag">head</sgmltag> elements of the response HTML.
</para>
</callout>
<callout arearefs="reuse.border.body">
<para>
The <varname>body</varname> components provides the <sgmltag class="starttag">body</sgmltag> element.
It also provides support for JavaScript related to &Rollover; buttons, such as
the <varname>showInspector</varname> component.
</para>
</callout>
<callout arearefs="reuse.border.e">
<para>
The <varname>e</varname> component is a &Foreach; configured to work through
a list of page names (provided by the engine).
</para>
</callout>
<callout arearefs="reuse.border.insertName">
<para>
The <varname>link</varname> and <varname>insertName</varname>
components provide the inter-page navigation links.
</para>
</callout>
<callout arearefs="reuse.border.renderBody">
<para>
The <varname>renderBody</varname> component provides the actual content for the page. The
<classname>Border</classname> component is used on all three pages, but its a different
instance on each page, wrapping around different content specific to the page.
</para>
</callout>
<callout arearefs="reuse.border.showInspector">
<para>
The <varname>showInspector</varname> component provides the button below the
page names (the italicized "i" in a
circle) and will be explained shortly.
</para>
</callout>
</calloutlist>
</para>
<para>
The <classname>Border</classname> component is designed to be usable in other Tapestry
applications, so it doesn't hard
code the list of page names. These must be provided to the component as a parameter.
In fact, the
application engine provides the list.
</para>
<figure>
<title>Border.jwc </title>
<programlisting>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE component-specification PUBLIC
"-//Howard Lewis Ship//Tapestry Specification 1.3//EN"
"http://tapestry.sf.net/dtd/Tapestry_1_3.dtd"&gt;
&lt;component-specification class="tutorial.border.Border" allow-informal-parameters="no"&gt;
&lt;parameter name="title" java-type="java.lang.String" required="yes"/&gt; <co id="reuse.border.spec.title"/>
&lt;parameter name="pages" required="yes"/&gt; <co id="reuse.border.spec.pages"/>
&lt;component id="shell" type="&Shell;"&gt;
&lt;binding name="title" expression="page.engine.specification.name"/&gt; <co id="reuse.border.spec.shell-title"/>
&lt;/component&gt;
&lt;component id="insertPageTitle" type="&Insert;"&gt;
&lt;inherited-binding name="value" parameter-name="title"/&gt; <co id="reuse.border.spec.insertPageTitle"/>
&lt;/component&gt;
&lt;component id="body" type="&Body;"/&gt;
&lt;component id="e" type="&Foreach;"&gt; <co id="reuse.border.spec.e"/>
&lt;inherited-binding name="source" parameter-name="pages"/&gt;
&lt;binding name="value" expression="pageName"/&gt;
&lt;/component&gt;
&lt;component id="link" type="&PageLink;"&gt; <co id="reuse.border.spec.link"/>
&lt;binding name="page" expression="pageName"/&gt;
&lt;binding name="disabled" expression="disablePageLink"/&gt;
&lt;/component&gt;
&lt;component id="insertName" type="&Insert;"&gt;
&lt;binding name="value" expression="pageName"/&gt;
&lt;/component&gt;
&lt;component id="renderBody" type="&RenderBody;"/&gt;
&lt;component id="inspector" type="&InspectorButton;"/&gt; <co id="resuse.border.spec.InspectorButton"/>
&lt;/component-specification&gt;
</programlisting>
</figure>
<para>
<calloutlist>
<callout arearefs="reuse.border.spec.title">
<para>
Declares a required parameter for the border, the title that will appear on the
page.
</para>
</callout>
<callout arearefs="reuse.border.spec.pages">
<para>
Declares a parameter to specify the list of page names. We don't specify a
particular type because its pretty unbounded; the framework will accept
&List;, &Iterator; or a Java array.
</para>
</callout>
<callout arearefs="reuse.border.spec.shell-title">
<para>
We then provide the <varname>shell</varname> component with its <varname>title</varname> parameter;
this will be the window title. We
use the application's name, with is extracted from the application's specification.
</para>
</callout>
<callout arearefs="reuse.border.spec.insertPageTitle">
<para>
The <sgmltag class="starttag">inherited-binding</sgmltag> element allows a component to
share its parameters. Here the <classname>Border</classname>'s <varname>title</varname>
is used as the <varname>value</varname> parameter of the <varname>insertPageTitle</varname>
component (an &Insert;).
Using these
inherited bindings simplifies the process of creating complex components from simple ones.
</para>
</callout>
<callout arearefs="reuse.border.spec.e">
<para>
Likewise, the <varname>e</varname> component (a &Foreach;)
needs as its source the list of pages, which it inherits from
the <classname>Border</classname> component's <varname>pages</varname> parameter.
It has been configured to store each succesive page
name into the <varname>pageName</varname> property of the <classname>Border</classname> component;
this is necessary so that the <classname>Border</classname>
component can determine which page link to disable (it disables the current page since we're
already there).
</para>
</callout>
<callout arearefs="reuse.border.spec.link">
<para>
The <varname>link</varname> component creates the link to the other pages. It has a
<varname>disabled</varname> parameter; which,
when true, causes the link component to not create the hyperlink (though it still allows the
elements it wraps to render). The Java class for the <classname>Border</classname> component,
<classname>tutorial.border.Border</classname>, provides a method, <function>getDisablePageLink()</function>,
that returns true
when the <varname>pageName</varname> instance variable (set by the <varname>e</varname> component)
matches the current page's name.
</para>
</callout>
<callout arearefs="resuse.border.spec.InspectorButton">
<para>
This component will raise the Tapestry Inspector in a new window when clicked.
</para>
</callout>
</calloutlist>
</para>
<para>
So, the specification for the <classname>Border</classname> component must identify the
parameters it needs, but also the
components it uses and how they are configured.
</para>
<figure>
<title>Show Inspector Button</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/show-inspector.gif" format="GIF"/>
</imageobject>
</mediaobject>
</figure>
<para>
Clicking on the button raises a second window that describes the current page in the application
(this is used when debugging a Tapestry applicaton). The Inspector is described in the next
chapter.
</para>
<para>
The final mystery is the <varname>wrapped</varname> component. It is used to render the elements wrapped by the
<classname>Border</classname> on the page containing the <classname>Border</classname>.
Those elements will vary from page to page; running
the application shows that they are different on the home, credo and legal pages (different text
appears in the central light-grey box). There is no limitation on the elements either: Tapestry is
specifically designed to allow components to wrap other components in this way, without any
arbitrary limitations.
</para>
<para>
This means that the different pages could contain forms, images or any set of components at all,
not just static HTML text.
</para>
<para>
The specification for the home page shows how the title and pages parameters are set. The title is
static, the literal value "Home" (this isn't the best approach if localization is a concern).
</para>
<figure>
<title>Home page specification</title>
<programlisting>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE page-specification PUBLIC
"-//Howard Lewis Ship//Tapestry Specification 1.3//EN"
"http://tapestry.sf.net/dtd/Tapestry_1_3.dtd"&gt;
&lt;page-specification class="org.apache.tapestry.html.BasePage"&gt;
&lt;component id="border" type="Border"&gt;
&lt;static-binding name="title"&gt;Home&lt;/static-binding&gt;
&lt;binding name="pages" expression="engine.pageNames"/&gt;
&lt;/component&gt;
&lt;/page-specification&gt;
</programlisting>
</figure>
<para>
The <varname>pages</varname> property is retrieved from the application engine,
which implements a <varname>pageNames</varname> JavaBeans property:
</para>
<figure>
<title>BorderEngine.java (excerpt)</title>
<programlisting><![CDATA[
private static final String[] pageNames =
{ "Home", "Credo", "Legal" };
public String[] getPageNames()
{
return pageNames;
}
]]></programlisting>
</figure>
<para>
How did Tapestry know that the type 'Border'
corresponded to the specification <filename>/tutorial/border/Border.jwc</filename>? Only because we defined
an alias in the application specification:
</para>
<figure>
<title>Border.application (excerpt)</title>
<programlisting>
&lt;component-alias type="Border" specification-path="/tutorial/border/Border.jwc"/&gt;
</programlisting>
</figure>
<para>
Had we failed to do this, we would have had to specify the complete resource path,
<filename>/tutorial/border/Border.jwc</filename>, on each page's specification, instead of the short alias
'Border'. There is no magic about the existing Tapestry component types
(&Insert;, &Foreach;, &PageLink;, etc. ... they each have an
alias pre-registered into every application specification. These short
aliases are simply a convienience.
</para>
</chapter>
<chapter id="inspector">
<title>The Tapestry Inspector</title>
<para>
Unlike scripting systems (such as JavaServer Pages and the like), Tapestry applications are gifted
with a huge amount of information about how they are implemented. The same component
object model that allows Tapestry to perform so many ordinary functions can be leveraged to
provide some unusual functionality.
</para>
<para>
Run the Border tutorial from the previous chapter and click on the show inspector button (the
gear icon in the lower right corner). A new window will launch, containing the Inspector:
</para>
<figure>
<title>Tapestry Inspector</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/Inspector-Spec.jpg" format="JPEG"/>
</imageobject>
</mediaobject>
</figure>
<para>
The Inspector displays live information from the running application; in fact, it is simply another
part of the application (the drop-down list of pages will include the Inspector page itself). The
Inspector is most often used to debug HTML generation by viewing the HTML templates.
It is also very useful in debugging problems where the wrong data is displayed, since it
allows the developer to navigate to the particular components and see directly what properties
are used.
</para>
<section id="inspector.navigation">
<title>Navigation</title>
<para>
The inspector allows the user to navigate to any page and any component on a page.
The drop down list in the upper left corner lists all pages in the application; changing
the selection immediately updates the Inspector.
</para>
<para>Next to the drop down list is the component path; a list of nested component ids, starting
with "page" to represent the page. Clicking on any id in the path changes the information displayed
below.
</para>
<para>
Underneath the component navigation tools are a set of tab buttons for the different
inspector views.
</para>
</section>
<section id="inspector.specification">
<title>Specification View</title>
<figure>
<title>Specification View</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/Inspector-Spec.jpg" format="JPEG"/>
</imageobject>
</mediaobject>
</figure>
<para>
The specification view shows several sets of information about the selected component.
</para>
<para>
First shown are basic properties, such as the specification path and Java class.
</para>
<para>
Each formal parameter is displayed. Unbound parameters will show no value in
the Binding column.
</para>
<para>
Beneath formal parameters are informal parameters (the <classname>Border</classname>
component has none, so there is nothing to see). Informal parameters are
usually mapped directly to HTML attributes. They are most often used with
components that generate a single HTML tag, such as the &ActionLink;,
&DirectLink; or &TextField; components.
</para>
<para>
If the component contains assets, they are shown next.
</para>
<para>
Any helper beans for the component are displayed last.
</para>
<para>
On the right side is a list of each embedded component and its type. Clicking
the component id will navigate to the selected component.
</para>
</section>
<section id="inspector.template">
<title>Template View</title>
<figure>
<title>Template View</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/Inspector-Template.jpg" format="JPEG"/>
</imageobject>
</mediaobject>
</figure>
<para>
The template view shows the HTML template for the component. It shows dynamic tags in bold,
and makes the component id a clickable link (which navigates to the component, but maintains
the Template View). This allows the developer to quickly drill down through the components.
</para>
</section>
<section id="inspector.properties">
<title>Properties View</title>
<figure>
<title>Properties View</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/Inspector-Properties.jpg" format="JPEG"/>
</imageobject>
</mediaobject>
</figure>
<para>
The properties view shows persistant properties stored by the page (or any components on
the page). Most pages do not store any persistent state (it is more often stored
in the application's visit object).
</para>
</section>
<section id="inspector.engine">
<title>Engine View</title>
<figure>
<title>Engine View</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/Inspector-Engine.jpg" format="JPEG"/>
</imageobject>
</mediaobject>
</figure>
<para>
The engine view shows information about the running application engine, as well as some details
from the application specification.
</para>
<para>
Under Operations are two buttons: the first restarts the application. The
second (when enabled
<footnote>
<para>
By default, the reset service (used by the reset button) is disabled.
To enable it, set the JVM system property
<varname>org.apache.tapestry.enable-reset-service</varname> to true.
The service is disabled since it is too tempting a target for a denial
of service attack.
</para>
</footnote>) resets the application, which forces a reload of all component specifications
and HTML templates. This is useful during development, since it allows for incremental development
without stopping and restarting the servlet container.
</para>
<para>
Below the operations is a binary dump of the application engine. This is useful when
developing to see how large the serialized state is, and perhaps gleam how it might be trimmed.
</para>
<para>
Further below (and not visible in the screen shot above), is a dump of the request context. This
is that vast amount of data also displayed when an unexpected exception is thrown.
</para>
</section>
<section id="inspector.logging">
<title>Logging View</title>
<figure>
<title>Logging View (Level Selection)</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/Inspector-Logging.jpg" format="JPEG"/>
</imageobject>
</mediaobject>
</figure>
<para>
The Logging view allows dynamic integration with the
<ulink url="http://jakarta.apache.org/log4j">Log4J</ulink> logging framework. The top half
of the page allows the logging level of any category to be
dynamically set. This is useful when debugging, since logging output for specific
classes
<footnote>
<para>
By convention, logging categories match the complete class name
of the corresponding class. All Tapestry logging categories
conform to this convention.
</para>
</footnote>
can be individually enabled or disable.
</para>
<para>
The right side is a small second form, allowing new categories to be created.
This can be useful to make broad changes in logging levels. For instance, creating
a category "org.apache.tapestry" would allow the logging level of all Tapestry classes to be
set in a single place.
</para>
</section>
</chapter>
<chapter id="workbench">
<title>Tapestry Workbench</title>
<para>
The Tapestry tutorial includes an additional application, the Workbench, which is used to show off
interesting Tapestry components and features.
</para>
<figure>
<title>Workbench</title>
<mediaobject>
<imageobject>
<imagedata format="JPEG" fileref="images/workbench-home.jpg"/>
</imageobject>
</mediaobject>
</figure>
<para>
The Workbench is divided into several areas, as shown by the tabs across the top of the page.
Over time, the Workbench and this tutorial document will expand together, and the number of tabs will
increase.
</para>
<para>
In addition to the Inspector, the Workbench has a useful feature which can be activated using the checkbox at the bottom.
When enabled, the complete (and verbose) information available about the request, session and context (normally
displayed by the Inspector's engine view) is shown at the bottom of each page.
</para>
<figure>
<title>Workbench (Showing Requests)</title>
<mediaobject>
<imageobject>
<imagedata format="JPEG" fileref="images/workbench-home-expanded.jpg"/>
</imageobject>
</mediaobject>
</figure>
<para>
This feature can be very useful if you are interested in exactly how Tapestry forms and links work.
</para>
</chapter>
<chapter id="local">
<title>Localization</title>
<para>
One of the most powerful and useful features of the Tapestry framework is the way in which it
assists with localization of a web application. This is normally an ugly area in web applications,
with tremendous amounts of ad-hoc coding necessary.
</para>
<para>
Because Tapestry does such a strong job of seperating the presentation of a component (its
HTML template) from its control logic (its specification and Java class) it becomes easy for it to
perform localization automatically. It's as simple as providing additional localized HTML
templates for the component, and letting the framework select the proper one.
</para>
<para>
However, the static text of an application, provided by the HTML templates, is not all.
</para>
<para>
Applications also have assets (images, stylesheets and the like) that must also be localized: that
fancy button labeled "Search" is fine for your English clients, but your
French clients will require
a similar button labeled "Recherche".
</para>
<para>
Again, the framework assists, because it can look for localized versions of the assets as it runs.
</para>
<para>
The locale application demostrates this. It is a very simply application that demonstrates changing
the locale of a running application
<footnote>
<para>
All the translations were performed using
<ulink url="http://world.altavista.com/">Babelfish</ulink>, and are probably quite laughable to
someone who actually speaks the alternate
languages.
</para>
</footnote>
</para>
<para>
A demonstration of localization is built into the Workbench, under the <acronym>L10N</acronym>
<footnote>
<para>
The "10" refers to the number of letters between 'l' and 'n' in the word 'localization'
</para>
</footnote> tab.
The page allows the user to select a new language for the application:
</para>
<figure>
<title>L10N Page (English)</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/localize-home-english.jpg" format="JPEG"/>
</imageobject>
</mediaobject>
</figure>
<para>
Selecting "German" from the list and clicking the "Change" button brings you to a new page that
acknowledges your selection:
</para>
<figure>
<title>Locale Changed (German)</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/localize-changed-german.jpg" format="JPEG"/>
</imageobject>
</mediaobject>
</figure>
<para>
Clicking the button (it's labeled "Return" in German) returns you to the L10N page to
select a new language:
</para>
<figure>
<title>L10N Page (German)</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/localize-home-german.jpg" format="JPEG"/>
</imageobject>
</mediaobject>
</figure>
<para>
The neat thing here is that the <classname>L10N</classname> page has been localized into
German as well; it shows
equivalent German text, the options in the popup list are in German, and the "Change" button
has been replaced with a German equivalent.
</para>
<section id="locale.template">
<title>Localization of HTML Templates</title>
<para>
Localization of HTML templates ia automatic. When Tapestry reads a template, it looks for a localized version of it.
In this example, in addition to the English language <filename>Localization.html</filename>, three additional files were created:
<filename>Localization_de.html</filename>, <filename>Localization_fr.html</filename> and <filename>Localization_it.html</filename>.
</para>
<para>
Tapestry tracks the locale for each user using either an HTTP Cookie, or the &HttpSession;. It makes sure that all templates for all components
on the page use the best available template; it does a standard search.
</para>
</section>
<section id="locale.assets">
<title>Localization of Assets</title>
<para>
In the L10N pages, there are images that are also localized.
Tapestry has a hand in this as well. As with HTML templates, Tapestry
searches for matches based on the user's locale.
</para>
<para>
Both context assets (assets that are part of the WAR) and private assets (assets that are stored in Java frameworks) can be localized. This is demonstrated
on the L10N page: the "Change" button is a private asset; the "Back" button is a context asset.
</para>
</section>
<section id="locale.other-options">
<title>Other Options for Localization</title>
<para>
In some cases, different localizations of the a component will be very similar, perhaps having only
one or two small snippets of text that is different.
In those cases, it may be easier on the developer to not localize the HTML template, but to
replace the variant text with an
&Insert; component.
</para>
<para>
The page can read a localized strings file (a <filename>.properties</filename> file) to get
appropriate localized text. This
saves the bother of maintaining multiple HTML templates. This is the same approach taken
by the Apache Struts framework.
</para>
<para>
All components on a page share the single locale for the page, but each performs its own search
for its HTML template. This means that some components may not have to be localized, if they
never contain any static HTML text. This is sometimes the case for reusable components, even
navigational borders.
</para>
</section>
</chapter>
<chapter id="summary">
<title>Further Study</title>
<para>
The preceding chapters cover many of the basic aspects of Tapestry. You should be comfortable
with basic Tapestry concepts:
</para>
<itemizedlist>
<listitem>
<para>Seperation of presentation, business and control logic
</para>
</listitem>
<listitem>
<para>Use of JavaBeans properties as the source of dynamic data
</para>
</listitem>
<listitem>
<para>How bindings access JavaBeans properties to provide data to components
</para>
</listitem>
<listitem>
<para>How components wrap each other, allowing for the creation of very complicated
components through aggregation
</para>
</listitem>
<listitem>
<para>
Different types of page properties (transient, dynamic, persistent)
</para>
</listitem>
</itemizedlist>
<para>
Tapestry is capable of quite a bit more. Also available within the Tapestry Examples package
(along with the tutorial code and this document) is the Virtual Library application (Vlib).
</para>
<para>
Vlib is a full-blown J2EE application, that makes use of Tapestry as its front end, and a set of
session and entity Enterprise JavaBeans as its back end.
</para>
<para>
Vlib also demonstrates some of the other aspects of developing a Tapestry application. It shows
how to create pages that are bookmarkable (meaning that their URL includes enough information
to reconstruct them in a subsequent session). It shows how to handle logging in to an application,
and how to protect pages from being accessed until the user is logged in. It has many specialized
reusable components for creating links to pages about books and people.
</para>
</chapter>
</book>