The most used model in Wicket is the PropertyModel
and its derivatives. This model allows the user to quickly and easily navigate object graphs using string-based property-path expressions, and is used extensively when displaying data or creating forms.
However, the PropertyModel
and its string-based expressions are also responsible for adding the most fragility to Wicket code. The standard Java tooling does not support refactoring strings that happen to contain references to java constructs such as methods or fields; thus errors in these string expressions are not discovered until runtime.
For example, the string used in the code below:
will not be updated by the tooling when the getState() method is renamed into getArea() and will thus cause an error only discoverable at runtime.
So why is PropertyModel
used so often in Wicket code even though it causes so many problems? The answer is simple: it is, by far, the easiest and most convenient way to achieve the functionality. We chose to sacrifice robustness of our code for development ease and convenience. What if there was another way to achieve the same, but without using strings?
Enter Bindgen, a Java 6 apt processor [1][2] capable of generating code that can represent the same property-expressions but using real Java objects instead of strings.
Bindgen allows the developer to transform code like this:
into code like this:
No more strings! And although standard Java tooling will not automatically rename state()
when the field or method it references is renamed, the compiler will immediately report all places that need to be updated via a compilation error.
So what does Bindgen get you? It gets you the same ease and convenience of PropertyModels
but with the added compile-time safety. Too good to be true? Try it out for yourself…
Setting up the environment
The following demonstrates how to set up a project that is built using maven and eclipse. This code is experimental, and there is not much documentation yet (see Sources section below), but what Bindgen does is worth trying.
Adjust your pom as described here: http://code.google.com/p/bindgen-wicket/wiki/ConfiguringMaven2
Now run mvn eclipse:eclipse
and the project should be ready to go using both eclipse and regular maven builds.
Using Bindgen
Using Bindgen itself is simple. Annotate a class whose properties you wish to access using the @org.bindgen.Bindable
annotation and Bindgen will generate a <ClassName>Binding
class that can be used to create Binding
instances that represent property expressions. Eclipse will generate these classes as soon as the class with @Bindable
is saved, so the <ClassName>Binding
class should be available as soon as you press Ctrl+S after adding the @Bindable
annotation.
For example:
Given
Bindgen will generate classes AddressBinding
and PersonBinding
enabling code like this:
Configuring Bindgen
Bindgen will generate bindings for all classes reachable from the annotated class. Most of the time this is not desirable, eg we do not necessarily want binding classes generated for java.lang.String
or for com.caucho.hessian.io.SerializerFactory
just because they are reachable from a class with the @Bindable
annotation. Bindgen provides a way to limit the generation of binding classes to certain packages by specifying a comma separated list of these packages. To do this, create a file called bindgen.properties
in the project’s base folder (the one that contains the pom file) and add the scope property, eg
Using Bindgen with Wicket
The bindgen-wicket module provides classes that facilitate integration between Wicket and Bindgen. These are BindingRootModel
and BindingColumn
.
BindingRootModel
is the replacement for the PropertyModel
, and its usage looks like this:
Or using a convenience wrapper:
The BindingColumn
class allows binding expressions to be used for creating DataTable
columns. Eg:
Conclusion
As we can see, Bindgen is able to remove one of the biggest pain points in Wicket code, and do so in a manner mostly transparent to the developer. Wicket is not the only framework that can benefit from Bindgen, any place in code where property expressions are kept in strings can be upgraded to a compile-safe Bindgen binding and if that is not an option at least the property expression string can itself be generated from a binding using the Binding#getPath()
method.
It is important to keep in mind that a lot of the code that makes all this possible is still fresh, and so there may be a bug here or there. Your feedback would be very appreciated.
Sources
Bindgen website: http://www.bindgen.org
My fork of bindgen repository which contains my latest tweaks: http://github.com/ivaynberg/bindgen
Bindgen official repository, this is where my changes eventually get merged: http://github.com/stephenh/bindgen
Bindgen-Wicket integration project: http://code.google.com/p/bindgen-wicket
Demo project: http://bindgen-wicket.googlecode.com/svn/trunk/bindgen-wicket-examples/