Introduction:
Tapestry is a component-based web application framework, written in Java. Tapestry is
more than a simple templating system; Tapestry builds on the Java Servlet API to build a
platform for creating dynamic, interactive web sites. More than just another templating
language, Tapestry is a real framework for building complex applications from simple,
reusable components. The current stable Tapestry release is version 5.1.0.5. Version 4 and
below are relatively similar, whereas version 5 contains substantial changes. This
document describes the parameters and bindings of Tapestry in detail. It is not a tutorial
that is available as a separate document. Instead, this is a guide to some of the internals of
Tapestry, and is intended for experienced developers who wish to leverage Tapestry fully.
An overview of Tapestry:
Tapestry is an object-oriented Java web application framework to implement applications
in accordance with the model-view-controller design pattern.
MVC Design Pattern:
The main aim of MVC design pattern is to separate the business logic and data (the Model)
from the display logic (the View).
Ideally, the Model should have no idea of what displays its data (and how that data is
displayed) as there can potentially be numerous different views. The View in its turn
should have no idea of where the data it displays come from, as data can potentially come
from just anywhere. It is for the piece of code named Controller to ensure that a correct
piece of data was sent for processing to the Model and that the result of that processing
was forwarded to a proper view.
Tapestry – Component Centric:
The hardest part of understanding Tapestry is the fact that it is component-centric not
operation-centric. Most web technologies (Struts , servlets, PHP, etc.) are operation-centric.
We create servlets (or Actions, or what have you) that are invoked when a user clicks a link
or submits a form. We are responsible for selecting an appropriate URL, and the name and
type of any query parameters, so that we can pass along the information that we need in
the URL.
Everything is different inside Tapestry. Tapestry applications consist of pages; pages are
constructed from smaller components. Components may themselves be constructed from
other components. Every page has a unique name, and every component within a page
has its own unique id ... this is a component object model. Effectively, every component
has an address that can easily be incorporated into a URL.
In practical terms, we don't write a servlet for the add-item-to-shopping-cart operation. In
fact, we don't even write an add-item-to-shopping-cart component. What we do is take an
existing component, such as Direct Link , and configure it. When the component renders, it
will create a callback URL. When you click that link, the callback URL (which includes the
name of the page and the id of the component within the page) will invoke a method on
the component ... and that method invokes your application-specific listener method.
Listener Methods:
Listener methods in Tapestry are very similar in intent to delegates in C#. In both cases, a
method of a particular object instance is represented as an object. We supply just the
listener method ... not an entire servlet. Tapestry takes care that our listener method is
invoked at the right time, under the right conditions. You don't have to think about how to
build that URL, what data goes in the URL, or how to hook it up to your application-specific
code--that's all handled by the framework.
Parameters and Bindings:
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. For example, on a welcome page,
a component might get the userName property from the visit object and insert it into the
HTML response. Each component has a specific set of parameters. Parameters have a
name, a type and may be required or optional.
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. 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.
There are two types of bindings: static and dynamic.
Static Bindings:
Static bindings are read-only; the value for the binding is specified in the component
specification as shown below.
currentDate expression. Which means: go to the page class, find a method named
getCurrentDate(), invoke it, and whatever it returns will be the value you are looking for. So
the next step is to create the page class with the getCurrentDate() method in it.
Dynamic Bindings:
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. In fact, dynamic bindings use property paths,
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.
Connected Parameters:
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.
Part of the <parameter> specification for a parameter is the direction, which can be one of
the following values:
in
Input parameter; the value is drawn from the binding (if bound) and applied to the
corresponding component property just before rendering the component.
form
A parameter which matches the semantics of a form component. The parameter is treated
like an in parameter when the page is rendering. When the form containing the
component is submitted, the connected property is read (after the component renders),
and the value applied to the parameter.
custom
Tapestry does not try to connect the parameter with any property; the component is
responsible for accessing the binding and retrieving or setting values. 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.
Defining a parameter as direction in 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).
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 corresponding instance variable.
Connected Parameter - Specification
<specification ...>
<parameter name="color" direction="in" java-type="java.awt.Color"/>
...
Connected Parameter - Java Code
In this example, the component writes its content inside a <font> element, with the HTML
color attribute set from the color parameter. RequestContext includes a static
convienience method for converting from a Color object to an encoded color that will be
meaningful to a web browser.
The parameter is optional and defaults to red if not specified (that is, if the parameter is not
bound). At runtime, Tapestry will invoke setColor() first (if the color parameter is bound). It
will then invoke renderComponent(). Finally (even if renderComponent() throws an
exception) it will invoke setColor() again, to restore it back to the default value, Color.RED.
This code includes a defect: because the parameter is optional, there is nothing to prevent
it from being bound to null.
Formal vs Informal Parameters:
Tapestry components have two types of parameters: formal and informal.
Formal Parameter:
Formal parameters are parameters defined in the component specification. Each formal
parameter has a specific (case sensitive) name and may be required or optional.
In many cases, there is a one-to-one mapping between a Tapestry component and a
specific HTML tag. For example, Body and <body>, Form and <form>, etc. In other cases, a
Tapestry component produces a known single HTML tag. For example, ActionLink,
DirectLink, PageLink and ServiceLink all produce an <a> tag. To support truly rich
interfaces, it is often necessary to specify additional attributes of the HTML tags; usually
this means setting the class of a tag so as to get visual properties from a style sheet. In
other cases, display attributes may be specified inline. In theory, these components could
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.
Informal Parameter:
Instead, Tapestry has the concept of an informal parameter. 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).
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 href attribute, even if you supply a Href (or
other variation) informal parameter.
Not all Tapestry components even allow informal parameters; this is explicitly stated in the
component specification.
Conclusion:
Since every entity of Tapestry project can be analyzed separately, it is easy to edit the code
as well as debug the errors when we create a complex web project. Tapestry components
are "black boxes" that are involved with both rendering HTML responses and responding
to HTTP requests. In this document we have learnt the fundamentals of different
parameters and components that are used in Tapestry.
Tapestry is a component-based web application framework, written in Java. Tapestry is
more than a simple templating system; Tapestry builds on the Java Servlet API to build a
platform for creating dynamic, interactive web sites. More than just another templating
language, Tapestry is a real framework for building complex applications from simple,
reusable components. The current stable Tapestry release is version 5.1.0.5. Version 4 and
below are relatively similar, whereas version 5 contains substantial changes. This
document describes the parameters and bindings of Tapestry in detail. It is not a tutorial
that is available as a separate document. Instead, this is a guide to some of the internals of
Tapestry, and is intended for experienced developers who wish to leverage Tapestry fully.
An overview of Tapestry:
Tapestry is an object-oriented Java web application framework to implement applications
in accordance with the model-view-controller design pattern.
MVC Design Pattern:
The main aim of MVC design pattern is to separate the business logic and data (the Model)
from the display logic (the View).
Ideally, the Model should have no idea of what displays its data (and how that data is
displayed) as there can potentially be numerous different views. The View in its turn
should have no idea of where the data it displays come from, as data can potentially come
from just anywhere. It is for the piece of code named Controller to ensure that a correct
piece of data was sent for processing to the Model and that the result of that processing
was forwarded to a proper view.
Tapestry – Component Centric:
The hardest part of understanding Tapestry is the fact that it is component-centric not
operation-centric. Most web technologies (Struts , servlets, PHP, etc.) are operation-centric.
We create servlets (or Actions, or what have you) that are invoked when a user clicks a link
or submits a form. We are responsible for selecting an appropriate URL, and the name and
type of any query parameters, so that we can pass along the information that we need in
the URL.
Everything is different inside Tapestry. Tapestry applications consist of pages; pages are
constructed from smaller components. Components may themselves be constructed from
other components. Every page has a unique name, and every component within a page
has its own unique id ... this is a component object model. Effectively, every component
has an address that can easily be incorporated into a URL.
In practical terms, we don't write a servlet for the add-item-to-shopping-cart operation. In
fact, we don't even write an add-item-to-shopping-cart component. What we do is take an
existing component, such as Direct Link , and configure it. When the component renders, it
will create a callback URL. When you click that link, the callback URL (which includes the
name of the page and the id of the component within the page) will invoke a method on
the component ... and that method invokes your application-specific listener method.
Listener Methods:
Listener methods in Tapestry are very similar in intent to delegates in C#. In both cases, a
method of a particular object instance is represented as an object. We supply just the
listener method ... not an entire servlet. Tapestry takes care that our listener method is
invoked at the right time, under the right conditions. You don't have to think about how to
build that URL, what data goes in the URL, or how to hook it up to your application-specific
code--that's all handled by the framework.
Parameters and Bindings:
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. For example, on a welcome page,
a component might get the userName property from the visit object and insert it into the
HTML response. Each component has a specific set of parameters. Parameters have a
name, a type and may be required or optional.
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. 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.
There are two types of bindings: static and dynamic.
Static Bindings:
Static bindings are read-only; the value for the binding is specified in the component
specification as shown below.
<component id="now" type="Insert"> <binding name="value" value="currentDate"/> </component>This is understood by Tapestry in the following way: to obtain the value, evaluate the
currentDate expression. Which means: go to the page class, find a method named
getCurrentDate(), invoke it, and whatever it returns will be the value you are looking for. So
the next step is to create the page class with the getCurrentDate() method in it.
Dynamic Bindings:
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. In fact, dynamic bindings use property paths,
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.
Connected Parameters:
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.
Part of the <parameter> specification for a parameter is the direction, which can be one of
the following values:
in
Input parameter; the value is drawn from the binding (if bound) and applied to the
corresponding component property just before rendering the component.
form
A parameter which matches the semantics of a form component. The parameter is treated
like an in parameter when the page is rendering. When the form containing the
component is submitted, the connected property is read (after the component renders),
and the value applied to the parameter.
custom
Tapestry does not try to connect the parameter with any property; the component is
responsible for accessing the binding and retrieving or setting values. 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.
Defining a parameter as direction in 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).
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 corresponding instance variable.
Connected Parameter - Specification
<specification ...>
<parameter name="color" direction="in" java-type="java.awt.Color"/>
...
Connected Parameter - Java Code
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(); } }
In this example, the component writes its content inside a <font> element, with the HTML
color attribute set from the color parameter. RequestContext includes a static
convienience method for converting from a Color object to an encoded color that will be
meaningful to a web browser.
The parameter is optional and defaults to red if not specified (that is, if the parameter is not
bound). At runtime, Tapestry will invoke setColor() first (if the color parameter is bound). It
will then invoke renderComponent(). Finally (even if renderComponent() throws an
exception) it will invoke setColor() again, to restore it back to the default value, Color.RED.
This code includes a defect: because the parameter is optional, there is nothing to prevent
it from being bound to null.
Formal vs Informal Parameters:
Tapestry components have two types of parameters: formal and informal.
Formal Parameter:
Formal parameters are parameters defined in the component specification. Each formal
parameter has a specific (case sensitive) name and may be required or optional.
In many cases, there is a one-to-one mapping between a Tapestry component and a
specific HTML tag. For example, Body and <body>, Form and <form>, etc. In other cases, a
Tapestry component produces a known single HTML tag. For example, ActionLink,
DirectLink, PageLink and ServiceLink all produce an <a> tag. To support truly rich
interfaces, it is often necessary to specify additional attributes of the HTML tags; usually
this means setting the class of a tag so as to get visual properties from a style sheet. In
other cases, display attributes may be specified inline. In theory, these components could
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.
Informal Parameter:
Instead, Tapestry has the concept of an informal parameter. 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).
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 href attribute, even if you supply a Href (or
other variation) informal parameter.
Not all Tapestry components even allow informal parameters; this is explicitly stated in the
component specification.
Conclusion:
Since every entity of Tapestry project can be analyzed separately, it is easy to edit the code
as well as debug the errors when we create a complex web project. Tapestry components
are "black boxes" that are involved with both rendering HTML responses and responding
to HTTP requests. In this document we have learnt the fundamentals of different
parameters and components that are used in Tapestry.