Tapestry Training -- From The Source

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

Friday, March 17, 2006

Bitten by IE once again

If there was ever a single piece of software I hated with a passion, it would be Internet Explorer. I had let my seething resentment for this abomination settle for a while, as I do all my browing and development using FireFox.

However, with my project developer hat on, I have to support IE. My client, of course, is interested in IE support (given their user base, IE use will likely predominate). On just one page of my application, we hit the Operation Aborted error. The symptoms of this is that a page paritially loads, then a modal popup announces "Operation Aborted", and when you click OK, you are sent to the "This page cannot be displayed" page. I'm still tracking down the documentation, but it appears to be about a race condition where JavaScript modifies the DOM before IE is ready for it.

Now, I would have thought Tapestry would be safe from this, because of its approach to JavaScript ... the fact that JavaScript goes in proper places, at the top of the document, or at the bottom, rather than scattered throughout the document.

Doesn't matter; this page uses a DatePicker component, nested within a few tables for layout (a new reason why tables for layout is a bad approach) and apparently that's enough to trigger this bug in IE.

Side note: I talk a lot about the importance of Feedback, that tools should clearly identify problems and guide you to solutions. On a grading scale of A - F, IE would receive the grade take out back and put down like a rabid dog on this issue. And many others.

I have a couple of leads on this bug:

I'm going to see if using the Tacos DatePicker will work better than the built-in Tapestry DatePicker.

Update: The Tacos DatePicker did the job. Better yet, the latest DatePicker (from Tacos 4 beta 2) is simply stunning from both a visual and a useability perspective.

Tuesday, March 14, 2006

NFJS examples ... not quite ready

Having a problem with Maven 2, so I haven't been able to upload the updated NFJS examples.

Wednesday, March 08, 2006

From the fanciful ideas category ...

I was just thinking about a kind of half-measure between normal Tapestry useage and Trails.

One of the tedious aspects of building a lot of web apps (using Tapestry) isn't the managing of data in and out of Hibernate or JDO, it's just the repetition of building a table, inside a form, with each row containing a FieldLabel and a TextField (or PropertySelection, or whatever).

Wouldn't it be nice if I could just plop the following into the middle of my form?

<span jwcid="@edit:EditObject" object="ognl:pojo"/>

And this magic EditObject component could build the rest for me? This, certainly, would leverage Trails code, or Trails-like code. I'm sure there would be additional parameters to control CSS, and to control which properties were to be editted. And, of course, some set of annotations to define the validation of those properties. Maybe even so carefully named Block components to provide row overrides? Again, very Trails.

I think this logic would kick ass when building prototypes.

I was just sending a reply to Matt Raible about Java web framework sweet spots. At the core of my response to why Ruby on Rails is gaining so much mind share is because its represents a solution, not a tool. The Java space, especially the open source crowd, has gotten really good a churning out extremely useful tools. However, we tend to leave the solutions to the motivated students. The lesson of Ruby on Rails is for us tool-makers to get fired up about creating solutions.

This is just the opposite of how Tapestry has evolved, which (of course) parallels the evolution of how my coding and design skills has evolved. Earliest Tapestry was a very pure tool, focused entirely on "animating" HTML tags, as well as the transient and persistent state management. Over time, the components evolved from a focus on a single tag to many tags, to entire behaviors (including early precursors to Ajax techniques). That's great, but it's been left to the imagination of the user to see how those tools fit together to allow you to create a useful application.

This is more than just a question of examples, it's really about the emphasis of the overall framework. "Here's how you edit your object (in one line)" should be the first thing new users see and learn ... giving the users the ability to exactly control the HTML should be lesson three or five or ten. That's where Trails is getting things very, very right.

Subversion: ouch!

So, all of a sudden, I'm having massive problems with my Subversion server. Basically, nothing works. I can't seem to check new files in (it appears to crash svnserve, resulting in broken pipe exceptions) and I also see problems checking files out.

I'm in the middle of a full backup of my laptop (before my big trip to Boston and then Tucson). I think, after that, I'll dump out my repository, and rebuild it as a file system (not Berkeley DB) version. I've heard people claim that SVN in server mode doesn't work well with Berkeley DB, but I had thought that was only when you have multiple users pounding on it.

This is frustrating, and bad timing. I'm concerned that I'll have data, or at least revisions, in SVN I can't access. We'll see. It's always something.

Anyway, this is why I haven't had a chance to update the code for the NFJS project to include the latest code and presentations. I can't get things into SVN so that I can properly build and deploy.

Update: It's possible that I have a bad sector on my hard drive (Acronis has found an unreadable sector ... it's impossible to say if this bad sector has anything to do with my Subversion repository). Either way, reading (if possible) the repository to a dump format, and building a new file-system repository, should be a reasonable approach.

Wednesday, February 22, 2006

Update to tapestry-flash

I've updated the build for tapestry-flash to compile for JDK 1.3. I haven't changed the version number, but Maven 2 should pull it down anyway, since the time stamp and MD5 have changed.

Gearing up for NFJS

If I've been a bit unresponsive lately, it's because I've been working full time++ for a client, and putting together new versions of my presentations for two upcoming No Fluff Just Stuff engagements: St. Louis and Boston.

I'm completely retooling my existing presentations on Tapestry and Tapestry components, and writing a brand new one about unit testing with EasyMock (which isn't about Tapestry per-se, but may include some details about testing Tapestry components).

The new examples will be available via my Maven 2 repository. I may even move the source code from my private SVN over to JavaForge at some point.

Tuesday, February 21, 2006

tapestry-spring: simpler and better

I've changed tapestry-spring to be simpler and better. It now leverages Spring's ContextLoaderListener, rather than trying to replace it. In addition, it is now compiled for compatibility with JDK 1.3.

Since it is so alpha, I haven't been bumping up the version number for these changes. I suspect a 1.0.0 release will come soon. This library is now down to one class, some XML configuration, and a little bit of test code.

Maven 2: Different compilers for main and test

I'm in a situtation where different parts of my code need to use different compiler options. My main code often must be targetted for JDK 1.3, whereas I often use JDK 1.5 features (such as annotations and generics) in my test cases.

As I'm slowly peeling back the layers of Maven 2, I am finding solutions to these kinds of problems. Elegant (if somewhat verbose) solutions.

It turns out that you can not only provide a configuration for a particular plugin, but you can provide the configuration for that plugin in the context of a particular goal (or goals). So my solution was:

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        <configuration>
                            <source>1.3</source>
                            <target>1.3</target>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
That is, when reaching the "compile" goal, use the provided configuration. In other compile situations, use defaults (in my case, inherited from a parent POM).

I'm still working out exactly how phase and goal interact. It seems like goals attach to different phases (to control order of execution) ... and you can name goals or phases on the command line. Then there's the question of how prefixes get mapped to plugins ... I feel I've only peeled back the first layer of this onion!

Sunday, February 19, 2006

Tapestry-Spring update

James Carman pointed out that I had mis-packaged the hivemodule.xml inside WEB-INF, not META-INF. This is what happens when you switch contexts 5,000 times per day. I've fixed this and updated the 0.1.1 distro in the repository. Also, I probably should be appending "-snapshot" to the version number (I have to see if that operates the same in the Maven 2 world as in the Maven 1 world).

Thursday, February 16, 2006

Updates to Tapestry @ JavaForge

I decided to move the home page for Tapestry @ JavaForge back to my web site, so I can control things a bit easier. Tapestry @ JavaForge is a small, but growing, collection of Tapestry extensions, designed to work with 4.0.

These projects are useful as themselves, but are also quite interesting as examples of how extensible Tapestry 4 is. One adds a new binding prefix, "prop:", that uses generated bytecode, not reflection, to read and update bound properties, and is designed as a replacement for OGNL in many simple situations. This is useful when performance really counts.

The second extension adds a new type of property persistence, "flash", that is similar to the Ruby on Rails flash storage ... data is stored on the server, but only until the next time it is used to render the page. The documentation explains how to combine the flash with the really easy redirect-after-post supported in Tapestry 4.

In other words, small but significant extensions to Tapestry's base functionality. And the configuration needed to make use of these? Drop the JAR onto the classpath. That's what HiveMind is all about. All the necessary configuration is encapsulated inside each JAR's hivemodule.xml deployment descriptor. It Just Works.

As importantly, there is now a Maven 2 repository for this work, http://howardlewisship.com/repository/. With a little work (described on the home page), you can set up your local Maven to pull down these files, just like any other dependency.

I'm a little better than luke-warm about Maven 2; it's a huge advance over Maven, but it has stability problems, much out of date documentation, and even more generated documentation with very little content. I guess it is encouraging people to guess and experiment, which I simply don't have the patience for right now. However, I do think there's a lot of promise in Maven 2, so I'll be sticking with it for a bit. I still indent to transform HiveMind 1.2 and Tapestry 4.1 into Maven 2 projects.

Tapestry promoted to Apache Top Level

Following our request to the Apache board, and a unanimous vote of the Apache Board, the Tapestry project is moving ... to the Apache top level. We've only just received notice of the vote, but very soon now, there will be tapestry.apache.org.

This isn't so much about Tapestry as it is about Jakarta, representing the fact that Jakarta is re-organizing into more of a "meta-incubator", with the goal of all succesful projects (outside of commons) moving up to the top level.

However, this is still a vote of confidence for Tapestry and the team and will make many things better/faster/easier for us. It's all good (but it's, yet again, more work for me!).

Monday, February 06, 2006

Why store passwords in the database?

I'm always amazed whenever I hear about some security fiasco where a whole bunch of user information, including passwords, is obtained from some unprotected database.

I mean, I may not be a security expert, but even I know that you don't need to store the actual password in a database to allow users to login in securely.

My standard approach is to store the MD5 checksum of the password, rather than the password itself. In fact, I go further; I build a string combining the user's login id and their password and generate and MD5 from that. This has the advantage that different users with the same password will have a different value stored in the database, making it that much more difficult to gain wider access, even if some other security flaw gives the black hats read access. It doesn't even give up the size of the password, since md5 checksum are always the same length, regardless of the number of bytes in the stream used to build the checksum.

This is still a naive approach; you can do more, including adding additional "salt" to the alogiritm (such as a random number, stored only on the server, that is also factored into the checksum). However, at the core of this approach is the desire to never store a password as plain text. Encrypt it, hash it, obscure it. All you need to do is prove that the user knows the password and you don't need to store the password in plain text to do that.

As I remember, this is the way that Unix has traditionally stored password data, so I continue to wonder whenever I see a design that stores passwords out in the open, waiting to be harvested by the black hats.

Friday, February 03, 2006

Take #2: Ubuntu

I eventually found out that VMWare and Mandriva (the successor to Mandrake) don't quite play well together -- there's no easy way to get VMTools installed, and without that, mouse and video performance is bad. Based on a few recommendations, I'm trying Ubuntu instead. So far, I've managed the necessary incantations to download and install Sun's JDK 1.5 but I've been annoyed by performance issues, even with VMWare tools installed.

Things run very, very slow, even at modest (1024x768) resolution. The mouse can't keep up with my, I click and drag and it see the move too soon after the click and starts to do a select area. You have to click the mouse, hold it still for noticable fraction of a second, then drag. My students will lynch me if I foist this upon them ... especially considering that this is on my monster Alienware system.

Based on some recommendations on the web, I'm updating to the 686 kernel right now, to see if that helps. The Mandriva install was much easier and more performant, but people say that Ubuntu is the one. So much to learn ...

Thursday, February 02, 2006

Capturing parameters in EasyMock 2.0

I love EasyMock, especially the new version, 2.0, which really takes advantage of JDK 1.5 generics. Without EasyMock (1.1) I don't think either Tapestry or HiveMind would be as high quality as they are.

But even with the friendlier 2.0 version, there's one difficult case: When the code you are testing creates an object and passes it to another mock object. With the built in tools, you can use the isA() argument matcher to check its type, but that's about it.

In my situation, the code I was testing was creating an interceptor and pushing it onto the InterceptorStack. The interceptor wasn't returned, just pushed, and the InterceptorStack was itself a mock. I need to get that interceptor object so I can invoke methods on it, to ensure that it was created properly.

My solution was to create an EasyMock arguments matcher that exists just to capture a single parameter value. Here's the class:

package com.javaforge.tapestry.epluribus.tx;

import org.easymock.IArgumentMatcher;

import static org.easymock.EasyMock.reportMatcher;

/**
 * An argument matcher that captures an argument value. This allows an object created inside
 * a test method to be interrogated after the method completes, even when the object is not a return
 * value.
 * 
 * @author Howard M. Lewis Ship
 * @param <T>
 *            the type of object to capture
 */
public class Capture<T> implements IArgumentMatcher
{
    private T _captured;

    public void appendTo(StringBuffer buffer)
    {
        buffer.append("capture()");
    }

    @SuppressWarnings("unchecked")
    public boolean matches(Object parameter)
    {
        _captured = (T) parameter;

        return true;
    }

    public T getCaptured()
    {
        return _captured;
    }

    public static <T> T capture(Capture<T> capture)
    {
        reportMatcher(capture);

        return null;
    }

}

When asked to match an argument, it returns true after storing the captured value for later.

The static capture() method is important, it's how we thread things together. Here's an example from my test case:

  Capture capture = new Capture();
  InterceptorStack stack = createMock(InterceptorStack.class);

  . . .

  stack.push(capture(capture));

  . . .

  TransactionalService interceptor = capture.getCaptured();

Again, while I'm training the method invocations on my mock, I setup the capture, using the static capture() method. Later, after I've invoked my test class (the TransactionInterceptorFactory), I can get that interceptor object that was pushed onto the InterceptorStack and invoke more methods on it. Seems to work like a charm!

Denim - sketch your website

I'm building out some new IP for Tapestry ... a funky way of saying, I'm building a new, better reference application. It's called ePluribus and it uses Tapestry 4, annotations, HiveMind and Hibernate 3; in the long run, it may also use Lucene and a smorgasborg of other frameworks. Eventually, I will use this code as the basis for a number of presentations and use it to improve my Tapestry Workshop. But right now it's still pretty formative.

One of the first steps in designing a web application is the site map; this is often a frustrating excercise with post-it notes or scraps of paper (or, god help you, sequence diagrams) that are hard for the developers to interpret, and pretty much impossible for the clients. For ePluribus I'm wearing many hats, one of which is to be the client, another to be the web designer. I also say ... use the right tool for the job.

As best I can tell, the right tool is DENIM. DENIM allows you to sketch out your site map (the UI is designed for a tablet, but works with a mouse). It's designed for very rapid prototyping. Your site map is a single document that you can scroll and zoom to access individual pages. You can literally scribble your site together, creating links and buttons and drawing lines to connect pages together. It uses a lot of gesture based input (again, good when you are using a tablet).

A key innovation is that DENIM can export a HTML prototype of your site! Here's a sample.

DENIM is free (it was created at the University of Washington) and is written in Java; can't seem to find anything about source code or licensing, however. It's so worth a look!

ePluribus is hosted on JavaForge. There's no home page yet (its under the Tapestry @ JavaForge umbrella). The source is in SVN as http://svn.javaforge.com/svn/tapestry/epluribus/trunk and can be viewed directly (though the HTML view seems to be out of date somehow!).

Tuesday, January 31, 2006

Tapestry at JavaOne 2006

I'm 50% for JavaOne this year; a BOF session for Tapestry ("The Component Edge: Creating and Using Components with Tapestry") was accepted, but a more introductory Tapestry session, like last year's, got passed over. I guess by JavaOne 2007 I'll be able to talk about Tapestry 5 :-).

As a BOF, there will be much more opportunity for interaction with the audience, and room for others to discuss what they are doing with Tapestry, which I think is exciting. No word on scheduling yet, beyond the May 16 - 19th for the overall conference. I probably won't stay for more than a day or two this year, as I have to be in London on the 22nd.

Friday, January 27, 2006

Tapestry/HiveMind Interview on Java Posse

The Java Posse just put up my PodCast interview about Tapestry and HiveMind that also touches on Spring, EJB3, WebObjects, JMS, Reflection ... you name it! I kind of expected them to edit it down a bit more than they did ... and they certainly left in a number of things I consider flubs! It's about 59 minutes long and was a lot of fun to do. Apologies in advance for everyone whose name I forgot or screwed up (yes, its Hugo Palma who's working on the Tapestry IntelliJ plugin).

Side note to the entire world: It's HiveMind; the "M" is upper case. Like my mother-in-law has said to my wife Suzanne: "If I had wanted to call you Suzy, that's what I would have named you."

Tuesday, January 24, 2006

VMWare a qualified success

I blogged previously about my experiments with VMWare, and now I'm actually using this approach to my Tapestry Workshop at a customer site.

I'd call it a qualified success and I will follow this approach further. Perhaps the biggest problem was the setting on the VM's memory size ... it was set to 512MB, but the client machines only had about 512MB of physical RAM and this caused a lot of thrash. Dropping this to 256MB (which can be done by editting the .vmx file, or as an option inside a launched VMWare image) made all the difference. This is certainly something that will take some care.

Performance seems to be reasonable. Of the ten students, eight are working fine, one has a few mysterious problems and may have to reload his image from the DVD and the last decided to "go it alone" and copy the labs off of the CD and work on his native desktop (in other words, follow the pattern I've used in previous Tapestry workshops).

Right now, we're using a Windows XP image and that's causing some problems; I'm in the process of building out a Mandriva Linux image instead ... no licensing concerns, and will probably outperform Windows XP by a good margin.

On the one hand, this client is a good choice for the experiment ... the desktops in their training center are uniform and recent (if not powerhouses). On the other hand, no previous clients I've trained had such a setup, so I'm still concerned about facing a more typical client ... one with a tangle of varied laptops and hand-me-down desktops of all shapes and descriptions.

Friday, January 20, 2006

Another article on Tapestry at DevX

DevX has just posted Rapid Java Web Application Development with Tapestry. It's a very fast paced intro to Tapestry and is balanced (yes, Tapestry does have its share of quirks!).

I've been doing my best to push Tapestry over the top for a couple of years now, ever since I left my last full time job and became a consultant. Tapestry really feels there, with more and more people jumping on the bandwagon (in a positive way).

Even here in Portland, I've already met with three different organizations that use Tapestry exclusively and love it. It's another sign that moving away from Boston was good for body and soul.

Monday, January 16, 2006

Getting started with VMWare

I got a great suggestion from one of the developers at shopping.com: Make use of the recent VMWare free player to setup the Tapestry Workshop environment. I had been aware of VMWare in the past (when smart people like Craig Dodson or Greg Burd swear by a product, you should take notice).

So what's VMWare? It's a virtual computer within your computer. It allows one physical workstation to act as if it were several different computers. This is incredibly useful in a large number of scenarios; my friend Scott was using it when developing Windows device drivers ... he could install the drivers into a virtual machine and when it blew chunks, instantly reset its state back to before the driver wreaked havok. People have used it to run multiple versions of Windows, just so they can run multiple versions of Internet Explorer for cross-release browser compatibility. You can also use it to run alternate operating systems ... run a Linux environment from inside windows, for example.

In fact, at this very moment, I'm installing Windows XP to a VM inside my new desktop ... even while iTunes is running on my non-virtual OS. It's even properly mixing the music with sound effects from the Windows XP installer.

My goal is to create a VMWare image containing my Tapestry Workshop environment. Using the free VMWare player, and a copy of my image, people will have the entire stack .. from the JDK, to Eclipse, to the plugins and projects, to Jetty and FireFox all pre-configured and installed and ready to start with the first lab. This has been taking as much as half a day to accomplish at most clients ... valuable time, and a distraction from people's excitement about learning Tapestry.