Wicket Tutorial: YUI AutoComplete using JSON and Ajax

Getting an AutoComplete JavaScript widget to work with a server-side framework involves a few more steps and integration points than what it would take for e.g. a simple date-picker widget. It makes for an interesting example that shows off the strengths of Apache Wicket when it comes to creating custom components – especially when Ajax and integrating third-party JavaScript and CSS is involved.

This tutorial covers the following topics:

  • Creating a re-usable Wicket custom component
  • How to use a Wicket Ajax “Behavior”
  • Integrating a third party JavaScript widget into a Wicket application
  • Packaging CSS and JS resources needed for the custom component
  • How the required CSS and JS can be contributed to the HTML <HEAD> on demand
  • Hooking into the Wicket Ajax life cycle
  • Returning custom JSON data from the Wicket server-side component

The Yahoo! User Interface Library (YUI) AutoComplete control is our target. Our example uses YUI version 2.7.0b which you can download from the YUI home page. We’ll be using Wicket 1.4 but this particular example should work unchanged in Wicket 1.3.X as well.

Note: The Wicket Extensions project includes an AutoComplete component (see it in action here) which should suffice for most of your AutoComplete needs. The YUI version arguably looks and feels richer (animation and all) and has a whole host of customizable options that are worth looking at.

You need Maven 2 installed. Create a new Wicket project by using the Wicket Maven quick-start archetype which you can find here: http://wicket.apache.org/quickstart.html

You can leave the archetype parameters unchanged, and go with the default “com.mycompany” and “myproject”. Run the Maven command to generate the project structure including quick-start Java code. In the resulting “pom.xml” add the following dependency (within the <dependencies> section) so that we can use the Jackson JSON processor library for converting Java data into JSON.

<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-mapper-asl</artifactId>
    <version>1.2.0</version>
</dependency>

Using something like Jackson means that you don’t have to worry about forming JSON by hand and dealing with things such as nesting, escaping characters etc.

Now you can import the project into your IDE. Instructions pertaining to your IDE of choice are available at the Wicket quick-start page itself. Our AutoComplete use case is to load a list of country names matching the user’s input. We’ll use the java.util.Locale class as a quick and easy data source. Create a new Java class in a “com.mycompany.util” package as follows:

public class LocaleUtils {

    private static final Set<String> countries;

    static {
        final Locale[] locales = Locale.getAvailableLocales();
        countries = new TreeSet<String>();
        for(final Locale locale : locales) {
            countries.add(locale.getDisplayCountry());
        }
    }

    public static String[] getCountryNamesMatching(String query) {
        List<String> list = new ArrayList<String>();
        for (final String country : countries) {
            if (country.toUpperCase().startsWith(query.toUpperCase())) {
                list.add(country);
            }
        }
        return list.toArray(new String[list.size()]);
    }

}

And let’s also get this other utility class out of the way before we get to the really interesting stuff. Here’s the listing of “com.mycompany.util.JsonUtils” which uses Jackson to convert Java data into JSON:

public class JsonUtils {

    private static final JsonFactory jf = new JsonFactory();

    public static String marshal(Object o) {
        StringWriter sw = new StringWriter();
        try {
            JsonGenerator gen = jf.createJsonGenerator(sw);
            new ObjectMapper().writeValue(gen, o);
            return sw.toString();
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

}

By looking at the simple “Basic Local Data” YUI AutoComplete sample, we can figure that the following YUI modules are needed as dependencies for the AutoComplete control:

  • yahoo-dom-event
  • animation
  • datasource
  • autocomplete

We need to copy these modules containing JS and CSS resources from the YUI downloaded distribution into our project structure. First create a “com.mycompany.yui” package where our YUI Wicket components will live. Under the “yui” folder on the file system, create a new folder called “res” (resources). Copy the folders listed above from the “yui/build” folder into the “res” folder. You should end up with something like this:

directory-structure

The JavaScript API documentation of the YAHOO.widget.AutoComplete component says that it requires a YAHOO.util.DataSource instance to work. For example, a LocalDataSource can be used when the data is available as an in-memory JavaScript array or JSON object. We will create a new JavaScript component that extends LocalDataSource and override the “makeConnection” method to get the data from a Wicket component over Ajax. Details of how to go about implementing “makeConnection” can be figured out by looking at the base method in “res/datasource/datasource-debug.js” – which is excerpted below for completeness:

// begin excerpt from YUI code
makeConnection : function(oRequest, oCallback, oCaller) {
    var tId = DS._nTransactionId++;
    this.fireEvent("requestEvent", {tId: tId, request: oRequest, callback: oCallback, caller: oCaller});
    var oRawResponse = this.liveData;    
    this.handleResponse(oRequest, oRawResponse, oCallback, oCaller, tId);
    return tId;
},
// end excerpt

In the code taken from YUI above, “oRequest” is the string the user has typed into the input text field. So it looks like after fetching the corresponding data, we need to call the “handleResponse” method to complete the flow, and the data is passed on as the “oRawResponse” method argument.

Here is the code we end up with for “WicketDataSource” which extends “LocalDataSource” the JavaScript “prototype” way and all. We re-use the “YAHOO.widget” namespace to keep things clean and reduce the risk of JavaScript name collisions:

YAHOO.widget.WicketDataSource = function(callbackUrl) {
    this.callbackUrl = callbackUrl;
    this.responseArray = [];
    this.transactionId = 0;
};

YAHOO.widget.WicketDataSource.prototype = new YAHOO.util.LocalDataSource();

YAHOO.widget.WicketDataSource.prototype.makeConnection = function(oRequest, oCallback, oCaller) {
    var tId = this.transactionId++;
    this.fireEvent("requestEvent", {tId: tId, request: oRequest, callback: oCallback, caller: oCaller});
    var _this = this;
    var onWicketSuccessFn = function() {
        _this.handleResponse(oRequest, _this.responseArray, oCallback, oCaller, tId);
    };    
    wicketAjaxGet(this.callbackUrl + '&q=' + oRequest, onWicketSuccessFn);
};

Our implementation (override) of the “makeConnection” method uses the Wicket Ajax engine to GET data from a given URL and we pass a callback function that will call “handleResponse” once the response from the server is received. The value typed by the user is passed as as a query-string parameter “q” which will be read on the server-side.

Update: for more details on the lightweight Ajax engine and JavaScript utilities that come along with Wicket, refer this article by Nino Martinez: Wicket Javascript Internals dissected. The script.aculo.us “Drag & Drop ListEditor” example by Al Maw is another useful resource and you can get the slides and code from this page.

We could have used YUI itself to make the XMLHttpRequest but the nice thing about using the Wicket Ajax engine is that you can spy on all the action using the very nifty Ajax Debug Window in “development mode” like this:

ajax-debug-window

You may be now wondering how “_this.responseArray” gets initialized once the server request completes. The “YAHOO.widget.WicketAutoComplete” (below) is a JavaScript convenience object we define to represent and wrap a single AutoComplete instance (along with the DataSource):

YAHOO.widget.WicketAutoComplete = function(inputId, callbackUrl, containerId) {
    this.dataSource = new YAHOO.widget.WicketDataSource(callbackUrl);
    this.autoComplete = new YAHOO.widget.AutoComplete(inputId, containerId, this.dataSource);
    this.autoComplete.prehighlightClassName = "yui-ac-prehighlight";
    this.autoComplete.useShadow = true;
    this.autoComplete.formatResult = function(oResultData, sQuery, sResultMatch) {
        return oResultData;
    };      
};

So if an instance of “WicketAutoComplete” has been assigned to a JavaScript variable “foo”, the responseArray can be initialized by assigning something to “foo. dataSource. responseArray”. We will take care of this in the Java code of the Wicket component, coming up shortly.

The YUI AutoComplete expects data received from the DataSource to be a JavaScript array of items. The customized “formatResult” method override above just displays each item as-is. You could implement more sophisticated routines such as bold-ing the part that matches the user input, and you can refer the YUI examples for more details. For now, we are keeping it simple, but in the future you may want to consider things like returning a multi-dimensional array or an array of JSON objects etc. from the server.

Our “WicketAutoComplete” JavaScript object constructor takes the following arguments:

  • inputId: HTML id of the input text field (needed by the YUI AutoComplete control)
  • callbackUrl: URL where the Wicket component will listen for Ajax requests, used by our custom WicketDataSource
  • containerId: HTML id of the <DIV> where the autocomplete results will be rendered (needed by the YUI AutoComplete control)

Take the two javascript listings above (“WicketDataSource” and “WicketAutoComplete”) and combine them into a single file called “YuiAutoComplete.js” within the “src/main/java/com/mycompany/yui” folder (or rather the “com.mycompany.yui” package). In the same folder, create “YuiAutoComplete.html” and “YuiAutoComplete.css” as follows:

<wicket:panel>
    <div class="yui-skin-sam">
        <div class="wicket-autocomplete">
            <input wicket:id="text"/>
            <div wicket:id="container"></div>
        </div>
    </div>
</wicket:panel>
.wicket-autocomplete { width: 15em; padding-bottom: 2em; }
.wicket-autocomplete div { font-size: 90% }

The HTML and CSS files are pretty straightforward. Setting the CSS “width” of an enclosing <DIV> is the recommended way of controlling the rendered size of a YUI AutoComplete control. The YUI CSS “skin” reference “yui-skin-sam” can be even kept at the <BODY> tag level (like in the YUI examples) but here we chose to have it self contained within our component.

And finally here is the code of “com.mycompany.yui.YuiAutoComplete” that ties everything together. A description of what is going on appears below the code listing:

public abstract class YuiAutoComplete extends FormComponentPanel {

    private TextField textField;
    private WebMarkupContainer container;

    public YuiAutoComplete(String id, IModel model) {
        super(id);
        textField = new TextField("text", model);
        textField.setOutputMarkupId(true);
        add(textField);
        container = new WebMarkupContainer("container");
        container.setOutputMarkupId(true);
        add(container);
        add(new YuiAutoCompleteBehavior());
    }

    @Override
    public void updateModel() {
        textField.updateModel();
    }

    private String getJsVarName() {
        return "YAHOO.widget." + textField.getMarkupId();
    }

    protected abstract String[] getChoices(String query);

    private class YuiAutoCompleteBehavior extends AbstractDefaultAjaxBehavior {

        @Override
        public void renderHead(IHeaderResponse response) {
            super.renderHead(response);
            response.renderJavascriptReference(new JavascriptResourceReference(YuiAutoComplete.class, "res/yahoo-dom-event/yahoo-dom-event.js"));
            response.renderJavascriptReference(new JavascriptResourceReference(YuiAutoComplete.class, "res/animation/animation-min.js"));
            response.renderJavascriptReference(new JavascriptResourceReference(YuiAutoComplete.class, "res/datasource/datasource-min.js"));
            response.renderJavascriptReference(new JavascriptResourceReference(YuiAutoComplete.class, "res/autocomplete/autocomplete-min.js"));
            response.renderJavascriptReference(new JavascriptResourceReference(YuiAutoComplete.class, "YuiAutoComplete.js"));
            response.renderCSSReference(new CompressedResourceReference(YuiAutoComplete.class, "YuiAutoComplete.css"));
            response.renderCSSReference(new CompressedResourceReference(YuiAutoComplete.class, "res/autocomplete/assets/skins/sam/autocomplete.css"));
            response.renderJavascript("var " + getJsVarName() + ";", getJsVarName());
            response.renderOnDomReadyJavascript(getJsVarName() + " = new YAHOO.widget.WicketAutoComplete('"
                    + textField.getMarkupId() + "', '" + getCallbackUrl() + "', '" + container.getMarkupId() + "');");
        }

        @Override
        protected void respond(AjaxRequestTarget target) {
            String query = getRequest().getParameter("q");
            String[] result = getChoices(query);
            String jsonResult = JsonUtils.marshal(result);
            target.appendJavascript(getJsVarName() + ".dataSource.responseArray = " + jsonResult + ";");
        }

    }

}
  • #01, #18: we extend FormComponentPanel instead of Panel so that we can seamlessly use our YuiAutoComplete component within Form instances
  • #09, #12: we ensure that HTML id-s are rendered and we pass them to the YUI AutoComplete control later
  • #14: automatically add the Ajax wicket behavior (explained below) to our custom component
  • #26: we define a method that subclasses can override to return data corresponding to user input
  • #28: we extend AbstractDefaultAjaxBehavior and we only need to override the “respond(AjaxRequestTarget)” method
  • #33 – 39: we ensure that the required JS and CSS files are contributed to the HTML <HEAD> of any page that contains our custom component. Tip: use “debug” versions of the JS files for e.g. “autocomplete-debug.js” instead of “autocomplete-min.js” to make debugging using FireBug easier. You probably don’t need the “debug” versions for a “production” version of this component. The good thing about loading resources like this from the package / classpath is that you can simply JAR it all up for use by other teams.
  • #40: we output a variable declaration (global to the page) to hold a reference to our “WicketAutoComplete” JavaScript object – which can be referenced later during the Ajax request for passing data to the web page, we derive the name from the HTML id of the input TextField (#23)
  • #41: ensure that an instance of our WicketAutoComplete JavaScript object is instantiated (and assigned to the variable described above) when the page loads, note how the HTML id-s and Ajax “callback” URL are determined
  • #47: get the value of the parameter “q” in the incoming Ajax request
  • #50: ask Wicket to execute some JavaScript when the request completes which will (pass) assign a JSON array value back via our “WicketAutoComplete” object reference

Now the project within your IDE should look like this:

project-structure

Edit the “HomePage.html” and “HomePage.java” files to use an instance of our Wicketized AutoComplete component as follows:

<html>
    <head>  
        <title>YUI Autocomplete Demo</title>
    </head>
    <body>
        Enter Country Name: <span wicket:id="autocomplete"></span>
    </body>
</html>
public class HomePage extends WebPage {

    public HomePage() {        
        add(new YuiAutoComplete("autocomplete", new Model("")) {
            @Override
            protected String[] getChoices(String query) {
                return LocaleUtils.getCountryNamesMatching(query);
            }
        });
    }

}

Note how we have implemented the “getChoices()” method to return a list of country names in this case. That should be it! Start Jetty by using the “com.mycompany.Start” class that should be under the “test” source folder structure. Browse to http://localhost:8080/ and try out your shiny new Wicket + YUI AutoComplete component.

I’ve uploaded the source code here: myproject.pdf – just rename this to a *.zip after downloading. Do comment if you spot any mistakes or stuff that can be improved.

Wicket Impressions, moving from Spring MVC / WebFlow

[update 2008-05-27: A detailed presentation that summarizes this blog post along with visuals and code snippets is now available]

For quite a while, I was trying to ignore the increasing buzz about Wicket, convincing myself that I don’t need yet another MVC framework (there are too many of them anyway), but somewhere between the Javalobby articles, the InfoQ article and the O’ReillyNet article – my resolve cracked and I took a closer look.

It looked interesting and so I decided to try and migrate a selected few JSP screens of an existing Spring MVC + Spring WebFlow application to Wicket. This went much better than I expected and two months later (not full time) – I completed migrating the entire app to Wicket and it is now ready for a new release as a 100% JSP free application. I’m now trying to convince my colleagues to try Wicket and they give me strange looks because I was an outspoken Spring MVC / WebFlow fan not so long ago : )

So is Wicket the one true MVC framework that a lot of us have been hunting for? At the moment, I tend to think so. Of course I have not tried GWT, Tapestry, RIFE, Stripes, JSF, Seam, Echo2 and the other usual suspects. And how is Wicket better than Spring MVC and Spring WebFlow? Having used both, I will attempt to elaborate below.

[update1: I tried JSF, and you can read my comparison of Wicket with JSF]

[update2: I also tried GWT and here is my comparison of Wicket vs GWT]

[update3: Detailed performance & memory usage comparison of Wicket with Seam / JSF]

Lots of information is already available on Wicket and how it compares with other frameworks in general, so here I will try to focus on what I experienced when I moved from JSP + SMVC + SWF to Wicket. The way I had to throw some of my pre-conceived notions about Java web-app development out of the window may be of interest to you. This is not a very structured list, but I’ve been meaning to put down these first-hand Wicket impressions for a while, so here goes:

Pure Java
If you like Java you will really like Wicket. Over the last two months, I was surprised to learn a few Java-isms that I was not aware of – such as some subtleties when using anonymous inner classes etc. To be really productive with Wicket I think that your Java fundamentals do need to be sound. But that is a good thing.

Great component model
The application that I migrated is a reasonably complex one, with a few CRUD screens, forms, search results etc – and Wicket offered almost everything I needed – and more. I was able to easily create a couple of custom components like:

  • a date picker (Wicket has one but I really wanted to write my own)
  • a variant of a multi-select check box where if there are pre-selected values, they “float” to the top of the list. It was very easy to create this by extending the Wicket multi-select checkbox.

Once you get used to components (even a web page is a component) as opposed to URL mappings and MVC actions, you can develop at a very fast clip. After experiencing the elegance of this kind programming model where you can show or hide components and control all this in pure Java code as opposed to hacking in JSP or JSTL, you really won’t feel like going back.

I used to hold the opinion that people new to Java who need to learn how to create web-applications could probably skip most of the Swing sections in the typical introductory course material and get straight on to Servlets and JSPs. Now I have completely reversed this thinking. Swing-style coding is the future for web-applications, I can feel it :)

In the old app I had written a custom JSP tag for the special multi-select checkbox list and was quite pleased with the result. I used to think that JSP tags were more than sufficient to create re-usable components and using Prototype directly was enough do fancy Ajax things etc. Now I have revised that opinion as well.

*Real* separation of concerns
Because the application I was developing had zero JSP scriptlets and strictly used JSTL, I was under the impression that I had reached nirvana in terms of separation-of-concerns.

Of course I was wrong again. In Wicket, all layout is controlled in good old HTML files. And Wicket has some neat-o ways to do markup-y things in the Java code itself – like for instance how you can color alternate rows when rendering an HTML table.

If you have a separate team doing the presentation look and feel work, they can truly work in isolation and their output can be used as-is. Although I don’t care about this at the moment (in my case, the UI designer and Java developer is the same guy), I’m sure that this is a big deal for most development shops. I am aware that Tapestry promises the same thing.

So this is a key difference from Swing where you have the concept of layout managers. As far as I know this is where frameworks like GWT and Echo2 are different from Wicket because they also have layout controlled in Java code. IMHO the Wicket approach is the right one because you get the separation of concerns advantage. And I personally like complete control of the markup. I mean, with all those silly cross-browser incompatibilities and the need to finely adjust spacing, padding, coloring and the like, this seems to be a good thing.

No JSPs
This is very nice because:

  • You can do things much more easily and efficiently in pure Java than in JSP and the limited syntax of JSTL. Ever had that nagging feeling that your JSPs were getting un-maintainable? Try Wicket!
  • You can refactor confidently and catch problems at compile time which are not possible with JSP. For example if in JSTL you were referring to a non-existent Javabean property, you may not know until the page renders.
  • Pages are no longer slow the first time you access them because there is no more JSP compilation step
  • in my case, the app embeds Jetty and now I don’t need the JSP jar dependencies any more. This cuts the Jetty footprint from about 7 MB (including Ant and the Eclipse Java compiler) to about 1.3 MB. I had tried JSP pre-compilation earlier but gave up because of differences in how Tomcat / Jasper does things vs the Glassfish JSP engine. Jetty + the web-app requires only a JRE to run if there are no JSPs.
  • A nice side-effect of using wicket is that your pages are guaranteed to be XHTML compliant

The downside is of course that you have to recompile a little more often than if you were using JSPs. Wicket does allow you to change the markup on the fly and refresh in debug mode. Also, Wicket provides out of the box, a very detailed error page with some nice syntax coloring telling you exactly where the problem lies in your markup or otherwise – which is very, very cool.

Re-usability
Ah, again, *really*. Re-usability in the JSP world is a custom tag or a hack-y include file.

I already mentioned how easy it is to extend (yes, extend as in the Java keyword) a Wicket component to customize how it behaves. And once you get the hang of the Wicket way of doing things and how to use anonymous inner classes to your advantage, life is good :)

So compared to doing things in JSP, it is far less work in Wicket to create a component that is truly re-usable, say across all the pages of your application. And unlike JSP tags, you get the full benefit of the Java OO features. Did I mention that the design of the whole Component class hierarchy in Wicket feels very nicely done?

Wicket also has a Panel concept where you can group custom markup + components and re-use the group as a single unit to your hearts content. IMHO you really don’t need any “tiles” kind of framework anymore, just create panels and instantiate them at will.

Templates for web pages
Something I really missed in the JSP world was the ability to have all pages have a consistent header and footer and I really hated having to add includes to all JSPs to achieve this. Not any more. Wicket has a brilliant markup inheritance concept that simply works.

Ajax
I’m guessing that this is the killer feature of Wicket. Control freak that I am, I was at first concerned that Wicket comes along with its own basic Ajax implementation, or in other words it provides a replacement for say Prototype or Dojo out of the box. I initially tried to use Prototype from Wicket and was pleasantly surprised when that proved really easy. Adding on Scriptaculous effects was no problem either.

But after trying the Ajax support in Wicket I was really impressed and soon decided to do away with all those *.js files lying around in my web-app. The very useful Ajax debug popup that automagically appears on your web pages in debug mode is one good reason to rely upon Wicket’s built in Ajax. Now I feel confident that I can implement any kind of fancy Ajax on my screens, so bring it on! One example that I tried and can mention here: it was very, very easy to create a page that runs a long running process in a separate thread and updates a progress indicator through Ajax every few seconds or so.

So using Ajax is as simple as telling Wicket that instead of showing or hiding a component using a full-page refresh, do that using Ajax partial-updates to a page. And it just works on any kind of component. And when you use Wicket’s Ajax features you typically never need to write a single line of Javascript.

I came across this quote that Ajax in Wicket is possibly better and much easier when compared to how you do it in Ruby on Rails, and I wouldn’t be surprised if this is true.

Form Binding and Validation
Spring MVC is extremely good at binding HTML forms to POJOs, (a far cry from Struts 1.X) -so can Wicket be any better? I really think so. Just like Spring MVC, as long as you have POJOs and you bind in terms of OGNL, you are fine. I was able to bind almost all of the POJOs that I was using in the earlier Spring MVC app to Wicket forms without any problem. Some things felt really cleaner – like how to have multiple submit buttons on a form.

When it comes to Form validation and displaying validation errors, there are so many options and great possibilities. By default, you can display errors in a section (feedback panel) at the top of the form. I was also able to create a Wicket “behavior” that would highlight the offending widget on the screen by applying a red CSS border around it. Lurking on the Wicket mailing list, I can see folks trying all kinds of cool things such as Ajax-ified feedback messages etc. The Wicket mailing list is a friendly source of help btw, and I got quick responses to all my newbie questions.

Other Neat Stuff
Wicket has some goodies and some of the ones that I ran into (there must be many more) were these:

  • File upload: After using Spring MVC where you have to choose say commons-fileupload and manually include the JAR, I was pleasantly surprised to see that Wicket supports multipart forms / file uploading out of the box.
  • File download: Wicket has a special “Link” class specifically for the purpose of downloading files. Wicket takes care of all the low-level details such as setting the right content-type for the browser – so you end up doing the Right Thing. You don’t need to deal directly with the HttpServletRequest or Response objects at all.
  • Buffered Image: I use JFreeChart and in the old app I had to hack around a bit to get a server-side created chart streamed into an IMG tag. Wicket has an Image component that works with a buffered image. Perfect!
  • URL Mounting: You can get those bookmarkable “friendly URLs” you want with this feature. I liked the support for “REST-ful” URLs out of the box: e.g: /foo/bar/param1/param2 – and how easy it was to use.

Security (Acegi) and Wicket
The old application was using the Acegi security framework. I started out retaining Acegi for authentication and co-existing with Wicket, which was easy. I soon realized that Wicket has a very simple authorization strategy and I was able to plug in custom code to check if a user is not logged in, redirect to a login a page etc. I was also able to easily handle an auto-login using a “remember-me” cookie.

An interesting thing from the security point of view is that the URLs that Wicket generates are inherently secure. In other words it is near-impossible to predict them as they are tightly tied to a user’s session. I came to the conclusion that simply programmatically hiding / unhiding links to secure pages based on the currently logged in user’s role would be sufficient for role-based access-control kind of security.

Acegi is very useful in a URL to MVC-action mapping situation because you can restrict URL patterns to roles – e.g. you can enforce URL wildcard rules like “/admin/**” can be viewed only by ROLE_ADMIN. I will probably continue to use Acegi for a planned remote REST/POX API to the app and for the LDAP and CAS support that it comes with etc. But I’m almost convinced that Acegi is overkill for a basic Wicket app. I am still going back and forth on this – so do let me know what you think.

Integration with Spring
This was easy. I was able to manage without the recommended wicket-spring integration JARs and went with a relatively simpler (and arguably less-elegant) approach – plucking the Spring context out of the ServletContext. In case you were wondering – I have so far used only the core wicket JAR and have not even used wicket-extensions. But I’m sure I’ll start using wicket-extensions soon, I can’t wait to try modal popup windows, the file-upload progress-indicator and the other cool stuff :)

Another example of how well thought out Wicket’s design comes across is that it took only a few lines of code to delegate to the Spring MessageSource instead of using Wicket’s default i18n. So I was able to use my existing resource bundles unchanged.

Integration with Hibernate
I use Spring on top of Hibernate and staying clear of the dreaded LazyInitializationException was as simple as adding the excellent Spring OpenSessionInViewFilter. Regarding GWT, from what I can make out, getting it to work with Hibernate may not be as easy because of the way it works. I would appreciate some comments confirming this, because this could be another key point to consider when you choose an MVC framework.

No Annotations Required
Yep, I’m one of those stubborn types who firmly believes that the only annotation that you should use is @Override. And as mentioned above I did not use the annotations based Spring integration. I bring this point up because from what I have heard – some of the other competing MVC frameworks rely a lot on annotations such as Tapestry, Stripes, RIFE, Seam etc. So if this really matters to you or if your environment is pre-JDK5.0, it could be another differentiating factor. Wicket already is famous for not needing any XML configuration at all. If the Wicket guys are reading this – I really appreciate that you have not forced users to use annotations and I sure hope Wicket stays that way!

WebFlow or Wicket
In the old app, I was using Spring WebFlow and was quite happy with it. So when I started out with Wicket, I was trying to figure out how I could integrate it with WebFlow. But as I understood Wicket more and more and things started to click in my mind, I realized that there is no way you can have them both – you have to choose one or the other.

I found WebFlow great because it allowed me to re-use subflows. So as an example if I had a use-case like “assign task to user” and the user did not exist, I could easily call the “create user” flow and then resume the original calling flow at the point where I branched out, you know – all that continuations stuff. But now my conclusion is that you can easily do all that – and more – with Wicket.

In Spring WebFlow, each view state in a flow is typically a JSP page. Which brings me to now think that it was not really designed to reuse anything more fine-grained than a JSP view – when interacting with a user. I already mentioned that Wicket allows you to reuse sections of HTML and group bunches of components into panels. So when you start trying to build more dynamic pages – I think Wicket wins. Navigating back and forth in Wicket really is child’s play, you just call or instantiate another page at will. You can even do tricks like holding a reference to a previous page, (a page is nothing but a good old Java component) so you can build any kind of complex kind of back-and-forth or wizard kind of navigation easily. I am aware that Wicket has a breadcrumb component and a wizard component but I have not tried them yet.

When it comes to Ajax, I really think Wicket is better than Spring WebFlow. Spring WebFlow is unquestionably a well designed framework, continuations and all, but sometimes I felt that certain things were constraining – for example you have to jump through a few hoops to access flow-scoped variables from outside a web-flow. When you use Ajax, you do need to do that kind of stuff a lot. In addition, in the Ajax world – you are invariably dealing with a section of a page – rather than the page in its entirety – and that is where IMHO the “page centric” nature of Spring WebFlow is a limitation.

A couple of other points, I did not use browser back-button support at all yet, but just from looking at the documentation, Wicket’s back-button support appears to be just as good, if not better. And nit-picking here – I don’t particularly like the huge UUID kind of URLs that SWF generates by default eg: /like?_flowExecutionKey=_c815540C2-03F1-39F8-19E9-D56FC0E377B7_k14D5E331-7B59-EA45-CF06-672C33FA2097
But Wicket is quite the opposite, with a “URL compressor” turned on by default.

End Note
Are there any points I have missed? Please feel free to comment and help me straighten out my thoughts on comparing some of these MVC frameworks. The migration was done on an open source app, so if you want me to pull out some “before and after” code samples, do let me know!

Afterthoughts [update 2007-03-03]
Triggered by the comments and also a few additional things that popped into my mind:

  • Unit Testability: I have not used this yet, but if this means a lot to you – Wicket has that covered. And before you look down on my lack of best practice :p let me hastily add that I use Watij, but not as much as I should have.
  • Custom Session: You can create a typesafe custom Session class and you never need to deal with the HttpSession at all.
  • Refactoring: It is worth repeating that because of no JSPs, no templating (like for e.g. Velocity) and no XML – refactoring of the Java code is simple within your plain old IDE and no special tooling is required. I can vouch for that, I am using NetBeans and have no extra modules (plugins) installed. Debugging, setting break-points and stepping-through plain old Java code is far more easier than trying to do the same thing with e.g. JSPs.
  • Simplified Stack: In my case I replaced [HTML + JS + Prototype + JSP/EL + JSTL + Spring MVC + Spring WebFlow] with [HTML + Wicket]. Also note that whether Acegi needs to be there or not is debatable.
  • Clean separation from Service Layer: This is somewhat specific to Seam and let me elaborate a little. I attended one of Gavin King’s Seam sessions recently and was a little taken aback (so were quite a few others) by the suggestion that the notion of a re-usable middle tier (service layer) is grossly over-rated, i.e. a) you normally never find yourself swapping out a layer, and b) a change in one layer always impacts the other, no matter how loosely coupled they are. Conclusion: so why bother, and you are better off merging all the layers into one. You know the feeling – like wtf, is this back to the monolithic JSP-directly-accessing-DB days!? Anyway – now I can stand up and testify that I was able to cleanly take out Spring MVC and replace with Wicket with only minimal changes to the service interface (mostly feature adds and minor refactoring). This is worth thinking over, and I also feel that a well defined Service Layer is easier to expose as an XML remote API, which I plan to do soon. Just my 2c, do comment!