Tapestry Training -- From The Source

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

Saturday, October 23, 2004

Upgraded HiveMind to Forrest 0.6

The Forrest team recently announced Forrest 0.6. When I first switched HiveMind away from Maven, a key concern was documentation, and Forrest provides much more and better functionality than Maven.

However, like Maven, with Forrest you venture forth from the narrow blessed path at your own risk. I had much trouble getting HiveMind's documentation to build to my liking under Forrest. The tool is complex, with many, many, moving parts (including Cocoon !) and will often create wrong output, rather than report an error.

In 0.5, I discovered that I had to include <index href="index.html"/> in my site.xml file to get the tabbed navigation views to work properly.

The upgrade to 0.6 is supposed to be simple but I found a number of problems:

  • The imported Ant script doesn't work (details in the forrest-user mailing list). Even if it did, it would pollute my project with many additional targets. My workaround was to re-invoke Ant using the patch Ant libraries shipped with Forrest (!)
  • Had to completely rebuild the skinconf.xml file
  • cli.xconf had to be changed, and it was necessary to create a project.configfile entry in my forrest.properties file
  • Forrest 0.6 kept emitting the exact same file as index.html at each level of my project. This was not only the wrong information for the hivemind, hivemind-lib and hivemind-examples folders, but didn't render because relative paths to the style sheets were broken. The only workaround I found, and this is very ugly, is to change all the document hrefs in my site.xml to use complete paths to each file (i.e., <index href="hivemind/index.html"/>).
  • I've been getting various errors related to wholesite.html and wholesite.pdf, so I turned that off.

All told, I spent quite a few hours getting this working to this level, most of it the kind of blind thrashing that's necessary when tools don't validate properly, or report problems usefully. Many times I strongly considered working backwards to Forrest 0.5.

The results so far are a cut above what we had before. The smaller fonts are good (as is the JavaScript based font-size control). There are a number of layout glitches (at least under FireFox) related to long entries in the left-side menu. I with I had more control over what menu items start expanded (maybe I do have that, but Forrest documentation is paradoxically pretty poor and I've yet to find it).

Forrest 0.6 also gives you more control over style, by making it easy to integrate some custom CSS styles into the default stylesheet (this is part of skinconf.xml) and allowing all elements to accept a class attribute.

Overall, I'd give Forrest a C+ as a grade; the final results once working are a good B+/A- ... but the process for getting there (especially when factoring in the pain of an upgrade) has to be considered. The cost of adoption can be extraordinarily high, especially if you have an existing project and existing documentation.

Fortunately, unlike the Maven team, the Forrest team recognizes this at least as far as their version numbers; 0.6 indicates that it is still an alpha or beta release and they know they have some distance to go before they can call it a true product.

[FIXED] Abandoned by Google Mail

It's funny how quickly you can become dependent on a service ... and if it's a free service, with no support, that can be a problem. In my case, it's Google Mail, which no longer lets me log in (it hangs for a while with the "Loading ..." message, then pops up a window about trying again later).

I've let myself get dependent on GMail for nearly all of my Tapestry and HiveMind correspondence but there seems to be no support outside of this support group. Is it moderated? My posting about this problem hasn't shown up. I'm not the only one with this problem, but since Google Mail doesn't have any accessible support, I'm a bit hosed!

If and when I get this resolved, I may rethink my usage of GMail. It's fast, convienient, and searchable ... but none of that is helpful if you can't depend on it!

Updated 10/26: And GMail is back again. I added a bug to their support system (only accessible once logged in) complaining that ... their bug system is only accessible once logged in!

Thursday, October 21, 2004

Speaking at NoFluffJustStuff this weekend

I'll be speaking at the Boston No Fluff Just Stuff this weekend (Oct. 22 - 24th). My three sessions (Tapestry forms, Tapestry components, HiveMind) are all on Sunday, but I'll be around the entire weekend. Jay has been experimenting with alternatives to the expert panel, such as "Birds Of A Feather" or other smaller, more focused interactions and that I like and am more comfortable with.

Tuesday, October 05, 2004

Now that's handy

[Eclipse Clover View]Clover has always been useful (and Cenqua has always been generous with licenses for open source projects), but I finally got around to installing their Eclipse Plugin. It's not perfect, but it is simple and it does work. My old procedure was the run my tests using Ant to build the code coverage, then switch back and forth from a web browser to my source code. This is much, much easier (though the markers for lines that have not executed are a bit too subtle ... I'd prefer a change in background color, as with the HTML report).

Meanwhile, it's time to fill in some of gaps in my code coverage test suite!

Monday, October 04, 2004

Worst Eclipse Crash --- Ever

Somehow, after years of flawless service, my Eclipse workspace just self-destructed. Don't know what caused it, it just freaked out while I was switching between projects. My .log file says:

!SESSION Oct 04, 2004 18:44:07.894 ---------------------------------------------
eclipse.buildId=I200406251208
java.version=1.4.1_01
java.vendor=Sun Microsystems Inc.
BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=en_US

!ENTRY org.eclipse.osgi Oct 04, 2004 18:44:07.894
!MESSAGE An error occured while automatically activating bundle org.eclipse.core.resources (27).
!STACK 0
org.osgi.framework.BundleException: Exception in org.eclipse.core.internal.compatibility.PluginActivator.start() of bun
        at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:975)
        at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:937)
        at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:421)
        at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:293)
        at org.eclipse.core.runtime.adaptor.EclipseClassLoader.findLocalClass(EclipseClassLoader.java:110)
        at org.eclipse.osgi.framework.internal.core.BundleLoader.findLocalClass(BundleLoader.java:371)
        at org.eclipse.osgi.framework.internal.core.BundleLoader.requireClass(BundleLoader.java:336)
        at org.eclipse.osgi.framework.internal.core.BundleLoader.findRequiredClass(BundleLoader.java:914)
        at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:399)
        at org.eclipse.osgi.framework.adaptor.core.AbstractClassLoader.loadClass(AbstractClassLoader.java:93)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:255)
        at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:315)
        at org.eclipse.ui.internal.ide.model.WorkbenchAdapterFactory.(WorkbenchAdapterFactory.java:26)
        at org.eclipse.ui.internal.ide.model.WorkbenchAdapterBuilder.registerAdapters(WorkbenchAdapterBuilder.java:33)
        at org.eclipse.ui.internal.ide.IDEWorkbenchAdvisor.initialize(IDEWorkbenchAdvisor.java:155)
        at org.eclipse.ui.application.WorkbenchAdvisor.internalBasicInitialize(WorkbenchAdvisor.java:165)
        at org.eclipse.ui.internal.Workbench.init(Workbench.java:789)
        at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:1325)
        at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:254)
        at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:141)
        at org.eclipse.ui.internal.ide.IDEApplication.run(IDEApplication.java:96)
        at org.eclipse.core.internal.runtime.PlatformActivator$1.run(PlatformActivator.java:335)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:273)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:129)
        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.core.launcher.Main.basicRun(Main.java:183)
        at org.eclipse.core.launcher.Main.run(Main.java:644)
        at org.eclipse.core.launcher.Main.main(Main.java:628)
Caused by: org.eclipse.core.internal.resources.ResourceException: The resource tree is locked for modifications.
        at org.eclipse.core.internal.resources.WorkManager.checkIn(WorkManager.java:93)
        at org.eclipse.core.internal.resources.Workspace.prepareOperation(Workspace.java:1628)
        at org.eclipse.core.internal.resources.Workspace.close(Workspace.java:298)
        at org.eclipse.core.resources.ResourcesPlugin.shutdown(ResourcesPlugin.java:324)
        at org.eclipse.core.internal.compatibility.PluginActivator.start(PluginActivator.java:52)
        at org.eclipse.osgi.framework.internal.core.BundleContextImpl$1.run(BundleContextImpl.java:958)
        at java.security.AccessController.doPrivileged(Native Method)
        at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:954)
        ... 30 more
Root exception:
org.eclipse.core.internal.resources.ResourceException: The resource tree is locked for modifications.
        at org.eclipse.core.internal.resources.WorkManager.checkIn(WorkManager.java:93)
        at org.eclipse.core.internal.resources.Workspace.prepareOperation(Workspace.java:1628)
        at org.eclipse.core.internal.resources.Workspace.close(Workspace.java:298)
        at org.eclipse.core.resources.ResourcesPlugin.shutdown(ResourcesPlugin.java:324)
        at org.eclipse.core.internal.compatibility.PluginActivator.start(PluginActivator.java:52)
        at org.eclipse.osgi.framework.internal.core.BundleContextImpl$1.run(BundleContextImpl.java:958)
        at java.security.AccessController.doPrivileged(Native Method)
        at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:954)
        at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:937)
        at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:421)
        at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:293)
        at org.eclipse.core.runtime.adaptor.EclipseClassLoader.findLocalClass(EclipseClassLoader.java:110)
        at org.eclipse.osgi.framework.internal.core.BundleLoader.findLocalClass(BundleLoader.java:371)
        at org.eclipse.osgi.framework.internal.core.BundleLoader.requireClass(BundleLoader.java:336)
        at org.eclipse.osgi.framework.internal.core.BundleLoader.findRequiredClass(BundleLoader.java:914)
        at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:399)
        at org.eclipse.osgi.framework.adaptor.core.AbstractClassLoader.loadClass(AbstractClassLoader.java:93)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:255)
        at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:315)
        at org.eclipse.ui.internal.ide.model.WorkbenchAdapterFactory.(WorkbenchAdapterFactory.java:26)
        at org.eclipse.ui.internal.ide.model.WorkbenchAdapterBuilder.registerAdapters(WorkbenchAdapterBuilder.java:33)
        at org.eclipse.ui.internal.ide.IDEWorkbenchAdvisor.initialize(IDEWorkbenchAdvisor.java:155)
        at org.eclipse.ui.application.WorkbenchAdvisor.internalBasicInitialize(WorkbenchAdvisor.java:165)
        at org.eclipse.ui.internal.Workbench.init(Workbench.java:789)
        at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:1325)
        at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:254)
        at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:141)
        at org.eclipse.ui.internal.ide.IDEApplication.run(IDEApplication.java:96)
        at org.eclipse.core.internal.runtime.PlatformActivator$1.run(PlatformActivator.java:335)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:273)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:129)
        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.core.launcher.Main.basicRun(Main.java:183)
        at org.eclipse.core.launcher.Main.run(Main.java:644)
        at org.eclipse.core.launcher.Main.main(Main.java:628)

!ENTRY org.eclipse.osgi Oct 04, 2004 18:44:07.924
!MESSAGE Application error
!STACK 1
java.lang.NoClassDefFoundError: org/eclipse/core/resources/IProject
        at org.eclipse.ui.internal.ide.model.WorkbenchAdapterFactory.(WorkbenchAdapterFactory.java:26)
        at org.eclipse.ui.internal.ide.model.WorkbenchAdapterBuilder.registerAdapters(WorkbenchAdapterBuilder.java:33)
        at org.eclipse.ui.internal.ide.IDEWorkbenchAdvisor.initialize(IDEWorkbenchAdvisor.java:155)
        at org.eclipse.ui.application.WorkbenchAdvisor.internalBasicInitialize(WorkbenchAdvisor.java:165)
        at org.eclipse.ui.internal.Workbench.init(Workbench.java:789)
        at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:1325)
        at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:254)
        at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:141)
        at org.eclipse.ui.internal.ide.IDEApplication.run(IDEApplication.java:96)
        at org.eclipse.core.internal.runtime.PlatformActivator$1.run(PlatformActivator.java:335)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:273)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:129)
        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.core.launcher.Main.basicRun(Main.java:183)
        at org.eclipse.core.launcher.Main.run(Main.java:644)
        at org.eclipse.core.launcher.Main.main(Main.java:628)

And that's no help.

From an hour or more of careful experimentation, I determined that deleting the file C:\workspace\.metadata\.plugins\org.eclipse.core.resources\.snap lets Eclipse start back up ... but loses all my projects. This includes my current project (no uncommitted changes) as well as my jakarta-tapestry project (yes, uncomitted changes!).

Sometimes the computer knows when you are having a bad day, and decides to make it worse!

Friday, October 01, 2004

Royalty Check

Wow! Tapestry In Action has sold enough copies over the last six months to not only pay back the original advance Manning gave me, but even go over and kick me back a modest royalty check. I'd forgotten that could even happen. With some big announcements in the works for Tapestry and for myself, all hopefully feeding into book sales, I bet this isn't the last check I see from them!

Competition is good, right?

Just noticed on the GoogleAd side bar of my own blog that ArcMind (Rick Hightower's consulting/training company) is offering Tapestry Training. The outline is exactly what I'd like to be presenting ... fortunately or not, I've been so bottled up working on a large Tapestry project that I've made no progress on my own equivalent course.

I've talked with Rick a few times and I've worked with Drew Davidson (whose also associated with ArcMind, and is the creator of OGNL). These are guys who have used Struts, JSF and Tapestry in anger (that is, as part of real production work). I've been encouraging Drew to write a fair comparison of Tapestry and JSF; I think it would be a valuable document for JSF and Tapestry users alike, as well as providing the Tapestry team with even more direction.

Hopefully, ArcMind's offering will be a success, and will grow the Tapestry community by more than he steals away from me :-).

Using Tapestry's Table component

Tapestry's Table component is very powerful and very flexible. The Table component can draw information from a variety of sources ... everything from in memory to an external database via SQL and everything in between. The component can handle column sorting automatically, can work inside a Form, and can be broken apart into smaller pieces so that you can provide your own look and feel for parts of it (such as the navigation control for moving between pages of results). You can configure it using a short string, or provide any of several modeling objects to control pagination, data access, column content and order and so forth. Whew!

All that power and flexibilty makes it hard to pick up; John Reynolds has taken up the guantlet and come up with a kind of interactive tutorial and guide to this important Tapestry component.

Maybe he'll follow up with a guide to the Tree component? We can only hope!