Introduction
Ironically, property models are the most convenient and the most troublesome feature of wicket. Lets use the following code as an example:
They are convenient because they make it trivial to bind components to domain objects using one line of code.
They are troublesome because they create code that is easily broken and whose breakage is not apparent until run time. For example: if the AddressBook#getAddresses()
method used in the example above is renamed to AddressBook#getLocations()
it will not cause a compilation error, but rather a runtime error when the page is rendered. This is a big problem unless every page of the webapp is covered by a unit test.
Possible Solutions
There are a couple of ways to fix the problem:
- Do not use property models; instead create models that implement get/setObject methods using plain java code. Advantages: easy, refactor-safe, compile time errors. Disadvantage: extremely verbose, no default null handling in intermediate return values.
- Use proxies to generate dynamic builders. Advantages: refactor-safe, compile time errors. Disadvantages: runtime overhead (negligible), use of proxies limits code features(cannot intercept final methods, etc), most likely cannot be inlined thus requires at least two lines of code instead of one.
- Use a source code analyzer to check the expression string at compile time. Advantages: compile time errors, most visibility into code – can take advantage of generic types that are erased at runtime. Disadvantages: not refactor-safe, requires source for all classes used in the expression, extra compilation step.
- Use
apt
to generate metadata about classes and use that metadata instead of the expression string. Advantages: transparent code generation in java 6, compile time errors. Disadvantages: not refactor-safe, only works for classes compiled with apt processor. - Use a bytecode analyzer to check the expression string at compile time or test time. Advantages: compile or test time errors, no extra compilation step if used in test time. Disadvantages: not refactor-safe.
Salve's solution
The bytecode analyzer is what I chose to implement as part of Salve. It is implemented as a unit test that scans for use of salve.expr.Pe
class that represents property expressions in Salve.
A property model usage with Salve’s Pe class can look something like this:
While the unit test can be installed by creating a simple subclass of Salve’s PropertyExpressionTest
within the project being checked:
When executed the unit test will scan for .class files in the project folder, look for instantiations of Pe
class and check the string expression. Any error will cause the test to fail.
Using Pe
class does make the code somewhat longer. The checker supports definitions of custom classes. For example we can create a more convenient version of the PropertyModel like so:
With usage:
And let the unit test know about it by specifying a matching rule:
Rules match on the last x parameters. In the above example the rule will match instantiations of the PeModel
class with constructor signature of (*,Class rootType, String path)
.
Another convenient usage pattern is binding components to fields of the parent via: new PropertyModel(this, “name”);
. Salve’s checker supports this pattern like so:
Installing Salve:
Conclusion
The code to all this is still brand new, raw, unfinished, undocumented, untested, etc. This is mainly a call out for feedback and ideas. Let me know what you think.