| <!-- $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="intro"> |
| <title>Introduction</title> |
| <para>Tapestry is a comprehensive web application framework, written in Java.</para> |
| <para>Tapestry is not an application server. |
| Tapestry is a framework designed to be used inside an application server.</para> |
| <para>Tapestry is not an application. Tapestry is a framework for creating web applications.</para> |
| <para>Tapestry is not a way of using JavaServer Pages. |
| Tapestry is an alternative to using JavaServer Pages.</para> |
| <para>Tapestry is not a scripting environment. Tapestry uses a component object model, |
| not simple scripting, to create highly dynamic, interactive web pages.</para> |
| <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> |
| <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.</para> |
| |
| |
| <para> |
| Tapestry reconceptualizes web application development |
| in terms of objects, methods and properties |
| instead of URLs and query parameters. |
| </para> |
| |
| <section id="intro.scripting"> |
| <title>Scripting vs. Components</title> |
| <para>Most leading web application frameworks are based on some form of |
| scripting. These frameworks (often bundled into a web or application server) include: |
| |
| <itemizedlist mark="opencircle"> |
| <listitem> |
| <para> |
| <ulink url="http://java.sun.com/products/jsp/">Sun JavaServer Pages</ulink> |
| </para> |
| </listitem> |
| <listitem> |
| <para>Microsoft Active Server Pages</para> |
| </listitem> |
| <listitem> |
| <para> |
| <ulink url="http://www.macromedia.com/software/coldfusion/">Allaire ColdFusion</ulink> |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| <ulink url="http://www.php.net/">PHP</ulink> |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| <ulink url="http://www.webmacro.org/">WebMacro</ulink> |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| <ulink url="http://freemarker.sourceforge.net/">FreeMarker</ulink> |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| <ulink url="http://jakarta.apache.org/velocity/index.html">Velocity</ulink> |
| </para> |
| </listitem> |
| </itemizedlist> |
| </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> |
| <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> |
| <para>For example, here's a snippet from a hypothetical |
| JavaServer Page that displays part of a shopping cart. |
| </para> |
| <informalexample> |
| <programlisting> |
| <emphasis><% |
| String userName = (String)session.getAttribute("userName"); |
| %></emphasis> |
| <h1>Contents of shopping cart for |
| <emphasis><%= userName %></emphasis>:</h1> |
| </programlisting> |
| </informalexample> |
| <para>Most of the text is static HTML that is sent directly back to the client web browser. |
| The <emphasis>emphasised</emphasis> text identifies scripting code.</para> |
| <para>The first large block is used to extract the user name from the &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> |
| <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> |
| <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> |
| <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 %</sgmltag>, |
| 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> |
| <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> |
| <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 works quite differently; it allows existing tags to be marked |
| as dynamic in a completely unobtrusive way.</para> |
| <para>A Tapestry component is any HTML tag with a <varname>jwcid</varname> |
| attribute ("jwc" stands for "Java Web Component"). |
| For comparison, an equivalent Tapestry template to the previous JSP example: |
| </para> |
| <informalexample> |
| <programlisting><![CDATA[ |
| <h1>Contents of shopping cart for |
| <span jwcid="insertUserName">John Doe</span>:</h1> |
| ]]></programlisting> |
| </informalexample> |
| <para>This defines a component named <varname>insertUserName</varname> on the page. |
| To assist HTML development, a sample value, "<literal>John Doe</literal>" is included, but |
| this is automatically editted out when the HTML template is used by the framework. |
| </para> |
| <para> |
| The <sgmltag class="starttag">span</sgmltag> tag simply indicated where the Tapestry component |
| will go ... it doesn't identify any of its behavior. That is provided elsewhere, in |
| a <link linkend="spec.component-specification">component specification</link>. |
| </para> |
| <para>A portion of the page's specification file defines what |
| the <varname>insertUserName</varname> component is and what it does: |
| </para> |
| <informalexample id="intro.ex"> |
| <programlisting> |
| <component id="insertUserName" type="&Insert;"> <co id="intro.ex.co.id-and-type"/> |
| <binding name="value" expression="visit.userName"/> <co id="intro.ex.co.value-binding"/> |
| </component> |
| </programlisting> |
| <calloutlist> |
| <callout arearefs="intro.ex.co.id-and-type"> |
| <para> |
| The <varname>id</varname> attribute gives the component a unique identifier, that matches against the |
| HTML template. The <varname>type</varname> attribute is used to specify which kind of component |
| is to be used. |
| </para> |
| </callout> |
| <callout arearefs="intro.ex.co.value-binding"> |
| <para> |
| Bindings identify how the component gets the data it needs. In this example, |
| the &Insert; component requires a binding for its <varname>value</varname> |
| parameter, which is what will be inserted into the response HTML page. This |
| type of binding (there are others), extracts the userName property from the |
| visit object (a central, application-defined object used to store most |
| server-side state in a Tapestry application). |
| </para> |
| </callout> |
| </calloutlist> |
| </informalexample> |
| <para>Tapestry really excels when it is doing something |
| more complicated than 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> |
| <para>In the JSP world, this would look something like: |
| </para> |
| <informalexample> |
| <programlisting><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>"> |
| <emphasis><% } %></emphasis> |
| <img border=0 src="<emphasis><%= imageURL %></emphasis>" alt="Checkout"><emphasis><% |
| if (showLink) |
| out.println("</a>"); |
| %></emphasis> |
| </programlisting> |
| </informalexample> |
| <para>This assumes that <varname>applicationObject</varname> exists to |
| determine whether the user has entered any checkout items. |
| Presumably, this object was provided by a controlling servlet, or placed into the |
| &HttpSession;. |
| </para> |
| <para> |
| The corresponding Tapestry HTML template is much simpler: |
| </para> |
| <informalexample> |
| <programlisting><emphasis><![CDATA[ |
| <a jwcid="checkoutLink"><img jwcid="checkoutButton" alt="Checkout"/></a>]]></emphasis> |
| </programlisting> |
| </informalexample> |
| <para> |
| A bit more goes into the page's specification : |
| </para> |
| <informalexample id="intro.ex2"> |
| <programlisting><component id="checkoutLink" type="&PageLink;"> <co id="intro.ex2.co.checkoutLink"/> |
| <static-binding name="page">Checkout</static-binding> |
| <binding name="disabled" expression="visit.cartEmpty"/> <co id="intro.ex2.co.link-disabled"/> |
| </component> |
| |
| <component id="checkoutButton" type="&Rollover;"> <co id="intro.ex2.co.Rollover"/> |
| <binding name="image" expression="assets.checkout"/> |
| <binding name="disabled" expression="assets.checkoutDisabled"/> |
| </component> |
| |
| <external-asset name="checkout" URL="/images/Checkout.gif"/> <co id="intro.ex2.co.checkout-asset"/> |
| <external-asset name="checkoutDisabled" URL="/images/Checkout-disabled.gif"/> |
| |
| </programlisting> |
| <calloutlist> |
| <callout arearefs="intro.ex2.co.checkoutLink"> |
| <para> |
| Component <varname>checkoutLink</varname> is a &PageLink;, a component that creates |
| a link to another page in the application. Tapestry takes care of generating the |
| appropriate URL. |
| </para> |
| </callout> |
| <callout arearefs="intro.ex2.co.link-disabled"> |
| <para> |
| The <varname>disabled</varname> parameter allows the link to be "turned off"; here it is turned |
| off when the shopping cart is empty. |
| </para> |
| </callout> |
| <callout arearefs="intro.ex2.co.Rollover"> |
| <para> |
| A &Rollover; component inserts an image; it must be inside some kind of |
| link component (such as the &PageLink;) and is sensitive to whether the link |
| is enabled or disabled; inserting a different image when disabled. Not shown here |
| is the ability of the &Rollover; component to |
| generate dynamic mouse-over effects as well. |
| </para> |
| </callout> |
| <callout arearefs="intro.ex2.co.checkout-asset"> |
| <para> |
| Tapestry uses an abstraction, <link linkend="components.assets">assets</link>, |
| to identify images, stylesheets |
| and other resources. The &Rollover; component wants a reference to |
| an asset, not a URL. |
| </para> |
| </callout> |
| </calloutlist> |
| </informalexample> |
| <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> |
| <para> |
| By contrast, the Tapestry developer is concerned with the behavior of |
| components and has an elegant way of specifying that behavior dynamically. |
| </para> |
| </section> |
| <section id="intro.interaction"> |
| <title>Interaction</title> |
| <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</varname> and that there's a servlet used for making changes to the shopping cart. |
| </para> |
| <informalexample> |
| <programlisting><![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>]]></programlisting> |
| </informalexample> |
| <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: |
| </para> |
| <informalexample> |
| <programlisting><![CDATA[ |
| <tr> |
| <td> <span jwcid="insertName">Sample Product</span> </td> |
| <td> <span jwcid="insertQuantity">10</span> </td> |
| <td> <a jwcid="remove">Remove</a> </td> |
| </tr>]]></programlisting> |
| </informalexample> |
| <para> |
| Because of the component object model used by Tapestry, the framework knows exactly |
| "where on the page" the <varname>remove</varname> component is. |
| It uses this information to build an appropriate URL that references the <varname>remove</varname> component. |
| If the user clicks the link, the framework will inform the component to perform the desired action. |
| The <varname>remove</varname> component can then remove the item from the shopping cart. |
| </para> |
| <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> |
| <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> |
| <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. |
| </para> |
| </section> |
| <section id="intro.security"> |
| <title>Security</title> |
| <para> |
| Developing applications using Tapestry provides some modest security benefits. |
| </para> |
| <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> |
| <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. |
| </para> |
| <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. Again, this is a common limitation of servlet applications |
| in general. |
| </para> |
| <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. |
| </para> |
| </section> |
| |
| <section id="intro.web-app"> |
| <title>Web Applications</title> |
| <para> |
| Tapestry has a very strong sense of what an application is, derived from an |
| XML specification file. This file identifies and gives names to |
| all the pages in the application, and identifies certain other key classes as well. |
| It also gives a human-readable name to the entire application. |
| </para> |
| <para> |
| In other systems, there is no application per-se. There is some kind of |
| 'home page' (or servlet), which is the first page seen when a client |
| connects to the web application. There are many pages, servlets (or equivalent, |
| in other frameworks) and interrelations between them. There is also some amount |
| of state stored on the server, such as the user name and a shopping |
| cart (in a typical e-commerce application). The sum total of these |
| elements is the web application. |
| </para> |
| <para> |
| Tapestry imposes a small set of constraints on the developer, chiefly, that the application |
| be organized in terms of pages and components. These constraints are |
| intended to be of minimal impact to the developer, imposing an acceptible amount of |
| structure. They create a common language that can be used between members |
| of a team, and even between the technical and creative groups within a team. |
| </para> |
| <para> |
| Under Tapestry, a page is also very well defined: It consists of a component |
| specification, a corresponding Java class, an HTML template, and a |
| set of contained components. |
| </para> |
| <para> |
| By contrast, when using JavaServer Pages there are one or more servlets, embedded JavaBeans, |
| a JSP file and the Java class created from the JSP file. There isn't |
| a standard naming scheme or other way of cleanly identifying the |
| various elements. |
| </para> |
| <para> |
| Interactivity in Tapestry is component based. If a component is interactive, such as |
| an image button with a hyperlink (<sgmltag class="starttag">a</sgmltag>), clicking on the link |
| invokes a method on the component. All interactivity on a |
| page is implemented by components on the page. |
| </para> |
| <para> |
| JavaServer Pages bases its interactivity on servlets. Interactive portions of a page must |
| build URLs that reference these servlets. The servlets use a variety of ad-hoc |
| methods to identify what operation is to take place when a link is clicked. Since there |
| is no standard for any of this, different developers, even on the same project, |
| may take widely varying approaches to implementing similar constructs. |
| </para> |
| <para> |
| Because pages are components, they have a well-defined interface, which describes to |
| both the framework and the developer how the page fits into the overall application. |
| </para> |
| <para> |
| At the core of any Tapestry application are two objects: the engine and the visit. |
| The engine is created when the first request from a client arrives at the server. |
| The engine is responsible for all the mundane tasks in Tapestry, such as managing the request cycle. |
| It is sort of a dispatcher, that handles the incoming request and runs the |
| process of responding to the request with a new HTML page. |
| </para> |
| <para> |
| The visit is a second object that contains application-specific data and logic. |
| Its type is completely defined by the application. |
| In an e-commerce application, the visit might store a shopping cart |
| and information about the user (once logged in). |
| </para> |
| <para> |
| Both the engine and the visit are stored persistently between request cycles, inside the |
| &HttpSession; object. |
| </para> |
| <para> |
| The engine also provides services. Services are the bridge between URLs and components. |
| Services are used to generate the URLs used by hyperlinks and form submissions. |
| They are also responsible for interpreting the same URLs when they are later |
| triggered from the client web browser. |
| </para> |
| </section> <!-- intro.web-app --> |
| |
| |
| <section id="intro.features"> |
| <title>Features</title> |
| <para> |
| The framework, based on the component object model, provides a significant number of other features, |
| including: |
| |
| <itemizedlist> |
| <listitem> |
| <para>Easy localization of applications</para> |
| </listitem> |
| <listitem> |
| <para>Extremely robust error handling and reporting</para> |
| </listitem> |
| <listitem> |
| <para>Highly re-usable components</para> |
| </listitem> |
| <listitem> |
| <para>Automatic persistence of server-side client state between request cycles</para> |
| </listitem> |
| <listitem> |
| <para>Powerful processing of HTML forms</para> |
| </listitem> |
| <listitem> |
| <para>Strong support for load balancing and fail over </para> |
| </listitem> |
| <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 source code |
| and compiled into Java classes. This greatly simplifies debugging |
| since no code is generated at runtime. |
| </para> |
| </footnote> |
| </para> |
| </listitem> |
| <listitem> |
| <para>Easy deployment</para> |
| </listitem> |
| <listitem> |
| <para>The <link linkend="inspector">Inspector</link>, |
| which allows developers to debug a running Tapestry application</para> |
| </listitem> |
| </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> |
| <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</para> |
| </listitem> |
| <listitem> |
| <para>XML / XHTML support</para> |
| </listitem> |
| <listitem> |
| <para>Improved WAP / WML support</para> |
| </listitem> |
| <listitem> |
| <para>A real-time performance "Dashboard"</para> |
| </listitem> |
| </itemizedlist> |
| </para> |
| </section> <!-- intro.features --> |
| |
| |
| |
| </chapter> |