<view-state id="whatever">
<on-entry>
<evaluate expression="myService.doSomething()" />
</on-entry>
<transition on-exception="example.Exception" to="my-error-page" />
</view-state>
If you want to send the user to the same error page no matter in which state the exception occurs, you can define this transition as a global transition. Combined with flow inheritance you can take this one step further and define consistent exception handling for all your flows. Quite nifty.
There's just one thing to keep in mind however. This isn't quite obvious and it's also not mentioned in the documentation which is otherwise pretty good: The transition matches only if an exception of the exact class you specified if thrown. It will not match subclasses of the specified exception. Instead Webflow will unpack the exception until it either reaches the root cause or finds a match for your on-exception class.
So let's say you have the following exception hierarchy:
package example;
public class SystemException extends java.lang.RuntimeException {
public SystemException(java.lang.Throwable cause) {
super(cause);
}
}
package example;
public class MyServiceException extends SystemException {
public MyServiceException (java.lang.Throwable cause) {
super(cause);
}
}
And this flow definition excerpt:
<view-state id="whatever">
<on-entry>
<evaluate expression="myService.doSomething()" />
</on-entry>
<transition on-exception="example.SystemException" to="my-error-page" />
<transition on-exception="java.lang.RuntimeException" to="fatal-error-page" />
</view-state>
Now let's assume doSomething does something stupid like
try {
int id = Integer.parseInt(parameterString);
} catch(NumberFormatException e) {
throw new MyServiceException(e);
}
I bet that at first glance any Java programmer would expect the user to see
my-error-page
or at least fatal-error-page
. Instead the user will get to see a very confusing stack trace because Webflow will look at your on-exception transition and (leaving out some Webflow specific exceptions wrapped around our exception) check if - MyServiceException == SystemException
- NumberFormatException == SystemException
- MyServiceException == RuntimeException
- NumberFormatException == RuntimeException
and then, having found no exact match, proceed to the standard exception handler which simply displays the full stack trace. Now if you change your view-state definition to
<view-state id="whatever">
<on-entry>
<evaluate expression="myService.doSomething()" />
</on-entry>
<transition on-exception="example.SystemException" to="my-error-page" />
<transition on-exception="example.MyServiceException" to="my-error-page" />
<transition on-exception="java.lang.RuntimeException" to="fatal-error-page" />
</view-state>
then Webflow will find an exact match for the second transition and send the user to the error page.
Conclusion
Even if Spring Webflow's exception handling mechanism looks very much like a try/catch block it behaves differently. I still find it quite useful for handling specific exceptions in specific circumstances. But to reliably send the user to an error page I'd use some other mechanism as it will quickly become cumbersome and error prone to list all the exceptions that might possibly occur.