Tapestry: A Component-Centric Framework

Tapestry is an open source web application framework written in Java. Highly-interactive and content-rich applications can be easily developed using this framework.

Tapestry offers advantages including a high-performance coarse-grained pooling strategy, high code-reuse, line-precise error reporting, and lots more. Tapestry applications can be run on any servlet container since the apps are 100 percent container agnostic.

Adoption of the tapestry framework eliminates writing servlets and building URLs and query parameters to servlets, even though Tapestry is built on top of the servlet API. Developers can concentrate on coding the application's functionality because Tapestry takes care of the "plumbing" code (creating URLs, dispatching incoming requests, managing server-side state, and so forth).

The Tapestry Distribution

The Tapestry distribution is available for download at the Tapestry home page on the Apache site.

The Tapestry distribution consists of the following pieces, illustrated in Figure 1:

The main Tapestry framework: This archive consists of all the core components of the Tapestry framework. It also contains other classes that are used to provide services to pages and components.

Contributed components: This is a collection of powerful add-on components for Tapestry contributed by the community. It has components that are used to create the HTML response for rendering the pages. These components can be used in applications, to incorporate logic constructs in HTML templates.

Annotation support classes: This library contains annotations. These annotations require JDK 1.5 to run. Annotations are modifiers that could be added to the code. One can perform some operations inside Java code that otherwise would be specified in the page or component specification.

Portlet support: An add-on module for Tapestry that enables the use of Tapestry to create JSR-168 portlets.

Figure 1. Contents of the Tapestry distribution

Tapestry has a number of additional runtime dependencies, which must be downloaded and packaged with the above JAR files in the WEB-INF/lib folder of Tapestry web applications. This can be done by the Ant build scripts for Tapestry, which will automatically download these dependencies. The build scripts require some configuration to work; details are available at the Tapestry Wiki. Alternatively, these dependencies may be downloaded from Howard Lewis Ship's quick-start directory as tapestry-libraries.tar.gz (21.7 MB). Copy the dependencies to the WEB-INF/lib folder or any shared library folder to make the JAR files available to the application that is to be developed based on the Tapestry framework. Since Tapestry is a library, this is all that needs to be done for the installation part.

The Component World of Tapestry

A Tapestry application is different from other traditional web applications based on operation-centric frameworks (e.g., Struts, PHP, etc.). A Tapestry application is a collection of web pages, and each web page is in turn constituted of components. The web pages in Tapestry are nothing but simple HTML pages (not JSPs) and are called as HTML templates. The entire web application is built from these templates and specifications, using minimal Java code.

The Tapestry framework acts as a layer between the Tapestry application and the Java Servlet Container. Tapestry uses the standard Servlet API objects and functions within the existing Servlet container. Since the application components access the Tapestry components, the developed application is totally oblivious of the Servlet API.

In Tapestry-based applications, all the elements on a web page (forms, links, test-boxes, etc.) are represented as components. At the heart of the design is the interface org.apache.tapestry.IComponent. All objects that may be used to provide dynamic content on a Tapestry-based page are represented by this class.

Figure 2 shows some of the classes and their relations. In short, everything from forms, components of forms like text areas, text fields, hidden fields, submit buttons, and links are represented as objects, methods and properties (each component having its own id). This is the unique Component Object Model (COM).

Tapestry and MVC

Model-View-Controller (MVC) is a design paradigm based on separating the user interface of an application from its domain logic. Tapestry's MVC Model 2 implementation is one of the most pure implementations of the pattern.

Model: The model could consist of domain objects and/or a database.

View: In Tapestry, the view is the simple HTML file with special markers that define the use of components.

Controller: The controller here is a XML page Specification (.page file). A Page object (which implements the org.apache.tapestry.IPage interface) also assists the page specification file as the controller.

An Example

The simplest way to understand the Tapestry framework is to see it in action. The following is an example that demonstrates how easily applications can be developed using this framework.

By using an example of an online bank, a user can view his balance, place an order for a check book, or edit his profile. Like every application there is a home page called Home.html.

Notice how this is simple HTML, barring one element: jwcid, which means Java web component ID and is unique to Tapestry. As mentioned earlier, every component in the Tapestry framework has its own ID. When this HTML page is being rendered, a component is created and identified by this jwcid. So, how will Tapestry determine which type of component to create? This information has to be specified in the Home.page file. Here is a snippet from Home.page:

In this case, a component of the type DirectLink, identified by jwcidviewBalanceLink, will be created. Behind the scenes, before the page is rendered, Tapestry creates a page object of the type org.apache.tapestry.html.BasePage. The second step is to create all the components described in the .page file and associate them with the Page object. Then the Page reads the HTML file for information to render the page. When it encounters the component with the ID viewBalanceLink, the control is transferred to this object for rending the link. This component will output the resultant HTML link to the page to be rendered. In this entire process, the HTML page is used only for reference and is called the template. Here is the code snippet from Home.java:

Here we can see a new expression: listener. When a user clicks the link, the listener that has a binding with that component will be called by Tapestry. So, a listener method has to be defined in the page's class:

The effect of clicking the link in this scenario is to display the account balance page. So, we have a AccountBalance.html, AccountBalance.page, and the Page object class AccountBalance.java. But how do we define a relation between the home page and AccountBalance page? Have a look at this:

The most interesting change that we have made here is the annotation: @InjectPage. Annotations are a new feature of J2SE 5.0 (Tiger) and are used to provide metadata. Annotations take the form of an at sign (@), followed by the annotation name. You thwn supply data to the annotation--when data is required--in name=value pairs.

Using @InjectPage annotation, we have injected the AccountBalance page into our component. Now this page is available for our listener method to change its value, interact with a backend system or simply display the page named "AccountBalance" as the response after this listener method returns. And this is exactly what we have done!

Note that there is an Object Graph Navigational Language (OGNL) expression in the value. OGNL is a powerful open source expression language used to get and set properties of Java objects. When the Tapestry comes across ognl: it knows that what follows it is an OGNL expression and not a string constant.

Validation and Error Reporting

Consider a scenario in our banking application in which the user wants to edit his profile. On the "Edit Profile" page there are certain mandatory fields such as name and date of birth. These validations can be done with minimal coding in Tapestry. In the banking application, we have a HTML file for updating personal details (PersonalDetails.html), an XML template (PersonalDetails.page), and the Page Object class(PersonalDetails.java).

Starting by modifying the Page Object class, we add an ValidationDelegate attribute type. This attribute holds the list of error messages, in case the validation fails. But how does Tapestry know what to validate? Here's how: we update the page specification (PersonalDetails.page) to inform Tapestry that the component name is mandatory. Also, we bind the ValidationDelegate to the Form (personalDetailsForm) to record any validation errors in its components.

Conclusion

This article shows how simple it is to develop a web application using Tapestry. Tapestry adopts a component approach to web development, making it possible to move all the boring plumbing code out of the application and into the framework. Applications developed using the Tapestry framework naturally adapt to the Tapestry philosophy of simplicity, consistency, efficiency, and feedback.