Monday, July 6, 2009

Transitions and Exceptions

There's this nice little feature in Spring Webflow that allows you to transition to a specific flow state when an exception occurs. Let's consider a simple example: If one of the methods you call from within a flow throws an exception you can send the user to an error page:
<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





  1. MyServiceException == SystemException


  2. NumberFormatException == SystemException


  3. MyServiceException == RuntimeException


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

Saturday, July 4, 2009

Spring Webflow and JSF: Trouble with end-states

I've been using Spring Webflow 2 and JSF for some time months now and there's still one thing that bothers me (well, actually more than one, but this seems fundamental):

How do you reconcile the different approaches to state handling between these two frameworks? While Webflow makes it very easy to handle the current state of a Wizard-like pageflow that state is lost when you reach an end state. Say you have a very simple pageflow containing just three pages:
<view-state id="enter-details">
<transititon on="continue" to="check-details">
</view-state>
<view-state id="check-details">
<transition on="confirm" to="confirmed">
</view-state>
<end-state id="confirmed"/>

Using this flow with JSF you can use all the usual JSF components on the first two pages (the view states). The last page is a different story however. Since this is an end state Webflow will purge the flow state. Fine and dandy if it weren't for JSF's ugly reliance on the component tree to handle command buttons and links.

So what happens if you have a <h:commandbutton> or something similiar on your end state's page and the user clicks on that button? Well, surprise, the user gets redirected to the first page. As far as I can tell, JSF tries to find the current state but Webflow (correctly) determines that the requested flow has ended. Standard behaviour in this case is to redirect to the start page of the flow.

So on my current project we have built our site out of Webflows that are wired together using standard HTML links ("output links" in JSF parlance). Not really a nice solution but for now it works.


The drawbacks:

  • Some pages are enclosed in flows even though they aren't part of a "real" process (e.g. an overview page). Those flows don't even have natural end states.

  • You have to be extra careful not to use commandLinks, commandButtons or anything similiar on your end states' views. And this distinction between regular view and end state views makes working with templates quite ugly..

I'm still looking for a better way to mix free-style navigation with flows in JSF but so far I've come up blank.