how to deal with globally uncaught exceptions caused by user navigating to view Y before view X has finished rendering

Ian Springer ian.springer at redhat.com
Wed Feb 15 16:04:09 UTC 2012


I just checked in a fix to master for 
https://bugzilla.redhat.com/show_bug.cgi?id=754521. It was a class of 
issue we've seen before, where the user goes to view X and then quickly 
goes to view Y, before X has completed rendering. Typically, this 
results in NPEs, because the code we added to support Selenium locators 
destroys view widgets when it thinks we're done with them. My fix for 
754521 was as follows:

1) create a new ViewChangedException runtime exception

2) in places where we detect that the user has switched to a different 
view than the one we are still rendering, throw a ViewChangedException, 
rather than continuing on rendering. Here's an example from 
AbstractTwoLevelTabSetView.updateTabContent():

         String currentViewPath = History.getToken();
         if (!currentViewPath.startsWith(this.baseViewPath)) {
             throw new ViewChangedException(this.baseViewPath + "/" + 
getSelectedItemId());
         }

Places that are good candidates for such code are in onSuccess() methods 
of async callbacks, because while we were waiting for the async call to 
return, the user could have navigated away to another view.

3) add code to the uncaught exception handler in CoreGUI that gracefully 
handles ViewChangedExceptions by simply logging a DEBUG message to the 
gwt log:

         GWT.setUncaughtExceptionHandler(new 
GWT.UncaughtExceptionHandler() {
             public void onUncaughtException(Throwable e) {
                 if (e instanceof ViewChangedException) {
                     ViewChangedException viewChangedException = 
(ViewChangedException) e;
                     String obsoleteView = 
viewChangedException.getObsoleteView();
                     Log.debug("User navigated to view [" + currentView 
+ "] before view [" + obsoleteView
                         + "] was done rendering - rendering of view [" 
+ obsoleteView + "] has been aborted.");
                 } else {
                     
getErrorHandler().handleError(MSG.view_core_uncaught(), e);
                 }
             }
         });


If you encounter a similar bug in the future, you should be able to 
leverage this new mechanism. Essentially, you would just need to find 
the right place in the code to do step 2) from above.



More information about the rhq-devel mailing list