Eclipse Plugin Nearly Ready

As the title says, the Eclipse plugin for Frame2 is almost done. Of course, “done” is a relative term. In this case, it means that the plugin has been updated to work with the latest version of the framework, including the updated DTD. Unfortunately, it also means that it still has the same limitations that previous versions have had. Specifically, the plugin still does not support creating a Frame2 project with web services support. This can be added by hand and the Frame2 plugin won’t stomp on it, but neither are there any nice wizards for the web services tasks. This limitation is the next item on my list – I’m pretty sure that I can create an optional plugin that can be downloaded that will add the web services functionality for anyone that wants it. This will also help keep the download size of the “core” plugin as small as possible.

One other feature that the plugin is missing is a contextual editor for the Frame2 configuration file. It would nice to be able to get contextual help and hints when editing the config by hand, but I don’t think I have the time in the forseeable future to research how this works in Eclipse.

The plugin will be available for download from Sourceforge and I will be creating an update site here at frame2.megatome.com that will be the preferred way of getting the plugin.

Documentation is Always the Hardest Part

I’ve pretty much wrapped up the coding for the next version of Frame2, and decided to give the documentation a once-over. All in all, it’s not too bad, but it’s not great either. There’s some real improvements that can be made, but I don’t see it happening any time soon.

Don’t get me wrong – I don’t mind writing doc. One of the most annoying things is to download some open source project and not have any idea how to use it. The issue I have with writing doc is that the feedback loop is entirely different than for code. When writing code with automated tests, there’s immediate feedback if you’ve done the right thing or not. With doc, I have to wait for someone to actually read the doc and give me feedback. This is frustrating because: 1) I don’t know if anybody even uses Frame2, 2) of the people that do use it, I don’t know if they read the doc, and 3) if the doc sucks, does anybody care enough to tell me?

So, for now I’ve corrected obvious errors in the doc, but I haven’t added anything. I’ve started a list of things to cover, but I don’t know when I’ll get to them. Third party dependencies need to be explained better, and web services interaction needs a LOT of coverage.

I feel that the getting started guides and the example applications are sufficient to get somebody up and running with a basic Frame2 application. If I’m wrong, hopefully somebody will let me know!

Adding Features Uncovers Old Bugs

This seems to happen to me no matter what project I’m on. I try to implement a simple (or not so simple) feature and end up exposing at least one bug that’s been hiding around for quite some time.

As an illustration, I was adding a feature to Frame2 today to allow some parameters to be passed between events within a mapping – without making developers use the HttpSession. After a couple of false starts, I had a pretty good idea of how to make the changes and starting ripping out code. I came up with several unit tests, and modifying code to make the tests pass. Pretty standard Test Driven Development…until I wrote the test that made sure event chain validation was working.

Before I explain the bug, I’ll give a high level description of how Frame2 works when submitting form data:

  1. Form is filled out by user and submitted.
  2. Frame2 looks up the appropriate event from the form action.
  3. An instance of the appropriate event is created and populated by the introspector.
  4. Based on the mapping for the event, Frame2 will then validate the event. This may involve the Commons Validator if it is enabled and/or overriding the Event.validate() method.
  5. If validation fails, the mapping must specify a location for the framework to forward back to. This is usually the same page where the data was entered.
  6. If validation passes, the framework then iterates through the list of event handlers, calling the handle() method. If handle() returns null, the framework continues to the next handler. A non-null result usually indicates an error, and the framework will forward to the location named by the result.
  7. Once all of the handlers have been invoked, the framework will default to the view specified in the mapping. Usually this is just a JSP, but Frame2 supports forwarding to events – called “chaining”.
  8. For the chained event, Frame2 is supposed to start back at step 2 and continue processing until it gets to the end of the chain.

Notice how I say “supposed to” in step 9? The framework was actually only going back to step 6, bypassing validation for all events except the first in the chain. Subtle, yes, and unlikely to be noticed by most users – but it’s still a bug. The bug has now been fixed so that the framework goes back to step 2 like it’s supposed to.

In case you’re wondering what use event chaining is, here’s an example:

  1. User enters a blog entry and submits the form.
  2. Frame2 validates the form data and begins invoking handlers.
  3. The blog entry handler persists the data, perhaps to a database.
  4. The default view for the event mapping tells the framework to forward to the “View All Blog Entries” event.
  5. The framework creates the appropriate object and begins invoking the handlers in the “View All” mapping.
  6. The “View All” handler populates all of the blog entries into the “View All” event.
  7. The default view for the “View All” mapping is a JSP page, which renders the blog entries using JSTL.

Chaining allows the “View All” event to remain its own entity that can be invoked separately, while allowing the same functionality to be used in other places where appropriate.

By way of comparison, the feature I added allows the above sequence to be reworked like this:

  1. User enters a blog entry and submits the form.
  2. Frame2 validates the form data and begins invoking handlers.
  3. The blog entry handler persists the data, perhaps to a database.
  4. The blog entry handler sets an attribute with the new entry id into the context.
  5. The default view for the event mapping tells the framework to forward to the “View Specific Blog Entry” event.
  6. The framework creates the appropriate object and uses the context value(s) with the introspector to set some values in the event.
  7. The framework then begins invoking the handlers in the “View Specific” mapping.
  8. The “View Specific” handler looks up the entry with the specified id and sets its data into the event.
  9. The default view for the “View Specific” mapping is a JSP page, which renders the blog entry using JSTL.

A Consistent API is a Very Good Thing

I’ve been mucking around in the codebase, cleaning up pieces of the API that annoy me. Some bright person thought that it was a good idea to have a method that returns an Iterator to access a collection. I’ve been cleaning up all of these methods, changing them so that they return an actual collection – occasionally made unmodifiable through the Collections class.

Naturally, this has led to unit test failures. Almost all of the failures have been easy to fix, but once in a while one pops up that makes me scratch my head. Here’s an example:

public void testGetIfEmpty() {
assertTrue(this.errors.isEmpty());
assertNull(this.errors.get(FOO));
assertEquals(0, this.errors.get().length);
assertEquals(0, this.errors.get(FOO).length);
}

In this instance, errors is a class that aggregates individual Error objects. The get(String) method returns an array of Error objects that have the specified key, while get() retrieves all Errors. Pretty simple, right? After my changes, the test fails on the assertNull() statement, which isn’t surprising.

The failing statement read assertNull(this.errors.iterator(FOO)) before I removed the iterator() method, which tells us a bit about how the API was originally coded. The iterator(String) returned null if there were no matching entries. However, as the unit test clearly shows, get(String) returns an empty array when it doesn’t find any matching Errors. Quite the difference, and potentially confusing – the developer has to remember which method may return null.

The API has now been changed to return an empty collection instead of null when no matching errors are found. Now, a user of the API simply has to call the method and loop over the result (zero times if it’s empty) without having to make null checks.

Whoever wrote this unit test obviously knew that one method returned null while the other returned an empty array for the same input, since the test passed. I only wish that person would have looked at those lines and noticed the inconsistency, instead of leaving it for me to find.