Sunday 28 January 2007

Bah! What is JBO-35007?

Within a running JDeveloper ADF Faces application, the error "JBO-35007 row currency has changed" is one that throws new developers off guard. It's easy to leap to the conclusion it's an ADF bug. A search of the OTN forums doesn't provide much help, and the JDev doco is just a tad forgetful on this topic in explaining it in laymens terms (you know, for stupid people like me ;).

Basically, and I do mean basically, the JBO-35007 error is not a bug in ADF, but rather a powerful feature for capturing the inappropriate usage of the browser back button by the user.

To understand the back browser problem in the web world, Duncan Mills and Peter Koletzke's recent Oracle JDeveloper 10g for Forms and PL/SQL Developers text (I recommend this book for beginner JDev ADF Faces programmers) sums this issue up well:

"Users may be accustomed to clicking the browser's Back button to return to the preceding page or the Refresh button to reload the page. This can cause problems with web applications that use a Controller layer, like the JSF controller or Struts, because the browser Back button returns to the preceding page in the browser's page history without calling code in the Controller layer. The controller will therefore, not know the state of the application's data and the preceding page will either not load properly or may place the application into an inconsistent or unstable state. The same problem occurs with the browser's Refresh button, which just reloads the same page, again without calling Controller layer code. This is a problem in all web applications, not only those using J2EE technology or ADF libraries."

I like this simple summary, because a more complex discussion involves stateful web applications, web controllers and conspiracies about J.F. Kennedy. In other words, too much detail to go into in a blog, and besides I've a baby girl to look after.

Anyhow, I mentioned that in ADF the JBO-35007 error is in fact a powerful feature for capturing the inappropriate usage of the browser back button by the user. (How powerful? Well, go code it yourself and find out how difficult it would be to implement)

There is mention of this feature in the following JDev doco page.

It's really quiet easy to replicate this feature (and error) in action, and useful to do so to learn when this feature shows its warty little head.

1. First create a JSF web page with an ADF Faces table bound to your favourite VO. Include the table selection control, as well as a submit button.


2. Create a 2nd web page as an input form based on the same table.

3. In your faces-config.xml file create a navigation rule from page 1 to page 2.

4. In the first web page change the submit command button action attribute to the new navigation rule

5. Run your app from the table page.

6. Select any record, then the submit button which will navigate to the 2nd page.

7. On the 2nd page, press the browser back button.

8. The browser has returned to a local cached copy of the 1st page. Select any other record, and press the submit button again.

9. JBO-35007 will be raised.

To get around this issue, on the 2nd page we should provide a command button to navigate back to page 1, and discourage the user from using the browser back button.

And as can be seen from the JDev doco note, the row currency token mechanism can be turned off per page through the pageDef for your page, setting the EnableTokenValidation to false. (Be warned you should not flippantly turn this off for pages, or blanket turn it off for all pages, as the mechanism is an important one)

Hopefully in the future Oracle JDev team will provide an advanced feature to allow the user to leap back to the page they back-buttoned away from, or alternative a clearer error message (which you can override anyway). Or possibly future browsers will support a back button Javascript event that will allow a forced page submit to occur.

9 comments:

Steve Muench said...

In 10.1.3.1, you can also disable current row validation on an iterator by iterator basic by setting the "StateValidation" property of an iterator to "false" without disabling it for all iterators in the page definition.

Unknown said...

Even if the problem is well understood and may arise in other frameworks too, it looks like to me that your web app runs against the browser ways.

I mean, instead of "discouraging" users to use the back button, something that will happen for sure, your app should be able to work with this back button.

In my point of view something must be done in the ADF framework about this. Maybe providing something a bit different from the actual MVC pattern we use. Changing patterns.

Please also note that some frameworks don't suffer from this problem. So, this is not a lost battle.

What is also really annoying is that Oracle provides cryptic messages very difficult to understand for a beginner and very unacceptable for a user.

Chris Muir said...

Well I guess it depends on if you view it as half glass full or half glass empty situation. The feature is there, it's just incomplete, so I'm going half glass full.

So how would you like to see the feature handled when the user clicks the back button, then resubmits the cached page? What would you want the framework to specifically do?

I suggest you write up your suggestions and post to OTN. Believe it or not, from my experience the JDev team does listen.

Unknown said...

Unfortunately, I've got to regard this glass as almost entirely empty. I *always* have to disable back-button validation, and handle it myself. Why? Because of that error message. There is, so far as I can tell, *no* documented way to capture or change the default message that appears, which is, of course, an utterly unacceptable message in a production application. (Honestly, how many times in the course of using a quality web app have you gotten an error message that refers to row keys?)

I've loved a *lot* of what JDev has done in 10.1.3, but token validation is a total bust. Without some way for the developer to trap it (it doesn't seem to ever get passed to handleError()), it's worse than nothing.

Chris Muir said...

Fair call. Make sure to post your comments to the JDev OTN forum. Your opinion needs to be expressed and heard where the JDev team actively participate. You never know, Steve Muench might post an example on how to remap the error message, or we may see a fix in 11g.

Unknown said...

I'd mentioned this before on the forums, but I've recently done one better and filed a ticket, with two results:

1) It turns out I was a little unjust. You *can* change the message yourself, using a custom resource bundle (do a help search for "JBO custom" [not including quotes].

2) The remaining problem is the JBO code itself, which can't be turned off by calling JboException.setAppendCodes(false) because there's no real way to get access to the exception before it displays (this was the part of my previous message that *was* just). My support tech filed a bug on this issue; perhaps we'll see it resolved in the future.

Personally, I see the following three things (I've communicated this to Oracle) as good alternative enhancements to have, in descending order of wonderfulness:

1) Real back-button support--admittedly, this is not easy. But it's the ideal to strive for.

2) A code hook where I can actually handle a JBO-35007 error myself, so that either *I* can try to implement back-button support or (less ambitiously) perform cleanup operations and direct the user as I want. The ability to substitute my own subclass for DCBindingContainer would, I think, do this (so that I could override validateToken() and catch/handle the exception).

3) A way to specify exactly how the JBO-35007 error appears (including turning off the code display, so that users will only see my message, rather than "JBO-35007: My message".

Unknown said...

And we have a workaround!

It is, indeed, a bug that the exception never gets passed to DCErrorHandler.reportException(BindingContainer, Exception).

However, it *does* get passed to FacesPageLifecycle.handleError(PageLifecycleContext, Exception).

If you follow the ADF Developer's guide's instructions on how to implement custom error handlers, it tells you how to use your own FacesPageLifecycle subclass (although it doesn't talk about overriding handleError).

That pretty much satisfies my #2 "enhancement" above (though for elegance and simplicity's sake, I'm glad the bug is still open). I'm a far happier camper.

Chris Muir said...

Thanks for posting the followup comments and taking timeout to raise an SR with Oracle. Giving the number of people who are bouncing off this blog-post via Google searches on JBO-35007, I'm sure your additional findings will assist some of them on taking this to the next step. I'll include a link to your OTN post:

http://forums.oracle.com/forums/message.jspa?messageID=1750748#1750748

So, glass on the way to becoming 1/2 full? ;)

Thanks Avrom, nice work :)

Marcus said...

Wow, this is the most informative web page on jbo-35007 that I've seen in years! (and it's been online since 2007-01... Bah!)

Too bad I didn't find this page before. Now I'm not sure if it is worth to go back to my app just to fix a weird error message.

What is not mentioned anywhere, is that the pt_BR translation of this message is awful. Look for yourselves:

Original: "Row currency has changed since the user interface was rendered. The expected row key was {0}"

In Portuguese: "A moeda da linha foi alterada pois a interface do usuário foi convertida. A chave de linha esperada era {0}"

Back to English, word for word: "The coin of the row has been changed because the user interface has been converted. The expected row key was {0}".

1 - Bad translation for "currency" (at first I thought it was a bad translation for "token"...)
2 - Bad translation for "since"
3 - Bad translation for "rendered".

It's so bad it's offensive! It makes no sense at all, even to the most experienced developers. With translations like these, who needs enemies?