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!

Tuesday, March 22, 2005

Tapestry Portlet Hello World

Well, it's only been a few weeks of hacking, but I do have Tapestry Portlet Hello World running side eXo:


For the moment, I'm calling these "Taplets" (Tapestry Portlets). There's still a lot of work to be done, but this is very encouraging.

Meanwhile, the changes in 3.1 continue to mount and the other committers (including Erik Hatcher, who has been a Tapestry 3.1 alpha guinea pig) have started to chant 4.0! 4.0!. That's one option, another is to restore a few missing things, like RequestContext as access point to the HttpServletRequest. Also, the fact that the AbtractComponent base class makes the getMessages() and getComponentId() methods abstract is giving Eric more headaches.

Of course, if it becomes Tapestry 4.0, I would start really fixing things, starting with abstract classes and required inheritance. Here's a thumbnail of what Tapestry 4.0 would do:

  • The page/component hierarchy still exists, but there's exactly one implementation of IComponent.
  • Each component has a peer; this is the class you provide.
  • The peer has the component injected into it.
  • The peer does not extend any class, though it can implement the various listener interfaces (such as PageValidateListener).
  • The peer may implement IRender, in which case the component will delegate rendering to the peer (otherwise, the component will render using a template).
  • The page/component specification is optional.
  • Page/component names must map directly to Java classes (by searching for classes within a list of packages). These classes are for the peer.
  • The .page file must be in the same folder as the .class file.
  • Each page or component must have a unique peer class.
  • The class loader will be extended to modify the peer class as it loads:
    • Instance variables are self-encapsulated (i.e., _myProperty --> getMyProperty()).
    • Accessor methods are rewritten for transient, persistent, or parameter properties
    • Other enhancements as with 3.1 today (i.e., to inject assets, components, ASOs, etc.).

It's pretty daunting, especially considering my current schedule, and it needs to be fleshed out some. The end result will be more distant from Tapestry 3.0 than 3.0 is from 2.3. It will be a chance to clean house: to clearly separate APIs (such as IComponent) from SPIs (such as TemplateParser or ComponentSpecificationSource). In fact, I would love to have a internal package where backwards compatibility from release to release is a non-issue. It's also a chance to get rid of the leading 'I' on interfaces!

Scary stuff; times like this I really feel the weight of the world on my shoulders.

Monday, March 21, 2005

Reminder: Orlando JUG

As a reminder, I'll be presenting a session on Tapestry to the Orlando JUG on April 12, 2005. Thanks again to Jay Zimmerman, of NoFluffJustStuff Symposiums for setting this up.

Thursday, March 17, 2005

Tapestry CMS components

There are some people doing some pretty radical stuff with Tapestry of late. Kris Rasmussen recently showed off a number of screen shots of a very client-side intensive Tapestry CMS application.

All of the menus and behavior are done on the client side, using Tapestry. Basically, an outer ContentShell component wraps around some number of ContentSection components ... each of which is a container for content from a repository. If you are logged in as the administrator, the ContentShell displays the menus and allows you to upload images, edit content, etc. Exceptionally cool stuff! I hope he releases it as open source at some point.

Wednesday, March 16, 2005

See you at JavaOne!

Official news came down this morning; I will be presenting Tapestry at JavaOne 2005. I have a standard 60 minute time slot (45 minute presentation, 5-10 minutes of Q&A) and that's going to be a challenge. In addition, I'll be on the Web Frameworks Expert Panel, an hour-long, five-way discussion that will (tentatively) include Ed Burns for JSF, Craig McClanahan for Struts/Shale, Jason Carreira for WebWork, myself for Tapestry, and possibly one additional, unnamed person (Echo? Wicket?).

800

Just added a few more portlet tests to the Tapestry test suite ... there are now 800 tests in the suite (plus about 650 in the HiveMind test suite). That's a lot of tests! Since I adopted EasyMock as the core of my unit testing strategy, the number of tests in both products has exploded. However, I think I'm hitting a ratio of 3:1, maybe even 5:1, test code vs. production code. In this respect, my current development style, especially for something as ambitious as Tapestry 3.1, is a marathon and not a sprint.

More things for my to do list:

  • Look at integrating Tapestry and Spring WebFlow
  • Work on the Tapestry Getting Started guide

Tuesday, March 15, 2005

Pressing forward with Portlet Tapestry

The refactoring has been continuing for Tapestry. I've been working for weeks, adding features and doing the refactoring needed to support the Servlet API and the Portlet API simulateneously.

Over five years ago (yowza!) when I was first working on Tapestry, there was a point early on where I had to code for well over a week just to get to "Hello World". In retrospect, that was a sprint and this is a marathon ... also, I don't code the same way anymore (writing tests as you go is a slower process that gets your to your destination faster).

Regardless, in the next couple of days, I should get to "Hello World", Portlet style. Of course, that's just part of my to do list:

  • Write some Tapestry articles/tutorials for DeveloperWorks
  • Create some Ajax components
  • Learn Trails
  • Create a Trails presentation for NoFluffJustStuff
  • Writes some more labs
  • Learn enough about JSF to compare it to Tapestry properly
  • Get the Tapestry Support Network up and running
  • Create a forum/community/component-bazaar application for the Tapestry Support Network

The worst is, I'm sure I'm forgetting a bunch of things!

Wednesday, March 09, 2005

Got my laptop back

When I told people my tale of woe concerning my laptop, I got a fairly universally negative response about Dell. All I can say is that, short of dumping a tall glass of water into my keyboard, I've had zero problems with my Inspiron 8200 for nearly three years.

In fact, Dell under-promised and over-delivered. The five to seven business days turned into ... two. They picked it up Monday afternoon, and I got it back Wednesday afternoon with a new keyboard and new (or refurbished) system board. Everything seems to be working fine. I was steeling myself for a full re-install of Windows and all my tools (which is not a bad idea, to be sure!) and instead I'm back up and running as if it never happened.

So I'm back to puzzling out the eXo portlet container. Looks like it will be easier, once I get the hang of it, to test my Portlet Tapestry using eXo than Jetspeed ... it's just a much richer, more polished tool. Ultimately, I'll verify against a number of different containers.

Tuesday, March 08, 2005

OOSCON 2005

Just received my invite to present at the O'Reilly Open Source Convention, August 1 - 5, 2005 in Portland, OR. Erik Hatcher has spoken very highly of this conference ... but trying to say anything about Tapestry in 45 minutes is going to be very tough.

Meanwhile, I'm waiting to hear back about my JavaOne session (on Tapestry). I've gotten some hints and assurances from the inside that this will happen.

I've gotten a bit spoiled by TheServerSide Symposium and No Fluff Just Stuff who pay your expenses. For Portland, I can live with it (it's a fun city to visit) ... and for JavaOne, that's just too good on the resume to miss!

Monday, March 07, 2005

Back from TheServerSide Symposium

I can't even start to detail everything that's happened in the last few days. Basically, I started my trip by dumping a glass of water into my trusty laptop, which fried the motherboard. Fortunately, I had bought a backup laptop (primarily used by my wife Suzanne) and was able to borrow this for the trip ... the contents of my internal hard drive was eventually recovered by Tech Fusion. I was literally sitting down at my laptop to run my traditional pre-trip backups when this happened and my nearest backups were a couple of weeks out of date.

Viva open source -- most of my development environment was downloadable from the Internet, and my key files (the Presentation Examples) are online.

The actual sessions went quite well; my Tapestry session was a bit different from the rest of the conference, more hands on, as I develop the trusty old Login page. The audience was really into it, asking lots of great questions. JSF Spec Lead Ed Burns was in the front row.

The second session went a little short and we made up for it with more detailed Tapestry and HiveMind questions.

I also worked with Chris Nelson, the creator of Trails. I think Trails will be a very important technology for Tapestry for both practical and marketing reasons. Trails has some unusual use cases that we will extend the APIs to handle more cleanly (for example, Trails needs to "sniff" for pages, based on naming conventions, that may or may not exist).

I spent a good amount of time talking to Ed Burns; he was showing me the internals of the JSF RI and we were comparing notes. I also talked to Rob, Colin and others from the Spring team, particularily about Spring Web Flow. Just from ten minutes of conversation, I believe we spotted a couple of changes to Spring Web Flow that would make it easier to integrate with Tapestry. Ten minutes of face time can be worth a month of emails.

In fact, for me, that was the point of the conference: networking (and a little sales). I lost track of the number of people I talked to about Tapestry and Tapestry training. Now, the TSS attendees are not a representative sample of the J2EE market I think, but what I heard was how many people want to use Tapestry, or at least learn about it. Something about Tapestry does picque many people's interest, and inspire a kind of passion in those that already use it. But the JSF juggernaught is still looming in the background, and I'm continuing to look for ways to better differentiate Tapestry from JSF.

Dion and Ben's Ajax presentation was eye opening for me. Yes, it's technology that's been around for a while ... it's just that a bunch of small things have aligned to make it more practical to do significant client-side work. Tapestry is already well positioned to create "Ajaxian" components and applications because it has two key pieces: 1) sophisticated, dynamic JavaScript generations. 2) an abtraction (engine services) that will support rich communication between the client side and the server side ... without requiring any configuration inside web.xml.

Additionally, I found some time (really, stress relief) to work on code. Big accomplishment was improved line precise exception reporting, where the location is supplemented by the actual source listing (with the offending line highlighted). This is a good idea, but I created (of course) a wildly flexible implementation where adapters for objects can be registerred that are used to output or describe objects. I can see this being helpful when dealing with existing objects that don't implement a decent toString().

I'm busy using this technology to output the Servlet API and other stuff on the Exception page and working towards eliminating the RequestContext object ... everything you'd do with it in Tapestry 3.0 is either methods directly on the IRequestCycle, or has been moved to other services, or is an object (such as the WebRequest or HttpServletRequest) that can be injected into pages, components or services.

Meanwhile, I get to work in a somewhat crippled manner for the next week or so while my laptop is at Dell getting replaced or refurbished. I miss my big, high-resolution screen and my normal keyboard (the feel of the Inspiron 1000 keyboard is "cheap" by comparison with the 8200) and lots of other little things. I also miss about half a gig of RAM and a faster CPU. I'm kind of hoping that Dell replaces my computer with a newer Inspiron (with USB 2.0). But I can squeek by for a little longer like this.

Tuesday, March 01, 2005

Monday, February 28, 2005

HiveMind: JMX and AOP Aliance Support

Achim Hugen is preparing to contribute JMX Support for HiveMind. Looks very much like I had originally envisioned. As with Tapestry, I'm looking forward to a time where I'm not the one doing the most interesting work.

Additionally, James Carman has spun up and started checking in code; he's added support for AOP Alliance style interceptors. He's also written an article on HiveMind for TheServerSide.com which should be up soon.

Simplify, Simplify

Getting more and more interesting things done in the Tapestry 3.1 code base is getting easier and easier.

One complaint I've gotten about Tapestry 3.0 is that, even if you use all implicit style components on a page, you still need to provide an XML page specification, just to specify the page class name.

I just checked in some changes to how page class names are determined; there's now an chain of command, that starts with the page specification itself, and ends with a default page class name.

In the middle is a new workhorse: NamespaceClassSearchPageClassProvider. This short little class gets a property from the namespace (the containing application or library specification). The property is a list of package names to search, looking for the page's class. So you might add <meta key="org.apache.tapestry.page-class-packages" value="org.mycompany.myproject"/> to the application specification, and just put your page classes into the org.mycompany.myproject package. And you're done.

It's a command separated list of package names, so you can have a couple of packages to search.

I think, gradually, the XML side of Tapestry will wither. I already have plans to support JDK 1.5 annotations in Tapestry to control a lot of the things you would currently do using XML ... and for some things, the XML is still the best approach.

I suspect that in the Tapestry 4.0 time frame, we'll re-organize the artifact triumvirate (specification, class, template) such that we start with the page or component class, then locate the optional specification and template. In 3.1 (and earlier) we start with the page name, find the spec, determine the page class from the spec.

Thursday, February 24, 2005

Crawl, Walk, Run

I've started my first few steps towards adding JSR-168 Portlet support to Tapestry.

I've got a pretty good handle on what it's going to look like, but I have a lot of unanswered questions, so I'm building a few simple, exploratory examples.

I've started with Jetspeed 2.0-M1 and hit all the standard problems. Like having to use Maven, and tripping over a JDK 1.4 bug.

I'm also getting a bit of a taste of my own medicine ... getting started with Jetspeed is painful for lack of documentation, a common concern for Tapestry. I'm sure it makes perfect sense from the inside, but as an outsider, I'm a bit daunted by some big gaps in the documentation. You have to learn a lot by experimentation.

I'm beginning to get a picture of how everything will fit together. HiveMind will be key here. There'll be an abstraction layer of the servlet/portlet APIs. In a few critical places, there'll be a lot of indirection to allow servlet implementations vs. portlet implemenations ... I expect to use the ServicePropertyFactory quite a bit in these situations. I haven't seen anything that looks like a show stopper yet. Time to roll up my sleeves and have some fun!

Tuesday, February 22, 2005

Defensive Patents

I see stories like this and get pretty angry. The largest companies are doing themselves, and the American programmer, a disservice with this blind attention to software patents. Obviously, as an open-source programmer, software patents make no sense to me. Code is protected by copyrights. If you have competitors, beat them on implementation, or service. Playing the big-bad patent card is an admission that you can't compete otherwise.

I'd like to think that companies are pursuing defensive software patents ... but if big software companies (including IBM, Microsoft and Sun) really wanted to do something good for the American IT industry, they would work to end software patents entirely. These companies have big, big political muscle (because that's what money is), and if they wanted to accomplish such a goal, they could.

The fact that they don't do more than maintain the status quo (and funnel bushels of money to the lawyers) says that they are afraid of competing fairly. Meanwhile, which each frivolous, obvious patent the ability of the entire software world to create something without being paranoid about a cease-and-desist arriving in the mail arbitrarily erodes ... and with it, a fractional amount of our competitiveness, individually and as a country.

Monday, February 21, 2005

Updated PresentationExamples.zip

Once again, I've updated my presentations. Only two big changes: the libraries have been updated to Tapestry 3.0.2, and I've replaced the lame "Developer Address Book" page (used to demonstrate the Table component), with a page that reads your iTunes music library. I supposed that's a problem if you don't have an iPod, and the correct solution is to go get one.

Follow the downloads link (from the list of links on the right side of the page) to find PresentationExamples.zip.

Saturday, February 19, 2005

Life is a blur

Just did a whirlwind trip to Utah to speak at the Utah Java User's Group. It was a fun trip, and a great crowd. Jamis Buck warmed them up with a great demo on Rails.

This was a good change from my last couple of Tapestry sessions; a much more dynamic crowd (numbering well over a hundred), asking useful and challenging questions. I got some good feedback that I hope to incorporate into my upcoming TSSS and NFJS presentations. Basically, rather than explain some of the details of validation poorly, I'll explain it briefly and omit a lot of details.

Also, I had some better ideas for the table component demonstration. Rather than "address book", I think I'll do, "iTunes inventory". I can then highlight rows that have my highest rated songs.

I was busy on the flight out and back. I've added to HiveMind something new and desired ... the ability to use pure POJOs as services ... POJOs without interfaces. This took a little doing ... the internals of HiveMind are specifically geared around interfaces (not just for interceptors, but for all the proxies that control just-in-time creation).

What I did is generate a synthetic interface for services that are really beans. The interface contains all the public methods that aren't inherited from java.lang.Object. The synthetic interface also extends any interfaces implemented by the bean. This seems to work quite well; I started up a vote to release HiveMind 1.1-alpha-2 to get this out into the field and see what works and what doesn't.

It's a little odd that the interface attribute of the <service-point> can now be a ordinary class (not an interface). This does create a split internally ... there's the declared interface (which may be a class) and the service interface (which either matches the declared interface, or is synthetic).

I also added a very simple, light-weight initialization to the instance: object provider. You can now specify, in addition the class name, a list of properties and values. The values are converted from strings to the property's type and assigned. It's using pretty much the same techniques as the smart translator. In fact, just writing this blog entry, I realized a bunch of other places this can and should be used.

Finally, I did some work on Tapestry; adding unit tests for components. This is long overdue; we need tests before we can start doing some of the big improvements with respect to form handling and input validation that are coming down the pike. I started small, with Insert and Conditional. Even so, I found some gaps in the coverage that aren't covered by the integration tests. Eventually, I hope to get rid of all the existing integration tests and replace it with just unit tests and handful of "sanity check" integration tests.

Tuesday, February 15, 2005

Tapestry DHTML Menus

Glen Stampoultzis has put together a nifty library for creating client-side JavaScript menus. It's called Krysalis Menu.

I like the way you construct the menu using hierarchy of components, using component enclosure (i.e., nesting of tags within the template) to define menu structure. It's how I thought about doing the same thing.

The library is on an ASL-derived license, and the documentation indicates a desire to move into Tapestry proper at some point. I suspect they'd be welcome!

Friday, February 11, 2005

Tapestry and Ruby On Rails at Utah JUG

Next week (Feb 17 2005), I'll be presenting a bit on Tapestry to the Utah Java User's Group. Jamis Buck will also be there, discussing Ruby on Rails. Jamis has done some very interesting work in the Ruby space, some of it based on HiveMind, and it will be interesting to meet him.

HiveMind and Pico Compared on java.net

Just read IoC Container Face-Off by Ken Ramirez, an article introducing the concepts of Inversion of Control and two implementations: HiveMind and PicoContainer. Curiously, Spring is not even mentioned!

I had a few minor issues with the article; I don't think it made the case very well for IoC. It confused "dependency injection" with IoC (dependency injection is an important component of IoC, but inversion of control also includes life cycle issues well beyond connecting collaborating services).

Where the article was weakest was on that exact topic: collaborating services. If you have just a main() method call a single service, IoC looks like a waste of effort. It's when you build complex systems with many moving parts ... but those individual parts are simple and testable, that IoC approach really shines.

The code to load a descriptor is too complicated; just put the hivemodule.xml on the classpath and use RegistryBuilder.constructDefaultRegistry().

Some of the terminology is about six months out of date; "singleton" and "deferred" became "primitive" and "singleton". Ken also garbled what the LoggingInterceptor does (it logs method entry and exit, not just exceptions) and missed most of the strengths of HiveMind:

  • HiveMind supports a large number of modules, which is why the extended naming is necessary.
  • It is very common to have many services implementing the same interface.
  • Not even a mention of configurations, HiveMind's most obvious differentiator from other containers.
  • No mention of factories ... many of the most interesting services in HiveMind are created dynamically at runtime.

For a supposed "face off", there was no direct comparison of the two frameworks. Here, I'll do it: PicoContainer is lighter weight, has an aversion to XML, doesn't mandate the use of interfaces, and uses constructor injection exclusively. HiveMind is driven by XML, is much heavier weight (what with support for many modules, interceptors, configurations, etc.), enforces the use of interfaces, and allows setter and constructor injection.

HiveMind 1.1 and multiple locales

In HiveMind 1.0, you set the locale once, when you build the Registry and you're stuck with it. That's pretty limiting, especially for a web application. In a web application, such as a Tapestry application, each request (which is to say, each thread) may be using a different locale.

Using a fixed locale limits the usefulness of HiveMind. It means that the business layer can't generate localized error and status messages and pass them back up to the presentation layer. Instead, you fall into the same old trap of exceptions and return codes so that the presentation layer can generate those messages (in the proper locale). That's counter to whole philosophy of HiveMind: colocate everything, so that they can collaborate richly and limit the amount of ugly plumbing and klude code you have to write.

Well, I just checked in changes to HiveMind 1.1 that allow the locale to be changed at any time. The Registry's locale is simply the default locale. The key aspect of this is the Messages object injected into your services ... this was how services gained access to localized messages. The new implementation is smart enough to combine the request message key with the thread's current locale when obtaining a message.

For a web application, this means that, as soon as the client's locale is determined, the ThreadLocale service should be notified. Any messages generated by any services will then be in the proper locale. Tapestry 3.1 has a particular place for doing this ... in fact, the engine's getLocale() and setLocale() methods will be changed to simply invoke getLocale() and setLocale() on the ThreadLocale service.

Thursday, February 10, 2005

Streamlining HiveMind Module Deployment Descriptors

We often complain about the verbosity of J2EE XML descriptors. The HiveMind descriptors are better, but there's still a lot of typing.

One thing that struck me is that there's a lot of needless duplication of package names. Often the package name matches the module id ... and yet, you'll retype the package name for each service interface and each class.

So ... what if HiveMind just assumed that simple interface and class names were in a specific package? Also, what if the module id and the package name aren't the same?

What I've done it to add a package attribute to <module> (which defaults to the module id). I modified a lot of code to make use of the package name when a reference to an object type shows up (and, in fact, improved the code by adding a resolveType() method to the Module interface). Literally, if the class name as is doesn't exist, then a second try (prefixing the class name with the package name) is made.

This simplifies the examples module descriptor:

<?xml version="1.0"?>
<module id="examples" version="1.0.0" package="org.apache.hivemind.examples">
    <service-point id="Adder" interface="Adder">
        <create-instance class="impl.AdderImpl"/>
        <interceptor service-id="hivemind.LoggingInterceptor"/>
    </service-point>
 . . .

What's nice is that you can use "relative" class name, such as impl.AdderImpl, which is really org.apache.hivemind.examples.impl.AdderImpl. See what I mean about less typing?

Perhaps we'll go further in the future ... for instance, maybe <service-point> should assume that the interface matches the service point id?

There will be cases where you really want to support many different packages in the same JAR. You will then either type fully qualified class names, or split your JAR into multiple logical modules using <sub-module>.

Sunday, February 06, 2005

Global message catalog for Tapestry

One of the most frequent requests for Tapestry is support for global message catalogs. In Tapestry, each page and each component can have its own message catalog and that's good ... but this can also foster some duplication between templates. This is now fixed as TAPESTRY-242.

I think of all the changes I've made in Tapestry 3.1 so far, this may be the first one that came as a request from the community, rather than a change I've made from my personal vision for 3.1. Now that the basic infrastructure of 3.1 is in place (but subject to some revision and refactoring) I believe we'll be seeing these kind of changes rolling out quite frequently.

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.

Saturday, January 29, 2005

Should MyEclipse support Tapestry? Using Spindle?

There's a discussion going on in the MyEclipse discussion forums about adding Tapestry support to MyEclipse. I haven't used MyEclipse, but I've heard of it ... it's a collection of Eclipse plugins to support J2EE development and supports a number of standards and tools, such as JSF and Hibernate.

Now, MyEclipse is inexpensive (it's based on an annual subscription, which is a fun idea), but it is proprietary and Spindle is free ... but I can't help thiking that an improved/integrated Spindle as part of MyEclipse would be a good thing, and may help offload some of Geoff's vast effort with maintaining and extending Spindle (especially if some improvements worked backwards into Spindle). I can't wait for Geoff to weigh in on this.

Wednesday, January 26, 2005

Improved HiveDoc

I've spent the last day or so, in between shoveling out my driveway, creating a new HiveDoc XSLT stylesheet with pretty darn good results. I think this is more readable, and better layed out (to support more tweaking of the CSS). Still not as pretty as Spring's equivalent (and is thiers a direct result of ours, or parallel evolution?).

TSS relaunches on Tapestry

Sure, it's old news that TheServerSide has been running on Tapestry for the last several months (I did the work myself). I also wrote an article about it, which is finally available ... at TheServerSide.

Safety First

Friends Don't Let Friends Code Struts

'Nuff said.

Monday, January 24, 2005

Airline Frustration

Sunday 3pm. Nope (2 hours on hold) -- Monday 6:40 am. Ok, REALLY, (40 minutes on hold) Monday 3:00 pm and you connect through Pittsburgh. No jokes, 3:30 pm and you WILL make your connection. 65 minutes in line ... oh, you won't make your connection.And you skycapped your luggage ... very silly. Gee, that's odd, the computer can't locate your baggage information, but that happens sometimes ... I'm sure it will turn up.

So Suzy had to come pick me back up at the airport, and 93S to Quincy was jammed solid so we (2 hours) detoured through Jamacia Plain and Dorcester.

The theme of this blog was a trip to San Francisco to give some Tapestry training. Alas, we'll just have to do it next week instead.

Wednesday, January 19, 2005

HiveMind 1.1-alpha-1 Released

An early preview of HiveMind 1.1, 1.1-alpha-1 has been released on Jakarta.

I think the code and functionality in this release is stable and well tested, but the HiveMind crew has a lot more that will go into the final 1.1. release!

Sunday, January 16, 2005

HiveMind In Chains

The Gang of Four's Chain of Command pattern is a very useful one; we all use it all the time whether we realize it or not. David Geary has just started using it in anger with JSF, starting with the Jakarta commons-chain framework.

Chain of Command is very simple; you have a list of objects that all implement a particular interface. You simply invoke the same method on each object (in a specific order), until one object indicates that the chain is complete. Typically, the methods return a boolean, and a return value of true indicates that the chain has completed.

HiveMind 1.1 already includes an implementation of the Adapter pattern, so it was time to create Chain of Command. This takes the form of a general purpose ChainBuilder, service and a ChainFactory service used to build service implementations.

What differentiates HiveMind's Chain of Command from other implementations is that command interface is arbitrary and application-specification. There's no Command interface; the implementation generated on-the-fly adapts to whatever interface you already have. Your interface may have any number of methods, with any return type (including void), parameters, and exceptions. In 132 lines of (non-comment) code.

Using the ChainFactory, the chain of command becomes just another service implementing the interface, ready to be injected into any other service that needs it.

Tapestry 3.1 already has a number of command chains; it will be nice to refactor that code around this new functionality.

I think a good number of developers out there are aware of many of the Gang of Four's design patterns ... but they have trouble seeing how those patterns fit into their own applications. In the Gang of Four designs, the necessary objects are always instantiated and connected to each other, but it's left as a puzzle to the reader to deduce how they got that way.

In my terminology, the Gang of Four patterns focus on the production state of the system and of the individual services, and ignore the construction state, just as they ignore the destruction state. To be honest, I don't have a copy of the book handy, and I'm sure some of the examples do get involved in this issue (and some of the patterns are themselves creational).

Regardless, HiveMind takes this bull by the horns, because it's largely about the construction state. Using a combination of a HiveMind configuration and a service implementation factory, it becomes quite natural to have a configuration that describes those production state relationships, and create a service that encapsulates the behavior with those relationship in place. For chains, the configuration point describes the commands and their order, and the service implementation factory builds a service implementation that invokes the methods on the commands for you.

Tuesday, January 11, 2005

Monday, January 10, 2005

NFJS Schedule for 2005

Jay and I worked out my appearanced at the No Fluff Just Stuff symposiums series for the first half of 2005. I'm trying to keep it down to two per month, and it's looking good.

  • Atlantic Northeast Software Symposium
    Philadelphia, PA
    March 11-13, 2005
  • Gateway Software Symposium
    St. Louis, MO
    March 18-20, 2005
  • New England Software Symposium
    Boston, MA
    April 8-10, 2005
  • win Cities Software Symposium
    Minneapolis, MN
    April 29-May 1, 2005
  • Rocky Mountain Software Symposium
    Denver, CO
    May 13-15, 2005
  • Central Florida Software Symposium
    Orlando, FL
    June 24-26, 2005

I'm doing my three standard sessions(Tapestry, Tapestry Components and HiveMind) and am working on additional sessions... one of which is about bytecode enhancements for testing (EasyMock) and at runtime (Javassist and HiveMind). Once Tapestry 3.1 is in beta, I may add a session on improvements in 3.1.

I hope to have some more "real world" examples ready shortly ... and, I can't wait to simplify the presentations with the 3.1 improvements. So much less handwaving will be needed! I won't have to talk about parameter directions anymore!

Saturday, January 08, 2005

Friday, January 07, 2005

Seperation of Concerns vs. Inheritance

One of my coding catch-phrases is Aggregation Trumps Inheritance. By that, I mean that combining small simple objects is a more powerful technique than inheritance.

I didn't always think this; coming out of the Objective-C/NextStep camp, I was used to using lots of inheritance. In fact, for a long time, I thought a framework was a set of base classes for me to subclass. You can see this in the implementation of Tapestry, where you start with Tapestry base classes.

Even at the time, I was concerned that the Three Amigo's had a problem with UML. Namely, that when doing a sequence diagram, it was very, very awkward to show the flow from an object to a super-class implementation of a method. There simply wasn't the necessary geometric direction to draw the line. This seemed to be a problem ... UML didn't seem to handle inheritance very well, and that made it difficult to diagram some design I had ... especially those that involved overriding a base class implementation of a method.

In retrospect, the idea of a framework as a set base classes is a bit flawed. It made sense in Objective-C land, due to the lack of a garbage collector. Memory management was a buggy, agonizing process (remember retain cycles, anyone?) so you wanted to minimize the number of objects allocated. Therefore, better to subclass and allocate a single object than to allocate several related objects and have to manage who-owns-who.

That isn't the approach I take any longer; if you look at Tapestry or HiveMind, you'll see how I trust the garbage collector, and use large numbers of really small objects ... objects that may implement an interface or two, but are otherwise inheritance unencumbered, you know, POJOs (plain old Java objects). And, if I'm inclined to diagram in UML, it works fine ... no ambiguity about which object owns which implementation of which method. HiveMind especially has very few base classes or interfaces exposed to your code, which is the way it should be.

As I take Separation of Concerns ever more seriously, I see more places where I was using inheritance out of inertia or reflex, and I can code better using smaller objects. Most often, I'm using the GoF Strategy pattern.

For example, I was adding a simple expression parser to HiveMind. I started thinking about a PropertyToken and a ClassNameToken as the leaves of my AST (Abstract Syntax Tree), with AndToken, OrToken and NotToken classes to add structure. Each node would have an evaluate() method that would return true or false. The leaf tokens would do some real work (see if a JVM System Property is true, or see if a class exists) and the other tokens would combine those values together. Real CompSci Parser 101 stuff.

But then I noticed that I really had two concerns here: the structure of the AST, and the way each node is evaluated. Using inheritance, I would inherit the AST structural behavior from some AbstractToken base class, and the subclasses would each provide their own evaluate() method implementation (as well as any additional properties).

One I saw it that way, I realized that the evaluation part was completely separate and could be factored out. My final solution for the AST uses a Node class, and an Evaluator interface. Each Node owns a left and right child node, and an evaluator. The Node class is about the structure of the AST ... the evaluator is about how the Node evaluates to true or false. The Node.evaluate() method internally delegates to the Node's evaluator (the Node passes itself as a parameter).

The end result was much less code to write and test. First I tested the Node class to make sure that structure and evaluation worked correctly. Then I defined Evaluator implementations (AndEvaluator, OrEvaluator, etc.). Ultimately, And, Or and Not were completely stateless internally ... so I made them singletons. Breaking the code apart this way made it easier to mock Evaluators when I was testing Nodes and vice-versa.

I'm tackling a similar problem in Tapestry now: re-worked the way page recorders work. In Tapestry 3.0, a page recorder is responsible for persisting certain page properties into the HttpSession as attributes, and restoring page properties from those attributes in later requests.

The 3.0 code is broken in a couple of ways; the page recorders are owned by the engine, not the request cycle, which can cause conflicts when you build a Tapestry application using frames (updating the frames cause race conditions as different threads use and update the page recorders in different ways).

It was always my intention to allow different implementations of IPageRecorder, so that other schemes could be used, such as storing data in HTTP Cookies ... but that never happened.

With HiveMind providing configuration and infrastructure, it will be much more reasonable to make this pluggable. In the long term, I want to support more complex life cycles for page data ... such as properties that stay persistent until you navigate to some other page in the application.

So, I'm finding that in the new code, the page recorder is a thin buffer between the page instance and a PropertyPersistenceStrategy object that does the actual work ... and the strategy object is dynamically looked up (using a name stored in the page specification), which is the key to pluggability.

The page recorders can now be lightweight, created as part of a request and discarded afterwards; goodbye thread contention and it simplifies the life cycle and the IPageRecorder interface.

Anyway, back to the moral: if you can subdivide an object into smaller pieces ... do it! Any time you can change an "is-a" relationship to a "has-a" relationship, you are going to find advantages in coding, testing, the works!

Pragmatic vs. Dogmatic Languages

So, my earlier post about Ruby has sparked some comments by Glenn Vanderburg. Glenn was standing next to Dave Thomas during that "hard sell" of Ruby, then proceeded to demo Seaside (a continuations-based Smalltalk web framework) during the drive back to the airport (I only got a small peek because I was driving; Erik Hatcher was the main audience).

Glenn appreciated the fact that the update-copyrights.rb script I wrote, shabby as it is, is a Ruby script, and not Java code in Ruby syntax. In other words, I did my best to use Ruby-isms, including a little duck typing, blocks and iterators, and so forth. We're both disappointed I didn't write tests for the script.

This gave rise to some interesting email discussions, the most productive part of which was some new terminology (that I hope I did not subconsciously steal from someone):

  • Pragmatic vs. Dogmatic Everyone gets hung up on types, dynamic vs. static, scripting vs. everything-else. I'd rather use "pragmatic" for non-type-encumbered languages (Ruby, Perl, Python, JavaScript) and "dogmatic" for Java, C#, COBOL ... everything that forces structure down your throat.

    In Java 1.5 terms, I like auto-boxing, but generic types are abominable! It makes the code much harder to read and the only gain is a minimal amount of compile-time type safety -- that isn't really safe. Dogma. Apparently, the dogma at Sun is "Java code that performs no casts must not ever throw a ClassCastException". So much for Dave Thomas' (and others') idea that the Java compiler should just quietly inject the necessary cast for you (based on all the other type information available) ... instead we have the syntax from hell.

  • lovely spareness is what I like in Ruby, maybe Python ... what I miss from Objective-C. You get a lot done in very little code and the code is readable. It's the aesthetic of Ruby and it's admirable.

These terms, and the concepts behind them, resonate with me. Looking at how both Tapestry and HiveMind have been evolving, it is much the same: less dogma (well, beyond "services must have interfaces!"), and much more spare XML, with the frameworks doing a better job figuring things out from defaults. Ruby is full of this same "less is more" philosophy, which is why people get passionate about it.

My last big change of language was from Objective-C on NextStep. The transition from Objective-C to Java was initially very painful ... so much of Java seemed unnecessarily baroque. And a couple of key features I pined for were missing. In fact, those features have only shown up more recently as Java Aspects ... and as Ruby Mixins. Aspects make my eyes hurt; Mixins are minimal, and powerful. Ruby is something like coming home again to Objective-C.

Ruby won't be my primary tool for a while (if ever), but it's definitely something I want to keep in my toolbox, ready to go.

Wednesday, January 05, 2005

Playing with Ruby

So, I've been peeking a bit at Ruby in tiny fractions of spare time over the last few weeks. Ruby is a fully object-oriented, dynamically typed, scripting language. It's designed to be fast, simple and powerful. It has closures which are just amazing (if difficult to describe), and the lack of types makes many common programming patterns much less of a chore.

I first heard about it a year or two ago from Greg Burd (he's the kind of guy who just knows what's cool before other people do).

I didn't really give Ruby another look until recently. Dave Thomas really pushes Ruby on a (somewhat confused) Java audience at the NoFluffJustStuff symposiums. I too was resistant, but Dave really does a hard sell on Ruby ... how could I resist something that's "a dog's breakfast, but it works!". Dave sealed the deal by sending me a copy of his book (Programming Ruby, aka "The PickAxe", which suffered an untimely death-by-NyQuil on the way out to ApacheCon and had to be replaced).

In fact, with the rolling over of the new year, I found a good use for Ruby ... a copyright updater for the Tapestry and HiveMind source code. I had written one in Python a ways back, but it was limited to Java files.

What I wanted was something a bit smarter ... that would be able to adapt to different types of files (Java, XML, properties) and would be able to update the copyright message, rather than overriding it. That is, convert:

# Copyright 2004 The Apache Software Foundation
To:
# Copyright 2004, 2005 The Apache Software Foundation

My trembling first journey into Ruby is this script, which does the work. It's sloppy, doesn't report errors well, and took me too long to write (almost as long as it would have in Java!) ... but it works and is impressively fast. In fact, I've been very surprised at just how fast Ruby is to load, parse and execute. Visibly faster than Python ... faster than Java I'd bet.

I may have to revise some of my comments in my upcoming TheServerSide Tech Talk (filmed last April).

Dr. Dobb's reviews Tapestry In Action

Just came across this brief review of Tapestry In Action. They generally liked it, but criticized the index (a not unheard of complaint). The index was the most painful part of a very painful process, and it shows.

Monday, January 03, 2005

Why Open Source? OSS vs. Proprietry vs. In House

This detailed paper is a great reference to the advantages of open source over proprietary software. It eloquently and elegantly rebuts much of the FUD out there (such as "OSS is anti-capitalism").

On a related subject, a discussion of open-source vs. in house development has come up at a No Fluff Just Stuff discussion. The gist of it was, that with open source software, you don't have the same level of knowledge of the inner workings of a framework that you would have if you developed it in house. This makes in house frameworks more enticing than equivalent open source projects.

Erik Hatcher had a great response to that. Pretend that the open source project was developed in house. Have one team developer work with the "in house team" that developed the "internal project" to learn how to use it ... exactly the same way as you would if the project really was developed in house. The difference is that you'll use more email and less phone or cube visits.

You'll still do the same things ... build some prototypes and experiments, hit some brick walls, work your way through them. At the end of the day, the team will have expended the same effort to get up to speed on the "in house" project. The advantage is that there will be more expertise in the greater world on the open source project, and it will (generally) have more functionality, and be better tested.

In other words, have someone on the team "own" the open source project. In the unlikely event that the open source project is abandoned by its original developers, you still have the complete source code and necessary expertise to continue maintaining and extending the project.

In effect, open source gives you all the benefits of an in house development (such as availability of source code, and direct access to the developers) without the actual costs of developing the software in house.

A final note; more than one architect I've talked to has stated (more or less) "any project that begins by designing the perfect framework for the project, fails." That's because, at the outset of a big project, you don't know the domain well enough to design a framework for that domain. At the end of the project, you do.

With open source software, you can bypass that; you gain the expertise of others who have already blazed a trail into your domain. Or, at the very least, you get to build your project out of larger building blocks. Either way, you're likely to get to that first iteration, where you actually know something about your application domain, that much faster.

2004 Timeline

So on this timeline of Java events in 2004, HiveMind gets mentioned (Sep. 24th) but Tapestry does not! Perhaps I need to change this blog's title to "HiveMind (and a little Tapestry)"?

Sunday, December 19, 2004

Groovy, Junit, Eclipse

So, during my trip to Javapolis, I hit that point where I had typed one parenthesis too many. You know the one, it involves class casts, the very ones the compiler could so easily do for us automatically. Mine typically look like:

    protected IPage newPage(String name)
    {
        MockControl control = newControl(IPage.class);
        IPage result = (IPage) control.getMock();

        result.getPageName();
        control.setReturnValue(name);

        return result;
    }

That's from a bit of a unit test, where I'm using EasyMock to create unit test fixtures on the fly.

Regardless, for my unit tests, I really get sick of all the casting and types. So I'd like to start using Groovy. Recoding the above code snippet into Groovy should end up looking like:

    protected newPage(name)
    {
        control = newControl(IPage.class)
        result = control.getMock()

        result.getPageName()
        control.setReturnValue(name)

        return result
    }

No return types. No variable types. Fewer parenthesis (remaining ones are there for aesthetic reasons). Ultimately, I think this is easier to read.

Is Groovy ready for prime time? Not for me. It's very important for my development cycle to be able to hit the Run... button in Eclipse and see the clean, green sweep of the progress bar in the JUnit view.

No such luck with Groovy. I created the simplest test case I could think of:

package com.examples;

import  groovy.util.GroovyTestCase


class TestStuff extends GroovyTestCase
{

     void testSomething() {
       assertEquals (true, true)
    }
}

I then used the JUnit launch configuration to find all of my tests in my project ... which should just be this TestStuff class. Instead:

java.lang.RuntimeException: No filename given in the 'test' system property so cannot run a Groovy unit test
	at groovy.util.GroovyTestSuite.loadTestSuite(GroovyTestSuite.java:97)
	at groovy.util.GroovyTestSuite.suite(GroovyTestSuite.java:85)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:324)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.getTest(RemoteTestRunner.java:364)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:398)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:305)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:186)
Failed to invoke suite(): java.lang.RuntimeException: Could not create the test suite: java.lang.RuntimeException: No filename given in the 'test' system property so cannot run a Groovy unit test

OK. From what I can tell in the documentation, this means that Groovy has a placeholder TestCase class that is dependant on a JVM system property to identify the actual, single (!) script to execute. We'll just focus in, and run tests in my src folder instead (already less than ideal, because I often have several folders of unit test classes).

Boom. JUnit sees no unit tests in the source folder.

The only way I've found to get my Groovy unit test to execute is to use Run ... -> Groovy. That's great for running one set of tests, available in a single unit test class. HiveMind has 523 tests, Tapestry has over 580 ... these tests are spread across a large number of individual unit test classes. I don't maintain a JUnit test suite any more because JUnit support in both Eclipse and Ant will "scan" for my test cases. When I make changes, I need to easily run all of my tests.

I don't doubt I could cobble something together using Ant to get my tests to execute, but my preferred work style is to stay in Eclipse. Shutting back and forth between Eclipse and the command line, or even running Ant from inside Eclipse is not acceptible. The JUnit support inside Eclipse blows away what's available at the command line ... the direct access to code lines, the ability to get a diff on failed assertions (something you may have missed ... double click on the mismatch message in the stack trace and a diff window pops up to show you exactly what didn't match). And the general pleasantness of the green bar. I don't want to run my unit tests outside of Eclipse ... but, for the moment, Groovy is trying to force me to.

What I did eventually find was that I needed to generate a TestSuite for my Groovy tests:

package com.examples;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

/**
 * @author Howard M. Lewis Ship
 */
public class GroovySuite extends TestCase
{
    public static Test suite() throws Exception
    {
        TestSuite suite = new TestSuite("Groovy Tests");

        suite.addTestSuite(Class.forName("com.examples.TestStuff"));

        return suite;
    }
}

This works, but is not my ideal. I have to use the full qualified class name as a string, otherwise Eclipse's builder considers this an error (the Groovy builder doesn't seem to be well integrated into Eclipse, so Eclipse has no knowledge of Groovy classes). I also have to maintain this file as I add new Groovy test classes. Further, this must be coded in Java (so Eclipse knows about it). I'll probably bite the bullet, and make do with this, but it's not as nice as simply writing Groovy tests and seeing them run.

Along the way, I found out that the Groovy plugin for Eclipse is quite primitive. Syntax or other errors in the Groovy code do not display in the editor, the tasks view or the problems view. Further, the old .class file is left behind, which further muddies the water. Basically, I'm left without a lot of confidence that what I've typed into the editor is what's running ... any errors in my Groovy code and some earlier version of the code runs instead. In unit testing terms, that means the potential for a lot of false positives on tests runs!

The prevaling wisdom in the Groovy community is that the best way to get your feet wet with Groovy is to start using it for unit tests. Sounds like a great idea, but I think the Groovy team needs to focus on this use case, especially with respect to Eclipse and other IDEs.

Saturday, December 18, 2004

Servlet mapping limitations

Had a bit of a disappointment this week, while working (from my hotel room) on Tapestry.

I've been blissfully unaware of some limitations of the URL mappings for servlets in the web.xml file. Tapestry has always had a single, simple mapping, typically to /app and everything else went into query parameters. That's changing for 3.1 and now I'm seeing some limitations in what I can do.

I'd like to support some mappings, such as /Home.direct/border.link. That is, the direct service, the Home page, and the component border.link.

Alas, this can't be done. The web.xml mapping <url-pattern>*.direct</url-pattern> will not match the above path. You'd think it would (and set the path info to /border.link) but it simply doesn't match at all.

Give how rich Apache mod_rewrite is (despite the limitation of being written in C), you'd think there would be more flexibility for matching paths to servlets in the Servlet API. I haven't read the 2.4 specs yet, do they address this? Regardless, Tapestry has an explicit goal to stay compatible with Servlet API 2.2. I suspect this can be overcome as well with container specific extensions, but that's another thing to avoid since it always leaves some people out in the cold.

One possibility is that I'll support pattern of /direct and create a URL like /direct/Home/border.link, but given that the page name may itself have slashes (i.e., admin/AdminMenu or some such), this introduces unwanted ambiguities.

Alternately, map /Home and use a URL of /Home/direct/border.link ... but suddenly, we start having a mapping for each page in the application!

In fact, I wish there was some delegate functionality in the servlet API, where application code could take over the identification of servlets for incoming paths. This would allow much richer paths. Alternately, it would be nice if the information provided in web.xml could be augmented, from Servlet.init(ServletContext) with additional information provided by the application. This would allow us to not repeat ourselves, by having Tapestry provide mappings based on the available EngineServiceEncoder mappings.

I have no taste for bureaucracy, but I may yet need to find a place on the Servlet API expert group.

Sunday, December 12, 2004

Updated PresentationExamples.zip

I've uploaded a new-and-improved PresentationExamples.zip. This is the file containing the source code from the examples I demonstrate at various user sessions. This update includes a bunch of new ideas from a recent road trip, and includes the Tapestry (and HiveMind) libraries. It's a zip of my local Eclipse workspace.

Tapestry 3.1 update

I've been squeezing in time to do a lot of updates to Tapestry 3.1 over the last few days, including on my flights to and from shopping.com.

I've been updating the Wiki with details on some of these changes ... at a high level the additions are:

  • Friendly URLs (!)
  • Modularity (put your pages in subdirectories)
  • Lots of new binding prefixes: asset:, component:, bean:, and listener:

Tapestry 3.1 continues to shape up as a radical rethinking of Tapestry 3.0 ... while staying about 95% backwards compatible. It will be both more efficient and more expressive (not to mention supremely extensible). Remember: Less is More!

Tapestry @ JavaPolis on Wednesday

I can't believe it, but I am flying all the way to Europe and back (plains, trains and automobiles!) for a one-hour Tapestry session at JavaPolis. I'm presenting on Tapestry from 16:55 to 17:55 on Wed December 15th 2004.

Back from shopping.com / Defer component

Just got back from a three day Tapestry training session with Jordan, Matt and the crew at shopping.com out in San Francisco. This was a fun, exhausting trip but everyone (myself included) got a lot out of it. Things I learned:

  • Be emphatic. The labs are good, even if they appear tedious. Nobody wanted to do them, and everyone was glad that they did.
  • Let Matt explain the problem before giving him the (wrong) solution.
  • Have a template Tapestry/Intellij project ready to go.
  • Flash memory keys are cool (we passed lots of files around, without having to get involved with network setup).

Along the way, we solved (to a degree) some problems with input validation (in Tapestry 3.0). If you've used validation, you know that you can get field labels to be decorated, along with the fields themselves. However, when using a loop around the input fields, there's an off-by-one error that causes the wrong label to be decorated.

The problem is that the FieldLabel component relies on the ValidField having the correct value in its name propery when the FieldLabel renders, so that it can work with the validation delegate to determine if and how to decorate the label. However, the name property isn't set until the ValidField renders itself (that's when it obtains its name from the Form component, and registers itself with the validation delegate as the active component).

So if the FieldLabel and the ValidField are in a loop that executes three times, and the 2nd rendering of the ValidField is the one that's in error, its the 3rd rendering of the FieldLabel that gets decorated. Woops.

So, the trick is ... we need to render the ValidField before the FieldLabel, but the output from that rendering must still occur after the VAlidField renders. Why that output order? Because that's that's how typical, western language forms are output, with FieldLabels rendering first (and thus, to the left of) ValidFields.

So, at the customer site, I created a component I call Defer. Defer takes a Block as a parameter. It renders the Block into a buffer, then renders its body, then outputs the buffered content from the Block. Something like:

public abstract Block getBlock();

protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
{
  IMakrupWriter nested = writer.getNestedWriter();

  getBlock().renderBody(nested, cycle);

  renderBody(writer, cycle);

  nested.close();
}

How is this used? The FieldLabel is enclosed inside a Defer, and the ValidField is enclosed inside a Block.

  <span jwcid="@Defer" block="ognl:components.fieldBlock">
    <span jwcid="@FieldLabel" field="ognl:components.inputName"/>:
  </span>

  <span jwcid="fieldBlock@Block">
    <input jwcid="inputName" ... />
  </span>

So the inputName component renders first, then the FieldLabel renders, then the HTML from the inputName component is output. This is once of the many things I love about Tapestry ... its not just a stream of text, its actual objects. Once you see past the false reality of a text stream to the Matrix of the Tapestry component object model, you are free to twist time (or at least, control the order of rendering).

It's far from perfect ... as with using a ListEdit/ListEditMap combination inside a Form, it forces too much of Tapestry's internals into your lap ... but it is workable, and it is only needed for special cases where a ValidField component's name changes while the form renders -- which is to say, only when using the FieldLabel/ValidField combination inside a loop.

Monday, December 06, 2004

HiveMind and EasyMock

I've become a big fan of EasyMock. EasyMock is a way to create mock implementations of interfaces, for use in your unit tests. This fits in really well with the overall IoC concept, since when testing class A your can inject mock versions of interfaces B and C.

With EasyMock, you create a control for your interface. The control is an instance of MockControl. From the control, you can get the mock object itself. You then train the mock (I call it a "zombie"). As you invoke methods on the zombie, the sequence of operations is observed by the control.

When you invoke a non-void method, you tell the control what value to return. You can also have any method throw an exception.

You then switch the zombie into replay mode and plug it into the class you are testing. That code interacts with the zombie as if it was, well, whatever it should be.

HiveMind includes a HiveMindTestCase base class that improves this somewhat. Using EasyMock out-of-the-box, you write a lot of test code that just manages the controls . HiveMindTestCase does this for you, which makes it more practical when you have half a dozen pairs of controls and zombies.

For example, here's part of the test for Tapestry's ExternalService:

    public void testService() throws Exception
    {
        MockControl cyclec = newControl(IRequestCycle.class);
        IRequestCycle cycle = (IRequestCycle) cyclec.getMock();

        IExternalPage page = (IExternalPage) newMock(IExternalPage.class);

        Object[] serviceParameters = new Object[0];

        cycle.getParameter(ServiceConstants.PAGE);
        cyclec.setReturnValue("ActivePage");

        cycle.getPage("ActivePage");
        cyclec.setReturnValue(page);

        LinkFactory lf = newLinkFactory(cycle, serviceParameters);

        cycle.setServiceParameters(serviceParameters);
        cycle.activate(page);
        page.activateExternalPage(serviceParameters, cycle);

        ResponseOutputStream ros = new ResponseOutputStream(null);

        ResponseRenderer rr = (ResponseRenderer) newMock(ResponseRenderer.class);

        rr.renderResponse(cycle, ros);

        replayControls();

        ExternalService es = new ExternalService();
        es.setLinkFactory(lf);
        es.setResponseRenderer(rr);

        es.service(cycle, ros);

        verifyControls();
    }
This code tests the main code path through this method:
   public void service(IRequestCycle cycle, ResponseOutputStream output) throws ServletException,
            IOException
    {
        String pageName = cycle.getParameter(ServiceConstants.PAGE);
        IPage rawPage = cycle.getPage(pageName);

        IExternalPage page = null;

        try
        {
            page = (IExternalPage) rawPage;
        }
        catch (ClassCastException ex)
        {
            throw new ApplicationRuntimeException(EngineMessages.pageNotCompatible(
                    rawPage,
                    IExternalPage.class), rawPage, null, ex);
        }

        Object[] parameters = _linkFactory.extractServiceParameters(cycle);

        cycle.setServiceParameters(parameters);

        cycle.activate(page);

        page.activateExternalPage(parameters, cycle);

        _responseRenderer.renderResponse(cycle, output);
    }

Back in the test code, the newControl() method creates a new MockControl. The newMock() method creates a control and returns its mock .. in our unit test, we create an IExternalPage mock instance to stand in for a page named "ActivePage" and ensure that it is passed to IRequestCycle.activate().

The replayControls() and verifyControls() methods come from HiveMindTestCase; they invoke replay() and verify() on each control created by newControl() or newMock().

While replaying the zombie and the control work to ensure that each method is invoke in sequence and with the correct parameters.

The verifyControls() at the end is very important; an incorrect or out of sequence method call will be picked up in line (an exception is thrown), but an omitted method call can only be discovered by verifying the mock.

This technique takes a bit of getting used to; this "training" stage can easily throw you the first time through. Alternatives to EasyMock, such as jMock generally employ a different model, where you train the control (not the zombie), passing in method names and identifying arguments or other expectations in various ways. I suspect jMock is ultimately more powerful and expressive, but I find EasyMock more effective. Your mileage may vary.

Regardless of which framework you choose, this technique works really well! In fact, it is often useful to use this technique to define the interface; this is best explained by example. I was recently recoding how Tapestry performs runtime bytecode enhancement of component classes. The old code was monolithic, one big class. I broke that up into a bunch of "workers". Each worker was passed an object, an EnhancementOperation, that was a facade around a lot of runtime bytecode machinery. EnhancementOperation has methods such as addInterface() and addMethod() that are directly related to code generation, and a number of other methods, such as claimProperty(), that were more about organizing and verifying the whole process.

What's fun is that I coded and tested each worker, extending the EnhancementOperation interface as needed. Once all the workers were tested, I wrote the implementation of EnhancementOperation, and tested that. The final bit was an integration test to verify that everything was wired together properly.

Now, if I had sat down (as I might have done in the past) and tried to figure out all of, or even most of, the EnhancementOperation interface first, I doubt I would have done as good a job. Further, having EnhancementOperation be an un-implemented interface meant that it was exceptionally fluid ... I could change the interface to my heart's content, without having to keep an implementation (and that implementations' tests) in synch.

In sum, EnhancementOperation was defined through a process of exploration; the process of building my workers drove the contents of the interface and the requirements of the implementation. And all of this was possible because of the flexibility afforded by EasyMock.

Out of the box, EasyMock only supports interfaces; behind the scenes, it uses JDK proxies for the zombies. There's an extension that uses bytecode enhancement to allow arbitrary classes to be instrumented as mock object zombies.

This is cool as I rework the Tapestry test suite; often components collaborate and there is not an interface; I still want to be able to mock-up the other component, so I need to use the EasyMock enhancements.

Ultimately, I extended HiveMind's newControl() method to allow an interface or a class to be specified, and to do the right thing for each. Here's an example, where I'm testing how a Block and RenderBlock component work together:

    public void testNonNullBlock()
    {
        Creator c = new Creator();

        MockControl bc = newControl(Block.class);
        Block b = (Block) bc.getMock();

        RenderBlock rb = (RenderBlock) c.newInstance(RenderBlock.class, new Object[]
        { "block", b });

        IMarkupWriter writer = newWriter();
        IRequestCycle cycle = newRequestCycle();

        b.getInserter();
        bc.setReturnValue(null);

        b.setInserter(rb);

        b.renderBody(writer, cycle);

        b.setInserter(null);

        replayControls();

        rb.render(writer, cycle);

        verifyControls();
    }

I'm testing RenderBlock, so I use the Creator (an improved version of Tapestry TestAssist) to create and initialize a RenderBlock instance (even though RenderBlock is an abstract class).

The Block, which will have methods invoked on it by the RenderBlock, is a mock object (a zombie). Notice that its created exactly the same as creating a mock for an interface.

This is a huge improvement over the old approach for testing in Tapestry: build a simple application inside the integration test framework. The problem is, the integration test framework is very slow (several seconds per test) and cranky. A small amount of integration testing is important, but cumbersome for the amount of tests a product as sophisticated as Tapestry requires (at the time of this writing, Tapestry has approximately 545 tests, including about 30 integration tests). Unit tests, by definition, are more precise about failures.

I predict that the number of tests in Tapestry may well double before Tapestry 3.1 leaves beta! But then again, I'm pretty well test infected!

Tapestry URLs: Half way there

Did a lot of work this weekend on the Tapestry URL front. If you are familiar with Tapestry, you know that nobody likes the way the URLs are formatted. That's an issue, because you don't have control over it ... Tapestry is responsible for building the URLs (a very good thing) and does all the dispatching and so forth (again, very good thing). But Tapestry's butt ugly URLs are a problem for many.

  • All URLs are built off a single servlet, typically /app. This defeats J2EE declarative security, which is path based.
  • The URLs are longish. For example,
    /app?service=direct/0/Home/$Border.login&sp=T
    

    There's a rhyme and a reason to all that, but its very much oriented around the code and not the users.

  • The use of the slash ("/") character in the URLs is an impediment to breaking the application into modules (i.e., putting the admin pages into an "admin" folder).
  • The emphasis on query parameters means that most of an application, after the home page, will be "off limits" to any kind of web spider, such as Google.

I spent a large portion of the last few days working on this, and I'm halfway there. Of course, you'd hardly know it ... if you download Tapestry from CVS and built it right now, you'd see that the URLs have gotten longer! That service query parameter has been broken up into several smaller variables: service, page, component and, where necessary, container. Some Tapestry engine services add others (for example, the asset service has a path query parameter).

That means that the example URL from before would look like:

/app?component=$Border.login&page=Home&service=direct&sp=T

The next step (the work not yet done) is more fun ... what if we converted the page query parameter into more path info, and the service into an extension? Then our URL is much friendlier:

/Home.direct?component=$Border.login&sp=T

If we map the .html extension to the home service, then a Tapestry page looks like a normal HTML page:

/Home.html

From a client web browser, that's a reference to a Tapestry page, even though it looks like just the page's HTML template. With this kind of mapping, you might not even use the PageLink component in your templates, just ordinary HTML links:

<a href="misc/About.html">About</a>

Instead of:

<a jwcid="@PageLink" page="misc/About">About</a>

Getting all of this to work for your own application will require:

  • Adding path mappings to your web.xml:

    <servlet-mapping>
      <servlet-name>app</servlet-name>
      <url-pattern>*.html</url-pattern>
    </servlet-mapping>
    
  • Adding configuration data (HiveMind contributions) to your application, to tell Tapestry about those mappings:
    <map-extension extension="html" service-name="page"/>
    

Hopefully, I can get more of this going in the next couple of days (I have a long flight out to San Francisco this week).

Still, even with these improvements, the URLs aren't perfect. There's some dissatisfaction with bookmarking URLs with the external service; any extra data is encoded into the sp parameter, which has a prefixing system to identify the type of data. For example, "S" for string, "d" for double, "O" for serialized object.

Talking with Erik, I had the idea that we could simplify things by creating a service that bookmarked some set of page properties into the URL. So you might have a ShowAccount page, with an accountId property of type long. The accountId property gets converted into a string as part of the URL. Later, when the request is submitted, the accountId query parameter is converted back to a long and pluggined into the page's actionId property. The end result is a "pretty" URL:

/ShowAccount.html?accountId=972

This is much prettier than what you might accomplish in Tapestry 3.0 using the external service:

/app?service=external/ShowAccount&sp=l972

I think Erik wants to go further though ... he wants something like:

/ShowAccount/972

I think we can get there. I think we can allow one extension to map to different services (here, the extra query parameter hints that this is a bookmark service, not a page service, request). It's a matter of flexible interfaces and flexible HiveMind contributions.

Wednesday, December 01, 2004

Experiments with Tapestry and JDO

I'm working on some new examples to replace (or give context to) my existing set. I've decided that its is more useful to do some hand waving and build examples that hit a database than it is to stick to simple examples with no back end at all.

To that mind, I'm building a new example application, ePluribus, a survey site. I'll let you decide for yourself if the name is punny enough. ePluribus will be based on Tapestry 3.0.1 and HiveMind 1.0 (for the moment), and I'm using JDO for the back end. Once Tapestry 3.1 is stable, I will heave a great sigh of relief and switch over to that (the connection between Tapestry and HiveMind is somewhat jury rigged in Tapestry 3.0.1). I expect that ePluribus will be more relevant than the aging Virtual Library (discussed in the last chapters of Tapestry in Action).

I was very excited by JDO when I first heard about it back at JavaOne 1999. I've never understood why Sun didn't get fully behind JDO ... maybe because it doesn't require an application server. Talk about write once, run anywhere! Political questions aside, JDO seems ever more ready for prime time.

I had a good experience on TheServerSide.com, integrating Steve and Bruce's Kodo JDO code with the new Tapestry front end. Kodo is very clean, very well documented, and provided some great support to me. I've heard rumbles about performance, but TheServerSide.com is running faster than ever since the changeover (something I attribute to Kodo's query caching combined with Coherence's cluster-wide cache).

However, for a new, redistributable chunk of example code, Kodo (a proprietary product) is not the solution. Currently, I've turned to JPOX, a very well spoken for open-source product. It appears to use a variation of the Apache Software License 1.0.

They already have a port of the Virtual Library to JPOX. If they can handle the Virtual Library, they can handle ePluribus.

I've already implementing basic infrastructure, such as HiveMind interceptors to manage transactions. and injecting the PersistenceManager into other services. In fact, HiveMind's threaded service model is perfect for this kind of thing ... a fixed proxy is injected into other services. Invoking methods on the proxy delegates out to a per-thread implementation. This is what HiveMind is all about ... the other services can use the services provided by the PersistenceManager without worrying at all about its life cycle.

The JDOTransactionInterceptor ensures that transactions are committed after each method invocation (unless specifically told not to) and that any thrown runtime exception rolls back the current transaction.

Think about the amount of code you would typically write ... get the PersistenceManagerFactory, get the PersistenceManager from it, start and commit a transaction, close the PM. Don't forget rolling back transactions when exceptions are thrown. That's lots of code around your individual service methods that isn't necessary when using a HiveMind-based IoC approach.

As a side benefit, a PersistenceManager is only obtained from the PersistenceManagerFactory, and a transaction is only started, the first time (per request) that a method is invoked on the PersistenceManager proxy. The current production code on TheServerSide may start and commit half a dozen transactions per request. When that code is refactored around a "thin stack", there will never be more than one transaction per request for the majority of pages (that only read, not update, information).

JPOX is responsible for transactons and JDBC connection pooling ... and suddenly, there's no need for an application server at all. That's "thin stack" ... less code, same (or more) functionality.

So the question is ... why not Hibernate? I actually started this project a ways back, on Hibernate. In theory, JDO and Hibernate are largely equivalent ... in practice, I've just enjoyed JPOX and Kodo more than Hibernate. The documentation has been easier to follow, the XML configuration has been more natural to me, the error reporting is a notch better (but certainly not state of the art). I've been more effective, faster, with JPOX than I was with Hibernate, and whether the difference is in the two packages, or just the experience I've gained elsewhere in the last couple of months, I don't know or care. Viva choice! Viva Open Source!