I've been working with Wicket for almost a year. We've just released our first product that uses Wicket for the user interface, and so it seems like a good time to take stock. Unfortunately, it's not a public site, it's an installable enterprise product, so I can't show it to you. If you don't want to read further, here's the executive summary: Wicket rocks!
I was hired as the GUI Architect for this project. I came to it with many years of GUI experience, mostly using Swing, but without a lot of web development experience.
Because of my Java and Swing background, I was drawn to Wicket. It maps fairly closely to the Swing model of development. So does GWT, but when I evaluated it, it seemed so different from other J2EE frameworks that I felt it was a step too far. No HTML, and no WAR files, for example. This made my colleagues nervous, who were used to Struts and PHP. Me too, as a matter of fact.
I had done some pretty serious prototyping for another project with Tapestry, and there were certain things I liked, like runtime bytecode generation. But the learning curve was pretty steep. At one point I needed to create a custom component, and to do so I needed to learn about engine services and other arcane things that I felt made the process too hard. By contrast, custom components are Wicket's bread and butter, and they are very easy to build.
I also took a close look at JSF. It seemed overly complex to me, and not much of a departure from the Struts era. It came across as a technology designed by committee, with the combination of several complementary libraries required to get the job done, and there are still too many configuration files.
So we decided to use Wicket. Luckily, my management and colleagues were open-minded enough to give it a try. The application was a management console for an enterprise application. It did not require high user traffic, so were weren't concerned about what people talk about as the biggest issue with Wicket, namely scalability. We certainly have had no performance problems ourselves.
Right out of the gate I was able to create a framework with Wicket 1.2.6 that my colleagues could fill out, consisting of a navigation tree, a base page, database-driven tables, graphic buttons, page headers, tabbed panels, and a CSS-based look and feel.
One of Wicket's advantages is the strict separation of design from behavior, that is, HTML from code. While we did not have a web designer on the team who built the HTML (the developers did this), and therefore didn't get any mileage from the separation in that sense, we definitely gained from having all the behavior in Java code, because it gave us all the power of refactoring, compile-time error checking, and maximum reusability.
What I mean by maximum reusability is that I could create a component with quite a bit of logic behind it, e.g. a button that disables itself based on various combinations of server state, and all it would take to add it to a page would be
<input type="submit" wicket:id="button" />in the HTML, and
new MyButton("button")in the Java code. In other words, it looks no different than adding an ordinary button. I could then change the behavior of MyButton as needed without affecting the HTML. And of course, there are no XML files to worry about keeping up to date, like struts-config.xml.
I made a big effort to encourage reusability on the team, which of course is one of the big reasons to use Wicket. So we made sure there was no cut-and-paste reuse, and instead if a component did not have a necessary feature, I spent the time to add that feature. This paid off in spades later in the project, since if a bug was found or the look and feel needed tweaking, it typically only needed a change in one place. The number of bugs related to errors in the Wicket code was tiny. All the bugs were about the business behavior, that is, misunderstandings between product management and engineering, which is where you want any bugs to be.
We had some unusual requirements, for example, there were some legacy applets that we needed to support because we didn't have the time to convert them to Wicket. It turned out to be easy to get the applets to communicate with the server using Wicket. We also embedded the applet page in a zero-height frame to avoid reloading the applets with each page view. The web pages were displayed in another frame. Again, Wicket was able to support this.
As a result of all this, we actually stayed ahead of our middle tier colleagues for much of the product cycle. I was very pleased with the result.
Anyone who learns Wicket will tell you that the key to mastering it is understanding how models work. Models are the "M" in "MVC", and all decent frameworks are architected this way. But Wicket takes it to its logical extreme, meaning that all data, including resources, should be obtained through models, and it takes a bit of getting used to.
The most complex reusable component we built was a custom form. In our case, I wanted all our forms to look the same, using the same mix of basic form controls. So I wanted a way to build forms entirely in code. I achieved this by using Wicket fragments. These are like mini panels and are very useful.
The result is that a client can create a very powerful form control using one line of code, e.g. a text field that has an attached label, marks the form as dirty using the onchange event, lays itself out in a pleasing way, can hide itself and its label dynamically (using Ajax), can disable itself based on session properties, and using a common CSS look and feel. Oh, and supports accessibility by using the label tag properly. No doubt I did it this way because of my Swing background, but it paid off for us. Interestingly, by the time it was done, it looked much like a GWT widget, but that's probably because GWT was also inspired by Swing.
Upgrade to Wicket 1.3
After our first release, we decided to upgrade, which I was nervous about, but it's always the best option when using an open source library so you can get support from the team and stay current with features and bug fixes. This turned out to be OK but not painless. Certainly it was a lot easier than going from Tapestry 4 to 5, which pretty much required a complete rewrite. The migration guide helped somewhat (it was incomplete and not very explanatory) but it still required a fair amount of work and investigation. One example: you now need to supply a MarkupProvider for fragments; I still don't know why, since it's not explained adequately anywhere. But we got it done without much code surgery, which shows that Wicket cares about backwards compatibility.
Separation between presentation and business logic
One of the criteria in my choice of Wicket was, does it promote good separation between presentation and business logic? Our experience says yes. We have a well-defined API layer that the middleware provides, and the Wicket code makes calls to it (exclusively from Java code) at exactly the right level of abstraction. For example, when you add an employee to the employees table, you would do it in the onSubmit() method of the Add Employee button. Furthermore, you handle exceptions in the same place, which 90% of the time, add an error message to the feedback panel on the same page. It ends up being a small amount of localized code, all in the same place.
Resources and Localization
Wicket has a nice model whereby it looks for resource strings in the properties file for the component in question, and if it doesn't find it there, it walks up the component hierarchy to find it. This worked well for us, but sometimes it can be difficult to work out where Wicket is trying to find a resource. Some people prefer to put all strings in one file; Wicket supports this too, but I feel it's easier to tame resource string proliferation if the strings are closest to where they are used.
We made use of the "style" session attribute for branding purposes. The style is intended to "skin" your application but it works well for branding too. It works by adding another suffix to the filename of a properties file that it's looking for.
One of the things that could be improved is the explanation of resource-related APIs. For example I still don't know when I should use
component.getString(), ResourceReference, or StringResourceModel. As with most mature frameworks, over time the number of APIs has proliferated, and sometimes it's hard to keep them straight. This is a pity given Wicket's very clear mission of simplicity.
We ended up using a free Eclipse plugin called Wicket Bench, which is surprisingly useful. When you open a Wicket page (a Java class), it looks for other files with the same name but different suffixes (e.g. html, css, properties) and loads them in different tabs in the same view, so it's easy to switch between them.
When we upgraded to Wicket 1.3 we decided to switch from Ant to Maven for our builds, with mixed results. First of all, the middleware team didn't want to switch to Maven, and Maven doesn't play too well in a heterogeneous environment. Second, I found I had to add Ant tasks to Maven to do simple things like deploying my WAR file to JBoss. It blows me away that Maven doesn't have a simple copy task built in.
On the plus side, it was trivial to upgrade from Wicket 1.3 BETA, to RC, to GA by just changing the pom.xml file. And the Maven integration with Eclipse is very nice.
Tip: make sure you create links lazily. That is, don't create the web page that is the destination of the link until onClick() is called. This was our biggest performance bottleneck. Fortunately, Wicket 1.3 has deprecated the constructor that takes a WebPage and so it encourages you to do the right thing.
We've found there tends to be a proliferation of the trio of classes that implements a paging data table, namely DataProvider, DataPanel and DataView, along with their supporting properties files. Keeping them in sync can be a pain. It would be nice if there was a single class in the API that implements the common case where all three are used once to implement one table, i.e. where there's no reuse of a DataProvider for other tables.
Things I'd love to see
I can't wait for the release that was going to be Wicket 2.0, namely a reorganization of the API to provide better error messages and support for generics. One of the hardest things to deal with is the obscurity of error messages. In this one sense I preferred Tapestry.
I wholeheartedly recommend Wicket!