Introduction</> <para>Tapestry is a comprehensive web application framework, written in Java.</> <para>Tapestry is not an application server. Tapestry is a framework designed to be used inside an application server.</> <para>Tapestry is not an application. Tapestry is a framework for creating web applications.</> <para>Tapestry is not a way of using JavaServer Pages. Tapestry is an alternative to using JavaServer Pages.</> <para>Tapestry is not a scripting environment. Tapestry uses a component object model, not simple scripting, to create highly dynamic, interactive web pages.</> <para>Tapestry is based on the Java Servlet API version 2.2 It is compatible with JDK 1.2 and above. Tapestry uses a sophisticated component model to divide a web application into a hierarchy of components. Each component has specific responsibilities for rendering web pages (that is, generating a portion of an HTML page) and responding to HTML queries (such as clicking on a link, or submitting a form).</> <para>The Tapestry framework takes on virtually all of the responsibilities for managing application flow and server-side client state. This allows developers to concentrate on the business and presentation aspects of the application.</> <section id="intro.scripting"> <title>Scripting vs. Components</> <para>Most leading web application frameworks are based on some form of scripting. These frameworks (often bundled into a web or application server) include: <simplelist> <member>Sun JavaServer Pages</> <member>Microsoft Active Server Pages (ASP)</> <member>Allaire ColdFusion</> <member>PHP</> <member>WebMacro</> <member>FreeMarker</> </simplelist> </para> <para>All of these systems are based on reading an HTML template file and performing some kind of processing on it. The processing is identified by directives ... special tags in the HTML template that indicate dynamic behavior.</> <para>Each framework has a scripting language. For JavaServer Pages it is Java itself. For ASP it is Visual Basic. Most often, the directives are snippets of the scripting language inserted into the HTML.</> <para>For example, here's a snippet from a hypothetical JavaServer Page that displays part of a shopping cart. </> &start-listing;<emphasis><% String userName = (String)session.getAttribute("userName"); %></> <h1>Contents of shopping cart for <emphasis><%= userName %></>:</h1>&end-listing; <para>Most of the text is static HTML that is sent directly back to the client web browser. The <emphasis>emphasised</> text identifies scripting code.</> <para>The first large block is used to extract the user name from the <classname>HttpSession</>, a sort of per-client scratch pad (it is part of the Java Servlet API; other systems have some similar construct). The second block is used to insert the value of an expression into the HTML. Here, the expression is simply the value of the userName variable. It could be more complex, including the result of invoking a method on a Java object.</> <para>This kind of example is often touted as showing how useful and powerful scripting solutions are. In fact, it shows the very weaknesses of scripting.</> <para>First off, we have a good bit of Java code in an HTML file. This is a problem ... no HTML editor is going to understand the JavaServer Pages syntax, or be able to validate that the Java code in the scripting sections is correct, or that it even compiles. Validation will be deferred until the page is viewed within the application. Any errors in the page will be shown as runtime errors. Having Java code here is unnatural ... Java code should be developed exclusively inside an IDE.</> <para>In a real JavaServer Pages application I've worked on, each JSP file was 30% - 50% Java. Very little of the Java was simple presentation logic like <sgmltag class=starttag>%= userName %</>, most of it was larger blocks needed to 'set up' the presentation logic. Another good chunk was concerned with looping through lists of results.</> <para>In an environment with separate creative and technical teams, nobody is very happy. The creative team is unlikely to know JSP or Java syntax. The technical team will have difficulty "instrumenting" the HTML files provided by creative team. Likewise, the two teams don't have a good common language to describe their requirements for each page.</> <para>One design goal for Tapestry is minimal impact on the HTML. Many template-oriented systems add several different directives for inserting values into the HTML, marking blocks as conditional, performing repetitions and other operations. Tapestry adds exactly one directive, and it's designed to look just like an HTML element.</> <para>A Tapestry component is identified by a &jwc-tag; tag (Java Web Component). For comparison, an equivalent Tapestry template to the previous JSP example: </> &start-listing;<h1>Contents of shopping cart for &start-jwc; id="insertUserName"/&end-jwc;:</h1>&end-listing; <para>This defines a component named <varname>insertUserName</> on the page. Because we use just a single tag, &jwc-tag; for <emphasis>all</> components, it is not possible to say exactly what dynamic content will be inserted ... that is defined elsewhere. The &jwc-tag; tag simply identifies where the dynamic content will go.</> <para>A portion of the page's specification file defines what <varname>insertUserName</> is and what it does: </> &start-listing;<component> <id><emphasis>insertUserName</></id> <type>Insert</type> <bindings> <binding> <name>value</name> <property-path>application.userName</property-path> </binding> </bindings> </component>&end-listing; <para>This declares <varname>insertUserName</> as an <classname>Insert</> component; a component that inserts some text into the response page's HTML. It further identifies what gets inserted (the component's <varname>value</>) as the <varname>userName</> property of the application object.</> <para>Tapestry really excels when it is doing something more complicated that simply producing output. For example, let's assume that there's a checkout button that should only be enabled when the user has items in their shopping cart.</> <para>In the JSP world, this would look something like: </> &start-listing;<emphasis><% boolean showLink; String imageURL; showLink = applicationObject.getHasCheckoutItems(); if (showLink) imageURL = "/images/Checkout.gif"; else imageURL = "/images/Checkout-disabled.gif"; if (showLink) { String linkURL; linkURL = response.encodeURL("/servlet/checkout"); %></emphasis> <a href="<emphasis><%= linkURL %></>"> <emphasis><% } %></> <img border=0 src="<emphasis><%= imageURL %></>" alt="Checkout"><emphasis><% if (showLink) out.println("</a>"); %></emphasis>&end-listing; <para>This assumes that <varname>applicationObject</> exists to determine whether the user has entered any checkout items. Presumably, this object was provided by a controlling servlet, or placed into the <classname>HttpSession</>. </para> <para> The corresponding Tapestry HTML template is much simpler: </> &start-listing;<emphasis><![CDATA[ <jwc id="checkoutLink"><jwc id="checkoutButton"/></jwc>]]></>&end-listing; <para> A bit more goes into the component specification (a separate XML document that describes the configuration of the page and all components on the page; an excerpt is shown here): </> &start-listing;<![CDATA[ <component> <id>]]><emphasis>checkoutLink</><![CDATA[</id> <type>Page</type> <bindings> <static-binding> <name>page</name> <value>Checkout</value> </static-bindint</value> </static-binding> <binding> <name>disabled</name> <property-path>visit.cartEmpty</property-path> </binding> </bindings> </component> <component> <id>]]><emphasis>checkoutButton</><![CDATA[</id> <type>Rollover</type> <bindings> <binding> <name>image</name> <property-path>assets.checkout</property-path> </binding> <binding> <name>disabled</name> <property-path>assets.checkout-disabled</property-path> </binding> <static-binding> <name>alt</name> <value>Checkout</value> </static-binding> </bindings> </component>]]>&end-listing; <para> Again, a bit of hand waving is necessary. Here the <varname>checkoutLink</> component creates a link to the <varname>Checkout</> page of the application, but only if the visit object (an object that tracks client state) enables it (the link is disabled if the shopping cart is empty). The <varname>checkoutButton</> is a <classname>Rollover</> component, which automatically adjusts its image based on whether the link that surrounds it is enabled or disabled (it can also handle changing the image dynamically when the mouse is moved over it). We also reference the image assets, <filename>Checkout.gif</> and <filename>Checkout-disabled.gif</>, indirectly. </> <para> The point of this example is that the JSP developer had to worry about character-by-character production of HTML. Further, the ratio of Java code to HTML is quickly getting out of hand. </> <para> By contrast, the Tapestry developer is concerned with the behavior of components and has an elegant way of specifying that behavior dynamically. </> </section> <section id="intro.interaction"> <title>Interaction</> <para> Let's continue with a portion of the JSP that would allow an item to be deleted from the shopping cart. For simplicity, we'll assume that there's an object of class <classname>LineItem</classname> named <varname>item</> and that there's a servlet used for making changes to the shopping cart. </> &start-listing;<![CDATA[ <tr> <td> ]]><emphasis><%= item.getProductName() %></emphasis><![CDATA[</td> <td> ]]><emphasis><%= item.getQuantity() %></emphasis><![CDATA[</td> <td> ]]><emphasis><% String URL = response.encodeURL("/servlet/update-cart?action=remove" + "&item=" + item.getId()); %></emphasis><![CDATA[ <a href="]]><emphasis><%= URL %></emphasis><![CDATA[">Remove</a> </td> </tr>]]>&end-listing; <para> This clearly shows that in a JSP application, the designer is responsible for "knitting together" the pages, servlets and other elements at a very low level. By contrast, Tapestry takes care of nearly all these issues automatically: </> &start-listing;<![CDATA[ <tr> <td> ]]>&start-jwc; id="insertName"/&end-jwc;<![CDATA[ </td> <td> ]]>&start-jwc; id="insertQuantity"/&end-jwc;<![CDATA[ </td> <td> ]]>&start-jwc; id="remove"&end-jwc;Remove&close-jwc;<![CDATA[ </td> </tr>]]>&end-listing; <para> Because of the component object model used by Tapestry, the framework knows exactly "where on the page" the <varname>remove</> component is. It uses this information to build an appropriate URL that references the <varname>remove</> component. If the user clicks the link, the framework will inform the component to perform the desired action. The <varname>remove</> component can then remove the item from the shopping cart. </> <para> In fact, under Tapestry, no user code ever has to either encode or decode a URL. This removes an entire class of errors from a web application (those URLs can be harder to assemble and parse than you might think!) </> <para> Tapestry isn't merely building the URL to a servlet for you; the whole concept of 'servlets' drops out of the web application. Tapestry is building a URL that will invoke a method on a component. </> <para> Tapestry applications act like a 'super-servlet'. There's only one servlet to configure and deploy. By contrast, even a simple JavaServer Pages application developed using Sun's Model 2 (where servlets provide control logic and JSPs are used for presenting results) can easily have dozens of servlets. </> </section> <section id="intro.security"> <title>Security</> <para> Developing applications using Tapestry provides some modest security benefits. </> <para> Tapestry applications are built on top of the Java Servlet API, and so inherits all the sercurity benefits of servlets. Most security intrusions against CGI programs (such as those written in Perl or other scripting languages) rely on sloppy code that evaluates portions of the URL in a system shell; this never happens when using the Java Servlet API. </> <para> Because the URLs created by Tapestry for processing client interaction are more strongly structured than the URLs in traditional solutions, there are fewer weaknesses to exploit. Improperly formatted URLs result in an exception response being presented to the user. Tapestry URLs are also harder to spoof, since they are very conversational ... the exact form of the URL is dependent on any or all of the previous interactions between the client and the server in the same session. </> <para> Where the Java Servlet API suffers is in client identification, since a session identifier is stored on the client either as an HTTP Cookie or encoded into each URL. Malicious software could acquire such an identifier and "assume" the identity of a user who has recently logged into the application. Still, because of the conversational nature of the Tapestry URLs it would be difficult for an automated intruder to progress through the application from that point. </> <para> Finally, Tapestry applications have a single flow of control: all incoming requests flow through a few specific methods of particular classes. This makes it easier to add additional security measures that are specific to the application. </> </section> <section id="intro.features"> <title>Features</> <para> The framework, based on the component object model, provides a significant number of other features, including: <itemizedlist> <listitem><para>Easy localization of applications</></> <listitem><para>Extremely robust error handling and reporting</></> <listitem><para>Highly re-usable components</></> <listitem><para>Automatic persistence of server-side client state between request cycles</></> <listitem><para>Powerful processing of HTML forms</></> <listitem><para>Strong support for load balancing and fail over </></> <listitem> <para>Zero code generation <footnote> <para>That is, Tapestry templates and specifications are interpreted as is. Unlike JSPs, they are not translated into Java classes and compiled. </> </> </para> </> <listitem><para>Easy deployment</></> <listitem><para>The Inspector, which allows developers to debug a running Tapestry application</></> </itemizedlist> </para> <para> The point of Tapestry is to free the web application developer from the most tedious tasks. In many cases, the "raw plumbing" of a web application can be completely mechanized by the framework, leaving the developer to deal with more interesting challenges, such as business and presentation logic. </> <para> As Tapestry continues to develop, new features will be added. On the drawing board are: <itemizedlist> <listitem><para>Support for easy cross-browser DHTML</></> <listitem><para>XML / XHTML support</></> <listitem><para>Improved WAP / WML support</></> <listitem><para>A real-time performance "Dashboard"</></> <listitem><para>Journaling / Playback</></> </itemizedlist> </para> </section> </chapter>