Spring Web Flow exception handling - spring

How to prevent exception,
if requestParameters.sortBy is passed as string (java.lang.NumberFormatException) or is missing (java.lang.NullPointerException)?
<view-state id="journeySearch" model="journeyForm">
...
<transition on="sort">
<set name="journeyCriteria.sortBy" value="requestParameters.sortBy" type="int" />
<evaluate expression="bookingService.searchJourneys(journeyCriteria)" result="viewScope.journeys" />
</transition>
</view-state>

requestParameters.sortBy will be null if it doesn't exist, but it should not throw a NullPointerException
about the NumberFormatException, you could use something like that:
<global-transitions>
<transition on-exception="java.lang.NumberFormatException" to=""/>
</global-transitions>
you could also implement your own exception-handler and use it with <exception-handler bean=""/> you can use it at the flow or state level.

Related

Spring webflow start flow and event from a external link (p.e. confirmation email)

App send a confirmation email when an user register a new account. That email contain a link which must start a specific eventId for a flow which got several events. Link redirect properly to flow but I don't get this flow start on confirmation eventId, always it starts on first eventId called login.
url: ....aio/spring/login?_eventId=confirmation&code=cmFmYWVscnVpenRhYmFyZXNAZ21haWwuY29t
I have read possible solutions using externalRedirect and others commands written on flow rules but I don't need that way.
Login flow
<view-state id="login" view="login.xhtml">
<transition on="entry" to="connect"/>
<transition on="recoveryPass" to="recovery" />
</view-state>
<action-state id="connect">
<evaluate expression="login.connect()" />
<transition on="yes" to="finish" />
<transition on="no" to="login" />
</action-state>
<view-state id="recovery" view="recovery.xhtml" model="loginFields">
<transition on="return" to="login" />
<transition on="sendPass" to="recoveryPass" />
</view-state>
<action-state id="recoveryPass">
<evaluate expression="login.recoveryPass()" />
<transition on="yes" to="login" />
<transition on="error" to="error" />
</action-state>
<action-state id="confirmation">
<on-entry>
<set name="confirmationCode" value="requestParameters.code" type="string" />
</on-entry>
<evaluate expression="login.confirmation(confirmationCode)" />
<transition on="yes" to="confirmationOk" />
<transition on="no" to="noUserFound" />
<transition on="error" to="error" />
</action-state>
<view-state id="confirmationOk" view="confirmation.xhtml">
<!--<set name="viewScope.code" value="found" /> -->
</view-state>
<view-state id="noUserFound" view="confirmation.xhtml">
<!--<set name="viewScope.code" value="notFound" />-->
</view-state>
<end-state id="finish" />
<subflow-state id="error" subflow="error">
</subflow-state>
eventId is not same as state id - eventId is required for transitioning in a current state(action/view) of an ongoing flow and not for launching a new flow. The reason why you are landing on login could be that login-flow xml might be mapped to /login mapping and view-state with id login is the start state(by default first state in flow definition file will be start state) of login flow. You need to have a decision state as on start state to redirect to a state based on some conditional parameter whether you need to go to login state or confirmation state.
EDIT:
For example, if code parameter is present only when request is from email, then your flow definition could be like:
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<input name="code" type="java.lang.String">
<decision-state id="checkFlowStart">
<if test="code == null" then="login" else="confirmation"/>
</decision-state>
<view-state id="login" view="login.xhtml">
<transition on="entry" to="connect"/>
<transition on="recoveryPass" to="recovery" />
</view-state>
....
Have you considered make a particular state (implementation in that state) as a different flow. When using in the main flow it can be used as sub-flow and whereas it gives you a freedom to be launched as a separate flow as well.
The parameter from the main flow can be passed as inputs to the sub-flow. Below is an example I am trying to explain myself
Main Flow
<view-state></view-state>
<action-state></action-state>
<!-- This is the re-usable flow which can be used within this flow and can be
launched independently -->
<sub-flow>
<input/>
</sub-flow>
Another spring flow - can be plugged in from another flow or used independently
<input/>
<action-state></action-state>
<end-state></end-state>

Invalid _eventId in spring webflow

Is there any way to handle unexisting eventIds or absence of eventId parameter in spring webflow?
e.g. for this webflow
<flow xmlns="http://www.springframework.org/schema/webflow" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<view-state id="welcome">
<transition on="goForward" to="nextStep"/>
</view-state>
<view-state id="nextStep">
<transition on="endFlow" to="finishStep" />
</view-state>
<end-state id="finishStep"/>
<global-transitions>
<transition on="cancel" to="finishStep"/>
</global-transitions>
</flow>
How to handle requests with param like _eventId=unexistingAction or requests with no _eventId param ?
This will normally produce a page with stack trace...
no transition found on occurence of event in state of flow...
This is how you can handle transitions that don't exist:
<global-transitions>
<transition on-exception="org.springframework.webflow.engine.NoMatchingTransitionException" to="handlingViewState">
<evaluate expression="handlingBean.handle(flowExecutionException)"></evaluate>
</transition>
</global-transitions>

Display Spring WebFlow Global Transitions

I am a new at using Spring Webflow. I am trying to use Global transitions. In my jsp I am trying to display all the transitions using
<c:forEach var="transition" items="${flowRequestContext.currentState.transitions}">
<c:out value="${transition.id}"/>
</c:forEach>
I can see all the transitions for the currentState but I cannot see the global transitions.
My understanding was the global transitions would be available in all the view-states. To get the global transitions do I need to access it in a different way?
Here is a simplified version of my flow xml
<flow xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/webflow"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<view-state id="state1" view="view1.jsp" model="person">
<transition on="submit" to="state2"/>
<transition on="cancel" to="canceled"/>
</view-state>
<view-state id="state2" view="view1.jsp" model="person">
<transition on="complete" to="complete"/>
</view-state>
<view-state id="canceled" view="view1.jsp" model="person">
<transition on="resubmit" to="resubmit"/>
</view-state>
<action-state id="reassign">
<evaluate expression="CustomAction.reassign(flowRequestContext)"/>
</action-state>
<end-state id="complete"/>
<global-transitions>
<transition on="cancel" to="canceled"/>
<transition on="reassign" to="reassign"/>
</global-transitions>
</flow>
So when in state1 in the jsp how canI display all the tranistions including global?
Thanks for any help in advance
Better late than never.
There is a getGlobalTransitionSet method, but it's in the implementation org.springframework.webflow.engine.Flow class, not in the interface FlowDefinition. This method returns a TransitionSet that is not iterable by forEach, but fortunatelly it has a toArray method.
If you feel comfortable with this, you could use:
${flowRequestContext.activeFlow.globalTransitionSet.toArray()}
Tested.

Restarting Spring Web Flow with new model values

I am trying to find a way that if the user restarts the flow the values are not in it. If you look at my flow below you can see that the user enters data, previews it and then saves it.. after the save the user can go back to enter new data into the input screen but with my current setup the screen shows the pre-data. how can I clear it on restart?
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<var name="customer" class="org.uftwf.domain.Customer"/>
<view-state id="helloworld" view="input.jsp" model="customer" popup="true">
<transition on="submit" to="preview" />
<transition on="cancel" to="thanks" validate="false"/>
</view-state>
<view-state id="preview" model="customer">
<transition on="cancel" to="helloworld"/>
<transition on="accept" to="save">
<evaluate expression="hellowWorldFlowActions.addCustomer(customer)"/>
</transition>
</view-state>
<view-state id="save" model="customer">
<transition on="accept" to="thanks"/>
</view-state>
<view-state id="thanks">
<transition on="restart" to="helloworld"/>
</view-state>
</flow>
One simple way is to define a reset() method on your Customer class and call that in whichever <view-state> <on-entry> (like "thanks") or <transition> ("restart") makes sense.
Have you tried:
<view-state id="helloworld" view="input.jsp" model="customer" popup="true">
<on-entry>
<set name="flowScope.costumer" value="new org.uftwf.domain.Customer()" />
</on-entry>
<transition on="submit" to="preview" />
<transition on="cancel" to="thanks" validate="false"/>
</view-state>
You can do an external redirect to do same flow. Here is an example.
<var name="customer" class="org.uftwf.domain.Customer"/>
<view-state id="thanks">
<transition on="restart" to="doRestart"/>
</view-state>
<end-state id="doRestart" view="externalRedirect:nameOfThisFlow"></end-state>

String var assigned to view-state?

I am not sure if this is possible in webflow 2.2.1
flow.xml
<view-state id="flowId1" model="flowModel1" view="/WEB-INF/templates/Flow_Form/form1.jsp">
<set attribute="strVar" value="${'someStringVar'}" />
<transition on="step1" to="step1Action" />
</view-state>
jsp
<h1>${strVar}</h1>
Essentially, I'd like to assign a String var that will change value accross individual view-states, that is set in the flow.xml..
Are there any other simple recommended approaches to this?
Thanks
got it.. :)
<view-state id="flowId1" model="flowModel1" view="/WEB-INF/templates/Flow_Form/form1.jsp">
<on-entry>
<evaluate expression="'someStringVar'" result="flowScope.strVar"/>
</on-entry>
<transition on="step1" to="step1Action" />
</view-state>

Resources