Today our guest is Howard Lewis Ship from the Apache Tapestry team. In the first part of the interview, Howard discusses origins of Tapestry, evolution of the framework and shows its key design concepts. He also explains what frameworks could be nicely used as the UI layer for Tapestry application.
What were the origins of Tapestry? It has a fairly long history, has it evolved over the years?
Howard is the creator and PMC chair of Tapestry Framework
Back in around 1998 or 1999, I was exposed briefly to Apple's WebObjects; this is the granddaddy of all component-based web frameworks. I didn't get the chance to do a full application in it, just some demos and proof-of-concepts, but I was intrigued by the relationship between server-side controllers, elements in the component's template, and client-side markup. Being an engineer, I was pretty interested in how it all worked internally. Since it was closed source, there was no code to look at or people to contact, so I was left with my own imagination.
Somewhere around the summer of 2000, I found myself stuck in the back of a MBTA Green line train (I was living in Boston at the time) in sweltering heat and without air-conditioning, so you can image that my mind wandered. I went from guessing about how WebObjects worked to thinking about how something similar might be implemented in Java.
Later on, somewhere later in 2000, I found myself on the bench, between client projects (I was then a consulting for Primix Solutions); I ended up writing the first version of Tapestry while waiting for my next assignment. It's pretty limited, almost pathetic, compared to what's inside Tapestry 5 now, but some of the core ideas of pages, components, persistent state, and event handlers were present way back then. That code, the "prototype that escaped the lab", was my first big project in Java. I do remember spending several days of frantic coding to get to a "hello world" application.
We used that early version of Tapestry on a pretty sophisticated currency exchange portal for Citibank in 2001; the application was developed but never deployed, alas, due to politics at the client. I still have a lucite paper-weight from that project: the "home" page of the application (which, perversely, was the only page NOT implemented in Tapestry!).
What I don't have is a recollection of how I came up with the name "Tapestry"; I vaguely remember toying with using terms like "Weaver" instead of "Component", but eventually came to my senses. I've always believed in giving projects a real, memorable name, rather than an acronym.
How would you place Tapestry in the world of web frameworks? Is it closer to component ones like Wicket and whole JSF world or a bit like modern MVCs like Grails and Play?
Tapestry is very clearly a component-based framework, which places it in the same camp as Wicket and JSF; SpringMVC, Grails, and Play! are all action-oriented frameworks, like Struts.
Putting modesty aside, I'd like to think that Tapestry has really been a leader from a purely technological perspective. Being a product, and not a standard (such as JavaServer Faces) has meant it could evolve rapidly and radically; the change from Tapestry 4 to Tapestry 5, now five years in the past, was a pretty dramatic leap frog.
Here's a few things that I believe Tapestry did first, and I still think does better than anybody:
- Reusable components (2001)
- Detailed, useful exception report (2001)
- "Invisible" instrumentation in templates (2002)
- Line-precise exception reporting (2004)
- Integrated bytecode weaving/meta-coding (2005)
- Live class reloading (2006)
- Full exception report for Ajax errors (2012)
I'm sure not all of these will be familiar to readers, but they are all part of a specific pattern: making life easier for the developer. My general approach to that is SCEF: Simplicity, Consistency, Efficiency, and Feedback.
Simplicity means that templates and code are concise and readable; it also means (as of Tapestry 5) that you don't extend from framework-provided base classes. I do take some criticism to heart; even some of our sharpest end users have a reasonable complaint, to the effect that "you can customize almost anything in Tapestry, and in only a few lines of code ... but it can take you three days to figure out where those lines of code go."
Consistency means that what works at one level, such as a complete page, works at other levels, such as components inside a page. Thus a nested component has the same freedom to store persistent data, nest other components, and respond to component events as the top-level page ... and the implications of that is what spurs Tapestry's pretty fantastic code reuse story.
Feedback is all important to me; starting with my first forays into Java web development using straight servlets and JSPs. The fact that in those environments, any failure resulted in (at best) a server stack trace in the browser is appalling. The further fact that I'd have to restart in the debugger and, often, pull apart the exception to figure out what actually failed is unacceptable, but all standard operating procedure in many other frameworks. Tapestry is just the opposite; a runtime exception gets a very detailed report, that attempts to include all the information you'd need to determine the real cause. For me, a bare NullPointerException without further explanation is a total failure of the framework, and simply doesn't happen in Tapestry. The line-precise exception reporting means that all the runtime objects in Tapestry keep a reference to the file and line of the template they originate in ... the exception report can say "You have an error on line 10" ... and even show you an excerpt of that file directly in the report.
Basically, if your framework doesn't provide truly excellent feedback, then it is getting in the way: whatever gains you might get form a framework can be offset when the framework doesn't do what you want, and gives you no clue how to fix it.
Efficiency, though, is not about developer productivity per-se. My belief is that no matter how great the development environment is, someone is ultimately going to compare framework or apps based on response times, so I want Tapestry to be at or near the front of the pack. This blog post compares a number of different frameworks on the same simple application functionality. It's a couple of years old, and I don't know how the other frameworks have evolved, but Tapestry 5.3 is significantly faster, operated more concurrently, and consumes considerably less memory, than the Tapestry 5.2.5 used in the tests.
Further, I've always wanted Tapestry to scale up before scaling out ... switching from one server to a cluster, even with sticky sessions, always comes with considerable costs and challenges. Tapestry uses shared data in the HttpSession very differently than other frameworks; where server-side state exists, Tapestry application tend to use more attributes with simpler, immutable values (such as Numbers and Strings), which is more in accordance with how the Servlet API was designed, and fits better with how it is implemented. There are definite ambiguities in how web containers work with HttpSession attribute values that are mutable; Tapestry tries to limit those, and
deal with them correctly when they exist.
Now, all of these are goals, and they are constantly evolving from release to release. It's more a way of thinking "how useful is this feature". In fact, I can easily point to a number of places where I've added a feature that may not be simple, or not consistent, but seemed like a good idea at the time; maybe it made one thing a bit more concise. I now regret those decisions (mostly four or more years old) because they ultimately undermine the usefulness of the framework, by making its inner model more mysterious and difficult to communicate.
Well, I didn't entirely mean this to be entirely a marketing pitch for Tapestry. I think many of the frameworks have very keen ideas, and Tapestry has a bit of baggage I wish I could jettison in a backwards compatible way. Certainly, all of the major players borrow ideas from each other. Many people prefer to the Wicket model, which uses lots of inheritance and inner classes (for callbacks), but doesn't require the kind of "leap of faith" accompanied by Tapestry's runtime bytecode weaving. On the JSF front ... well, you can't really compare a single project to a standard aggressively driven from the top down. Still, some of the IDE integration supporting JSF is quite enviable.
What about UI widgets in Tapestry? It is possible to integrate it with Bootstrap, jQuery UI, YUI, ExtJS?
I have existing clients who have used Tapestry with Sencha's ExtJS for quite some time, though I understand they are on path to switching to jQuery and perhaps jQueryUI. In any case, Tapestry has bundled Prototype and Scriptaculous since the first version of Tapestry 5, and many of the existing components provide a lot of functionality based on these libraries. I'm working on projects that use Tapestry, with jQuery, jQuery UI, Twitter Bootstrap, and even Backbone all at once; it can be done, but you can see some rough edges where all the bits and pieces don't merge together ideally. You also have the issue of deploying a large JavaScript payload that includes this big shopping list of frameworks and libraries -- it can affect that critical first page view time.
The current focus on Tapestry 5.4, the next big release, is almost entirely focused on JavaScript and a big part of that is introducing the long-awaited abstraction layer that will allow the application to select Prototype or jQuery, or virtually anything else (by writing an client-side adapter). We're also going modular, leveraging RequireJS and the asynchronous module definition, to load JavaScript as needed, and in parallel (and hopefully, in much smaller volume).
Tapestry 5.4 will also bundle Bootstrap, and many of the existing components are being updated to make use of Bootstrap's CSS classes.