Skip to main content

JSF and Power Windows

Posted by cayhorstmann on June 27, 2010 at 6:22 AM PDT

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, 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

  • Cost be damned; fetch the data from the database again
  • Have the information travel to the client and back to the server
  • Build a fake set of choices just for validation
  • Fuss with the flash
  • Ask “What would Gavin King do?”, embrace server-side state and use conversation 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?

Related Topics >>

Comments

Perfect analogy

Even when power windows work, they provide little to no value over manual windows. They require power, they crush small fingers w/o a care in the world, you can't control the speed and they usually take longer to roll up and down (a big deal in an emergency). All that up front (development) cost and higher repair (maintenance) bills for what? JSF is power windows indeed.

Even when power windows work,

  • Even when power windows work, they provide little to no value over manual windows.
I think it's way more comfortable when you need to roll up or down the window that's on the opposite of your seat ;)

But analogies aside, I think the rule in JSF is rather simple; the model that was used to render the view should be available during post back in exactly the same state. I agree that this is a common pitfall in JSF for beginners and especially dataTable based views are often a problem area here.

As Ed mentions, this has been the case since the very beginnings of JSF. Especially in the early days, there was very little to no documentation about this. Even Ed's excellent and comprehensive book about JSF did not really mention this explicitly as far as I can remember. Back in the day Apache's Tomahawk did a fairly decent job in explaining the problem and providing several tools to assist in dealing with this (e.g. the saveState tag and the ability of Tomahawk's dataTable to automatically retain the model).

With present day JSF and Java EE you can easily store the model in your backing bean if you use the view scope instead of the request scope. Alternatively you can indeed also use CDI's conversation scope if multiple views are involved. Of course, people still working with JSF 1.2 can use Tomahawks saveState tag for storing the model between requests.

Source of quibble?

Hello Cay,

Thanks for your excellent blog post.  Top quality content, as usual.

You say:

  • 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.

Are you quibbling with this behavior?  IF so, I have to say, for security's sake, it can't be any other way.  We simply must beform this check.  In fact the Quicken Online team listed this as one of the reasons that they liked JSF.  ALso, this feature is not new to JSF 2.0, it's been there from the beginning.

You also say:

  • Of course this is clearly documented in the spec

Here, you have a valid quibble.  We're always trying to improve the spec, so if this isn't clear, I'm sorry.

Thanks for posting it.

Ed

No, I am not quibbling with

No, I am not quibbling with the behavior. It makes sense for JSF to do the validation--many developers probably wouldn't think about it. (Obviously I didn't :-))

Cheers,

Cay