Posts tagged ‘ComponentClassTransformWorker’

Problem description

Recently I joined a project that uses tapestry5 – a very nicely organized framework for web development with its own IoC mechanisms, great error reporting and very productive. And to me it still holds closest to its goal – it should be easy to edit templates by non-programmers.

Let’s get to the point – on one of pages I needed to display a dropdown, which is quite nicely described in tapestry documentation but to make things a little bit more compliated the model for the dropdown must be persistent throughout requests. At first it looked like a piece-of-cake, just add @Persist annotation to the model field and that’s it. Under jetty (which is our development environment) it all looked nice but after moving to Glassfish (which is production environment) strange error apeared, stating that SelectModel cannot be stored in session since it doesn’t implement Serializable interface.

So I asked our tapestry guru what’s going on and why can’t I store SelectModel in session and he said that Tapestry components should not be stored in session and the only thing that I should keep in session is the backing list for which model should be regenerated for each request.

This sounded awkward to me – because I envisioned all this repetitive code for converting Lists into SelectModels so they could be properly displayed.

I knew there had to be a better way…

Solution description

Fortunately tapestry gives you a very nice way of hooking into it’s bytecode manipulation mechanisms by implementing ComponentClassTransformWorker2 (available since v. 5.3), so I decided on a following solution:

  • each field that is supposed to be displayed as dropdown should be marked with some specialized annotation (say @SelectModel)
  • there should be some magical way of transforming a list into a SelectModel behind-the-scenes so view gets it with no other line of code.

I started googling and I found very similar case – there was a mixin, that should be added to every label. This post gave me an idea how to plug my bytecode manipulation service into tapestry plugin mechanism.

Usage

  1. As mentioned in solution plan the whole thing starts with annotating field with @SelectModel annotation, which requires name of field that should be used as option label.
  2. And you need to register SelectModelPropertyWorker in you AppModule.

And that’s it – now you can start using your list field as SelectModel in your view under the same name.

As you may have noticed SelectModelPropertyWorkier injects ServiceModelFactory under weird name so the getter can produce you model. If you know a better way to do it – just leave a comment and I’ll definitely include it.

Known limitations

There are 2 things that you need to keep in mind when working with @SelectModel annotation:

  1. The field cannot have @Property annotation since tapestry default mechanism will try to generate getter and setter which might give weird exceptions
  2. Since there cannot be getter so you cannot add it manually as well

Source code

Source code available on github.