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!

Friday, September 15, 2006

BeanForm Component

One of the compelling features in Rails is the ease with which forms for creating/editting/updating/deleteting objects can be created. Tapestry has a lot of power under the hood with respect to forms, but it still doesn't come cheap enough out of the box. A centerpiece of Trails is a component that builds a full form for editting an arbitrary object. This idea has resurfaced as a new standalone component, BeanForm.

Just plug the following into your page's HTML template:

<span jwcid="@bf:BeanForm" bean="ognl:pojo" save="listener:save" delete="listener:delete"/>

BeanForm will build a complete form from this, adapting to each individual property's type. If you are using EJB3 or Hibernate annotations, BeanForm will pick up those annotations to build out appropriate client- and server-side validations.

And its extremely extensible and customizable even beyond that. Cudos to Daniel Gredler for putting this together.

Monday, August 28, 2006

Tapestry 5 Progress: Class Reloading

Hit one of my first major hurdles for Tapestry 5 this morning: class reloading. Tapestry 5 periodically scans .class files to see if they have changed, and will clear caches and discard class loaders so that it can process the changes. I've finally gotten the code base to a point where I can demonstrate this and it works. And it's fast!

This is a huge productivity win: you change your classes and see the changes immediately. No restart, no redeploy. The same logic works for templates and other resources (you make the change, you see the change).

Speed is excellent; obviously, I have only a tiny fraction of Tapestry 5 implemented, and the page I'm using is very trivial (just a single component). However, even on my laptop, and with all debugging output enabled, refresh is instant.

By the way, here's my page template:

<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">    
    <head>
        <title>First Tapestry 5 Page</title>
    </head>
    <body> 
        <p>
            This is the <span style="color:green">First Tapestry 5 Page, ever!</span>.
        </p> 
        <p>
            Output from HelloWorld component:  <t:comp id="foo" type="HelloWorld"/>                
        </p>
    </body>`
</html>

And here's the HelloWorld component:

package org.apache.tapestry.integration.app1.components;

import org.apache.tapestry.MarkupWriter;
import org.apache.tapestry.annotations.ComponentClass;
import org.apache.tapestry.annotations.RenderTag;

@ComponentClass
public class HelloWorld
{
    @RenderTag
    void renderMessage(MarkupWriter writer)
    {
        writer.write("I Am HelloWorld");
    }
}

I need to do more experimentation; my environment is Eclipse 3.1.2 + Jetty (4) + JettyLauncher plugin. There's ample opportunity for the servlet container to screw us, in terms of their class loaders getting in the way (not showing file changes). Lots of people seem to use the Sydeo Tomcat plugin as well, so I need to check that.

Sunday, August 20, 2006

Update to tapestry-testng

A new version of tapestry-testng has been upload. The version number (1.0.0-SNAPSHOT) has not changed, but there's some new features:

  • Uses TestNG 5.1, the latest version
  • Supports running tests in parallel

The previous versions of tapestry-testng uses a simple instance variable to store the EasyMock control. This is insufficient for TestNG, which creates a single instance of a test case class, and then will invoke test methods on it from multiple threads. This new vesion of the library stores the EasyMock control in a thread local variable.

However, I'm still not 100% certain this will work properly due to how TestNG operates ... it doesn't seem to invoke cleanup methods from the correct thread. See this forum posting. If TestNG is, in fact, broken, then we'll have to wait for a TestNG 5.2 that fixes the issue.

Thursday, August 03, 2006

New version of tapestry-testng

I quietly released a nearly-final version of tapestry-testng a couple of days ago. The new version number is 1.0.0-SNAPSHOT.

The only significant change is that I changed the TestBase base class to extend from org.testng.Assert, so you can access all the assertion methods without doing a static import.

Wednesday, July 26, 2006

Metaprogramming Java with HiveMind

Holy time management, Batman! I'm going to get a bad rep at OSCON for running over time (last year, though, it was due to a late start).

Basically, I was pacing myself for a 60 minute session, not the 35 they give you. Way too many slides, too much intro, kept me from the cool stuff at the end of the session. Also, there were lots of good, informed questions.

My goal for the Tapestry session, later today, is to run under. But I don't see that happening.

Tapestry for PHP?

PRADO is a PHP framework expressly inspired by Tapestry. As you might expect, all I've had a chance to do is glance over the documentation, but it looks very nice, and their site is very professional and slick.

I suspect the relationship between PRADO and Tapestry is the same as between Tapestry and WebObjects ... the precursor "proves" the space is viable, but I doubt the implementation is all that similar, given the different languages involved.

Still, best of luck ... but don't expect any framework, on any platform, to keep up with Tapestry 5!

Monday, July 24, 2006

Tapestry 5 Updates

Even during OSCON, I've been churning out code for Tapestry 5.

The new Tapestry IoC container is rapidly coming together. The idea of replacing XML with Java classes (and naming conventions and annotations) is working out wonderfully. I'm busy putting together a version of configurations about now.

What I'm finding, as I code and also predict how the final Tapestry code will use the container, is some steep improvements.

In Tapestry IoC, injection isn't into your beans via property setters or constructors, the way it is in HiveMind and Spring. Instead, injection is via parameters to your builder methods. Injection via method parameters will also occur into decorator methods (the replacement for service interceptor factories) and into contribution methods (which contribute values into service configurations).

For example, in Tapestry IoC:

package org.example.myapp.services;

import org.apache.tapestry.ioc.annotations.Id;

@Id("myapp")
public class MyAppModule
{
  public Indexer buildIndexer()
  {
    return new IndexerImpl();
  }
}
The above defines a service "myapp.Indexer" as an instance of IndexerImpl. The equivalent in HiveMind would be :
<module id="myapp" version="1.0.0" package="org.example.myapp.services">
  <service-point id="Indexer">
    <create-instance class="IndexerImpl"/>
  </service-point>
</module>
That's already an improvement. By the time you start talking about dependencies, things get even better:
  public Indexer buildIndexer(String serviceId, Log serviceLog, 
    @InjectService("JobScheduler") JobScheduler scheduler, 
    @InjectService("FileSystem") FileSystem fileSystem)
  {
    IndexerImpl indexer = new IndexerImpl(serviceLog, fileSystem);
      
    scheduler.scheduleDailyJob(serviceId, indexer);

    return indexer;
  }

What's worthy of note here is how dependencies get injected in (as method parameters), and that lifecycle concerns about the IndexerImpl are separate from its implementation. We don't have to inject the scheduler into IndexerImpl in order for the Indexer to be scheduled by the scheduler ... that concern can be implemented only in the builder method.

To accomplish that kind of separation in either HiveMind or Spring would require quite a bit of hackery; say, creating a new, specialized kind of ServiceImplementationFactory (in HiveMind) that understood about talking to the scheduler. Lots of work for something that can be expressed so easily in a short, simple, testable Java method.

I think that we'll see this approach bear fruit in the form of fewer services to accomplish the same goals. It will allow for non-service objects to easily receive injected services ... such objects can be created "on the side" by builder methods (or contributor methods).

This is the theme for all of Tapestry 5: Simpler, easier, faster, more understandable, more powerful. Avoid XML. Improve productivity. Make the framework adapt to your classes and your methods, rather than the other way around.

Maven Thoughts

As much as I disliked Maven 1 , I've come to enjoy and rely on Maven 2.

It's getting things done for me. It's fast. The Maven plugin for Eclipse (that is, an Eclipse Plugin that support Maven, rather than the Maven plugin that generates Eclipse control files) seems to work well, automatically picking up changes to my pom.xml, as well as automatically downloading dependencies. And I'm using it in a tough way, given that I'm also using AJDT on the same projects.

I also think that the new "almost plain text" format is a godsend; it makes it much eaiser to quickly assemble good documentation. I'm trying to write documentation before writing code for Tapestry 5.

Still, maybe 20% of the time, I don't feel that I'm using a tool so much as appeasing a petty god. There's still a number of things I can do easily in Ant that seem to require writing Maven Mojos and plugins to do in Maven.

Sunday, July 02, 2006

Synchronization Costs

I've been doing a bit of work on the Tapestry 5 code base. I'm really interested in making Tapestry 5 screaming fast, and since the code is based on JDK 1.5, we can use concurrency support. Previously, I've blogged about using an aspect to enforce read and write locks. I decided to write a simple benchmark to see what the relative costs were.

As with any benchmark, its only an approximation. I tried enough tricks to ensure that Hotspot wouldn't get in there and over optimize things, but you can never tell. HotSpot is a devious piece of software.

I got interesting, and strange, results:

For a base line, I executed the code with no synchronization whatsoever (simple). The cost of synchronization (synched) shows that synchronization is pretty darn cheap, just an increment on top of the baseline code. The aspect graph shows the cost of using the @Synchronized aspect to maintain a reentrant read/write lock (that is, shared read lock combined with an exclusive write lock). Finally, the rw graph shows the cost of writing code that maintain the read/write lock in normal code (rather than having it added via the aspect).

Synchronization has some overhead. Using the @Synchronization aspect is about 4x as expensive as just using the synchronized keyword on a method. Strangely, the aspect version operates faster than the pure code version for reasons I can't explain, except it must have something to do with how AspectJ weaves my code (a lot of code I write ends up as private static methods after weaving, which may have some runtime performance advantage).

These results demonstrate an important tradeoff: if your application only occasionally has multiple threads hitting the same methods, then you might want to choose synchronized, since you aren't in danger of serializing your threads. By serializing, we mean that only one thread is running and all other threads are blocked, waiting for that thread to complete a synchronized block. Serialized threads is what causes throughput for a web site to be bad, even though the CPU isn't maxed out ... it's basically, Moe, Larry and Curly fighting to get through a single, narrow door all at the same time (they race to claim the single, exclusive lock).

Tapestry, on the other hand, will have a number of choke points where many threads will try to simultaneously access the same resource (without modifying it). In those cases, a shared read lock (with the occasional exclusive write lock) costs a little more per thread, but allows multiple threads to operate simultaneously ... and that leads to much higher throughput. Here, Moe, Larry and Curly get to walk through their own individual doors (that is, each of them has a non-exclusive read lock of their own).

As with any benchmark, my little test bench is far, far from a simulation of real life. But I think I can continue to make use of @Synchronized without worrying about tanking the application. In fact, just as I predicted Tapestry 4 would out-perform Tapestry 3, I believe Tapestry 5 will out perform Tapestry 4, by at least as much.

Thursday, June 29, 2006

Chris Nelson talks to TSS about Trails

Chris Nelson's interview with TheServerSide has been published. In the interview he talks about Trails, Rails and Tapestry. The interview is full video here. Alas, viewing the interview requires an install of the obnoxious RealPlayer.

Wednesday, June 28, 2006

Ajaxified Javadoc with Tapestry

Just found out about www.javaref.com which is a single site that contains JavaDoc for over 80 open source frameworks. The site itself is built on Tapestry 3, Tomcat and MySQL and uses a lot of slick Ajax effects, including an update-in-place data grid and nice tab views, plus a few kinds of "spring loaded" dialogs. It's always gratifying to see people do something really nice and really slick (and really useful) with Tapestry.

Tuesday, June 20, 2006

Beyond domain specific languages

Most of my friends, growing up in the late 70's and early 80's, have played Zork or some other kind of interactive fiction.

Although the earliest adventures used just a simple VERB NOUN syntax, the Infocom games had much more powerful parsers that could understand more complete and natural inputs such as "take all the rings but the silver one" or "put the red battery inside the lantern".

In high school (say, around 1982), I wrote a very stupid, very simple adventure game, "Obelisk Adventure", in PDP-11 Basic. It had pointless quests, a maze with a big nasty rat (the maze wasn't mappable, because it randomly moved your stuff around). I think I also remember an endless hallway (poorly implemented). I also implemented a few similar adventures on my home computer (first a Challenger 1P, later an Atari 800). This was my claim to fame for a while, as pathetic as it appears in retrospect.

These games didn't go away after Infocom and the other publishers disbanded due to the onslaught of more graphic games (such as Castle Wolfenstein and, eventually, Doom and Quake). The underlying virtual machine was reverse engineered and an open source community grew up around virtual machine implementations, as well as languages and tools that compiled down to that machine: Graham Nelson's Inform.

The Inform language is a mix of general purpose features, and a lot of very domain specific stuff -- specific to the idea of simulating a world of rooms, objects and actors, and specific to the hooks of the natural language parser. Here's some Inform 6 source, stolen from the Wikipedia entry:

Constant Story "HELLO WIKIPEDIA";
Constant Headline "^An Interactive Example^";

Include "Parser";
Include "VerbLib";

[ Initialise;
    location = Living_Room;
    "Hello World";
];

Object Kitchen "Kitchen";
Object Front_Door "Front Door";

Object Living_Room "Living Room"
    with
        description "A comfortably furnished living room.",
        n_to Kitchen,
        s_to Front_Door,
    has light;

Object -> Salesman "insurance salesman"
    with
        name 'insurance' 'salesman' 'man',
        description "An insurance salesman in a tacky polyester 
              suit.  He seems eager to speak to you.",
        before [;
            Listen:
                move Insurance_Paperwork to player;
                "The salesman bores you with a discussion
                 of life insurance policies.  From his
                 briefcase he pulls some paperwork which he
                 hands to you.";
        ],
    has animate;

Object -> -> Briefcase "briefcase"
    with
        name 'briefcase' 'case',
        description "A slightly worn, black briefcase.",
    has container;

Object -> -> -> Insurance_Paperwork "insurance paperwork"
    with
        name 'paperwork' 'papers' 'insurance' 'documents' 'forms',
        description "Page after page of small legalese.";

Include "Grammar";

And you can do a lot with that language, and I've occasionally looked into trying my hand at it. But it never seemed like a good use of my time (if I'm not working on Tapestry, I try to find things that get me away from my computer, and preferably, out of my house).

... so I was stunned and amazed when I saw the new version of Inform, Inform 7. The language above is gone and replaced with a natural language of tremendous power and flexibility. The same example, also off Wikipedia, in the new language:

"HELLO WIKIPEDIA" by A Wikipedia Contributor

The story headline is "An Interactive Example".

The Living Room is a room. "A comfortably furnished living room." The Kitchen is
north of the Living Room. The Front Door is south of the Living Room.

The insurance salesman is a man in the Living Room. The description is "An insurance
salesman in a tacky polyester suit. He seems eager to speak to you." Understand "man"
as the insurance salesman.

A briefcase is carried by the insurance salesman. The description is "A slightly
worn, black briefcase." Understand "case" as the briefcase.

The insurance paperwork is in the briefcase. The description is "Page after page of
small legalese." Understand "papers" or "documents" or "forms" as the paperwork.

Instead of listening to the insurance salesman:
    say "The salesman bores you with a discussion of life insurance policies. 
    From his briefcase he pulls some paperwork which he hands to you.";
    now the player carries the insurance paperwork.

Graham's goal was to allow non-programmers to write interactive fiction. The "code" is readable by human and compiler alike. Many of the examples in the documentation maintain this look, even when doing complex things like adding new verbs to the grammar (though some of the advanced examples are less natural language, and occasionally some Inform 6 code is injected as well).

What's important is that is not COBOL. It's not complex and rigid and verbose ... it is, in fact, very concise and the parser is very, very flexible and adaptive to the user. For example, you can easily talk about related nouns in a pretty random order. By editting the source, you can discuss the briefcase before describing the insurance salesman ... I tried this, juggling the order of the paragraphs in the example and the game still compiled and worked identically.

It also has a lot of cool features, such as instead of reading the letter the third time, say "Re-reading the letter isn't going to change what it says." The language is expressive enough to understand lots of dependent information, such as actions occuring in sequence, related to the "game clock", or repetition. Much of the game logic takes the form of "before" and "instead of" rules around normal actions, built into the language (and its standard library).

This language is partnered with a special purpose IDE and reams of excellent, hyperlinked, integrated documentation and examples. Further, the IDE has built in regression testing capabilities (you can play a game within the IDE and record the responses, then later play back the game after changing the source). The IDE also generates maps and indexes of the game.

Sure, he's been working on this for a long time (Inform 6 is nearly ten years old). But the possibilities of this, taken out of this specific domain, are impressive. Imagine writing complex behaviors in succinct natural language instead of verbose XML. I'm normally against MDA approaches, but I think this kind of natural language deserves a careful assessment to see where it can be used within the enterprise.

Saturday, June 10, 2006

Teamwork Live --- Powered by Tapestry

And yet another very nice Tapestry application: TeamWork Live. This was created in San Francisco by CollectiveSoft and is meant to be a competitor to Basecamp.

Friday, June 09, 2006

Vaisala StrikeNet online

The "little" project that's taken up a lot of my time over the last year, Vaisala StrikeNet is now online. Vaisala is my client in Tucson, AZ that I've occasionally mentioned. It's a bit of a niche application (Vaisala tracks lightning strikes across North America, mostly for use by the insurance industry) but it is written using Tapestry 4 ... in fact, I made improvements to Tapestry 4 based on "rough edges" I found while coding this application.

Monday, June 05, 2006

Whoa ... Spring doesn't lazily instantiate beans?

Just stumbled across a blog about Lazy Bean instantiation in Spring 2.0. This is kind of funny to me ... lazy instantiation is so important, so part of the base line of IoC container functionality, that I just assumed Spring already did this.

Update: Spring has had lazy instantiation since at least 1.2, but it isn't on by default.

For the record: this is one of the essential services provided by HiveMind since day 1. HiveMind lazily instantiates everything it can, and is smart, using a pair of proxies for efficiency.

Why a pair? For threading efficiency. The outer proxy is visible to the world and delegates (initially) to the inner proxy. The inner proxy has its methods synchronized as it is responsible for thread-safe lazy instantiation. Once the service (and its interceptors) are instantiated, the inner proxy replaces itself inside the outer proxy. That is, once the service is instantiated, the outer proxy delegates directly to the service implementation, and the inner proxy is no longer needed (it is released to the garbage collector).

Doing things this way is a great idea, but not my great idea. It was suggested a long ways back by someone in the HiveMind community. I'd like to extend proper credit on this, but my memory is weak.

Using double proxies is a very powerful technique, since it ensures thread-safe, just-in-time instantiation of the service, without paying the cost of synchronizing every method in the outer proxy. It's an example of what an IoC container buys you ... I don't think you'd want to code three implementations of every service interface manually, but if you skimp, you undercut the performance and scalability of your app. Using HiveMind, two of the three implementations (the outer and inner proxies) are just provided for you.

So, that's a new answer to one of the more frequent questions posed to me: Why does Tapestry use HiveMind and not Spring?. A: Spring does not provide all the capabilities that Tapestry requires, such as lazy instantiation. Tapestry consists of about 200 services, but very few of those are needed at startup, and a percentage will not be used in most applications. Lazy instantation is a huge win for Tapestry.

Futher, HiveMind's approach to lazy instantiation and proxies means that you can have mutually dependant services ... services that are injected into each other. The proxies, and the lazy instantiaton, means that you bypass the normal chicken-and-the-egg problem of which service to instantiate first. I've used this technique to break untestable, monolithic code into two halves that can each be properly unit tested.

Back to Spring ... now that it looks like Spring 2.0 has lazy instantiation, I have to question the fact that it defaults to OFF.

Sunday, June 04, 2006

And we're back ...

Just got back from three weeks away from everything: first a week at JavaOne, where Tapestry received a Duke's Choice Award in the open source category. From there, it was two weeks on the island of Hawaii (the "big island" ... it's about the size of Connecticut). That was two weeks where I had virtually no internet connection (certainly not at the cottage I was staying at) and really, really didn't even think about Tapestry. Instead, it's been horseback riding, hiking, snorkeling and boogie boarding. Sun, surf and sand. That's quite a change, and a good break (normally, I close my eyes and start seeing code). This was good for me, I'm feeling just a little less frantic than before ... I've been doing too much for so long that I've forgotten what it means to relax. I'm literally tan, fit and well-rested.

I'm also pretty much done with my work at Vaisala, with that app going live in a couple of days. I'm looking forward to a chance to focus on Tapestry full time for a bit, I have code for Tapestry 5 to write, and updates to labs and examples and documentation, new sessions to write (such as for OSCON 2006). Meanwhile, Jesse and Brian have been kicking ass on Tapestry 4 and Tapestry 3, and Geoff looks ready to get Spindle upgraded to Tapestry 4. So there's a lot going on.

I'm also fired up to get people's attention on Tapestry. We need more articles and more evangelism and more books on Tapestry and I'll have more time to spearhead and motivate people on those lines. I'm already beginning to think of 2006 as the Summer of Tapestry.

Tuesday, May 16, 2006

Tapestry: Duke's Choice!

In something that was a bit of a surprise, even to me, Tapestry won a Duke's Choice award for innovation at this years JavaOne.

They gave me a big, wacky Duke statuette (photos to follow). I mean, I'm going to have to hire a pack mule to get it back to Portland. I also had a chance to chat with James Gosling about Java. I complimented him on the language and told him how much fun I'm having with it, which he seemed to like. On the other hand ... when I mentioned wanting "all the goodness of Java, but a little less typing", ala Ruby, he went a little cold. I didn't realize until this moment that he might have thought I was referring to object typing (an issue for another day) when I was, in fact, talking about keystrokes!

Meanwhile, I had to drop off the statuette in the room, so I've probably missed the Java bloggers meetup at the Thirsty Bear. We'll see if I can catch the end of that.

Been having a lot of fun so far ... maybe tomorrow, I'll even make it to a session!

Monday, May 08, 2006

NFJS Meltdown

I had a "perfect storm" of technical problems at yesterday's No Fluff Just Stuff which, sadly, scared away part of the audience. I had just re-installed Eclipse on my laptop, reorganizing my plugins into multiple extension folders (so make my eventual upgrade to Eclipse 3.2 easier) and I inadventently installed a new version of the Eclipse Jetty Launcher plugin, version 1.4, that is not compatible with Eclipse 3.1, just with 3.2. Ouch!

So, a lot of fumbling there that got in the way, took up time, and put me off my game. But people were still very interested and involved, with good questions. I have to think about the JavaScript stuff though ... it may just be too much for people who haven't seen Tapestry before.

See folks at JavaOne!

Tuesday, May 02, 2006

Synergy

So I find myself with a crowded desk: one laptop, a mouse for the laptop, a keyboard, mouse, and two LCDs for my desktop. It's getting crowded, and it gets annoying to switch between different keyboards.

Worse, I do all my Thunderbird email just off the laptop (though I use GMail on either or both). That's a problem when I need to copy text from the desktop to the laptop so I can mail it.

Solution: Synergy -- an open source tool that links my computers together.

Synergy allows me to use my desktop keyboard and mouse on both systems. The mouse travels off the left side of my desktop screen onto the right side of my laptop screen. Focus follows mouse, at least in terms of system-to-system, so I can type on one system (say, the desktop), move the cursor over to the laptop, and type into the active window there (without clicking). Synergy even updates window decorations.

Better yet ... Synergy supports cut-and-paste of text between the two systems. So I can copy text out of my browser on the desktop and paste it into compose window on the laptop. I had thought about using a KVM switch at one time, and this kicks its ass! In fact, had I discovered this a few weeks ago, I might not have bought a mouse for the laptop.

Not an issue for me, but it's also cross-platform. It supports Windows, Linux and (with some problems) Mac OS X.

Monday, May 01, 2006

Tapestry 5 Class Reloading

One of the big advantages of the scripting approaches to Web development (including JavaServer Pages at one extreme, and Ruby on Rails at the other) is the velocity of change: you can change a source file (a JSP, a Rails template, or even a Rails class) and see the change immediately.

Tapestry has traditionally been more aggressive at caching. Once a template or a XML file has been loaded into memory, it's stuck there. During development, you can use the -Dorg.apache.tapestry.disable-caching=true trick to turn caching off, but this not only slows down every request, but includes a memory leak that will eventually tank the application. It also does not reload classes the way it reloads templates and XML files.

In my quest to create a Java web development environment as compelling as Rails, one of the first things to tackle is this aspect of development: effective and efficient reloading of component classes.

The first pass at that code is now in the repository. Component classes are loaded in their own class loader (this is also necessary for the Tapestry AOP code that transforms classes as they are loaded into memory). When any transformed classes are changed externally, the class loader, as well as all instances derived from those classes, are discarded and a new class loader is created.

This will work quite well, far better than Tapestry 4's development mode. In production, you'll just throttle down how often Tapestry checks to see if underlying files have changed.

What's interesting is that I've been working, off and on, on this code base since mid-February. This compares to how long I spent on the initial prototype of Tapestry way back in January 2000, where I spent a couple of weeks of furious coding just to get to a simple "Hello World". In those couple of weeks, I "invested" Tapestry with many of the features that would grow with it, and support that growth, over the intervening years: the component hiearchy, the original and primitive form of templates, component parameter bindings, page loading, external XML page and component specifications, and other things long forgotten. To me, it was like winding up a spring ... all that upfront effort paid off with a basic design that was "right enough" to keep Tapestry innovative for many years.

The reason I'm starting with a new code base for Tapestry 5 is that it's time to wind up that spring again. The couple of design errors, only apparent after the fact, in Tapestry 4 are all but insurmountable without a fresh start (this primarily includes the requirement to extend base classes, but there are many more subtle things that need addressing). Further, the fact that I've spent a chunk of time getting the basic class loading and AOP support in place, the fact that anything web application is still weeks or months off, is actually a Good Thing. The spring is going to be wound nice and tight, full of energy for years to come.