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.
#{formBean.property}
. formBean
#1 is instantiated.#{formBean.property}
value
expressions are evaluated again.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?