Most leading web application frameworks are based on some form of scripting. These frameworks (often bundled into a web or application server) include:
Microsoft Active Server Pages
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.
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.
For example, here's a snippet from a hypothetical JavaServer Page that displays part of a shopping cart.
<% String userName = (String)session.getAttribute("userName"); %> <h1>Contents of shopping cart for <%= userName %>:</h1>
Most of the text is static HTML that is sent directly back to the client web browser. The emphasised text identifies scripting code.
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.
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.
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.
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
<%= 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.
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.
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.
A Tapestry component is any HTML tag with a jwcid
attribute ("jwc" stands for "Java Web Component").
For comparison, an equivalent Tapestry template to the previous JSP example:
<h1>Contents of shopping cart for <span jwcid="insertUserName">John Doe</span>:</h1>
This defines a component named insertUserName
on the page.
To assist HTML development, a sample value, "John Doe
" is included, but
this is automatically editted out when the HTML template is used by the framework.
The <span>
tag simply indicated where the Tapestry component
will go ... it doesn't identify any of its behavior. That is provided elsewhere, in
a component specification.
A portion of the page's specification file defines what
the insertUserName
component is and what it does:
<component id="insertUserName" type="Insert
">
<binding name="value" expression="visit.userName"/>
</component>
The | |
Bindings identify how the component gets the data it needs. In this example,
the |
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.
In the JSP world, this would look something like:
<% 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"); %> <a href="<%= linkURL %>"> <% } %> <img border=0 src="<%= imageURL %>" alt="Checkout"><% if (showLink) out.println("</a>"); %>
This assumes that 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
HttpSession
.
The corresponding Tapestry HTML template is much simpler:
<a jwcid="checkoutLink"><img jwcid="checkoutButton" alt="Checkout"/></a>
A bit more goes into the page's specification :
<component id="checkoutLink" type="PageLink
"> <static-binding name="page">Checkout</static-binding> <binding name="disabled" expression="visit.cartEmpty"/> </component> <component id="checkoutButton" type="Rollover
"> <binding name="image" expression="assets.checkout"/> <binding name="disabled" expression="assets.checkoutDisabled"/> </component> <external-asset name="checkout" URL="/images/Checkout.gif"/> <external-asset name="checkoutDisabled" URL="/images/Checkout-disabled.gif"/>
Component | |
The | |
A | |
Tapestry uses an abstraction, assets,
to identify images, stylesheets
and other resources. The |
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.
By contrast, the Tapestry developer is concerned with the behavior of components and has an elegant way of specifying that behavior dynamically.