I have been using Wicket for a while now and I’ve occasionally wondered if GWT provides any kind of advantage over Wicket. Recently I got a chance to do a comparison and instead of coding a simple “Hello World” kind of example, I decided to try something a little more complex. Hopefully this brings out the differences between Wicket and GWT more clearly.
The target functionality is a one-page application that shows data in a single table. Item counts are displayed categorized under multiple “spaces” (workspaces). There is a summary row. The user can click on a space to expand the count of items grouped by status. The screenshots below show how the table looks when fully collapsed and then when one or more spaces are expanded.
It may look simple, but some of the tricky parts are:
- The number and names of the possible status types can be different for different spaces. For example, the first space above has 3 status types and the second 2. This means that we can’t use different columns to group this data. Multiple rows are used instead to display the break-up when expanded.
- Some of the table cells have to be merged to represent the grouping by space and then by status. In HTML terms, this requires some careful control of TD “colspan” and “rowspan” attributes.
- In addition to the cell merging described above, the style / color of the different cells has to be controlled to differentiate the total count from that grouped by status. For example, the grand total on the last row is in bold. In HTML terms, this requires control over the CSS “class” attribute, coordinated with a style-sheet.
First, let us look at the Java code common to the GWT and Wicket implementations.
The “Space” Domain Object
The class above represents a “space” and it internally uses a Map to hold status names and corresponding counts. There is a method that calculates the total count, a getter for the space name and list of states, and helper methods to add and get status data.
As the functionality is kind of a ‘dashboard’ view – and inspired by the “Cheesr” example that the Wicket In Action book uses, I decided to call this application “Dashr”. “DashrService.java” is a singleton that returns a list of spaces with dummy data. The relevant code is shown below:
So on to the comparison then. In the side-by-side layouts below, GWT is on the left and Wicket is on the right.
Although it is not really a domain class, “DashrService.java” has been kept in the “domain” package along with “Space.java” to keep things simple.
On the GWT side, the recommended package structure and naming conventions (“client”, “public”, etc.) are being used. Our example is a pure client-side application and server-side communication or RPC is not being used at all. All the client-side code and behavior is in “DashrEntryPoint.java” itself. “Dashr.html” simply serves as a placeholder for the rendered application.
It may first appear that Wicket requires more files than GWT but one difference is that the GWT code is not communicating with any server-side functionality at all. Wicket would feel more natural to Java web-app developers familiar with web.xml and the like, and integrating additional server side code or frameworks such as Spring or Hibernate would be much more straightforward.
Another thing is that the Wicket application has been modularized into multiple Java components and corresponding HTML files. It was not really possible to do something similar for the GWT application which has all the UI code in a single file. The reasons for this difference will become clear when we look at the code in detail.
The UI class that acts as the “entry point” has to be configured for a GWT application. In our example we have only one UI class and this itself will be the entry point.
One more thing the main GWT config does is declare “Dashr.css” as the CSS file for our application. A GWT convention-over-configuration rule applies here as well which is that resources like CSS files and images are looked for in the “public” folder relative to the XML file by default.
For Wicket, there is no XML configuration but an “application” class that can hold configuration in Java code. The web.xml file is not really Wicket configuration but it is shown above for completeness and it names the Wicket “application” class used by the framework servlet / filter. In our example, “DashrApplication.java” is just pointing to the designated “home page”. Our home page is “DashrPage.java” for which the HTML markup is in “DashrPage.html”. The Wicket “home page” can be considered equivalent to the GWT “entry point”.
Because of the differences between GWT and Wicket – the code below does not really line up side by side. To make it easier to see how the code compares – some key sections from the GWT side have been mapped to the Wicket side using blue arrows. I have not mapped every possible thing because then the picture would be out of control! You can click on the picture to view a bigger version
- Wicket gives you complete control over the HTML. To render a table we use a “ListView” repeater control [DashrPage.java:18] which manipulates a <TR> section. In the case of GWT however, we have to use an abstraction over an HTML table either HTMLTable or FlexTable. Since we require control over the table “colspan” and “rowspan” attributes, we have to use the GWT “FlexTable” as the basic “HTMLTable” does not support this. What I experienced is that having to use the GWT API to output HTML limits you in certain ways, for example on the Wicket side I was able to freely include <TH> cells with <TD> cells but within the GWT table I could not mix <TH> cells. This may explain why the GWT “Dashr.css” file has an extra CSS class called “header” defined to compensate.
- Using Wicket’s built-in Ajax support, getting the expand / collapse functionality to work was very easy. It was a simple matter of replacing one (or more) rows of the HTML table over Ajax [RowCollapsedPanel.java:19, RowExpandedPanel:29]. Trying to do the same thing using GWT turned out to be quite a puzzle-solving exercise. To dynamically add or remove rows on a GWT table you have to know the row index and in our case the number of rows in the table changes dynamically. I ended up using a HashMap [DashrEntryPoint.java:21] to track the table row for each Space. This explains the mysterious code you see at lines 98 and 124 in DashrEntryPoint.java to update the Map when the table is changed.
- To dynamically set the contents of a table cell in GWT, you have to explicitly get a reference to the cell using the row-index and column-index. But in Wicket it is very easy to add or replace text as long as you know the wicket:id of the HTML element and it works fine for <TD> as well.
- One of the biggest problems I found with GWT is the poor separation of concerns. You can see a lot of code in DashrEntryPoint.java doing CSS styling on the HTML table and cells, for e.g. the calls to getRowFormatter() and getCellFormatter(). GWT does allow you to use a standard CSS file but you have to programmatically set the styles on your widgets in Java code. You can see for example how lines 42-43 in DashrEntryPoint.java map cleanly to markup and code on the Wicket side. The Wicket implementation does not have any CSS mixed in the Java code at all – it is all in the HTML where it belongs, and where web-designers can work freely. Most GWT widgets make use of pre-defined CSS names so this may not be a big problem for simple applications, but the web-designer would still need to know which CSS class names to use.
- This example only has a single widget on one page, but when you place multiple widgets on a screen, you have to think in terms of layout managers just like Swing. I have not gone into what GWT provides in detail but it looks suspiciously like the days of struggling with GridBagLayout :) I prefer the much simpler and effective Wicket approach of managing layout using standard HTML and I personally feel that web-designers need to be able to work directly with the HTML (and CSS) and have better control over things like cross-browser rendering quirks.
- One of Wicket’s strong points is that you can take chunks of HTML and model them as reusable panels. This makes the code modular and easier to read and it is obvious what the intent of “RowCollapsedPanel” and “RowExpandedPanel” is. Maybe with some more thinking and effort on the GWT side I could have had the inner classes ExpandClickListener and CollapseClickListener in separate files. But the code in the “onClick” event handlers is tightly coupled to the “table” and “spaceRowMap” properties of the enclosing class. I considered passing these as parameters in the constructor but decided it wasn’t worth the extra complexity and that the readability of the GWT code would reduce even more.
- The GWT host HTML page Dashr.html contains a hidden IFRAME to support use of the browser back-button. To be able to use this feature properly requires some extra effort as detailed in this page of the GWT documentation. I did not spend time trying to understand this and I simply used null for the “state token” on lines 50 and 82 of DashrEntryPoint.java. The Wicket application transparently supports the browser back-button. If you really need bookmarkable URLs, you then need to do some extra work in Wicket just like you have to do in GWT.
- On a more positive note, I used the latest GWT 1.5 and whatever little Java 5 features I used (generics, enhanced for loop) worked fine. You still need to be aware of some gotchas though.
- The GWT code does not communicate with the server at all which can indeed be considered one of the advantages of GWT because of performance. But it depends. What if I really wanted the data to come from the server? Then I would need to understand how to make server calls and perhaps even GWT serialization. The “Dashr” requirement is not something I cooked up and I am using something similar in an actual project. In the real-life requirement when a Space is expanded, the data has to come from the server (over Ajax) because it is an expensive “count (*)” kind of query in the database. In other words, the expanded data for all spaces cannot be loaded upfront in one go. Handling this kind of stuff in Wicket comes naturally, you *are* on the server all the time. And if you use Hibernate you have to be aware of certain issues with GWT. I hope to expand this example with RPC into another blog post soon and also explain why I think concepts like SOFEA have drawbacks that need to be taken into consideration.
You can’t do this using Adobe Flex!
I initially had an ambitious plan to include Adobe Flex and make this a three-way comparison. But as far as I can tell, there is simply no way to get the exact table grid behavior we require using Flex! Simple things like “rowspan” are simply out of the question. Some more recent Flex widgets like the Advanced Data Grid support grouping of data and expanding grouped items etc. – but come nowhere close to the requirement outlined at the beginning of this post.
I may have missed something, so do let me know in the comments.
Related previous blog post: A Wicket User Tries JSF – comparison of Wicket and JSF using a simpler example.