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!

Sunday, March 26, 2006

Static imports and Java language evolution

It's interesting to see the differences between how you expect to use a new language feature and how you end up really using it.

For example, of all the new features in 1.5, the idea of static imports seemed to be the least useful. From the outside, it looks like you get to say "min()" rather than "Math.min()". Big woop.

But what I've found when coding using static imports is a new clarity to my code. Once you strip away the class names from static methods the part that's left starts looking a lot like new and extensible language keywords.

For example, here's a test case from some code I'm working on.

    @Test
    public void test() throws Exception
    {
        Method m = ComponentFixture.class.getMethod("getSpringBean");

        EnhancementOperation op = newMock(EnhancementOperation.class);
        IComponentSpecification spec = newMock(IComponentSpecification.class);
        Location l = newMock(Location.class);
        Capturer c = newCapturer(InjectSpecification.class);

        spec.addInjectSpecification(capture(c));

        replay();

        new InjectSpringAnnotationWorker().performEnhancement(op, spec, m, l);

        verify();

        InjectSpecification is = c.getCaptured();

        assertEquals(is.getObject(), "someBeanName");
        assertEquals(is.getProperty(), "springBean");
        assertEquals(is.getType(), "spring");
        assertSame(is.getLocation(), l);
    }

What I've found as I've been coding is that I make extensive use of static imports. First of all, Eclipse makes it easy to do (Ctrl-Shift-M on top of a static method converts it to a static import). Secondly, my personal coding style has been to use a goodly number of static methods (if I can define a new feature in terms of the existing public API of an object, and I don't expect there to be a need for subclasses to override, then I create a static method that takes the object as a parameter).

Those assert calls at the end are actually static methods on an Assert class. Some of the other methods, newCapture() and capture(), are also static. The newMock() calls are inherited from a base class. It actually looks better inside Eclipse, because the static methods are italicized.

My point is ... the code is looking more expressive to me, as some clutter falls away. Autoboxing reduces some more clutter. So does the new for loop, and variable argument lists. Generics are still something of a wash (you get more self-describing code by adding lots of clutter, but you gain by removing explicit casts). The end result follows my motto, though: Less is More.

Has this transformed the language? No, and perhaps that's the point. It's some incremental change, but it's makes for some small steps in the right direction. It's not quite the fluid flow of static class methods in Ruby (used for all kinds of meta-programming), but it's still nice.

On the other hand, Sun is much less aggresive in adopting changes to Java than, say, Microsoft. I hate to say it, but innovation (where innovation is defined as adopting features that other languages have had for a decade or so) seems to be occuring in C# before Java. In some cases, it is occuring in other languages that execute on the Java VM. The end result is the same sad story of Sun chasing Microsoft's tail.

I'd like to see some real improvements to the language. I want Ruby blocks and continuations, C# autotype variables, built-in Aspect support and, hey, maybe something I don't know about that will make my programming life better (maybe a pervasive hot class reloader). In this respect, Java always feels like a perpetual bridesmaid, never a bride.

Saturday, March 25, 2006

NFJS Examples available

I've now been able to build and deploy the examples from my talks at No Fluff Just Stuff. The version number is still 0.0.1 and probably will be for some time. You can obtain a pre-compiled WAR, or a source distribution (with out without the PowerPoint slides) from http://howardlewisship.com/repository/com/howardlewisship/nfjs-tapestry/0.0.1/.

Huzzah! Subversion woes fixed.

I was just starting to panic. I was unable to check out my NFJS example code onto my desktop (it's stored using SVN, using my laptop as the SVN server). I've had some problems with Subversion recently, but thought I had resolved them.

From Eclipse, and from the command line, I was getting a wierd error:

bash-3.00$ svn update
svn: Can't read from connection: Software caused connection abort

From what I can tell, this occured when pulling down some large PowerPoint presentation files. Of course, there's no way to determine exactly what file the failure occured on, or why.

No output in the Subversion.log, or the svnserve command line, or any useful output from svn command line (or the Subclipse tool). Feedback anyone?

I tried updating to the latest Subclipse, 0.9.108. No help.

Finally, I tried upgrading my SVN server to 1.3.0 and ... things are working again!. Supposedly, I could have stayed with a Berkley DB repository using 1.3.0, but I've gone through the work of converting to a file system repository (as one of my attempts to resolve this problem), and I can't see the advantage of moving back. In fact, if anything, SVN seems faster or more responsive using the file system.

I should finally be able to get those NFJS examples up on the web site.

Friday, March 24, 2006

Back from the road! / tapestry-spring plans

Huzzah! Watch out Portland ... I'm back.

I actually had an exit row and room to work on the flight back from Phoenix. I decided to take another look at tapestry-spring.

First off, please remember that it is alpha code! So the fact that it completely doesn't work shouldn't be a total surprise. I had packaged the critical hivemodule.xml file under WEB-INF, not META-INF. Chalk it up to force of habit ... that's what you do for Tapestry applications, but this is a standalone library.

The reason I didn't catch that in my integration tests was ... I'm a lazy, lazy man. I need to learn how to write proper integration tests for tapestry-spring using Maven. I've actually had some good luck starting up Jetty inside a (TestNG) test case and that's the likely course I'll pursue.

So, that's half the story. The other half is dealing with Spring's prototype beans. This was someting that completely surprised me about Spring, once people started to complain. If you define a bean as having the prototype lifecycle, it means that you get a new instance everytime you ask the BeanFactory for it.

I really dislike that approach, because it puts the client code in charge of managing the lifecycle of the bean.

By contrast, HiveMind covers similar functionality quite differently. When you get a service from the Registry (the equivalent of getting a bean from the BeanFactory), you are going to get a proxy. Same service interface, different implementation (brewed up on the spot). The lifecycle of the service is hidden inside this proxy. You can share this proxy around, pass it between threads, even serialize and deserialize it (I think ... there's a few issues around that) and it just works.

The rough equivalent of Spring's non-singleton/prototype beans are HiveMind services with the threaded lifecycle. When you invoke a method on the proxy, it finds or creates a per-thread instance of the service. The per-thread instance is cleaned up and discarded at the end of the request (it's effectively built around a servlet request/response cycle).

This is used all over the place inside Tapestry. What's nice is that a piece of code can treat a proxy to a normal service, and a proxy to a per-thread service identically. That concern (in the AOP sense) is buried inside the proxy. Makes testing easier too.

Now, back to Tapestry's @InjectObject annotation. It's just broken for Spring prototype beans, because it's built around HiveMind's design, not Spring's. So it gets the object out of HiveMind just once and holds onto it, passing it into pages and components via a constructor (this is part of Tapestry's enhanced subclass approach to AOP).

For prototype beans, that means that we get the bean just once per component, and are stuck with it for any instances of that component we instantiate. The same bean instance, once aquired from Spring, will be passed into each new instance of a given page or component.

So ... I'm expanding tapestry-spring to add a specialized annotation, just for Spring. @InjectSpring will create a synthetic getter method that always gets a fresh instance of the named bean from the BeanFactory. That should solve the problem in its entirety.

I just need to stuggle with Maven a little bit more; I want to create a single JAR, where part of the code is compiled with JDK 1.3, but the annotation part is compiled with JDK 1.5. I think I can do this, by having multiple source roots (so src/main and src/annotations) and some extra compile executions.

Ultimately, this code may move under the Tapestry top-level project at Apache, once that gets set up. In the meantime, living at JavaForge is perfectly ok.

But, in the meantime, I need to catch up on the new Doctor Who, waiting for me on my Tivo. I've been out of town that long.

Sunday, March 19, 2006

NFJS code and slides ... just a little longer

It turns out I won't be able to post the updated slides and code from my No Fluff Just Stuff sessions because I left a critical password behind on my desktop. I'm on the road still (a long trip!) and will see about getting this stuff in place this coming weekend. Sorry for any inconvienience.

Friday, March 17, 2006

Zillow.com on The Boston Globe

Just noticed an article in The Boston Globe about Zillow.com. Zillow is a real-estate property values search application; at its core is a Flash application, but all the necessary infrastructure around that is Tapestry 4. I'm sure Ben and Dion are wondering why it's Flash and not Ajax.

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.

Thursday, January 12, 2006

Subclipse new project tricks

I go through a little rigamarole every time I start a new project in Eclipse; I use a Subversion repository (locally, or on Apache, or on JavaForge) and I use the Subclipse plugin.

Subclipse is nice, but weakest on the things you do least often, such as creating a project.

I always start in the SVN Navigator; I create my new folder for my project.

I then share the project; I chose the "Use specified folder name". Next, I hit the browse button and select the folder I just created in the navigator.

Back to the Share Project dialog; the cursor will be in the folder name field, and the text "New Folder" will be selected, ready for me to type "trunk".

When I hit OK, we switch over the a synchronize dialog to do the initial checkin. The bin folder will be in the list of resources to check in ... simply uncheck it for now. Then hit OK to check everything else in.

Because you didn't check in the bin folder, your project will appear dirty. That's OK. Now comes the real trick:

  • Execute a "Replace With Latest" (not an update) on your project.
  • Close and Re-Open your project (to get rid of the .svn folder that shouldn't be displayed, but is)
  • Now, use the Team > Add to svn:ignore context menu item on the bind folder
  • Finally, check in your changes (which will appear to be a change just to the project folder itself ... that's where svn:ignore data lives)

Why is this necessary? Somehow during the initial check in, the local project gets out of synch with the repository. I know not what or why, but it's been very consistent against every repository I use, so I think it is a Subclipse problem. If you don't do the replace with latest trick, you will get incredibly annoying SVN errors when you try to mark bin to be ignored ... you just won't be able to check in your project.

Maven and missing Java Transaction API JAR

I'm starting to work on a new demo for Tapestry 4.0 that will be based on Hibernate3, and I want to build on Maven. Of course, the first thing that happened is that I tried to have Maven resolve the dependencies and it tripped over the JAR for the Java Transaction API.

Now, a little background. The JTA is under some Sun license that keeps it from being distributed; so Maven has the POM for it in the central repository, but doesn't store the JAR file itself. So what do you do?

You have to get the JAR file yourself, and convince Maven to load it into your local repository.

Not that Sun makes it easy for you ... oh no. You have to click through a license agreement, then you get the class files, packaged as a ZIP file. This is insane! You'd think they would know what a JAR file is! Why not package the JAR inside the ZIP, maybe with a license and some documentation? Because that would make sense.

I suppose you could just rename the jta-1.0.1B.zip to jta-1.0.1B.jar and go from there. I chose to build a proper JAR first. I started by unpacking into a local directory, then used the jar command as normal:

bash-3.00$ ls -lag
total 0
drwx------+  3 None 0 Jan 12 18:27 ./
drwx------+ 22 None 0 Jan 12 18:27 ../
drwx------+  3 None 0 Jan 12 18:27 javax/
bash-3.00$ jar cf ../jta-1.0.1B.jar *
bash-3.00$ cd ..
bash-3.00$ jar tf jta-1.0.1B.jar
META-INF/
META-INF/MANIFEST.MF
javax/
javax/transaction/
javax/transaction/HeuristicCommitException.class
javax/transaction/HeuristicMixedException.class
javax/transaction/HeuristicRollbackException.class
javax/transaction/InvalidTransactionException.class
javax/transaction/NotSupportedException.class
javax/transaction/RollbackException.class
javax/transaction/Status.class
javax/transaction/Synchronization.class
javax/transaction/SystemException.class
javax/transaction/Transaction.class
javax/transaction/TransactionManager.class
javax/transaction/TransactionRequiredException.class
javax/transaction/TransactionRolledbackException.class
javax/transaction/UserTransaction.class
javax/transaction/xa/
javax/transaction/xa/XAException.class
javax/transaction/xa/XAResource.class
javax/transaction/xa/Xid.class

Next up, convincing Maven to put it into the repository in the correct place:

bash-3.00$ mvn install:install-file -Dfile=jta-1.0.1B.jar -DgroupId=javax.transaction -DartifactId=jta -Dversion=1.0.1B -Dpackaging=jar
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'install'.
[INFO] ----------------------------------------------------------------------------
[INFO] Building Maven Default Project
[INFO]    task-segment: [install:install-file] (aggregator-style)
[INFO] ----------------------------------------------------------------------------
[INFO] [install:install-file]
[INFO] Installing c:\temp\jta-1.0.1B.jar to C:\Documents and Settings\Howard\.m2\repository\javax\transaction\jta\1.0.1B\jta-1.0.1B.jar
[INFO] ----------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ----------------------------------------------------------------------------
[INFO] Total time: 1 second
[INFO] Finished at: Thu Jan 12 18:29:55 PST 2006
[INFO] Final Memory: 2M/3M
[INFO] ----------------------------------------------------------------------------
bash-3.00$

And, with that, you have the JTA jar, in place, ready to use in your projects.

Is Tapestry "The" Web Framework?

Gregg Bolinger thinks so. I'm glad he's having such a good time with Tapestry!

Wednesday, January 11, 2006

Tapestry Get-Together / Portland, OR

I'm having a little Tapestry get-together meet-and-greet next week (Jan. 18th) here in Portland. This is just a chance for me to meet people in the area, with Tapestry being a convienient conversation starter. We'll be meeting at Henry's Tavern. Discussions should include who'se using Tapestry and how, and where Tapestry is headed in the future. Drop me a line and I'll add you to the e-vite!

Tuesday, January 10, 2006

Tapestry for IDEA

Hugo Palma is now working on an IntelliJ plugin for Tapestry: Tapestry for IDEA. He appears to be factoring out the non-Eclipse specific code from Spindle, and reconnecting it inside IDEA's APIs. It's not clear how far along this process he is, but he is promising to post screenshots shortly.

2006 looks like a true banner year for Tapestry!

Monday, January 09, 2006

Comments now moderated

Well, the latest scumbags have found a way to make life suck for everyone ... in this case, blogger.com is letting spammers post ads for DVDs on my blog. In the meantime, I'm now moderating comments.

Saturday, January 07, 2006

Tapestry on Slashdot

Is it a sign of my personal immaturity that seeing a Slashdot article on Tapestry made my heart skip a beat? This is Slashdot covering the DeveloperWorks article and caught me by surprise. Let the fud/counter-fud begin!

Phew! Tapestry 4.0 is final!

Tapestry 4.0 (final) has been released. This is a great relief to me, because I really think I could explode, or at least spontaneously combust, if this didn't happen.

I'm exceptionally proud of this release; to some degree, we've rewritten Tapestry from the inside out. The new structure, based on HiveMind, makes Tapestry exceptionally extensible, and performance appears to be better than Tapestry 3.0 (so much less reflection going on). More importantly, the coding model has improved quite a bit. Component parameters now just work ... no fumbling with "direction", listener methods are really flexible and easy to use, the validation framework is much, much simpler, and the annotation support (still optional in 4.0) is very succinct and code-focused.

We're already doing some planning for Tapestry 4.1 which better not take two years ... I'm hoping more like 6 - 9 months. I have several tasks on my plate:

  • A fast, useable, productized integration test suite (that can be used to test applications)
  • Convert to a Maven 2 build
  • Rework form support to eliminate the rewind phase
  • Remove the need for pages and components to extend from Tapestry base classes

Meanwhile, time to sit back and see what people make of this new release.

Wednesday, January 04, 2006

Servlet 2.5: Where's the regexp?

Seeing the new features in Servlet API 2.5, the feature I most needed, regular expression matching, was missing. Regular expressions are in JDK 1.4 and above (Servlet 2.5 requires JDK 1.5), so its pretty amazing that a basic feature like this is missing. Certainly, there's a long history of using regexp to map paths, as built into Apache (especially mod_rewrite).

I was really hoping for this feature, to support even friendlier URLs.

Other features I'd like to see:

  • Proper notifications about session synchronization events --- I want to know when an object is being de-serialized from another server in the cluster so I can set up some necessary ThreadLocals
  • Programatic login, and the ability to provide an application-specific login page --- I want to be able to use Tapestry's superior form support to manage logins
  • Failover notification --- I think servlets should support clustering without sharing data, but providing a notification when a failover has occured. Data could be stored in the HttpSession, but would not be replicated. This would give a good middle-ground between single-server and enterprise cluster.
  • Servlet meta-data access --- servlets should be able to find out about their URL patterns and filters

I probably could go on ... I've been working around the Servlet API for quite some time.

Meanwhile, there are some improvements. The DTDs have been simplified (allowing multiple URL patterns per servlet is a step in the right direction --- how about allowing the mapping right inside the <servlet> itself?) The new annotation support looks quite nice ... in fact, putting annotations on fields instead of on methods is the (long term) way to get Tapestry away from using abstract classes, and I can almost smell the bytecode enhancement in the approach implied by the @Resource annotation.

Setting up Tapestry

I had a present this morning, as I scanned my favorite web sites ... an article on DeveloperWorks: In tune with Tapestry by Brett McLaughlin. This is a nice, fully fleshed out article about how to set up Tapestry, build it from source, and run the demos and tutorials.

There are days when I feel the weight of the Tapestry community firm and square on my shoulders, and then there are great days like this ... where the community really gives back. These are the days that remind me of what a giant killer Tapestry can be.

Saturday, December 17, 2005

Tapestry 4.0 Real Soon Now

It's been an interesting week. First off, I had to leave my brand new apartment in Portland, OR after one night to come down to San Francisco for a three-day intensive Tapestry training session. Teaching Tapestry to twenty people in three days is a bit of a challenge, but the results were very well received; I understand that all new web development at shopping.com will be done using Tapestry.

Meanwhile, in the struggle to get Tapestry 4.0 out, the first vote for a final 4.0 release failed, with a veto by Kent Tong. At issue was the gaps in the documentation, particularily the documentation related to validation. This prompted new contributer Jesse Kuhnert to fill in the gaps there, so I'm in the process of producing a new release candidate, 4.0-rc-2, which will be up on the mirrors sometime tomorrow.

This does show that the process works ... but also underscores that the way many other projects function might not be a bad idea. Apache will generally release numbered releases at intervals, and after the fact elevate a release to "final, stable" status. Had we been following that scheme, we'd be at, say, 4.0.15 around now. If we vote that as a stable release ... well, we don't have to do any extra work beyond publicising it; it will already have been available, presumably, for a week or two minimum before such a vote.

On another front, I've been using Maven 2.0, as I try to rework HiveMind to build under Maven instead of Ant. For the most part, Maven 2 feels more like a finished work than Maven 1 ever did. I haven't been wringing my hands in frustration the same way. I just now downloaded the 2.0.1 release and will see if that fixes the few issues I have been encountering ... such as reports not been run and integrated into the site output.

This does tie together ... once HiveMind and then Tapestry are building under Maven, even having regular point releases will be much less important, since it will be quite easy for the average developer to pull down the source and build. Alas, Tapestry and HiveMind currently raise the bar for that a little too high for the casual developer.

Has anyone gone through the process of converting Forrest XML documentation to Maven XML documentation? They are similar is concept but slightly different in details; I'd like to save myself the effort of writing conversion scripts and stylesheets.

Monday, December 05, 2005

Moved to Portland

Well, now we've gone and done it. My wife and I have sold our house outside Boston and relocated to Portland, Oregon. Time to start over in an entirely different part of the country, one that seems to align better with our tastes, politics, and interests. Given the people we met at OSCON 2005 (also in Portland), I know there's a good community of open-source and Java developers around here ... I can't wait to start meeting them!

Tuesday, November 29, 2005

Two New Tapestry Committers: Jesse Kuhnert and Kent Tong

The Tapestry project has just added two new committers!

  • Jesse Kuhnert - Very active with the Tacos component library, which is the hub for Ajax components for Tapestry. He's got an inside line on what it'll take to make Tapestry the best framework for Ajax development.
  • Kent Tong - Active mentor on the user and developer mailing lists, Kent has been keeping me and the other Tapestry committers honest for quite a while now ... and he's written a great book on Tapestry 4.

Great things are afoot for Tapestry ... fresh blood, a final 4.0 release very, very soon now, and an increasing number of high profile sites in the pipeline (and no, I can't say more).

Thursday, November 24, 2005

Tapestry @ JavaForge

I'm just about done setting up Tapestry @ JavaForge, an auxillariy site for Tapestry components and extensions. I've been experimenting with Maven 2, so far with some decent success. I especially like the Wiki-ish "almost plain text" format for documentation. Maven 2 still has a number of teething issues, and I haven't even touched the critical multi-project support yet, but I'm cautiously optimistic.

No downloads just yet, but the source code is easily accessible via Subversion and builds using Maven, which is close enough.

Can you tell I'd really rather not be balancing my checkbook right now? Merry Turkey-Day!

No Tapestry at ApacheCon 2005

Hope I'm not disappointing too many people, but I will not be able to do the Tapestry Tutorials at ApacheCon on Dec 10th, as previously stated. They didn't get enough tutorial sign ups by their Nov. 12th deadline (ApacheCon does terrible marketing), and since I really hadn't wanted to do a tutorial in the first place, I offered to cancel rather than try and come up with a last-minute way to boost attendance.

I was somewhat looking forward to this, since I haven't done any speaking engagements in too long. But with Tapestry 4.0 in the final stages, plus the fact that, as I type this, I'm supposed to be stuffing books into boxes for my move to Portland, Oregon next week ... well, let's just say that skipping ApacheCon goes a small way towards easing my monumental stress level.

Wednesday, November 23, 2005

Woops! Killed JavaForge.com

I think I may have killed JavaForge. No, really. It seems to have blown up and crashed hard as I was uploading the tapestry-prop site tarball into their documenent management system.

I really want to like JavaForge because it is free, and because it allows Subversion hosting ... but any instability (it's been down for about 30 minutes now) is troubling.

Tuesday, November 22, 2005

And Apache's JIRA is down again ...

... and I was going to fix all those outstanding Tapestry bugs tonight. :-) But I can't. Sigh.

Monday, November 21, 2005

Documentation for Script Templates

We're in the final stages of Tapestry 4.0, trying to fix the most glaring documentation ommissions, and tidy up the bug list.

I've finally written documentation for the script template specification DTD, which is a very important file with the growing emphasis on JavaScript with web applications.

Tapestry has had, for a few years, some very advanced code for generating dynamic JavaScript to complement the dynamic HTML. It's centered in these script templates that take care of organizing the JavaScript into two large blocks (one at the top of the page, the other at the bottom) ... which is better than having it scattered about. As importantly, the JavaScript generation understands the need for uniqueness, to prevent name collisions in client-side variables and functions. This is the infrastructure that lets clever folks, like the ones at Tacos, create Ajaxian components that just work without any special attention by the developer ... regardless of whether there's just one such component, or dozens, on a page. Tapestry has the infrastructure to support this, with out requiring any special configuration of servlets or anything else.

Friday, November 11, 2005

Improving Tapestry performance

I just spent the week with a high profile client that is interested in potentially using Tapestry for a very large scale site ... millions of hits per hour. Their in-house framework is quite capable of operating at this scale, through a combination of draconian restrictions on database access and server-side state, and a total avoidance of any kind of reflective object access. These are people who literally cannot give an inch on performance.

One of the ideas that bounced around was something promised for some future release of OGNL: bytecode enhancement. That is, in some cases, OGNL 3 is expected to identify places where it can create a class on the fly to expedite access to a property, rather than always relying on reflective access as it does today.

Alas, that hasn't happened yet, and Tapestry is still using OGNL 2.6.7.

But, I thought, what if we created a new binding prefix to use instead of OGNL, for this purpose. Because of HiveMind, this approach can be packaged seperately from the framework proper, and plug right in.

... and it works. I built a little peformance test harness and tried to figure out how many nanoseconds it takes to perform an operation; an operation involves a read and then an update. Here's one of the operations from the harness:

        Op op = new Op()
        {
            public void run(PropertyAccessor accessor)
            {
                Long value = (Long) accessor.readProperty();

                long primitive = value.longValue();

                accessor.writeProperty(new Long(primitive + 1));
            }
        };

The PropertyAccessor object is either created from bytecode, or implemented using OGNL (so that we can make the comparisons).

I did a number of test runs, with a number of operations:

              10000 iterations |  Direct ns |    OGNL ns
------------------------------ | ---------- | ----------
                 name - warmup |    4288.00 |  847364.00
                          name |    1777.00 |   18426.00
                  int - warmup |    2891.00 |   81127.00
                           int |     838.00 |    7497.00
                 long - warmup |    2969.00 |   28207.00
                          long |     617.00 |    7256.00

             100000 iterations |  Direct ns |    OGNL ns
------------------------------ | ---------- | ----------
                 name - warmup |    4282.00 |  819634.00
                          name |     972.00 |    7527.00
                  int - warmup |    2947.00 |   74425.00
                           int |     242.00 |    5955.00
                 long - warmup |    2910.00 |   27492.00
                          long |     209.00 |    6046.00

             500000 iterations |  Direct ns |    OGNL ns
------------------------------ | ---------- | ----------
                 name - warmup |    4182.00 |  852125.00
                          name |     857.00 |    6756.00
                  int - warmup |    2958.00 |   81820.00
                           int |     170.00 |    5724.00
                 long - warmup |    2793.00 |   34990.00
                          long |     215.00 |    5785.00

          2,000,000 iterations |  Direct ns |    OGNL ns
------------------------------ | ---------- | ----------
                 name - warmup |    4251.00 |  843185.00
                          name |     823.00 |    6553.00
                  int - warmup |    2927.00 |   48788.00
                           int |     144.00 |    5799.00
                 long - warmup |    2961.00 |   34945.00
                          long |     180.00 |    6173.00

The results show that the direct access is around 10x faster than reflective access, which is in line with the general documentation about reflection in JDK 1.5. Still, I'm troubled that the cost per operation seems to continue going down as the number of operations increases. This could be the effect of hotspot (though the elapsed time seems short for hotspot to get very involved) ... or it could represent a problem in my performance test fixture.

To use this, you just use the prefix "prop:" instead of "ognl:". And, of course, it only works for simple properties, not property paths or the full kind of expressions used in OGNL.

I'm hosting the code on JavaForge and will make some kind of release available soon. Perhaps it will migrate into the framework proper at some point.

Friday, November 04, 2005

Further Down The Trail

Chris Nelson has published his second article on Trails: Further Down the Trail.

Seems like a lot of people are getting excited about Trails, and rightly so. I can see a large number of projects getting built this way. It's a great idea, it's developer-focused, it gets you great results fast. I've also seen Chris mention integrating Ajax components from Tacos. What's easier than using Tacos components for Ajax behavior ... using Trails and getting the Ajax stuff for free!

By the way, one of the most remarkable aspects of Trails is that Chris has been building Trails as a way to learn Tapestry. Either Chris is really some kind of comic-book alien machine super-intelligence (unlikely, I've met him in person), or Tapestry isn't quite as hard to learn as some people seem to think. Perhaps Chris had less Struts and JSP clutter to unlearn.

Sunday, October 30, 2005

A cool 1500

Just finishing up a fix for TAPESTRY-602 and, in the process, added the 1500th test to the Tapestry test suite.

Saturday, October 29, 2005

Even Easier Tapestry Examples

I took up an idea posted on the Tapestry User mailing list and, starting with beta-12, the Tapestry examples will be even simpler to obtain and run.

The new Tapestry examples distribution will be a slice of a JBoss distribution, with the Workbench and Virtual Library deployed into it. Yep, about 99% JBoss and about 1% Tapestry, but it means you can untar the examples distro and execute run.bat or run.sh and have it all up and running in less than a minute.

Once again, the less enlightened will bitch and moan about using JBoss. And once again folks, this is a demo. Everyone has their favorite container and I've gotten annoying flak in the past for not supporting all of them ... as if that was the point. In truth, the point is to get an easy to run demo. Perhaps someday I'll use Geronimo, because it is also an Apache project. But I go a ways back with JBoss, so it was easy for me to build and package, and is a good use of my very scarce time.

There is NO dependency between Tapestry and JBoss. Deploy wherever you want, but if you want a quick turnkey demo, download the demos.

Why I like Annotations

I've been working with JDK 1.5 annotations for several months now with exceptional results. Perhaps it's just a reflection of Tapestry, which has grown to use a pattern of design and development I've come to call composite coding.

Composite coding is a kind of intersection point between traditional class-oriented inheritance, aspect oriented programming, and code generation. This approach was already in place nearly two years ago, in Tapestry 3.0. When you define a class as abstract, and define transient and persistent properties (using XML) in Tapestry 3 what you are really doing is compositing your code with code generated by Tapestry.

This is not so much different from traditional approaches, where we composite the code we write with code from base classes often written by others. That's the nature of inheritance (and whether that is a good model for creating a framework is a discussion for another day).

Aspect oriented programming is also a way of compositing different sets of code together to form the final class definitions used at runtime. In fact, aspect oriented programming is such a general term that it easily encompasses what Tapestry is doing ... in fact, the uses of composite coding in Tapestry are very much within the domain of aspect oriented programming: cross cutting concerns related to page pooling and server-side state managements.

What I like about annotations, as applied to Tapestry 4, is that it integrates the code with the composite operations to be applied to that code ... that is, to make a property persistent, add the @Persist annotation directly to the method, rather than configure persistence elsewhere (in Tapestry 3, in a companion XML file).

One of the complaints of this approach is that it puts configuration data directly inside Java class files, where changing configuration requires a recompile and re-deploy of the application.

That's a pretty lame complaint and, to my ears, is an attempt to resist change. Sorry folks, get used to embracing change ... or get used to saying "want fries with that?"

Even in an extreme example, say the EJB3 @Table annotation, the problem isn't with the annotations ... it's with the framework that utilizes those annotations. In many cases, an annotation sets a default value ... one that may need to be overridden to match a particular deployment configuration.

In fact, this is only one step removed from the mess that was EJB 1 and 2 ... where the mythical application deployer role was responsible for configuring and deploying EJBs by configuring deployment details such as ... databases and tables. Sun dropped to ball on describing how this person did their job. In fact, the only effective way would be to explode EARs, including nested WARs and JARs, so that the ejb-jar.xml configuration files (and their many friends) could be edited, then repackage everything.

In my view, BEA, IBM and JBoss (in fact, the whole application server sector) dropped the ball by NOT providing a mechanism to override the defaults specified inside the ejb-jar.xml files short of exploding and repackaging.

Annotations are no different; tools and frameworks that rely on annotations should take into account the fact that deployment may need to change some of the configuration specified in the annotations. In other words, don't work directly from the code annotations, construct a model from the annotations, but allow other vectors for controlling that model before it is acted upon.

Yes, this is the model used in Tapestry 4, where an optional specification is parsed into memory, then modified to reflect information gleaned from annotations.

One of the major things to admire about the Ruby community is the emphasis on The Code Is The Thing. Nobody gives a sneeze at all the meta-programming in Ruby, because, in the end, they still have the standard ways to read and update Ruby object properties. JDK 1.5 Annotations are as close as Java currently gets to meta-programming and it brings the emphasis back to The Code, where it belongs.

In the past, I often was asked why Tapestry used so much XML. "Because it's convenient" was my response ... a convenient way to store some rich, hierarchical data. Well, annotations are even more convenient, and I think, more in the spirit of how we should be coding.

Thursday, October 20, 2005

Apress book on Tapestry

Just spotted this forthcoming book on Apress's web site: Beginning Open Source Enterprise Java: Spring, Hibernate, JBoss and Tapestry

Beginning Open Source Enterprise Java takes you through the construction of a complex enterprise Java application centered around JBoss, Spring, Hibernate, Tapestry, Ant, and other supporting tools for development and testing. This book is ideal if you’re new to Open Source Java, and want to build enterprise Java applications from scratch, using the full range of available Open Source tools and frameworks.

This book features the most successful and prevalent Open Source tools, along with some lightweight frameworks and tools. You’ll learn how to build a complete enterprise application, how to integrate the different Open Source frameworks to achieve this goal, and techniques for rapidly developing such applications.

Sunday, October 16, 2005

Virtual Library Renovations

The Virtual Library example application has now been completely renovated for Tapestry 4.0. It now looks like an application built for Tapestry 4.0 from scratch. It makes heavy use of annotations, friendly URLs (including some custom friendly URLs), application HiveMind services ... all the 4.0 goodies. The only thing that didn't change was the underlying EJBs (except to switch over to HSQL from McKoi).

I'll be writing up some comparison documentation for the next beta.

One of the areas I'm really happy with is the inheritance hierarchy; I was able to flatten it quite a bit. The old code had several levels of inheritance, centered around the pageValidate() method used to restrict portions of the site to logged in users, or to users with admin privilege. The new system moves all that logic to a single base class and drives it by @Meta annotations (with some subclasses overriding the @Meta from the base class).

Part of the transformation was the removal of the VirtualLibraryEngine; all the logic that used to be there is now in various HiveMind services. I borrowed a page from Spring and created a RemoteTemplate service that can execute a RemoteCallback (with retry logic for RemoteExceptions). That removed lots of very ugly loop-and-retry logic from many pages and components, and replaced it with less-ugly RemoteCallback inner classes.

I hope people will take a close look at the new Virtual Library; it continues to be a small, manageable application that can serve as an excellent template for building much more ambitious, real-world projects.

Hopefully in the next beta release, these examples will be distributed as a pre-configured JBoss installation, rather than a patch on top of JBoss. This will be bigger but even easier to setup and execute. Probably in 4.1 we'll shift the code around to use JPox, Cayenne or Hibernate and get away from requiring EJBs or an application server.

Saturday, October 15, 2005

Blogs Aren't For Support

Hate to say this, but this Blog is not a forum for getting free Tapestry support. I've been seeing a creeping number of requests for help and this just isn't the right place!

For free community support, subscribe to the Tapestry User Mailing List and learn how to ask a question that will get a response.

For professional support, you can contact me, or any other member of the Tapestry Support Network to purchase support.

Sunday, October 09, 2005

Google: Tapestry

Just did a ego gratification Google search on the word Tapestry. Guess what's on the top of the list? Used to be, you had to qualify the search as "java tapestry" to even get close. Does this signal any new level of adoption of Tapestry?

In a perhaps related note, I got my Manning royalty statement for Tapestry In Action for April 1 - June 30th ... and it's my biggest (by 50%) to date. Perhaps that has something to do with my appearance at JavaOne?

Thursday, October 06, 2005

ApacheCon 2005 - Tapestry Tutorial

I'll be presenting a half-day tutorial on Tapestry at this year's ApacheCon -- an afternoon session on Saturday, Dec 10. This is a great chance to learn a lot about Tapestry and Tapestry 4.0 especially ... I'll be highlighting new features, such as annoations and the improved validation framework. The format will be based on my labs, though I don't think the time constraints will support interactive labs; instead, I'll be doing the work as we go, explaining Tapestry and Tapestry concepts while getting various mini-applications running in Tapestry.

ApacheCon is in San Diego this year, which offers fewer distractions than Las Vegas, but will be a pleasant change nonetheless.

Sunday, October 02, 2005

Ouch! Class.getDeclaredMethods() works differently in different JDKs

This one is a bit of a pain. In Tapestry, your page and component classes tend to be abstract, with Tapestry providing a subclass, filling in additional methods, fields, constructors and implemented interfaces.

Because of that, it's necessary to duplicate the checks normally done by the compiler ... to check that all abstract methods inherited from base classes actually end up with implementations in the enhanced subclass (that is, the subclass that Tapestry creates).

At the core of this is code that finds all the non-abstract methods in the enhanced class, and up its inheritance chain. It then checks all abstract methods from superclasses or implemented interfaces to ensure that each one is, in fact, implemented.

The problem here is that the built-in Eclipse compiler seems to work differently than the Sun JDK compiler (this is for JDK 1.5).

Under Eclipse, an abstract class that implements an interface does not report those interface methods from getDeclaredMethods(). The only methods reported are the ones in the actual code for the class.

Under Sun JDK, an abstract class will report those interface methods, even if it does not provide implementations for them.

Now, I have tests for this class that I usually run from Eclipse. In fact, this was triggered by a specific bug concerning methods inherited from interfaces; the code is broken into two parts (scanning the inheritance tree for implemented methods, and scanning the interfaces for missing methods). Under Eclipse, interface methods are not reported from getDeclaredMethods(), so we make it to phase 2.

Under Sun JDK, interface methods are reported, it looks like a non-interface abstract method with no implementation, and we fail in phase 1 -- breaking my unit test.

Sigh. This may take some hackery to work in Eclipse and in Sun JDK.

Friday, September 30, 2005

Reworking Tapestry Docs and Demos

A slight shift of focus this week, especially today. I've been reorganizing the Tapestry project home page; from now on, the menu down the left side will be consistent from page to page, not contextual. The tabs across the top are now more like book marks. I think it will make navigating the site easier. Further, the first thing on the home page is another set of quick links to the most common pages, including the Users Guide, Quick Start Tutorial, JavaDoc, etc.

Now, the Quick Start tutorials represent another way to ease newbies into Tapestry. It's been something I delegated to others in the past, and it never happened, so I've taken it back on myself. I'd rather be writing new code or even fixing bugs (or working on my own labs), but I've been getting feedback that the QuickStart is making a big difference among prospective Tapestry users. In fact, there have been over 2200 downloads of the tutorials already!

In the same light is my new approach to the Tapestry demos: the Workbench and the Virtual Library. In the past, you had to build them yourself (because they use non-ASF code, and can't be distributed from the ASF). But that's asking a lot from people, especially those just casually investigating Tapestry.

The new approach is a seperate distribution for the demos, that comes from a non-ASF server (http://howardlewisship.com/downloads/quick-start/).

This bypasses the concerns of the ASF ... though it does eat into my personal web site's bandwidth! But what it means is people can get up and running with Tapestry really easily:

  • Install JBoss 4.0.2
  • Install Ant
  • Unpack the examples
  • Run the build script using Ant
  • (Re)start JBoss
  • Run the applications in a browser

A step up from this would be an installer, but I was short on time to do that today. It would be nice, and there are reasonable, free installers out there. That would eliminate the need for Ant ... but I assumed that nearly everyone interested in Tapestry would already have Ant.

It's a shame that living inside Apache causes these convulsions; it will rise up again in 4.1 when we start wanting to do deeper integration of Tapestry and existing AJAX client-side JavaScript libraries ... that work will have to take place largely off-Apache.

Wednesday, September 28, 2005

Switch?

So, the other day, I had some time to kill at the mall, and I wandered into the Apple store. Walked up to a big G5 with the killer 30" display and downloaded Eclipse 3.1 ... well, within a couple of minutes I was pulling down HiveMind code from subversion and compiling and executing the tests.

Now, on the one hand, I had severe techno-lust. All that eye candy! Vast tracts of land on that giant screen. Candy shaped buttons. Going home in a way (I used to develop on NeXTSTEP). Realistically, I don't think I can afford the 30" display, but maybe the medium sized one and I probably need a pretty maxxed out G5 for the kind of stuff I want to be doing. I mean, right now I do all my development on a Pentium 4M 2ghz with 1 gig of ram and a 40GB drive, but I'm increasingly impatiant with my development environment ... but what I want is something that really screams for all the development I do! And I do want to do some video editting as well.

So, convince me ... I can build out a monster 'doze box and buy a big LCD, or I can go the Apple route. How much is the tax on all those pretty icons? What's the tarrif on the sleek design? And what's the future with the move to Intel looming?

Lots of small objects ... or too many?

I was very interested in the post Object Count Impact on Garbage Collection Performance. As someone who tends to work using large numbers of small objects, I do have a concern in the back of my head about crossing over the line ... especially in light of the slower startup speed of Tapestry 4 (on top of HiveMind) vs. Tapestry 3.

In fact, the article jives well with my thinking; Slobodan's minimum object count map (which he promises to discuss in later blog entries) may likely look like the class I have yet to write ... a very, very simple Map implementation tuned for a small number of keys and a very, very, very high ratio of reads to updates ... the kind of thing that, say, stores attribute values for an XML entity (something used all over HiveMind). I was thinking about using an Object[] array at the core, with alternating keys and values.

Meanwhile, I still think the general approach I take is the right one ... and the reason I can't ever go back to a non-garbage collected language. I like breaking problems into tiny pieces and getting emergent behavior by combining the tiny pieces into more complicated wholes. From an efficiency standpoint, Tapestry is doing the right thing by caching entire pages (not components within pages, which would be more of a JSP tag model). But Tapestry does use an awful lot of HashMaps.

Tuesday, September 27, 2005

Resurrecting the Virtual Library

One of my goals for the final release of Tapestry 4.0 is to have the Virtual Library working again. The goal is to work through the issues, fixing things that are broken by the upgrade from 3.0 to 4.0, and taking advantage of the new features of Tapestry 4.0 as well (including annotations). I think this is important; the Virtual Library is a small but real application, and seeing the upgrade path for it will allow other 3.0 users to extrapolate what they are facing in upgrading to 4.0.

Getting the Virtual Library working has been my focus all day today ... mostly struggling with JBoss. Eventually I gave up, and the turnkey demo will overwrite the defaultDB instance rather than create a vlib database instance ... with that compromise in place, I'm again making rapid progress.

I've also considered porting the code to Hibernate and/or JDO (away from entity EJBs). This would allow the entire application to be delivered as a simple WAR, not an EAR. Getting the shared libraries working in JBoss was a pain ... I had to set the Manifest Class-Path of my EJB jar, to pick up the shared libraries (shared by the EJBs and vlib.war). However, changing the ORM will partially confuse the issue of the changes in the presentation tier, so I doubt I'll even attempt that until 4.1 ... and by then, I might have a bigger, better Tapestry example.

Looks like I'll be spending some time manually re-entering the data as well; QuantumDB had problems exporting and importing the data for me.

Still, I should have most of it done this week .. assuming I continue to play hookey from my paying gig (don't count on it!) So we probably won't see this until beta-10.

Bad JBoss feedback

I harp on feedback all the time, here's an example of why it's so important.

I've been trying to deploy a new Hypersonic database into JBoss 4.0.2, to contain the data for the Tapestry virtual library demo.

When I deploy my application, here's the exception I get:

org.jboss.util.NestedSQLException: No matching credentials in Subject!
       at org.jboss.resource.adapter.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:107)
       at org.jboss.ejb.plugins.cmp.jdbc.SQLUtil.fixTableName(SQLUtil.java:159)
       at org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCEntityBridge.init(JDBCEntityBridge.java:143)
       at org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager.initStoreManager(JDBCStoreManager.java:420)
       at org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager.start(JDBCStoreManager.java:353)
       at org.jboss.ejb.plugins.CMPPersistenceManager.start(CMPPersistenceManager.java:157)
       at org.jboss.ejb.EntityContainer.startService(EntityContainer.java:340)
       at org.jboss.system.ServiceMBeanSupport.jbossInternalStart(ServiceMBeanSupport.java:272)
       at org.jboss.system.ServiceMBeanSupport.jbossInternalLifecycle(ServiceMBeanSupport.java:222)
       at sun.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
       at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
       at java.lang.reflect.Method.invoke(Method.java:585)
       at org.jboss.mx.interceptor.ReflectedDispatcher.invoke(ReflectedDispatcher.java:141)
       at org.jboss.mx.server.Invocation.dispatch(Invocation.java:80)
       at org.jboss.mx.server.Invocation.invoke(Invocation.java:72)
       at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:249)
       at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:644)
       at org.jboss.system.ServiceController$ServiceProxy.invoke(ServiceController.java:897)
       at $Proxy0.start(Unknown Source)
       at org.jboss.system.ServiceController.start(ServiceController.java:418)
       at sun.reflect.GeneratedMethodAccessor9.invoke(Unknown Source)
       at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
       at java.lang.reflect.Method.invoke(Method.java:585)
       at org.jboss.mx.interceptor.ReflectedDispatcher.invoke(ReflectedDispatcher.java:141)
       at org.jboss.mx.server.Invocation.dispatch(Invocation.java:80)
       at org.jboss.mx.server.Invocation.invoke(Invocation.java:72)
       at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:249)
       at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:644)
       at org.jboss.mx.util.MBeanProxyExt.invoke(MBeanProxyExt.java:177)
       at $Proxy49.start(Unknown Source)
       at org.jboss.ejb.EjbModule.startService(EjbModule.java:395)
       at org.jboss.system.ServiceMBeanSupport.jbossInternalStart(ServiceMBeanSupport.java:272)
       at org.jboss.system.ServiceMBeanSupport.jbossInternalLifecycle(ServiceMBeanSupport.java:222)
       at sun.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
       at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
       at java.lang.reflect.Method.invoke(Method.java:585)
       at org.jboss.mx.interceptor.ReflectedDispatcher.invoke(ReflectedDispatcher.java:141)
       at org.jboss.mx.server.Invocation.dispatch(Invocation.java:80)
       at org.jboss.mx.server.Invocation.invoke(Invocation.java:72)
       at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:249)
       at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:644)
       at org.jboss.system.ServiceController$ServiceProxy.invoke(ServiceController.java:897)
       at $Proxy0.start(Unknown Source)
       at org.jboss.system.ServiceController.start(ServiceController.java:418)
       at sun.reflect.GeneratedMethodAccessor9.invoke(Unknown Source)
       at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
       at java.lang.reflect.Method.invoke(Method.java:585)
       at org.jboss.mx.interceptor.ReflectedDispatcher.invoke(ReflectedDispatcher.java:141)
       at org.jboss.mx.server.Invocation.dispatch(Invocation.java:80)
       at org.jboss.mx.server.Invocation.invoke(Invocation.java:72)
       at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:249)
       at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:644)
       at org.jboss.mx.util.MBeanProxyExt.invoke(MBeanProxyExt.java:177)
       at $Proxy22.start(Unknown Source)
       at org.jboss.ejb.EJBDeployer.start(EJBDeployer.java:605)
       at org.jboss.deployment.MainDeployer.start(MainDeployer.java:964)
       at org.jboss.deployment.MainDeployer.start(MainDeployer.java:956)
       at org.jboss.deployment.MainDeployer.deploy(MainDeployer.java:775)
       at org.jboss.deployment.MainDeployer.deploy(MainDeployer.java:738)
       at sun.reflect.GeneratedMethodAccessor48.invoke(Unknown Source)
       at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
       at java.lang.reflect.Method.invoke(Method.java:585)
       at org.jboss.mx.interceptor.ReflectedDispatcher.invoke(ReflectedDispatcher.java:141)
       at org.jboss.mx.server.Invocation.dispatch(Invocation.java:80)
       at org.jboss.mx.interceptor.AbstractInterceptor.invoke(AbstractInterceptor.java:121)
       at org.jboss.mx.server.Invocation.invoke(Invocation.java:74)
       at org.jboss.mx.interceptor.ModelMBeanOperationInterceptor.invoke(ModelMBeanOperationInterceptor.java:127)
       at org.jboss.mx.server.Invocation.invoke(Invocation.java:74)
       at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:249)
       at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:644)
       at org.jboss.mx.util.MBeanProxyExt.invoke(MBeanProxyExt.java:177)
       at $Proxy8.deploy(Unknown Source)
       at org.jboss.deployment.scanner.URLDeploymentScanner.deploy(URLDeploymentScanner.java:325)
       at org.jboss.deployment.scanner.URLDeploymentScanner.scan(URLDeploymentScanner.java:501)
       at org.jboss.deployment.scanner.AbstractDeploymentScanner$ScannerThread.doScan(AbstractDeploymentScanner.java:204)
       at org.jboss.deployment.scanner.AbstractDeploymentScanner.startService(AbstractDeploymentScanner.java:277)
       at org.jboss.system.ServiceMBeanSupport.jbossInternalStart(ServiceMBeanSupport.java:272)
       at org.jboss.system.ServiceMBeanSupport.jbossInternalLifecycle(ServiceMBeanSupport.java:222)
       at sun.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
       at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
       at java.lang.reflect.Method.invoke(Method.java:585)
       at org.jboss.mx.interceptor.ReflectedDispatcher.invoke(ReflectedDispatcher.java:141)
       at org.jboss.mx.server.Invocation.dispatch(Invocation.java:80)
       at org.jboss.mx.server.Invocation.invoke(Invocation.java:72)
       at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:249)
       at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:644)
       at org.jboss.system.ServiceController$ServiceProxy.invoke(ServiceController.java:897)
       at $Proxy0.start(Unknown Source)
       at org.jboss.system.ServiceController.start(ServiceController.java:418)
       at sun.reflect.GeneratedMethodAccessor9.invoke(Unknown Source)
       at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
       at java.lang.reflect.Method.invoke(Method.java:585)
       at org.jboss.mx.interceptor.ReflectedDispatcher.invoke(ReflectedDispatcher.java:141)
       at org.jboss.mx.server.Invocation.dispatch(Invocation.java:80)
       at org.jboss.mx.server.Invocation.invoke(Invocation.java:72)
       at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:249)
       at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:644)
       at org.jboss.mx.util.MBeanProxyExt.invoke(MBeanProxyExt.java:177)

I'm still digging around; since I can connect to the database over TCP/IP, it means the problem is in the connection pool. Still, wouldn't it be nice if JBoss told me that? How about if it told me what was broken - where it was broken - how to fix it? That's the kind of support built into Tapestry and HiveMind: line precise exception reporting plus lots of attention to detail in error messages and error reporting in general.

Well, I've been hacking away for a couple of hours, and will probably be at it for a while yet. Not the way I like to spend my day. Sigh.

Sunday, September 25, 2005

Code Coverage Metrics and Testing

For the last few months, I've been tracking some metrics about Tapestry. On each release, and occasionally in between, I've been tracking the NCLOC (non-comment lines of code), the number of Java source files, the number of tests, and the percentage of code coverage (according to Clover).

I have an almost religous belief that you must have at least 85% code coverage for a project; I think the bugs "hide like cockroaches" and are only flushed out when you hit that magic number. Thus, I spend most of my coding time writing tests, not production code. The trick to staying sane is to remember that production code and test code are all just code, of the same cloth, each useless without the other.

You can see the amount of code growing with occasional downward spikes as dead code is located and eliminated (the large spike around 8/10 represents a change in how I was counting code, counting the code for each sub-project seperately -- these charts are just for the framework itself).

And here you can see the stuggle to keep the code coverage about 85%. Over this same time range, the number of unit tests has risen from 906 to 1,257.

In fact, the news is actually pretty good; in Tapestry most new code, code introduced for release 4.0, has close to 100% code coverage. These charts are just for the framework proper, but the annotations library, for example, has true 100% code coverage (every line of code, every branch).

The majority of new tests are written as true unit tests, on top of EasyMock ... all but about 40 of the tests, which execute mini-applications inside a simulated servlet container. Those 40 tests take about 220 seconds to execute; all the remaining tests combined take about 25 seconds. Thus the power of unit testing.

All told, the Tapestry project includes over 1400 tests. And Tapestry is built upon HiveMind, which has about 740 tests itself.

Personally, I'd like to see the code coverage numbers jump up into the 95% range. I think Tapestry 4.1 will start with a purge of dead and deprecated code, and then a lot of filling in the gaps in the unit tests, before we start working on new ideas. And I have a dream of simplifying the integration tests and productizing them, so that they can be used by Tapestry developers to test their applications. What would be great would be to link together something like jWebUnit with a mock-up of the servlet API, for true unit testing (without having to run the application in a seperate process).

Friday, September 23, 2005

TheServerSide Software Problems

It's discouraging to see news stories on TheServerSide.com about their ongoing software problems. TSS has been a banner site for Tapestry, and chatter about problems makes for bad Tapestry press.

The basic issue is that I signed on with The Middleware Company to do a number of phases of development for TheServerSide.com. The first phase was the basic translation of the site to a component object model, leaving all the functionality unchanged.

At the same time this was occuring, a seperate team was converting the backend access from entity EJBs to Solarmetric Kodo.

In the end, I had less than a week to integrate the two before going live. And yet, for the most part, the result was quite succesful -- measured partly by the amount of time that passed before anyone realized that the site had been almost completely rewritten.

However, with the acquisition of The Middleware Company by Tech Target, my involvement with TSS came to an end; the later, more interesting phases, where we simplified the stack and built considerable UI improvements, has not come to pass. All I've seen is the introduction of more and more ads on the site.

I can't talk to the root problem today; it is incredible frustrating that many posts get accepted and lost. That speaks to some horrible issue with transaction management and the database. Interestingly, TechTarget has not approached me for help ... this also speaks volumes as to where the problems lie.

From observation, and from discussions with Joe Ottinger, I do know that Tapestry is doing exactly what its supposed to be doing, that the functionality problems (missing posts and such) are a problem at the application layer (the stateless session bean used to manage transactions) and the interaction between that layer, Kodo, Coherence, JBoss and the PostgreSQL database. In fact, given the simplicity of the database schema (just six or eight tables) I suspect the problem really is in the configuration and integration of these elements.

Based on what I've read, and some high level discussions I had with them last winter, I believe TechTarget is building a single enterprise wide solution for all their many web sites, migrating away from the Tcl-based Vignette solution used by the majority of their sites, as well as the Tapestry-based solution for TSS.com and TSS.net. All I know about the solution is that it will be based on JEE (assuming that hasn't changed since our discussions).

Thursday, September 22, 2005

Enjoying Web Development with Tapestry

Kent Tong has updated his e-book on Tapestry 3, Enjoying Web Development with Tapestry, in two ways:

  • It now covers Tapestry 4.0
  • You can now buy it as hard copy instead of PDF

I think it's great that books on Tapestry 4 are coming into print ... and that I'm not the one writing them. Not only will other people have a perspective closer to the reader, but the very fact that people outside the central Tapestry developer community, people like Kent Tong and Warner Onstine, are expending the effort to write these books, underscores the kind of high quality community Tapestry has developed over the last few years.

Thursday, September 08, 2005

Minor Eclipse Gripe

I'm a big fan of Eclipse; I think its full of great ideas, and is a big gift to the larger community. It gets the job done quite well. And it's free.

However, there are quite a few rough spots.

One thing I hit quite often is mysteriously locked files. I see this most often when updating or replacing my directories from SVN (and sometimes CVS). Often, an update will fail with some kind of locking error on the local file.

Sometimes, I have the file open in another window. I close the window and the process works. Less than ideal, but I can live with it.

Sometimes, I don't have the file open and I still get the error. My working theory is that Eclipse has it locked, perhaps due to indexing. Often I have to restart Eclipse to get the lock to clear!

Am I the only one who experiences this?

Saturday, September 03, 2005

A clean slate ...

After well over two years, countless installs and uninstalls, a creeping sluggishness, and one horrific event, I finally bit the bullet and rebuilt my laptop computer. Yep, I've formatted the hard drive and reinstalled everything.

Along the way I learned a few things:

  • You have to install Outlook just to transfer mail messages into Thunderbird. Hopefully it will uninstall OK.
  • JDK 1.5 will only install into C:/Program Files/Java/, which gives Ant fits (the space in the file name is a problem). After trying things with shortcuts, I eventually copied C:/Program Files/Java/jdk1.5.0_04/ to C:/.
  • I no longer can remember how I got svnserve (the Subversion server) to start automatically! Woops! Perhaps its a difference between the cygwin version and the Win32 native version?
  • I'm having trouble getting my SSH and PGP keys back. This could cause some grief.

I'm still picking up the pieces but I'm very close now. A key tool for me in this endeavor was Acronis True Image, which can take a snapshot of an entire partition and put it on backup media (or an external drive, in my case). Even better, you can "mount" the image as a new read-only drive, making it easy to locate and retrieve individual files you need.

Will my Dell Inspiron 8200 return to its former glory? Hard to say ... I think Windows is getting slower with each release and security patch. But so far, things are much quicker. We'll see once we get to real development.

Update: I have Subversion for Cygwin working great. The missing piece was the --foreground argument. My repository is at c:/svnrepo, so my command line is: cygrunsrv --install Subversion --path /usr/bin/svnserve --chdir /cygdrive/c/ --args "--foreground -d -r svnrepo"

After that, I can start Subversion:

cygrunsrv -S Subversion

Once started, it appears to restart automatically on each reboot.

You can verify a correct start:

bash-3.00$ cygrunsrv -Q Subversion
Service             : Subversion
Current State       : Running
Controls Accepted   : Stop
Command             : /usr/bin/svnserve --foreground -d -r svnrepo