Scripting vs. Components

Most leading web application frameworks are based on some form of scripting. These frameworks (often bundled into a web or application server) include:

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"> 1
  <binding name="value" expression="visit.userName"/>  2
</component>
1

The id attribute gives the component a unique identifier, that matches against the HTML template. The type attribute is used to specify which kind of component is to be used.

2

Bindings identify how the component gets the data it needs. In this example, the Insert component requires a binding for its value 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).

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"> 1
  <static-binding name="page">Checkout</static-binding>
  <binding name="disabled" expression="visit.cartEmpty"/> 2
</component>

<component id="checkoutButton" type="Rollover"> 3
  <binding name="image" expression="assets.checkout"/>
  <binding name="disabled" expression="assets.checkoutDisabled"/> 
</component>

<external-asset name="checkout" URL="/images/Checkout.gif"/> 4
<external-asset name="checkoutDisabled" URL="/images/Checkout-disabled.gif"/>

1

Component checkoutLink is a PageLink, a component that creates a link to another page in the application. Tapestry takes care of generating the appropriate URL.

2

The disabled parameter allows the link to be "turned off"; here it is turned off when the shopping cart is empty.

3

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.

4

Tapestry uses an abstraction, assets, to identify images, stylesheets and other resources. The Rollover component wants a reference to an asset, not a URL.

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.