Add armor for exceptions thrown by Activity#onCancel Review at http://gwt-code-reviews.appspot.com/1446816 git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10272 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/activity/shared/ActivityManager.java b/user/src/com/google/gwt/activity/shared/ActivityManager.java index 45f41b5..8cb9a33 100644 --- a/user/src/com/google/gwt/activity/shared/ActivityManager.java +++ b/user/src/com/google/gwt/activity/shared/ActivityManager.java
@@ -95,13 +95,12 @@ * be minimized by decent caching. Perenially slow activities might mitigate * this by providing a widget immediately, with some kind of "loading" * treatment. - * - * @see com.google.gwt.place.shared.PlaceChangeEvent.Handler#onPlaceChange(PlaceChangeEvent) */ public void onPlaceChange(PlaceChangeEvent event) { Activity nextActivity = getNextActivity(event); Throwable caughtOnStop = null; + Throwable caughtOnCancel = null; Throwable caughtOnStart = null; if (nextActivity == null) { @@ -115,7 +114,7 @@ if (startingNext) { // The place changed again before the new current activity showed its // widget - currentActivity.onCancel(); + caughtOnCancel = tryStopOrCancel(false); currentActivity = NULL_ACTIVITY; startingNext = false; } else if (!currentActivity.equals(NULL_ACTIVITY)) { @@ -126,46 +125,26 @@ * them accidentally firing as a side effect of its tear down */ stopperedEventBus.removeHandlers(); - - try { - currentActivity.onStop(); - } catch (Throwable t) { - caughtOnStop = t; - } finally { - /* - * And kill them off again in case it was naughty and added new ones - * during onstop - */ - stopperedEventBus.removeHandlers(); - } + caughtOnStop = tryStopOrCancel(true); } currentActivity = nextActivity; if (currentActivity.equals(NULL_ACTIVITY)) { showWidget(null); - return; + } else { + startingNext = true; + caughtOnStart = tryStart(); } - - startingNext = true; - - /* - * Now start the thing. Wrap the actual display with a per-call instance - * that protects the display from canceled or stopped activities, and which - * maintains our startingNext state. - */ - try { - currentActivity.start(new ProtectedDisplay(currentActivity), - stopperedEventBus); - } catch (Throwable t) { - caughtOnStart = t; - } - - if (caughtOnStart != null || caughtOnStop != null) { + + if (caughtOnStart != null || caughtOnCancel != null || caughtOnStop != null) { Set<Throwable> causes = new LinkedHashSet<Throwable>(); if (caughtOnStop != null) { causes.add(caughtOnStop); } + if (caughtOnCancel != null) { + causes.add(caughtOnCancel); + } if (caughtOnStart != null) { causes.add(caughtOnStart); } @@ -180,9 +159,7 @@ * @see com.google.gwt.place.shared.PlaceChangeRequestEvent.Handler#onPlaceChangeRequest(PlaceChangeRequestEvent) */ public void onPlaceChangeRequest(PlaceChangeRequestEvent event) { - if (!currentActivity.equals(NULL_ACTIVITY)) { - event.setWarning(currentActivity.mayStop()); - } + event.setWarning(currentActivity.mayStop()); } /** @@ -204,6 +181,41 @@ } } + public Throwable tryStart() { + Throwable caughtOnStart = null; + try { + /* Wrap the actual display with a per-call instance + * that protects the display from canceled or stopped activities, and which + * maintains our startingNext state. + */ + currentActivity.start(new ProtectedDisplay(currentActivity), + stopperedEventBus); + } catch (Throwable t) { + caughtOnStart = t; + } + return caughtOnStart; + } + + public Throwable tryStopOrCancel(boolean stop) { + Throwable caughtOnStop = null; + try { + if (stop) { + currentActivity.onStop(); + } else { + currentActivity.onCancel(); + } + } catch (Throwable t) { + caughtOnStop = t; + } finally { + /* + * Kill off the handlers again in case it was naughty and added new ones + * during onstop or oncancel + */ + stopperedEventBus.removeHandlers(); + } + return caughtOnStop; + } + private Activity getNextActivity(PlaceChangeEvent event) { if (display == null) { /*
diff --git a/user/test/com/google/gwt/activity/shared/ActivityManagerTest.java b/user/test/com/google/gwt/activity/shared/ActivityManagerTest.java index ece9341..62a4af8 100644 --- a/user/test/com/google/gwt/activity/shared/ActivityManagerTest.java +++ b/user/test/com/google/gwt/activity/shared/ActivityManagerTest.java
@@ -305,18 +305,18 @@ assertEquals(0, eventBus.getCount(PlaceChangeRequestEvent.TYPE)); } - public void testExceptionsOnStopAndStart() { - activity1 = new SyncActivity(null) { + public void testExceptionsOnStartAndCancel() { + activity1 = new AsyncActivity(null) { @Override public void start(AcceptsOneWidget panel, EventBus eventBus) { super.start(panel, eventBus); bus.addHandler(Event.TYPE, new Handler()); } @Override - public void onStop() { - super.onStop(); + public void onCancel() { + super.onCancel(); bus.addHandler(Event.TYPE, new Handler()); - throw new UnsupportedOperationException("Auto-generated method stub"); + throw new UnsupportedOperationException("Exception on cancel"); } }; @@ -324,7 +324,7 @@ @Override public void start(AcceptsOneWidget panel, EventBus eventBus) { super.start(panel, eventBus); - throw new UnsupportedOperationException("Auto-generated method stub"); + throw new UnsupportedOperationException("Exception on start"); } }; @@ -340,9 +340,56 @@ fail("Expected exception"); } catch (UmbrellaException e) { - // HandlerManager throws this one + // EventBus throws this one assertEquals(1, e.getCauses().size()); + // And this is the one thrown by ActivityManager + UmbrellaException nested = (UmbrellaException) e.getCause(); + assertEquals(2, nested.getCauses().size()); + } + assertTrue(activity1.canceled); + assertNotNull(activity2.display); + assertEquals(0, eventBus.getCount(Event.TYPE)); + } + + public void testExceptionsOnStopAndStart() { + activity1 = new SyncActivity(null) { + @Override + public void start(AcceptsOneWidget panel, EventBus eventBus) { + super.start(panel, eventBus); + bus.addHandler(Event.TYPE, new Handler()); + } + @Override + public void onStop() { + super.onStop(); + bus.addHandler(Event.TYPE, new Handler()); + throw new UnsupportedOperationException("Exception on stop"); + } + }; + + activity2 = new SyncActivity(null) { + @Override + public void start(AcceptsOneWidget panel, EventBus eventBus) { + super.start(panel, eventBus); + throw new UnsupportedOperationException("Exception on start"); + } + }; + + manager.setDisplay(realDisplay); + + try { + PlaceChangeEvent event = new PlaceChangeEvent(place1); + eventBus.fireEvent(event); + assertEquals(1, eventBus.getCount(Event.TYPE)); + + event = new PlaceChangeEvent(place2); + eventBus.fireEvent(event); + + fail("Expected exception"); + } catch (UmbrellaException e) { + // EventBus throws this one + assertEquals(1, e.getCauses().size()); + // And this is the one thrown by ActivityManager UmbrellaException nested = (UmbrellaException) e.getCause(); assertEquals(2, nested.getCauses().size()); }