Illustrating Tapestry
-Harish Krishnaswamy
31 May 2003
 

Summary
"Yet another web framework?" one may wonder, while in fact Tapestry is a revolutionary framework that beckons the web development community. In one (big!) sentence, Tapestry is a flexible, comprehensive, easy-to-use open source Java web framework that introduces component-based-development to the presentation tier of web applications and marks a clear separation of technologies employed in its development. This document explores this statement from a bird's-eye view.
Definitions
Before we delve into that statement, let's quickly define a few terms for clarity and completeness of this document.
Component
a component is a "black box" with well-defined interfaces that serves a specific purpose and can be used as a building block to compose other components and applications via a plug-and-play mechanism.
Component Definition
a component definition is the blueprint of the component (like a Class in OO paradigm) that defines the component's behavior. A component definition may include static configuration files (XML, .properties files etc.) that help in defining the behavior of the component, in addition to its code itself.
Component Declaration
a component declaration is the usage of a component in the definition of another component. The defining component is known as container or parent component and the used component is known as contained or child component. It's like an attribute declaration in a Class in OO paradigm.
Component Instance
a single copy of a component definition (like an Object in OO paradigm) created from a component declaration.
Overview
Since the advent of Servlets and JSPs, one of the biggest problems that has plagued the Java web development community is the separation of presentation and content. There has been various attempts to solve this problem. Some use a template approach that introduces a whole new set of tags intermixed with some HTML and even some programming constructs. While other solutions take it to the other extreme where everything is done in code. Tapestry's unique solution to the problem is to keep the presentation in HTML (or other standard markup language), define the content in Java and tie the two together with an XML specification; and a grouping of these three pieces makes a Tapestry component which is the logical unit of a Tapestry application.
A Tapestry component, technically referred to as JWC (Java Web Component), is a web component that can be composed, instantiated, configured and aggregated to compose other components. A text field shown in a browser via a Tapestry application is an instance of a JWC. And so is the web page itself (a specialized JWC, technically referred to as "Page", as you will see later in this document), which may be composed by aggregating instances of other non-Page JWCs. Tapestry comes with a toolbox ("library") of JWCs that can be used right off the shelf. A JWC need not necessarily have to be a web interface component; it can be a control component like the Foreach component in Tapestry's core library. In addition, Tapestry framework provides a strong foundation for the developer to build custom JWCs (beyond the scope of this document) without the painful, error prone, grunt, plumbing work that is inevitable in building a web application. A Tapestry application's presentation tier is built using these JWCs.
Illustration 1
In order to understand what makes a JWC and see how the separation of technologies is achieved, among other things, let's open up the "black box" and take a peek at the internals of the JWC. Each JWC is typically made of three parts – an HTML template, an XML specification and one or more Java classes, although a simple JWC can be made by providing just the template. In this illustration let's use all three parts (a purist's approach) to show its industrial strength and then we'll see how it can be simplified for the casual developer, in our next illustration.
For the purpose of illustration, let's consider a simple one-page Tapestry application that greets the user as shown below.
 Welcome demonstration

Figure 1. Home.java
Now that we know a page is also a JWC (specialized), let's build the Page component (Home Page in our example) of our Welcome application using the core library components readily available.
Page Template
<html>
    <head>
        <title>Welcome to Tapestry!</title>
    </head>
    <body>
        Hello <span jwcid="user">User Name</span>! Welcome to Tapestry!
    </body>
</html>
Listing 1. Home.html
The template is the part where the static portions (plain text/HTML) of the web page needs to be provided, that will be rendered by Tapestry unaltered. In addition to providing the static portions of the page, the template is also used to provide placeholder tags for the dynamic portions of the page. The placeholder tags are XML-like (no scripting here!); in other words every open tag should have a matching close tag. The placeholder tags are identified by a unique user-provided id specified using a special markup tag attribute, jwcid, as shown in the <span> tag in our example template. It is the jwcid attribute of the placeholder tag that is of importance to Tapestry; the placeholder tag itself is irrelevant, it could have as well been <xyz> in our example. But the <span> tag was chosen instead, so the page can be previewed properly outside the context of a Tapestry application. The jwcid attribute instructs Tapestry that an instance of a JWC, declared in the specification below, is to be created and plugged in place of the placeholder tag. The placeholder tag can have attributes other than jwcid. How Tapestry interprets and treats these attributes is dependent on the specification of the component that will be plugged in. Some components (like the one we have used in our Home Page) instruct the framework to silently filter out the irrelevant attributes of the placeholder tag and its enclosing body when it is rendered. Thus our user JWC, when rendered, will not render the text "User Name" enclosed by the placeholder tag. Please see "Tapestry Component Reference" under "Documentation" on Tapestry's home page for more information on the core component specifications. In any case, the newly created instance of JWC is assigned the user-specified id (user in our example) that may be used later to reference this JWC. The template, as one can see, is for the most part plain HTML except for the XML-like placeholder tags. This allows a page designer with no programming knowledge, a more natural undertaker of the task to build and maintain web pages, to easily take over the responsibility of building and maintaining the template through the life of the application.
The naming convention for templates is the name of the component with a .html extension. And thus the template for our Home Page component is named Home.html. Templates are typically placed under the web application context. So, if Welcome is the context of our Welcome application, Home.html will typically be placed under webapps/Welcome.
Page Specification
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE page-specification PUBLIC
    "-//Apache Software Foundation//Tapestry Specification 3.0//EN"
    "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">

<page-specification class="demo.Home">
    <component id="user" type="Insert">
        <binding name="value" expression="userName"/>
    </component>
    
    <context-asset name="$template" path="Home.html"/>
</page-specification>
Listing 2. Home.page
The specification is the part that drives a component and ties its pieces together. It specifies, among a lot of things, the class that defines the behavior of the component under construction (called container or parent component – Home Page in our example), the template for the container component and the components to be plugged in place of the template's placeholder tags (called contained or child components). The defining class (also known as component class – demo.Home in our example) of the container component is specified using the class attribute of the specification root element – page-specification. The container component's template is specified using the context-asset element. A component can have any number of types of assets (images, stylesheets etc.) associated with it and the template is treated as one such type. The template asset is specified using the special name $template. The contained components are declared using the component element. Components thus declared are known as an "Explicit" or "Declared" components. The id attribute of the component element is like an Object reference in OO paradigm, and is the same as the jwcid attribute of the placeholder tag in the template. Thus the framework knows which component needs to be plugged into which placeholder tag. The type of component to be instantiated and plugged in is defined by the type attribute of the component tag. The component we use in our example is the Insert JWC that comes with Tapestry's core library. As mentioned before, every component serves a specific purpose and so is our Insert JWC. The Insert JWC, like the name suggests, inserts text (the user name in our example) in place of the placeholder tag in the template. The text to be inserted is provided to the JWC via a parameter defined by the component.
Parameters are the gateway to configure components in Tapestry. Component parameters are like method parameters, the difference being that component parameters can be two-way; i.e., component parameters can pass in a value and also spit out a value. Tapestry allows four different kinds of parameters - in, form, custom and auto. in parameters can only pass in a value to the component, while the other kinds can pass values in and out of a component. The value passed in to a component is stored in the JWC as a JavaBeans property (known as parameter-property) in the component class. The value passed out of a component is updated in the variable that was the source of the data that was passed in. This variable is usually an attribute of the container component class, although it can be any variable accessible from the container component class. In addition to the parameter property, the JWC houses another JavaBeans property called the binding-property. Every JWC parameter-property is accompanied by a binding-property. The name of the binding-property is the name of the parameter-property + the literal suffix Binding. The purpose of the binding-property, like the name suggests, is to bind a foreign object (unknown at the time of component definition) to the JWC parameter-property. This allows a JWC to be decoupled from the source of data for the parameter-property. This decoupling is achieved with the help of another incredible open source project – Object Graph Navigation Library, OGNL. The component class will use the parameter-property, via the JavaBeans-style get method, as needed in defining the component's behavior. The get<ParameterProperty> method in turn, will use the get<ParameterProperty>Binding method, which will retrieve the bound object at runtime, using OGNL, and make it available to the component class as shown in Figure 2. The parameter-property is specified in the JWC specification at the time of component definition and the binding-property is automatically created and provided by the framework at runtime. The actual binding of the foreign object, though, is specified at the time of component declaration in the container component's specification as shown in our Page specification (Listing 2).

Figure 2. Parameter mechanics
The binding is specified in the component declaration using the binding element, nested within the component element. The binding element has two attributes – name and expression. Attribute name specifies the name of the parameter and the expression attribute specifies the foreign object to bind. The foreign object is any object that can be accessed from within the container component class via a JavaBeans-style get method, or an object that is the result of an OGNL expression. The target foreign object can be a very distant object in the object graph and yet be accessed from right within the expression attribute. This object graph navigation is achieved via OGNL. Please see Resources for information on OGNL expressions.
The Insert JWC (user in our example) defines a parameter called value. This parameter is bound to the foreign object (userName in our example) via the binding element as shown in the example Page specification (Home.page). At runtime, the user instance JWC reads the value of userName attribute of demo.Home instance and updates the value parameter using OGNL. The user instance then uses the parameter value and inserts it into the template.
Page components are special JWCs, as already stated. Page components are root level components (can never be contained components) with special attributes and responsibilities. Page components cannot be used as building blocks to compose other components; that said, parameters are useless in Page components and so the framework forbids Pages from having parameters.
The naming convention for specifications is the name of the component with an extension of .page (for Page components) or .jwc (for non-Page components). And thus the specification for our Page component is named Home.page. Specifications are typically placed under the WEB-INF folder of the application context. So, if Welcome is the context of our Welcome application, Home.page will typically be placed under webapps/Welcome/WEB-INF.
Page Class
package demo;

import org.apache.tapestry.html.BasePage;

public class Home extends BasePage {

    private String userName = "Jack";

    public String getUserName() {
        return this.userName;
    }
}
Listing 3. Home.java
The last piece of our Page component is the component class – demo.Home. The component class is a JavaBeans class that typically houses the properties that supply data to the contained components. In other words, these properties typically act as foreign objects that will be bound to the parameters of the contained components. In our example, the Page component class (demo.Home) houses the userName property that supplies the name Jack to user, the contained Insert JWC, via the getUserName method. Note that the contained Insert JWC does not care where the name comes from. For all it cares, the getUserName method can get the name from a directory service, a database or a legacy system.
In addition to providing data, the component class also provides the behavior of the component via listener and lifecycle methods. The framework provides a base component class each for JWC (BaseComponent) and Page (BasePage) components that can be extended by applications. These base component classes provide the default behavior for the necessary listener and lifecycle methods thus keeping the extended classes short and simple like our example Page component class (demo.Home).
The naming convention for the component class is the name of the component itself. And thus our component class is named Home. Component classes are typically placed in the WEB-INF/classes folder under the context of the web application like any other Java web application. So, if Welcome is the context of our Welcome application, Home.class will typically to be placed under webapps/Welcome/WEB-INF/classes.
Application Specification
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE application PUBLIC
    "-//Apache Software Foundation//Tapestry Specification 3.0//EN"
    "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">

<application name="Tapestry Illustration 1">
    <page name="Home" specification-path="Home.page"/>
</application>
Listing 4. Welcome.application
The application specification to an application is like the component specification to a component; it ties the pieces of an application together. It specifies, among other things, the Pages that make the application. The specification root element, <application>, has a name attribute that can be used to specify an optional descriptive name for the application as shown in the Welcome application. In our example one-page Welcome application, the Home Page component is declared using the <Page> element within the <application> element. The <page> element has two attributes namely name and specification-path. The name attribute is used to specify a logical name for the Page component, which is typically the same as the principal physical name (Home part of Home.page in our example) of the component specification. The logical name for the Page component can be any identifier unique within an application. Although the name Home (exact case) has a special meaning in Tapestry. The framework will forward all unknown requests to the Page component named Home. The specification-path attribute is used to specify the physical name and location of the Page component specification relative to the current location (location of the application specification file). This is the only place one has to specify the physical path to the file; all other references to the component are specified using the logical name thus isolating the physical name and location changes.
The naming convention for the application specification is the application context name with an extension of application. And thus the specification for our Welcome application is named Welcome.application. As mentioned before, specifications are typically placed under the WEB-INF folder of the application context. So, if Welcome is the context of our Welcome application, Welcome.application will typically be placed under webapps/Welcome/WEB-INF.
web.xml
<?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 Welcome Application</display-name>

    <servlet>
        <servlet-name>Welcome</servlet-name>
        <servlet-class>org.apache.tapestry.ApplicationServlet</servlet-class>
        <load-on-startup>0</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>Welcome</servlet-name>
        <url-pattern>/app</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>
Listing 5. web.xml
Tapestry applications have a single entry point servlet, ApplicationServlet, that acts as a gateway/gatekeeper. A request that gets delegated to a Tapestry application is sucked in by the framework's ApplicationServlet, processed, packaged and finally handed off to the application, in the form of Objects instead of the cryptic request parameters, for further processing. org.apache.tapestry.ApplicationServlet is the framework provided default servlet that may be extended for special needs (a very rare situation). The application servlet is by convention (not a requirement) mapped to the /app URL pattern within the application context. The AppilicationServlet is specified in the web.xml as all Servlets in a Java web application. The web.xml file is placed, per Java specifications, under the WEB-INF folder of the application context. Now if we ran our Welcome example application within Tomcat, running at port 8080, within context Welcome, the request URL http://localhost:8080/Welcome/app will take us to the Home Page of our Welcome application that we just built.
Illustration 2
If you thought the previous illustration was a lot of work to show a simple welcome page, don't fret; this illustration is for you. Here, we'll use the same Insert JWC as in Listing 1, but we'll use it to show the user's visitng time instead. Before we continue, let me boost your spirits by showing you the code that is needed for this illustration.
Page Template
<html>
    <head>
        <title>Welcome to Tapestry!</title>
    </head>
    <body>
        Hello Jack!  Welcome to Tapestry!
        Your visiting time is <span jwcid="now@Insert" value="ognl:new java.util.Date()">Current time</span>
    </body>
</html>
Listing 6. Home.html
That's all we need for this illustration, of course other than the web.xml which we can reuse from the previous illustration. You don't need the page specification or the JavaBeans component class; neither do you need the application specification. The framework has default values defined for all these parts; we'll talk about these a little later, but for now let's focus our attention on the template.
The template, if you notice, looks pretty much the same as in Listing 1 except for the placeholder tag. The placeholder tag now has the component type (Insert) embedded into the jwcid attribute value using the @ symbol. This is just another notation for declaring a component. A component thus declared is known as an Implicit component. now is the unique ID for the component and may be omitted if you don't have the need to reference this component in your code. If the ID is omitted, the component is called an "Anonymous Implicit" component, and the framework would provide an arbitrary ID to such components for internal use. An Implicit component has to be declared and configured in the placeholder tag in its entirety thus avoiding the need for a separate specification. Parameters of Implicit JWCs are bound to their values right in the placeholder tag in the form of attribute-value pairs. So the value parameter of the Insert JWC, in our example, is bound to an instance of java.util.Date, as shown in Listing 6, using OGNL. Yes, OGNL expressions are perfectly acceptable for attribute values in templates. Any attribute value prefixed with ognl: is considered an OGNL expression. This type of binding is identical, in meaning, to the binding specified in the Page specification from our previous illustration.
The component declaration was only one part of our Page specification, the other parts being the Page class declaration (demo.Home) and the template declaration ($template asset). The framework by default searches for the template by the name of the component with a .html extension, so we can skip the $template declaration. We also do without the Page class, and thus the Page specification itself, with the help of the framework default Page class, org.apache.tapestry.html.BasePage. The missing application specification is also owed to the framework's default behavior. Knowing that the framework will forward all unknown requests to a Page component named Home, we skip the application specification too. Another point worth mentioning here is that the framework has the ability to search and locate pages and templates not explicitly specified in the specification. The framework performs this search in a methodical fashion, the details of which can be found in the JavaDocs of org.apache.tapestry.resolver.PageSpecificationResolver and org.apache.tapestry.engine.DefaultTemplateSource classes.
Now, if we place our template (Home.html) under the application context Welcome, and place the web.xml file under the WEB-INF folder, and ran our Welcome example application within Tomcat, running at port 8080, the request URL http://localhost:8080/Welcome/app will take us to the Home Page of our Welcome application that we just built.
Illustration 3
In the last two illustrations we have seen that Pages are composed by aggregating other JWCs. We have also seen that there are two different ways of declaring a component - Explicit and Implicit declaration. An explicitly delcared component in Tapestry can be configured partly in the template and partly in the specification. This allows UI specific parameters to be configured in the template and content related parameters to be configured in the specification. Also, a component can declare some components explicitly in the specifiction and other components implicitly in the template. This allows simpler light-weight components to be declared in the template while other highly configurable components to be declared in the specification thus avoiding the clutter in the templates. Here, we will mix and match these approaches for a more pragmatic illustration.
In this slightly enhanced version of our Welcome application, we will accept the user's name and date-of-birth and display the information back on a different page. The templates for the two pages are shown in Listings 7 and 8.
Page Template
<html jwcid="@Shell" title="Welcome Page">
<body jwcid="@Body">
<form jwcid="@Form" listener="ognl:listeners.submit">
    Customer Name: <input jwcid="custName" type="text"/><br/>
    Date-of-Birth: <input jwcid="dob" type="text" format="MMM dd, yyyy"/>
        (Month DD, YYYY)<br/>
    <input type="submit" value="Submit"/>
</form>
</body>
</html>
Listing 7. CustInfo.html
<html>
<body>
    Hello <span jwcid="@Insert" value="ognl:custName"/>!
        Welcome to Tapestry!<br/>
    Your Date-of-Birth is <span jwcid="@Insert" value="ognl:dob"/>.<br/>
    <a href="#" jwcid="@PageLink" page="CustInfo">Next Customer</a>
</body>
</html>
Listing 8. Welcome.html
The components used here are all part of Tapestry's core library. For information on these components, please refer to the component reference. The point illustrated here is the flexibility available in declaraing components. Note that the implicit components, Shell, Body and Form, reside abreast the explicit components, custName and dob, in the same Page in perfect harmony. Also note that the format of date-of-birth field is configured in the template and the value is configured in the specification as shown in Listing 9.
Page Specification
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE page-specification PUBLIC
  "-//Apache Software Foundation//Tapestry Specification 3.0//EN" 
  "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">

<page-specification class="demo.Welcome">
    <property-specification name="custName" type="java.lang.String"/>
    <property-specification name="dob" type="java.util.Date"/>

    <component id="custName" type="TextField">
        <binding name="value" expression="custName"/>
    </component>

    <component id="dob" type="DatePicker">
        <binding name="value" expression="dob"/>
    </component>
</page-specification>
Listing 9. CustInfo.page
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE page-specification PUBLIC
  "-//Apache Software Foundation//Tapestry Specification 3.0//EN" 
  "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">

<page-specification class="demo.Welcome">
    <property-specification name="custName" type="java.lang.String"/>
    <property-specification name="dob" type="java.util.Date"/>
</page-specification>
Listing 10. Welcome.page
The important thing to note here is that the component class, demo.Welcome, is shared between the two Page components. This is perfectly legal and the framework takes care of providing a fresh instance to each component thereby making it thread-safe. The new appearance here is the property-specification element. This element is used to define JavaBeans-style properties in the component class in a declarative fashion. The framework extends the component class (demo.Welcome) at runtime and provides the necessary accessors and mutators for these properties using bytecode enhancement. The component class can access these properties via abstract methods to define its behavior as shown in Listing 11.
Page Class
package demo;

import java.util.Date;

import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.html.BasePage;

public abstract class Welcome extends BasePage {

    public abstract void setCustName(String custName);
    public abstract void setDob(Date dob);
    public abstract String getCustName();
    public abstract Date getDob();

    public void submit(IRequestCycle cycle) {
        if (getCustName() != null
            && !getCustName().trim().equals("")
            && getDob() != null) {

            Welcome welcome = (Welcome) cycle.getPage("Welcome");
            welcome.setCustName(getCustName());
            welcome.setDob(getDob());
            cycle.activate(welcome);
        }
    }
}
Listing 11. Welcome.java
The submit method is a listener method attached to the Page's form (see template Listing 7) that will be invoked by the framework automagically when the form is submitted. The submit method forwards the request to the Welcome Page (cycle.activate) if the user provided both the custName and dob. The listener method passes the custName and dob to the Welcome Page prior to activating it. The component class can be obtained from IRequestCycle (cycle.getPage("Welcome")) by providing the component name. cycle is the object provided by the framework that represents the current request-response cycle.
Application Specification
<?xml version="1.0"?>
<!DOCTYPE application PUBLIC 
  "-//Apache Software Foundation//Tapestry Specification 3.0//EN" 
  "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">

<application name="Tapestry Illustration 3">
    <page name="Home" specification-path="CustInfo.page"/>
</application>
Listing 12. Welcome.application
Our Home Page (CustInfo), not following the default naming convention, is specified in the application specification. Reusing the web.xml from previous illustrations and running the Welcome example application within Tomcat, running at port 8080, with the request URL http://localhost:8080/Welcome/app will take us to the CustInfo Page of our Welcome application that we just built.
The framework - a flyby
In the above illustrations, we saw how we can utilize the services provided by the framework to build our applications elegantly and effortlessly. Here, we will skim the framework and unveil some of the key objects and their mechanics, that provide these services.
In the servlet world, every user request goes through a cycle of being sent to the server, mapped to a servlet, processed in the servlet using the query string, Request and Session objects, responded by the servlet by manually building the markup and finally rendered in the browser. This cycle entails a lot of convoluted, shoddy coding that frustrates developers and maintainers. Tapestry framework is a rich API that deconvolutes this coding and shields the developer from the shoddiness. The API, built on top of the servlet API, provides the developer with all the servlet functionality, and a lot more, in a much friendly way, in the form of objects that forms the framework.
We saw in our illustrations that Pages are made of three parts - the template, the specification and the component class, and that a Page may be composed of other components recursively that are in turn each made of the three parts. This means, when a request is made for a particular page, the framework has to build a single HTML page from the three parts of the Page component and recursively from the parts of its contained components. This build task entails resolving, parsing and loading the XML specification, instantiating the component class and resolving, parsing and loading the template. The "loading" step is where the bulk of the work happens that involves building all contained components recursively. This build step for each contained component entails the same steps as the build task of the Page. At the end of the build task, we have an object, Page object, of type org.apache.tapestry.IPage that represents the entire Page. BasePage that we saw earlier is an object of this type. The analagous object that you will have at the end of building a JWC will be of type org.apache.tapestry.IComponent. The making of these objects, as we have just learned, is quite an involved, time-consuming process, and it would be a huge performance impact to discard it after one use. This gives rise to the concept of caching.
When a request is made for a particular page, the framework goes to a Source object, org.apache.tapestry.engine.IPageSource, to get an instance of the requested Page. The Page Source object maintains a pool of Page objects previously built and pops one out for each request. If the pool is empty, it is responsibility of the Page Source object to build the Page object and provide it to the framework. Once the request is processed, the Page is returned back to the Source and put in the pool for future use. In addition to IPageSource, the framework has a few other Source objects that is used in obtaining other resources - org.apache.tapestry.engine.ISpecificationSource and org.apache.tapestry.engine.ITemplateSource are a couple of them. Each of these Source objects caches the objects they provide in a pool just like the Page Source. Ok, we now know that the framework goes to the Source object to retrieve the Page, but how does the framework know which Page was requested in the first place? Well, to answer that, we need a bit of understanding of the wiring in a Page.
When the framework renders a Page, contained components encode framework specific information including the Page name into the Page. Later when the Page is submitted, this information, in the form of request parameters, is read by the framework and parsed to retrieve the Page name and other information used by the framework for request processing. This responsibility of encoding and parsing of request parameters is entrusted with the framework Service object - org.apache.tapestry.engine.IEngineService. All components go to the Service object to encode or parse request parameters. The framework comes with a predefined set of Services for different types of user requests - enter a Tapestry application (org.apache.tapestry.engine.HomeService), navigate to a Page (org.apache.tapestry.engine.PageService), submit a form (org.apache.tapestry.engine.DirectService or org.apache.tapestry.engine.ActionService) just to name a few. When components encode the component-specific request parameters, they also encode the type of Service object they used to encode their request parameters. This enables the framework to go to the right Service object to parse the various components' request parameters. The other responsibility of the Service object is to manage the request processing (includes loading the Page and executing the listeners of the component class) and response rendering (includes setting the correct content type and rendering the Page) tasks. In addition to the framework provided Services, the developer is empowered with the framework API to implement any number of custom Services that one may need. These custom Services are specified in the application specification via the <service> element.
The framework provides support for many other aspects of a Tapestry application viz. localization, application state management, bytecode enhancement and script support to name a few. All these, and other, different aspects of the web application is managed by an aptly named pivotal framework object, the engine - org.apache.tapestry.IEngine. The Engine object is responsible for instantiating and initializing most of the objects needed for processing the request including the Service objects, Source objects, object pools and others. These objects that the Engine initializes are common to all instances of the application and so are held in the ServletContext. Application data common to all instances of the application is stored in a developer provided Plain-Old-Java-Object called the Global. The Global object, like other request-processing objects, is also managed by the Engine and stored in the ServletContext. The developer specifies the Global object via the application specification property org.apache.tapestry.global-class. Data specific to a single instance (user session data) will be stored in a special object called the "Visit". The Visit is also a developer provided Plain-Old-Java-Object that is managed by the Engine. The Engine creates the Visit when necessary and stores it in HTTPSession. The developer specifies the Visit object via the application specification property org.apache.tapestry.visit-class.
A user request mapped to a Tapestry servlet is delegated to the Engine for processing. The Engine handles the controller aspect and delegates it further to the Engine Service. This chain-of-responsibility requires that the objects needed for request processing be passed around to every single object that has any processing responsibility. Passing around a bunch of objects can and will be unmaintainable. To solve this problem, the framework wraps all objects required for request processing in a single object of type org.apache.tapestry.IRequestCycle. Request Cycle wraps the HTTPServletRequest, HTTPServletResponse, Engine, Engine Service and the current Page among other objects. With all this wrapped information, the Request Cycle acts as a facade to the request processing event.
Conclusion
We have explored, in this document, just the basics of what and how Tapestry can solve some of the prominent challenges in Java web development. To write everything about Tapestry requires writing a book and that's exactly what the creator of this framework has been doing the last several months. The book, published by Manning, is expected to be in stores before the coming winter.
Resources