JSF and Power Windows

My dad is visiting, and he just picked up his rental car. He proudly announced that he got a good deal on a compact car without power windows.

What's wrong with power windows? Nothing, of course, until they break. Then it becomes expensive to diagnose and fix the problem.

JavaServer Faces is like power windows. When stuff works, it is great. But when it fails mysteriously, it becomes expensive to figure out what went wrong. That's what just happened to me with a seemingly innocuous radio button set.

I ported a program that simulates classroom clickers to JSF 2.0 and CDI. (NB. NetBeans does a great job with autocompletion in JSF pages, and CDI is supported in 6.9.)

When the student answers a question from the instructor, I grab the question text from the database. I scan the question for lines starting with 1., 2., 3., etc., and if I find them, I show a set of radio buttons. (This scanning sounds weird, but the instructor sometimes dashes off a question quickly in the classroom and has no time for navigating a fancy UI for supplying the options.)

When the student clicks Save, I store the student's choice and optional text input in the database.

The old app dumped all server-side state into a session-scoped managed bean. I replaced the managed bean with a CDI bean and figured that I might as well be a good citizen and use request scope instead.

My app broke. The text was submitted fine. The radio button choice wasn't.

I couldn't figure out why. Of course, with request scoped beans, the flow is always a bit nasty because there are two bean instances.

  1. The page is rendered. The tags contain value expressions of the form #{formBean.property}.
  2. Request-scoped formBean #1 is instantiated.
  3. More of the page is rendered, and the HTML is shipped off to the client.
  4. This ends the request that caused the page to be rendered, and the request-scoped beans die.
  5. (Long wait)
  6. The user submits the form, starting a new request.
  7. The view is reconstructed, request values are decoded, and they are applied the model.
  8. Where should they go? The #{formBean.property} value expressions are evaluated again.
  9. Request-scoped formBean #2 is instantiated and filled with the request values.

For that second formBean instance, I saw no sense in fetching the question text from the database again, or for regenerating the choices for the radio button. I just wanted to capture and store the student's submission.

After all, the form submission contains the selected radio button and the contents of the text area. It will be set into formBean #2, and my action method can store it in the database.

Except, the radio button never made it. Only the text area contents did. Firebug showed them in the POST request, but the radio button choice didn't make it into my bean. The setChoice method was never called, as I found out when setting a breakpoint there. (NB. The NetBeans debugger does a great job with debugging JSF apps)

After much debugging , I noticed that the getChoices expression (which yields all the radio button choices) was evaluated after the POST. I didn't understand that. The choices are needed for rendering the radio buttons but not for decoding. That's why I didn't bother to recompute them.

Except, I was wrong. In JSF 2.0, the power windows kick in. Some helpful soul decided that one might as well check that the request value is one of the choices, to thwart any fiendish person who has manually crafted a POST request. If the submitted value isn't among the choices, validation fails and the model is not updated.

This is clearly documented in the spec. Just kidding—I found it here. Of course, I only found it after reading through the reference implementation source, feeling much like these guys fiddling with a power window regulator.

So, the moral is that the choices must be available both for rendering and for decoding.

There are many ways of dealing with that, short of going back to dumping everything back into session scope

So, is JSF bad and bloated and complex?

What do you have in your car? If you have the old turn-the-crank-to-lift-the-window apparatus, go ahead and post a comment on how bad and bloated and complex JSF is. But if you have power windows, maybe you are ok with a tradeoff between convenience and the occasional expensive failure?