Creator of Tapestry and HiveMind
Howard Lewis Ship is the creator and lead developer for the Apache Tapestry project, and the creator of the Apache HiveMind project. He has over fifteen years of full-time software development under his belt, with over nine years of Java. He cut his teeth writing customer support software for Stratus Computer, but eventually traded PL/1 for Objective-C and NeXTSTEP before settling into Java.Howard is the author of Tapestry in Action for Manning Publications (which covers Tapestry 3.0), and is currently the Director of Open Source Technology for Formos Software Development. He lives in Portland, Oregon with his wife Suzanne, a novelist.
Howard is polishing the last rough edges of Tapestry 5 in anticipation of a final 5.0 release.
Presentations by Howard Lewis Ship
Introduction to Tapestry 5
Tapestry 5 is a complete rewrite of Tapestry from the ground up. It takes everything good about Tapestry and cranks the volume up to eleven, while removing the frustrating parts of using Tapestry. This session takes the wraps off this new and innovative technology, showing off important new features such as live class reloading (the ability to change your Java classes and continue using the application without interruption or redeployment), the simplified coding model, and the total lack of XML. This session is of interest to those already using Tapestry 4, and those new to Tapestry and ready to jump on the bandwagon."Guerilla Unit Testing Part 1: TestNG
Part one (of two) covers the TestNG unit testing framework, and shows how it integrates with Selenium (for integration testing)."Guerilla Unit Testing Part 2: The Weird and Wonderful EasyMock
In part two (of two) we go in depth on EasyMock, the weird and wonderful tool for creating mock objects on the fly. We'll do a good bit of live coding as we examine how to use, tame and extend this powerful tool."Pragmatic Patterns with Tapestry 5 IoC
Everyone likes the Gang of Four design patterns, but it's not always clear just how to make use of them in your day to day coding efforts. Hidden inside Tapestry 5 is an Inversion of Control (IoC) container that is structured around several common patterns (Chain of Command, Strategy, Decorator and Filter Chain will be covered). This isn't academic navel-gazing ... this is about leveraging the common patterns so that you can write code you can easily test, and about creating frameworks and toolkits that can be easily extended.We'll see how Tapestry uses these patterns, and go from there into how you can apply the same techniques to your own projects, resulting in better, cleaner, more testable code."
Books by Howard Lewis Ship
by Howard Lewis Ship
- Definitive guide to Tapestry 3.0 covering all topics from basic links and forms up through input validation, dynamic JavaScript generation, and custom component creation.
- Available At: http://manning.com/books/lewisship
Tapestry Central
Thursday, May 29, 2008
Tapestry 5.0.12 will be available pretty soon; I'm working on one very important component that's tricky to get Just Right: AjaxFormLoop. This is a component that allows you to dynamically add and remove rows (or divs, list items, whatever) that represent detail objects under a master object ... think line items in an invoice. The trick, of course, it to make it useful as-is, and to make it easy to customize it. It takes a bit of state-management gymnastics to make everything work just right, but it's almost there.
I expect to get this finished up in the next couple of days along with other work, and create a 5.0.12 release early next week.
Thursday, May 15, 2008
At NFJS Boston last month, I ran into Alex Kotchnev. We had a number of chats about Tapestry and spurring wide adoption. I'm still working on some of those ideas. He's a NetBeans user whereas most of the documentation assumes Eclipse or IDEA. He's posted a blog about use Tapestry in NetBeans; specifically, using the Maven support to avoid typing the dreaded Maven project creation incantation.
Monday, May 12, 2008
Renat and Igor's long awaited article, Tapestry for Nonbelievers is finally available. Check it out!
My only real criticism is that, had they focused on Hibernate, some of the code that switches back and forth between entities and entity ids would be handled automatically by tapestry-hibernate.
They're promising to follow through with more articles. I welcome it!
Wednesday, May 7, 2008
I spent some time yesterday revamping the Tapestry 5 Tutorial; you can see the updates at the nightly build site.
In short order we turned the Address object into a Hibernate entity, and stored it in a MySQL database, then used a Grid to show the added Addresses. Later improvements to the Tutorial will show editting and removal of Addresses.
I think the correct reaction to this would be "Dude, where's my code?". The application code for this app is so small you'd think something was missing. But that's what's Tapestry is all about ... providing the structure so that the framework can do the busy work.
I also updated some of the existing screen shots to show the "error bubbles" style of client-side validation that's been in T5 for quite some time.
People have been pining for a 5.0.12 release, but I'm glad I've been holding it back; I've been having a chance to fix bugs, and finding many annoyances as I work on a client project. Working in earnest on a project is always the best way to find the rough edges, and the end result is much more polished.
I think I must be a harsher critic of Tapestry than most; I frequently add bugs along the lines of "when I screw up, Tapestry should tell me how to fix it".
Friday, April 25, 2008
When I was first starting on the Tapestry 5 code base, I started using AspectJ for parts of the framework. I wrote some clever aspects for defensive programming (cementing "don't accept null, don't return null" as an iron-clad rule) and created some aspects to simplify concurrent access to certain resources.
I certainly never expected Tapestry developers to have to use AspectJ: it's certainly a powerful tool, but it has downsides: AspectJ affects the build process in a signifcant way, it's tricky to use properly, it adds a huge runtime dependency and the plugin for Eclipse made the IDE slow(-er) and (more) unstable. I eventually stripped out the AspectJ code entirely and restricted the framework to traditional code (plus, of course, Javassist).
In the interrum a number of aspect oriented touches have appeared in Tapestry. Certainly, the entire mixins concept is an approach to aspect orientation. Likewise, service decorators. In fact, much of the class transformation mechanism is a way of performing aspect-oriented operations on Tapestry component classes. Creating your own parameter binding prefixes (something I often do for client projects) is another aspect: one focused on streamlining how data gets into or out of a component.
However, all of these techniques can be cumbersome; they are built on the Javassist "language" which is another tool that can be difficult to use properly. I've created layers around Javassist to make things easier, but it's still an uphill battle.
Earlier in the week I was able to take the Javassist out of service method advice. However, that's not quite enough ... I want it to be easier to apply advice to component methods just as easily.
Well, I did just that. There is a ComponentMethodAdvice that can be applied to component methods. It's only a slight variation from the MethodAdvice used on service interfaces (the difference being that the invocation allows access to the ComponentResources of the component).
Here's a concrete example: Part of tapestry-hibernate is the @CommitAfter annotation, which is used to commit the running transaction after invoking a method. There's a decorator that can be applied to services, and there's a worker that is automatically threaded into the Component Class Transformation chain of command.
The worker used to use the Javassist approach to decorate method invocations with the desired logic: commit the transaction when the method completes normally, or with a checked exception. Abort the transaction when the method throws a runtime exception.
First, the old code:
public class CommitAfterWorker implements ComponentClassTransformWorker
{
private final HibernateSessionManager _manager;
public CommitAfterWorker(HibernateSessionManager manager)
{
_manager = manager;
}
public void transform(ClassTransformation transformation, MutableComponentModel model)
{
for (TransformMethodSignature sig : transformation.findMethodsWithAnnotation(CommitAfter.class))
{
addCommitAbortLogic(sig, transformation);
}
}
private void addCommitAbortLogic(TransformMethodSignature method, ClassTransformation transformation)
{
String managerField = transformation.addInjectedField(HibernateSessionManager.class, "manager", _manager);
// Handle the normal case, a succesful method invocation.
transformation.extendExistingMethod(method, String.format("%s.commit();", managerField));
// Now, abort on any RuntimeException
BodyBuilder builder = new BodyBuilder().begin().addln("%s.abort();", managerField);
builder.addln("throw $e;").end();
transformation.addCatch(method, RuntimeException.class.getName(), builder.toString());
// Now, a commit for each thrown exception
builder.clear();
builder.begin().addln("%s.commit();", managerField).addln("throw $e;").end();
String body = builder.toString();
for (String name : method.getExceptionTypes())
{
transformation.addCatch(method, name, body);
}
}
}
Can you tell where that logic (when to commit and abort) is? Didn't think so, it's obscured by lots of nuts-and-bolt logic for creating the Javassist method body.
All that stuff with the BodyBuilder is a way to assemble the Javascript method body piecewise. It's certainly meta-programming, but it's only a few steps ahead of writing the Java code to a temporary file and compiling it. That's great for people into the meta-coding concept (count me in) but sets the bar so high that most people will never be able to tap into the power.
That's where the advice comes in: the new version of the worker is much simpler:
public class CommitAfterWorker implements ComponentClassTransformWorker
{
private final HibernateSessionManager _manager;
private final ComponentMethodAdvice _advice = new ComponentMethodAdvice()
{
public void advise(ComponentMethodInvocation invocation)
{
try
{
invocation.proceed();
// Success or checked exception:
_manager.commit();
}
catch (RuntimeException ex)
{
_manager.abort();
throw ex;
}
}
};
public CommitAfterWorker(HibernateSessionManager manager)
{
_manager = manager;
}
public void transform(ClassTransformation transformation, MutableComponentModel model)
{
for (TransformMethodSignature sig : transformation.findMethodsWithAnnotation(CommitAfter.class))
{
transformation.advise(sig, _advice);
}
}
}
Really simple! The advice is very clear on what happens when, as long as you realize that proceed() invokes the original method (the method "being advised"), and also Tapestry's way of handling thrown exceptions: runtime exceptions are not caught by proceed(), but checked exceptions are.
This simplicity opens up the power of aspects without all mental overhead of AspectJ's join points and declarative point-cut language. It's only a fraction of AspectJ's power, but it's a very useful fraction.
Further, AspectJ works entirely in a static way (it primarily works at build time), but this style of AOP is a mix of declarative and imperative. In other words, we can have active code decide on a component-by-component and method-by-method basis what methods should be advised. The above example is typical: it's driven by an annotation on the method ... but other cases jump to mind: triggered by naming conventions, by parameter or return types, or by thrown exceptions. And there's no guesswork about going from the advice to Tapestry services: its the same uniform style of injection used throughout Tapestry.
In the future, we may add other options, such as advice for field access, but what's just checked in is already a huge start.
