blob: 611f2bfab660b8925de080214410e0695509c8f0 [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="components">
<title>Tapestry Components</title>
<para>
Tapestry components are "black boxes" that are involved with both rendering
HTML responses and responding to HTTP requests.
</para>
<para>
A Tapestry component is defined by its specification.
The specification is an XML file that defines the type of the component, it parameters,
the template used by the component, any components embedded within it and
how they are 'wired up', and (less often) any assets used by the component.
</para>
<para>
At runtime, the specification is used to identify and instantiate a class
for the component. When the page containing the component is rendered, the
component will access its HTML template to find the static HTML and embedded
components it will render.
</para>
<section id="components.params">
<title>Parameters and Bindings</title>
<para>
Tapestry components are designed to work with each other,
within the context of a page and application. The process of rendering a page
is largely about pulling information from a source into a component
and doing something with it.
</para>
<para>
For example, on a welcome page, a component might get the <varname>userName</varname> property from
the <varname>visit</varname> object and insert it into the HTML response.
</para>
<para>
Each component has a specific set of parameters.
Parameters have a name, a type and may be required or optional.
</para>
<para>
To developers experienced with Java GUIs, it may appear that Tapestry
component parameters are the same as JavaBeans properties. This is not completely true.
JavaBeans properties are set-and-forget; the designer sets a value
for the property using a visual editor and the value
is saved with the bean until it is used at runtime.
</para>
<para>
Parameters define the type of value needed, but not the actual value.
This value is provided by a special object called a binding.
The binding is a bridge between the component and the parameter value,
exposing that value to the component as it is needed. The reason for all this
is to allow pages, and the components within them, to be shared by many
concurrent sessions ... a major facet in Tapestry's strategy
for maintaining application scalability.
</para>
<para>
When a component needs the value of one of its parameters, it must
obtain the correct binding, an instance of interface &IBinding;,
and invoke methods on the binding to get the value from the binding.
Additional methods are used with output parameters to update the binding property.
</para>
<para>
In most cases, discussed in the <link linkend="components.connected-params">next section</link>,
Tapestry can hide the bindings from the developer. In effect, it automates the process
of obtaining the binding, obtaining the value from it, and assigning it to a JavaBean
property of the component.
</para>
<para>
There are two types of bindings: static and dynamic.
Static bindings are read-only; the value for the binding is
specified in the component specification.
</para>
<para>
Dynamic bindings are more prevalent and useful. A dynamic
binding uses a JavaBeans property name to retrieve the value when needed
by the component. The source of this data is a property of some component.
</para>
<para>
In fact, dynamic bindings use
<link linkend="beans.property-path">property paths</link>, allowing
a binding to
'crawl' deeply through an object graph to access the value it needs.
This frees the components from relying totally on the properties of their container,
instead they are free to access properties of more distant objects.
</para>
</section>
<section id="components.connected-params">
<title>Connected Parameters</title>
<para>
In most cases, a developer is not interested in bindings; an easier model for developers is one
in which Tapestry uses the parameters and bindings to set properties of the component automatically.
Starting in release 2.1, Tapestry includes this behavior, with some constraints and limitations.
</para>
<para>
Part of the &spec.parameter; specification for a parameter
is the <emphasis>direction</emphasis>, which can be one of the following values:
</para>
<variablelist>
<varlistentry>
<term>in</term>
<listitem>
<para>
Input parameter; the value is drawn from the binding (if bound)
and applied to the corresponding component property just before rendering the component.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>form</term>
<listitem>
<para>
A parameter which matches the semantics of a form component.
The parameter is treated like an <literal>in</literal>
parameter when the page is rendering.
</para>
<para>
When the form containing the component is submitted,
the connected property is read (after the component renders),
and the value applied to the parameter.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>custom</term>
<listitem>
<para>
Tapestry does not try to connect the parameter with any property; the component
is responsible for accessing the binding and retrieving or setting values.
</para>
<para>
This type must be used for any kind of output parameter, or for an input parameter
where the property may be accessed other than during the rendering of the component.
</para>
</listitem>
</varlistentry>
</variablelist>
<note>
<title>Why aren't output parameters connectable?</title>
<para>
The problem is the timing of output parameters. Sometimes a parameter is only an output
parameter when the containing form is submitted (for example, any of the form related components).
Sometimes a parameter is output many times (for example, &Foreach;) while the component
renders.
</para>
<para>
The latter case may always be handled as custom; the former case may be handled
in the future.
</para>
</note>
<para>
Defining a parameter as direction <literal>in</literal> causes Tapestry to connect the parameter to the corresponding
property of the component. The parameter specification must identify the Java type of the property.
Properties must be read/write (they must have both
getter and setter methods).
</para>
<para>
Tapestry will set properties from parameters just before rendering the component. After the component renders,
the parameters are
cleared; they are returned to inital values. Tapestry reads these initial values just before it sets the
properties the first time. This makes it very easy to set defaults for optional parameters: just provide a default value
for the correspoinding instance variable.
</para>
<para>
If the property is connected to an invariant binding (a static or field
binding), then the property is set just once, and never cleared.
</para>
<para>
There are times when the parameter name can't be used as the property name. For example,
the &PageLink; component has a <literal>page</literal> parameter, the name of the page to link to.
However, all components already have a <literal>page</literal>
property, the &IPage; that ultimately contains them. The specification for the &PageLink; component
connects the <literal>page</literal> parameter to a property named <literal>targetPage</literal> instead.
</para>
<para>
Defining a connected parameter as required means that the parameter must be bound
<emphasis>and</emphasis> the binding must provide a non-null value. A runtime exception
is thrown when a required parameter's binding yields a null value.
</para>
<para>
The following examples show how to declare and use a parameter:
</para>
<example>
<title>Connected Parameter - Specification</title>
<programlisting><![CDATA[
<specification ...>
<parameter name="color" direction="in" java-type="java.awt.Color"/>
...
]]>
</programlisting>
</example>
<example>
<title>Connected Parameter - Java Code</title>
<programlisting>
public class ColorComponent extends &AbstractComponent;
{
private Color color = Color.RED;
public Color getColor()
{
return color;
}
public void setColor(Color color)
{
this.color = color;
}
protected void renderComponent(&IMarkupWriter; writer, &IRequestCycle; cycle)
throws RequestCycleException
{
writer.begin("font");
writer.attribute("color", ^RequestContext;.encodeColor(color);
renderWrapped(writer, cycle);
writer.end();
}
}
</programlisting>
</example>
<para>
In this example, the component writes its content inside a <sgmltag class="starttag">font</sgmltag>
element, with the HTML color attribute set from the <literal>color</literal> parameter. &RequestContext;
includes a static convienience method for converting from a
<classname>Color</classname> object to an encoded color
that will be meaningful to a web browser.
</para>
<para>
The parameter is optional and defaults to red if not specified (that is, if the parameter
is not bound).
</para>
<para>
At runtime, Tapestry will invoke <function>setColor()</function> first (if the <literal>color</literal>
parameter is bound). It will then invoke <function>renderComponent()</function>. Finally (even if
<function>renderComponent()</function> throws an exception) it will invoke <function>setColor()</function>
again, to restore it back to the default value, <literal>Color.RED</literal>.
</para>
<para>
This code includes a defect: because the parameter is optional, there is nothing to prevent
it from being bound to null.
</para>
</section>
<section id="components.informal-parameters">
<title>Formal vs. Informal Parameters</title>
<para>
Tapestry components have two types of parameters: formal and informal.
</para>
<para>
Formal parameters are parameters defined in the
<link linkend="spec.parameter">component specification</link>.
Each formal parameter has a specific
(case sensitive) name and may be required or optional.
</para>
<para>
In many cases, there is a one-to-one mapping between a Tapestry component and a specific
HTML tag. For example, &Body; and <sgmltag class="starttag">body</sgmltag>,
&Form; and <sgmltag class="starttag">form</sgmltag>, etc. In other cases, a Tapestry component
produces a known single HTML tag. For example, &ActionLink;, &DirectLink;, &PageLink; and &ServiceLink;
all produce an <sgmltag class="starttag">a</sgmltag> tag.
</para>
<para>
To support truly rich interfaces, it is often necessary to specify additional attributes
of the HTML tags; usually this means setting the <varname>class</varname> of a tag so as to
get visual properties from a stylesheet. In other cases, display attributes may be specified inline
(this is often the case with attributes related to display width and height, since
CSS support for these properties are inconsistent between the major HTML 4.0 browsers).
</para>
<para>
In theory, these components <emphasis>could</emphasis> define additional formal parameters for
each possible HTML attribute ... but there are a huge number of possible attributes, many of
which are specific to a particular browser.
</para>
<para>
Instead, Tapestry has the concept of an <emphasis>informal parameter</emphasis>.
This is an "additional" parameter, not specified
in the component's specification. In most cases, where informal parameters are allowed, they
are added as additional HTML attributes (there are a few special exceptions, such as the &Script; component).
</para>
<para>
Informal parameters do have some limitations. Informal parameters that conflict with the names of
any formal parameters, or with any of the HTML attributes generated directly by the component, are silently
ommitted. The comparison is case-insensitve. Thus, for a &DirectLink; component, you can not change
the <varname>href</varname> attribute, even if you supply a <varname>Href</varname> (or other variation) informal parameter.
</para>
<para>
Not all Tapestry components even allow informal parameters; this is explicitly
stated in the <link linkend="spec.component-specification">component specification</link>.
</para>
<note>
<title>Informal Parameters that are Assets</title>
<para>
Tapestry includes a special case when an informal parameter is actually an
<link linkend="components.assets">asset</link>. The URL for the asset is determined and that is the value
supplied for the attribute.
</para>
</note>
</section>
<section id="components.embedded">
<title>Embedded Components</title>
<para>
Under Tapestry, it is common to define new components
by combining existing components. The existing components are
embedded in the containing component. This is always true at the top level; Pages,
which are still Tapestry components, always embed other Tapestry components.
</para>
<para>
Each embedded component has an <varname>id</varname> (an identifying string)
that must be unique within the containing component.
Every non-page component is embedded inside some other
component forming a hierarchy that can get quite deep
(in real Tapestry applications, some pages have components nested three to five levels deep).
</para>
<para>
In some cases, a component will be referenced by its id path.
This is a series of component ids separated by periods, representing a path from
the page to a specific component. The same notation as a property path
is used, but the information being represented is quite different.
</para>
<para>
For example, the id path <literal>border.navbar.homeLink</literal>
represents the component named <varname>homeLink</varname>,
embedded inside a component named <varname>navbar</varname>,
embedded inside a component named <varname>border</varname>, embedded inside some page.
</para>
<para>
Tapestry components are "black boxes". They have a set of parameters that may
be bound, but their internals, how they are implemented, are not revealed.
</para>
<para>
Primitive components may not embed other components, or even
have a template. Nearly all the built-in components are primitive; they are
building blocks for constructing more complex components.
</para>
<para>
Alternately, a component may be implemented using a template and embedded components.
In either case, the names, types or very existence of embedded components is private,
hidden inside the containing component's "black box".
</para>
</section>
<section id="components.html-template">
<title>HTML Templates</title>
<para>Nearly all Tapestry components combine static HTML
<footnote>
<para>
The current releases of Tapestry is specifically oriented around HTML. Some support for
non-HTML languages, such as XML, XHTML or WML is already present
and will be expanded in the future.
</para>
</footnote>
from a template with additional dynamic content
(some few components are just dynamic content).
Often, a Tapestry component embeds other Tapestry components.
These inner components are referenced in the containing component's template.
</para>
<para>
One of the features of Tapestry is <emphasis>invisible instrumentation</emphasis>. In most
web application frameworks, converting a static HTML page into a usable template is a destructive process:
the addition of new tags, directives or even Java code to the template means that it will no
longer preview properly in a <acronym>WYSIWYG</acronym> editor.
</para>
<para>
Tapestry templates are instrumented using a new HTML attribute, <varname>jwcid</varname>, to any existing
element. Elements with such attributes are recognized by Tapestry as being dynamic, and driven by
a Tapestry component, but a <acronym>WYSIWYG</acronym> editor will simply ignore them. Once a template
is instrumented, it may be worked on by
<link linkend="components.html-production">both the HTML producer and the Java developer</link>.
</para>
<para>
Identifying a Tapestry component within
an HTML template
is accomplished by adding a <varname>jwcid</varname> attribute to a tag.
</para>
<informalexample>
<programlisting>
&lt;<replaceable>any</replaceable> jwcid="<replaceable>component id</replaceable>" ... &gt; <replaceable>body</replaceable> &lt;/<replaceable>any</replaceable>&gt;
</programlisting>
<para>or
</para>
<programlisting>
&lt;<replaceable>any</replaceable> jwcid="<replaceable>component id</replaceable>" ... /&gt;
</programlisting>
</informalexample>
<para>
Most often, the HTML element chosen is <sgmltag class="starttag">span</sgmltag>, though (in fact) Tapestry
completely ignores the element chosen by the developer, except to make sure the open and close tags balance.
</para>
<para>
The parser used by Tapestry is relatively forgiving about case
and white space. Also, the component id (and any other attributes) can be enclosed in
double quotes (as above), single quotes, or be left unquoted.
</para>
<para>
You are free to specify additional
attributes. These attributes will become
<link linkend="components.informal-parameters">informal parameters</link> for the Tapestry component.
</para>
<para>
The start and end tags for Tapestry components must balance properly. This includes cases where the
end tag is normally ommitted, such as <sgmltag class="starttag">input</sgmltag> elements. Either a closing
tag must be supplied, or the XML-style syntax for an empty element must be used (that is,
a slash just before the end of the tag).
</para>
<section id="component.html-template.localization">
<title>Localizing sections of a template</title>
<para>
Tapestry includes an additional template feature to assist with localization of a web application.
By specifying a <sgmltag class="starttag">span</sgmltag> element with a special attribute,
<varname>key</varname>, Tapestry will replace the entire
<sgmltag class="starttag">span</sgmltag> tag with a
<link linkend="components.localization.strings">localized string</link> for the component.
</para>
<para>
This construct takes one of two forms:
</para>
<informalexample>
<programlisting>
&lt;span key="<replaceable>key</replaceable>" ... &gt; ... &lt;/span&gt;
</programlisting>
<para>or</para>
<programlisting>
&lt;span key="<replaceable>key</replaceable>" ... /&gt;
</programlisting>
</informalexample>
<para>
If only the <varname>key</varname>
attribute is specified, then the <sgmltag class="starttag">span</sgmltag>
is simply replaced with the localized string. However, if any additional attributes
are specified for the <sgmltag class="starttag">span</sgmltag> tag beyond
<varname>key</varname>, then
the <sgmltag class="starttag">span</sgmltag> tag will be part of the rendered HTML, with
the specified attributes.
</para>
<para>
The upshot of this is that sections of the HTML template can be invisibly localized
simply by wrapping the text to be replaced inside a <sgmltag class="starttag">span</sgmltag>
tag. The wrapped text exists, once more, as sample text to be displayed
in a <acronym>WYSIWYG</acronym> editor.
</para>
</section> <!-- component.html-template.localization -->
<section id="component.html-template.body">
<title>Components with Bodies</title>
<para>
In Tapestry, individual components may have their own HTML templates. This is a very powerful
concept ... it allows powerful and useful components to be created with very little code. By contrast,
accomplishing the same using JSP tags requires either that all the HTML be output
from the JSP tag directly, or that the JSP tag use some additional framework, such as Velocity,
to enable the use of a template. In either case the JSP tag author will need to divide the code or template
into two pieces (before the body and after the body). Tapestry allows components to simply have
a single template, with a marker for where the body is placed.
</para>
<para>
During the rendering
of a page, Tapestry knits together the templates of the page and all the nested components to create
the HTML response sent back to the client web browser.
</para>
<informalexample>
<programlisting>
Container content <co id="component.html-template.body.container1"/>
&lt;span jwcid="component"&gt; <co id="component.html-template.body.component"/>
Body content <co id="component.html-template.body.content"/>
&lt;/span&gt;
More container content <co id="component.html-template.body.container2"/>
</programlisting>
</informalexample>
<calloutlist>
<callout arearefs="component.html-template.body.container1">
<para>
This portion of the container content is rendered first.
</para>
</callout>
<callout arearefs="component.html-template.body.component">
<para>
The component is then rendered. It will render, possibly using its
own template.
</para>
</callout>
<callout arearefs="component.html-template.body.content">
<para>
The component controls <emphasis>if</emphasis>, <emphasis>when</emphasis> and
<emphasis>how often</emphasis> the body content from its container
is rendered.
</para>
<para>
Body content can be a mix of static HTML and additional components. These
components are <emphasis>wrapped</emphasis> by the component, but are
<emphasis>embedded</emphasis> in the component's container.
</para>
</callout>
<callout arearefs="component.html-template.body.container2">
<para>
After the component finishes rendering, the remaining content
from the container is rendered.
</para>
</callout>
</calloutlist>
<para>
The body listed above can be either static HTML or other Tapestry
components or both. Elements in the body of a component are
wrapped by the containing component. The containing component controls the
rendering of the elements it wraps in its body. For example,
the &Conditional; component may decide not to
render its body and the &Foreach; component may render
its body multiple times.
</para>
<para>
Not all Tapestry components should have a body.
For example, the &TextField; component creates an
<sgmltag class="starttag">input type=text</sgmltag>
form element and it makes no sense for it to contain anything else.
Whether a component allows a body (and wrap other elements), or whether
it discards it, is defined in the
<link linkend="spec.component-specification">component's specification</link>.
</para>
<para>
Tapestry includes a special component, &RenderBody;,
which is used to render the body content from a component's container.
It makes it easy to create components that wrap other components.
</para>
</section> <!-- component.html-template.body -->
</section> <!-- components.html-template -->
<section id="components.html-production">
<title>Tapestry and HTML Production</title>
<para>
Tapestry is design to work in a large-scale environment, that typically features
two seperate teams: a "creative" team that produces HTML and a
"technical" team that produces Tapestry pages, components and Java code.
</para>
<para>
The division of skills is such that the creative team has virtually no knowledge of Java and
a minimal understanding of Tapestry, and the technical team has a limited understanding of HTML
(and tend to be color blind).
</para>
<para>
The typical workflow is that the technical team implements the application, using very minimal HTML
... that is, minimal attention to layout, font size, colors, etc. Just enough to be sure that
the functionality of the application is there.
</para>
<para>
Meanwhile, the creative team is producing HTML pages of what the finished application will look like.
These pages are like snapshots of the HTML produced by the running application.
</para>
<para>
<emphasis>Integration</emphasis> is the process of merging these two views of the application together.
Primarily, this involves marking up tags within the HTML page with
<varname>jwcid</varname> attributes,
to indicate
to Tapestry which portions of the page are dynamic. In this way, the
page can be used as a Tapestry HTML template. These changes are designed to be invisible to a
<acronym>WYSIWYG</acronym> HTML editor.
</para>
<para>
Tapestry includes a number of additional features to allow the HTML producers to continue
working on HTML templates, <emphasis>even after</emphasis> their initial efforts have been
integrated with the Java developer's code.
</para>
<section>
<title>Implicitly removed bodies</title>
<para>
In many cases, a component doesn't allow a body, but one may be present in the HTML template.
As usual, this is declared in
the <link linkend="spec.component-specification">component's specification</link>.
Tapestry considers that body to be a sample value, one which exists to allow the HTML producer
to verify the layout of the page using a WYSIWYG editor (rather than having to run the entire application).
Tapestry simply edits out the body at runtime.
</para>
<para>For example, an HTML producer may create an HTML template that includes a table
cell to display the user's name. The producer includes a sample value so that the
cell isn't empty (when previewing the HTML layout).
</para>
<informalexample>
<programlisting>
&lt;td&gt;&lt;span jwcid="insertName"&gt;John Doe&lt;/span&gt;&lt;/td&gt;
</programlisting>
</informalexample>
<para>
The &Insert; component doesn't allow a body, so Tapestry edits out the
content of the <sgmltag class="starttag">span</sgmltag> tag from the HTML template. The fact that
a <sgmltag class="starttag">span</sgmltag> was used to represent the &Insert; component in the
HTML template is irrelevant to Tapestry; any tag could have been used, Tapestry just
cares that the start and end tags balance.
</para>
<para>
At runtime, Tapestry will combine the HTML template and the &Insert; component to produce the
final HTML:
</para>
<informalexample>
<programlisting>
&lt;td&gt;Frank N. Furter&lt;/td&gt;
</programlisting>
</informalexample>
<para>
This editting out isn't limited to simple text; any HTML inside the body is removed. However,
none of that content may be dynamic ... the presence of a
<varname>jwcid</varname> attribute will cause a parsing exception.
</para>
</section>
<section>
<title>Explicitly removed bodies</title>
<para>
Another feature related to production and integration is the ability to remove sections of the HTML template.
Producers often include some optional portions on the page. The canonical example of this is a page that
shows a table of results; the HTML producer will usually include extra rows to demonstrate the look and layout of
a fully populated page.
</para>
<para>
The first row will be wrapped by a &Foreach; and otherwise changed to include dynamic links and output, but what about
the other rows?
</para>
<para>
To handle this case,
Tapestry recognizes a special <varname>jwcid</varname> attribute value: <literal>$remove$</literal>.
Using this special id causes
Tapestry to edit out the tag and all of its contents. Thus, each additional <sgmltag class="starttag">tr</sgmltag> in the
table should specify the value <literal>$remove$</literal> for attribute <varname>jwcid</varname>.
</para>
<informalexample>
<programlisting><![CDATA[
<table>
<tr jwcid="foreach">
<td><span jwcid="insertUserName">John Doe</span></td>
<td><span jwcid="insertAge">42</span></td>
</tr>
<tr jwcid="$remove$">
<td>Frank N. Furter</td>
<td>47</td>
</tr>
<tr jwcid="$remove$">
<td>Bob Doyle</td>
<td>24</td>
</tr>
</table>]]></programlisting>
</informalexample>
</section>
<section>
<title>Limiting template content
</title>
<para>
In a typical Tapestry application, some form of Border component provides a significant portion of every page.
This typically includes the outermost <sgmltag class="starttag">html</sgmltag>, <sgmltag class="starttag">head</sgmltag> and <sgmltag class="starttag">body</sgmltag>
tags, as well as <sgmltag class="starttag">table</sgmltag>s used to control layout.
</para>
<para>
In the static HTML pages from the creative team, this is not directly visible ... they <emphasis>must</emphasis>
include all the content
normally generated by the Border component in order to see what the HTML page actually looks like.
</para>
<para>
By default, the <emphasis>entire</emphasis> HTML template is the content for the page.
This causes a problem, even after a <sgmltag class="starttag">span</sgmltag>
is added, to represent the Border component ... much of the HTML is duplicated,
once from the static HTML, then dynamically from the Border component.
</para>
<para>
To eliminate this problem, Tapestry has a second special <varname>jwcid</varname> attribute: <literal>$content$</literal>.
Using this special id causes Tapestry to limit its view of the HTML template to just the content inside the tag. Anything outside
the defined content is completely ignored.
</para>
</section>
<section>
<title>Limits</title>
<para>
Ideally, the HTML pages created by the HTML producers would be used as is
as the HTML templates. Changes made for integration, the adding of <varname>jwcid</varname> attributes and such,
would be copied back into the HTML pages.
</para>
<para>
Given the use of the <literal>$remove$</literal> and <literal>$content$</literal>
<varname>jwcid</varname>'s,
this is practical
to a point. Once the application starts using a number of re-usable components, there
isn't a good way to perform the integration short of cutting and replacing
some of the HTML page content to form the HTML template.
</para>
</section>
</section>
<section id="components.localization">
<title>Localization</title>
<para>
Tapestry has built in support for localization, designed to be easy to use. This localization support
is defined in terms of transforming the user interface into a format appropriate the the locale
of the user. This primarily takes the form of localized text (translated into the
end-user's language), but can also affect other aspects of look and feel including colors, images and layout.
</para>
<para>
Tapestry has two different methods for supporting localization; developers are free to
mix and match solutions according to their own preferences.
</para>
<para>Each client connecting to the application will select a particular <classname>Locale</classname>.
When a page for the application is created, the locale is used to select the correct localized
resources.
Locales are defined by the ISO (International Standards Organization).
A locale consists of a language code (such as 'en' for English, 'de' for German or 'fr' for French)
and a country code (such as 'AU' for Australia, 'BE' for Belguim, or 'GB' for United Kingdom).
</para>
<para>
A client's initial locale is determined by analyzing HTTP headers provided with the initial request.
An application may override this default, which records a client-side cookie identifyng the desired locale.
An example of this is included in the Tapestry Workbench demonstration.
</para>
<section id="components.localization.strings">
<title>Localization with Strings</title>
<para>
Each individual component may have a set of <emphasis>localized strings</emphasis>.
Remember that pages are just a specific kind of component.
This set is built, much like the properties of a &ResourceBundle;, from one
or more <literal>.properties</literal> files. These files are located on the classpath, in the same
directory as the component specification (the <literal>.jwc</literal> file).
</para>
<para>
The search for strings is much the same as with &ResourceBundle;, except
that only <literal>.properties</literal> files are considered (&ResourceBundle; also looks for classes).
</para>
<para>
Example: for a component <filename>/com/skunkworx/skunkapp/Border.jwc</filename> and a locale of
<literal>fr_BE</literal> would be:
</para>
<itemizedlist>
<listitem>
<para>
<filename>/com/skunkworx/skunkapp/Border_fr_BE.properties</filename>
</para>
</listitem>
<listitem>
<para>
<filename>/com/skunkworx/skunkapp/Border_fr.properties</filename>
</para>
</listitem>
<listitem>
<para>
<filename>/com/skunkworx/skunkapp/Border.properties</filename>
</para>
</listitem>
</itemizedlist>
<para>
Searching for individual keys works just as with &ResourceBundle;, the search starts in the most
specific file (<literal>Border_fr_BE.properties</literal>) and continues downward if not found.
</para>
<para>
Components can gain access to their container's localized strings
via the &spec.string-binding;
element in the component specification.
</para>
</section>
<section id="components.localization.templates">
<title>Localization with Templates</title>
<para>
Tapestry allows multiple versions of HTML templates and assets (described in a
<link linkend="components.assets">later section</link>)
to be deployed with the application.
</para>
<para>
The base template name is derived from the specification name,
by changing the <literal>jwc</literal> extension to <literal>html</literal>.
For example, component <filename>/com/skunkworx/skunkapp/Border.jwc</filename>
will have a base template name of <filename>/com/skunkworx/skunkapp/Border.html</filename>.
This resource name is used as the basis of a search that includes the locale.
Various suffixes are inserted just before the '.html' extension.
</para>
<para>
A French speaking Belgian visitor would provoke the following search:
</para>
<itemizedlist>
<listitem>
<para>
<filename>/com/skunkworx/skunkapp/Border_fr_BE.html</filename>
</para>
</listitem>
<listitem>
<para>
<filename>/com/skunkworx/skunkapp/Border_fr.html</filename>
</para>
</listitem>
<listitem>
<para>
<filename>/com/skunkworx/skunkapp/Border.html</filename>
</para>
</listitem>
</itemizedlist>
<note>
<para>
This form of localization actually predates the alternate form,
using <link linkend="components.localization.strings">localized strings</link>.
Localizing the strings seperately from the rest of the HTML template
is generally a better and easier way. Localization of templates
will, in the future, be used primarily when changing the layout of the template ...
for example, to provide a right-to-left orientation in a Hebrew
localization.
</para>
</note>
</section>
</section> <!-- components.localization -->
<section id="components.assets">
<title>Assets</title>
<para>
Assets are images (GIF, JPEG, etc.), movies, sounds or other collateral associated
with a web application. Assets come in three flavors: external, context and private.
</para>
<para>
External assets live at an arbitrary URL. Context assets use a URL within the
servlet context hosting the Tapestry application;
these assets are deployed
within the same Web Application Archive (WAR) as the application.
</para>
<para>
Private assets come from the Java classpath and are
resources not normally visible to the web server.
</para>
<para>
Tapestry uses the assets concept to address two areas: localization and deployment.
</para>
<para>
For localization: internal and private assets are localized,
just like HTML templates. That is, the path name provided is
used as the basis for a search that takes into account the desired locale.
External assets can't be localized in this way.
</para>
<para>
Private assets allow for easy deployment because the assets are
packaged with the HTML templates and Java code of the application,
inside a Java Archive (JAR) file.
</para>
<para>
Private assets support re-usability; a re-usable component may be
packaged with supporting assets (typically, image files) and used in any
Tapestry application without change, and without having to locate, extract or
otherwise fiddle with those assets.
</para>
<para>
The Tapestry framework provides two ways of exposing the assets to the client web browser.
</para>
<para>
First, it provides a service that will access the asset dynamically.
The URL encodes the application servlet and the resource to download,
and Tapestry framework code will pump the bytes down to the client web browser.
This is the default behavior (and is most useful during development).
</para>
<para>
The second method involves copying the asset out to a directory visible
to the web server, and creating a URL for it in its final location.
This requires some extra configuration of the application.
This method also has some implications when deploying new versions of the web application.
These are addressed later in this document.
</para>
</section>
<section id="components.helper-beans">
<title>Helper Beans</title>
<para>
There is a second form of aggregation allowed with Tapestry components. The first way, covered previously, is to use embedded
components to extend the functionality of the outer component. In some cases, useful behavior can be isolated,
not into an additional component, but into a simple JavaBean.
</para>
<para>
These additional beans, called helper beans, are defined in the component specification, in the
&spec.bean; element.
Each bean has a unique name, a class to instantiate,
and a lifecycle (which controls how long the component keeps a reference to the bean). The specification
allows properties of the bean to be set as well, using
the &spec.set-property; and &spec.set-string-property; elements.
Helper beans are accessed through the <varname>beans</varname> property of the component.
</para>
<para>
Beans are created as needed, they may then be cached for future use according to their declared lifecycle.
The default lifecycle is <varname>request</varname>, meaning that the same bean will be returned until the end of the current request cycle.
</para>
<para>
An alternate lifecycle, <varname>page</varname>, means that once the bean is instantiated, it will continue to be available
for the lifetime of the page containing it. Remember that helper beans should never contain any client-specific
state, since a page will be used by multiple sessions and clients.
</para>
<para>
The last available lifecycle, <varname>none</varname>, indicates that the bean is not cached at all, and will be created fresh on
each property access.
</para>
<para>
Tapestry includes a handful of useful helper beans. &Default; is used to provide default values for optional parameters.
&ValidationDelegate;
and several implementions of &IValidator;
used with &ValidField;, it allows simple handling of validation and presentating validation errors.
&EvenOdd; is used by the <link linkend="inspector">Tapestry Inspector</link>; it generates a stream of values alternating between "even" and "odd"; this is combined with cascading stylesheets to make the rows alternate between white and grey backgrounds.
</para>
</section>
</chapter>