Tapestry Training -- From The Source

Let me help you get your team up to speed in Tapestry ... fast. Visit howardlewisship.com for details on training, mentoring and support!

Saturday, February 05, 2005

Tapestry, JSF and FUD

Rick Hightower's recent article, "JSF for nonbelievers: Clearing the FUD about JSF" has been prompting some discussion on TheServerSide. Now, one of the good things about JSF is the way it validates (in some people's eyes) the component based approach.

It's natural for me to be politely antagonistic towards JSF. JSF has an event model and a component model and I find it bothersome for them to claim that JSF has the event model and the component model. Their choices are just their choices.

The one thing to keep in mind during any discussion of these technologies is that, from a high enough level, they are all identical. HTTP requests flow in, HTML (or other) responses flow out. This is the same for CGI, Perl, ASP.NET, etc. The differences are not in what's possible, but in how hard it is to create and how easy it is to maintain. While I'm envious about certain aspects of JSF (particularly enforced acceptance from above, lack of enforced inheritance, and having design-time tool support built in), my basic stance, unchanged from reading this article, is that real-world Tapestry applications are faster to build, easier to maintain and extend, and more robust in deployment.

JSF and JSP technology

I find this aspect of JSF quite interesting; the choice to "support" JSP creates some strange behaviors that would be unacceptable if it wasn't coming out of Sun and the JCP. In Tapestry, when any aspect of a page or component changes (and you've disabled caching for development purposes), then templates and the component tree stay synchronized ... because they are the same thing.

Figure 1. Example application from an MVC point of view

The Tapestry equivalent of this is, I think, a bit simpler. There's a lot less to map because the Tapestry HTML template is tied to the component tree. Tapestry component and page classes are the equivalent of the JSF backing bean (though it is quite practical to have both properties and listener methods in a different object entirely).

faces-config.xml

One of the things that bothers me about JSF is the fact that managed beans are entirely global. Tapestry allows each page or component to manage its own set of beans, and has additional lifecycles (the equivalent of bean scopes).

For me, the navigation rules are problematic. Because of how the view (templates) and component model are so loosely tied together, it becomes necessary to add another level of abstraction, the outcome/view-id mapping. If you corner a JSF developer and challenge them about the need for this, the response will likely be something about supporting different views (i.e., WML, Flash, etc.) from the same set of controls.

And that's a problem for me. I've repeatedly asked my audiences at various events who has done this, or needed to do this ... support multiple view types with a single application. I've yet to find anyone who needs to do this or finds it practical. This was the promise of XML/HTTP pipelines before JSF. The problem is, this is a non-starter of an issue ... there's no such thing as a single application that support multiple views.

What there are is multiple similar applications that share common back end processing and data. When you try and have one application shoe horn into multiple view technologies, you are asking for a disaster ... I call it "coding inside a case statement". You'll be adding, removing, moving and morphing so much stuff that you end up with something that is brittle in all views.

My response when this kind of feature is asked for in Tapestry is to challenge the questioner about what the different forms of the application will look like ... and to point out that they are different applications that should share a common back end. In Tapestry terms, they may even share page classes and components ... but the templates should be unique to each view. The ultimate goal is to keep the templates clean because any other approach is incompatible with enterprise application development realities. Good for demos, bad for real life.

By contrast, Tapestry pages concentrate on just on type of markup at a time, typically HTML (but just as easily XHTML, XML or WML). This allows the kind of fine-grained control that demanding page designers expect.

Case in point is Tapestry's support for informal parameters. Tapestry components may optionally support additional, arbitrary parameters beyond the explicit, named (and typed) set of parameters. The majority do. The parameters (which may be literal values, evaluated expressions, or localized message -- just like formal parameters) are passed through as additional attributes on the element. This allows you to specify any and all HTML attributes; particularly useful for dealing with CSS styles or JavaScript event handlers. Having a tight binding between the template engine and the component model makes this easy ... using JSPs (where each JSP tag attribute must be formally defined) would make this impossible.

A final note; I'm dissapointed that the name of the calculator controller bean, CalcBean, is a "simple" name, not a qualified name. From the later examples, it appears that such simple names are mandated by the expression language. On large projects, this will simply encourage naming conflicts. My experience, as far back as 1997, is that naming conflicts can wreak havoc on large team projects ... and Tapestry is specifically designed so that naming conflicts simply don't occur.

Gluing the model and the view

Tapestry allows you to have as much or as little flexibility as you want in terms of where properties and operations are located. My preference is to co-locate properties and operations since that is the very definition of object-oriented programming.

In Rick's example, he demonstrates how the pure business logic can be externalized, by having a Calculator class that is separate from the CalculatorController that is referenced inside JSF.

Certainly the same thing is possible in Tapestry. The equalivalent Tapestry page class would be:

public abstract class CalculatorPage extends BasePage
{
  public abstract int getFirstNumber();
  public abstract int getSecondNumber();

  private Calculator _calculator = new  Calculator();

  // Listener method for adding.

  public void add(IRequestCycle cycle)
  {
    int result = _calculator.add(getFirstNumber(), getSecondNumber());

    showResult(result);
  }


  public void multiply(IRequestCycle cycle)
  {
    int result = _calculator.multiply(getFirstNumber(), getSecondNumber());

    showResult(result);
  }

  private void showResult(int result)
  {
    IRequestCycle cycle = getRequestCycle();
    ShowResult page = (ShowResult) cycle.getPage("ShowResult");

    page.setFirstNumber(getFirstNumber());
    page.setSecondNumber(getSecondNumber());
    page.setResult(result);

    cycle.activate(page);
  }
}

This demonstrates some advantages of JSF and some advantages of Tapestry. JSF doesn't require you to extend from a base class, which is a good thing (and a huge, backwards incompatible change for Tapestry, which is why it hasn't been implemented yet).

The abstract class, with abstract accessor, causes a bit of confusion; it is because Tapestry injects code into your class by subclassing it and filling in the abstract methods. In this way, it can efficiently manage the properties of your page ... storing persistent properties in the HttpSession as they change, and properly resetting the values for transient and persistent properties at the end of each request. This reflects the Efficiency principle of Tapestry ... the expensive to construct page objects are pooled between requests and shared by different sessions from one request to then next; the enhanced subclass fulfills the contract needed by Tapestry to safely share the page objects in this way.

The method invocation is the same; Tapestry 3.0 requires that such listener methods take a single IRequestCycle parameter (Tapestry 3.1 will likely improve on this). The difference is how that method is reference; in Tapestry, the Submit component can reference the method as:

  <input jwcid="@Submit" listener="ognl:listeners.add" value="Add"/>

In Tapestry syntax; this means "an anonymous instance of the Submit component, with its listener parameter bound to the add method of the page class".

An advantage for Tapestry, I think, is the way the two pages (the firsts containing the form, the second displaying the result) communicate ... in proper, type-safe Java code. The first page obtains an instance of the "ShowResult", casts it down from IPage to its actual type, and invokes methods on it to inform it of what it needs to operate. This is a clean interface between the two pages ... the first page concerned with collecting the two values and calculating a result, the second with displaying the two values and the result. The are many variations on this "Tapestry bucket brigade" that will appear more or less efficient. For example, we could have a data object containing the first and second numbers and the result, and pass that single object between the pages.

Again, this is more of an object-oriented approach. The pages of the application are objects, at least for Tapestry. The proper way for them to communicate is via methods and properties ... rather than the engineered coincidence that they both reference the same, arbitrarily named bean stored as an HttpServletRequest attribute. JSF is much more beholden to the Servlet APIs than Tapestry ... which does much more to hide the APIs and the mechanisms they represent.

A final note on this subject; in Rick's example, the CalculatorController was given session scope; a somewhat odd choice. I suspect the reasoning for this will become evident as the examples expand in later chapters. In any case, the Tapestry example will be request scope and the application will itself be stateless (no HttpSession).

JSP vs. HTML template

In this simple example, JSF has a slight advantage, in that JSF input fields include validation by default. Even so, the JSPs here would not preview correctly inside any HTML editor ... it would require a JSF-aware tool to render a preview properly. By contrast, Tapestry HTML templates are ordinary HTML elements and attributes (with an occasional extra, non-HTML attribute thrown in) and will preview properly in any WYSIWYG HTML editor.

In Tapestry, form input validation is an add-on, requiring a different component, TextField. However, Tapestry's form input validation is quite powerful, with tremendous control over look and feel, error message formatting and reporting, and support for complex forms containing loops and conditionals.

Conclusions

Rick has demonstrated that your can assemble simple JSF applications without tool support. JSF does have an improved model over typical Struts development. I look forward to more articles in this series, as I think it will prove an excellent way of differentiating Tapestry 3.0 from JSF.

Certainly, I've seen nothing in any publication about JSF that would make me consider "closing up shop". At a high level, yes, the seem quite similar ... but there are basic assumptions and pervasive practices in both JSF and Tapestry that result in worlds of differences when you sit down to build a real application.

I expect to take a few minutes to put together Tapestry versions of Rick's examples. Monitor this blog for the details ... and remember that Tapestry discussions are best held on tapestry-user@jakarta.apache.org.

8 comments:

Wilkes Joiner said...

I did the the same app in Tapestry today. This app is too simplistic to show off the merits of one framework over another, but it does give you a hint as to the weaknesses of JSF. It seems to me that JSF was designed under the assumption that you would want to keep stuff sitting around in the session. That is how the calculator and result pages communicate. Like you point out, in Tapestry you can relay information from one page to another using plain old Java messages. When I first saw this in Tapestry, it really blew me away. My reaction was "Wow, it really is an OO web framework." My Tapestry version is stateless without any effort or thought on my part. I don't know enough about JSF to make it behave in the same way.

a said...

Howard,

I would love to see you join the JSF Expert Group. I think you could provide a lot of insight in how to make web development faster, more palatable, and more straightforward to debug and fix when something goes wrong.

But, maybe JSF and Tapestry were meant for two different types of developers and/or development shops. It would be interesting to know how many corporations are running Tapestry applications on commercial (read "bought") application servers. I keep reading these comparisons between JSF and Tapestry but maybe the difference is the actual developer environment.

It just seems that JSF and Tapestry are targeting two different developer audiences and they keep getting compared over and over again.

Unknown said...

I'm beginning to feel that, yes, JSF and Tapestry have different target audiences. I think that JSF may be targetting simple, ugly, internal app development ... and area that will soon be dominated by Rich Internet Application development (Flex, Laszlo, or one of their successors).

Tapestry is staying focused on enterprise development, and enterprises tend to be quite picky about exact details of look and feel. Tapestry's component object model also, I've found, lends itself quite well to content management applications. It's very easy in Tapestry, by design, to have non-component objects integrate into the page rendering cycle and do very component-like things (such as generate links or even forms).

I also want to see more details on how JSF addresses complex forms: forms that contain loops and conditionals.

Additional, Erik Hatcher likes to "provoke" JSF-ers, such as Richard Geary, to duplicate the Table component. The Table includes links for sorting and paging through long contents ... these links just work, without any configuration in the application. This is the Tapestry way -- drop it in and let it work. JSF's approach is primarily a view component and apparently can't duplicate this.

Unknown said...

David Geary, not Richard Geary. Don't know why I do that every time (even in person, to my greater embarrassment).

Massimo said...

I've carefully read your post and would like to know what do you think about Barracuda .
I develop some apps with it and even if it's probably more rude then tapestry i it's dev model very clean and robust.
Hope to hear some thoughts of yours about that, maybe more on the -user ML.

Regards

John said...
This comment has been removed by a blog administrator.
John said...

One comment on the Tapestry's ability to preview the HTML template... This feature can easily be oversold. For example, if you are using contrib:Table you would have to be very dilligent to make the HTML table render itself to resemble the highly configurable contrib:Table component. Consider an HTML designer carefully laying out and tweaking an HTML table. Unless the Tapestry programmer mimics these settings, the design time and run time may not resemble each other much.

ciukes said...

From my point JSF is technology with solid background (java specification) made by people who want get money from this (by providing trainings, tools etc.).

My experience with JSF was real pleasure as long as I was using Sun Java Studio Crator. It is really easy to create web application with this. When I switched to "pure" eclipse - pain become.

This is why Tapestry is valuable for me. You don't need extra tools to do your job.

One other thing. There was discussion about what web framework to choose for developed application. I presented Tapestry and it's easy way of creating components with HTML editor (Quanta on Linux) - everyone was amazed. Then JSF was choosen... Most important thing was: JSF has Java specification.

This is the only situation when JSF wins over Tapestry: when you need to say your boss/manager/client you're following specification.