Spring Web Flow - Integrate Javascript confirm for a decision - java

I would like to integrate a javascript dialog into the start of a web flow where the option selected determines if an existing object is added to the flow or a new one is.
<on-entry>
<evaluate expression="appService.checkMembershipStatus(memberId)"/>
// this will check if the state is 'RENEW' and return boolean
// If returns true, then show javascript dialog to say "Renew existing?".
//If they select 'Yes', the existing membership is loaded into the flowScope.
//If they select 'No', then a new membership (object) is loaded into the flowscope
// else
// A new memebership (object) is loaded into the flowscope
</on-entry>
<view-state id="begin">
// continue as normal
</view-state>
Thanks

You can achieve this using <decision-state>. A sample flow is as below
<view-state id="screen1">
<transition to="checkMembershipStatus" />
</view-state>
<decision-state id="checkMembershipStatus">
<if test="appService.checkMembershipStatus(memberId)"
then="renewMembership"
else="loadNewMember" />
</decision-state>
<!--In this page show a javascript dialog (or custom JSP page) on load to get the answer [YES/NO] -->
<view-state id="renewMembership">
<transition on="Yes" to="loadExistingMember" />
<transition on="No" to="loadNewMember" />
</view-state>
<action-state id="loadExistingMember">
<evaluate expression="loadExistingMember()" result="member" />
<transition to="begin" />
</action-state>
<action-state id="loadNewMember">
<evaluate expression="loadnewMember()" result="member" />
<transition to="begin" />
</action-state>
<view-state id="begin">
// continue as normal
</view-state>

Related

spring webflow: check attribute value and execute action

I've two flows. From the first flow I'm passing a value to the second flow. I want to validate the value and if null then call an action. I tried with:
<input name="houseNumber" />
<evaluate expression = "houseNumber != null ? createHouse : deleteHouse" />
Where createHouse is an action. But it's not calling. Also tried like the below code. Can anyone tell me what's wrong with the below code?
<input name="houseId" type="int" />
<on-start>
<evaluate expression="houseId!= null" />
<transition on="yes" to="loadHouse" />
<transition on="no" to="emptyHouse" />
</on-start>
How can I call an action based on the input value
I found a way and thought it would be helpful for beginners if I share it here.
Flow 1
<subflow-state id="home" subflow="flow2">
<input name="homeId" value="requestParameters.homeId"/>
</subflow-state>
Flow 2
Create a decision-state and call it from an action state. You can call the action-state using 'start-state'
<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"
start-state="start">
<input name="homeId" type="int" />
<action-state id="start" >
<evaluate expression="controller.doSomething()"
<transition on="success" to="makeDecision" />
</action-state>
<decision-state id="makeDecision">
<if test="homeId== null" then="loadHome" else="emptyHome" />
</decision-state>
<action-state id="loadHome" >
<evaluate expression="controller.method1()" />
</action-state>
<action-state id="emptyHome" >
<evaluate expression="controller.method2()" />
</action-state>

Hibernate session available for queries but lazyInit not working in flow

I am using hibernate 4.3 and SWF 2.4 and encountered a strange behavior in a particular case which leads to a LazyInitializedException.
Here is the configuration of the flow :
<persistence-context />
<on-start>
<!-- attach enterprise to session if not provided -->
<evaluate expression="enterpriseInput ?: enterpriseDaoImpl.merge(externalContext.sessionMap.workingEnterprise)" result="flowScope.enterprise"/>
</on-start>
<view-state id="start" view="filiation/list">
<transition to="showBoardOrGoToList" on="save" />
</view-state>
<action-state id="showBoardOrGoToList">
<evaluate expression="flowController.displayBoard(event)" />
<transition on="yes" to="boardSubflow"/>
<transition on="no" to="something" />
</action-state>
<subflow-state id="boardSubflow" subflow="board">
<on-entry>
<evaluate expression="boardFlowController.initAdmins(enterprise)" result="flowScope.admins" />
</on-entry>
<input name="admins" value="flowScope.admins"/>
<output name="enterprise" value="flowScope.enterprise"/>
<transition on="save" to="end">
<evaluate expression="enterpriseDaoImpl.merge(enterprise)" result="flowScope.enterprise"/>
</transition>
<transition on="cancel" to="cancel"/>
</subflow-state>
...
When the flow reaches the following line :
<evaluate expression="boardFlowController.initAdmins(enterprise)" result="flowScope.admins" />
It will trigger a LazyInitializedException, the method initAdmins() basically does a Hibernate.initialize() on the elements of a list.
for (Administrator administrator : enterprise.getAdministrators()) {
Hibernate.initialize(administrator);
}
If just before that line a method on a DAO is called it will perform the DB query without issue, so this means that the hibernate session is available at this point but the lazyInit is not working.
BUT, if i call initAdmins() in the <on-start> tag of the flow, it works...
THe flow config that works :
<persistence-context />
<on-start>
<!-- attach enterprise to session if not provided -->
<evaluate expression="enterpriseInput ?: enterpriseDaoImpl.merge(externalContext.sessionMap.workingEnterprise)" result="flowScope.enterprise"/>
<evaluate expression="boardFlowController.initAdmins(enterprise)" result="flowScope.admins" />
</on-start>
<view-state id="start" view="filiation/list">
<transition to="showBoardOrGoToList" on="save" />
</view-state>
<action-state id="showBoardOrGoToList">
<evaluate expression="flowController.displayBoard(event)" />
<transition on="yes" to="boardSubflow"/>
<transition on="no" to="something" />
</action-state>
<subflow-state id="boardSubflow" subflow="board">
<input name="admins" value="flowScope.admins"/>
<output name="enterprise" value="flowScope.enterprise"/>
<transition on="save" to="end">
<evaluate expression="enterpriseDaoImpl.merge(enterprise)" result="flowScope.enterprise"/>
</transition>
<transition on="cancel" to="cancel"/>
</subflow-state>
...
The problem is that i want to initialize the admins only when required as this is a costly operation.
There is very little information on the mechanism of the persistence-context so i couldn't find an explanation on this.
For me it really looks like a bug.
Can someone highlight me or explain why the lazyInit is broken outside of the on-start tag?
Thx
Not sure what is causing the issue. I've experienced this before and I agree I think it is a bug as well however I don't think it's worth the effort to troubleshoot because we'd have to step into the WebFlow code (done this a few times... not fun takes hours) and I've always found a simple and unintrusive work around (5 minutes or less).
Try to initialize the admins in the following transition
<transition on="yes" to="boardSubflow"/>
see if it works now. (would be the same effect as on-start inside the subflow in terms of your goals of efficiency).
<transition on="yes" to="boardSubflow">
<evaluate expression="boardFlowController.initAdmins(enterprise)" result="flowScope.admins" />
</transition>
If that doesn't work try to create an action-state who's sole purpose is to init admins. Place this action-state in the middle the transition -> subflow call. Make sure to evaluate the expression in either the on-start or on-exit of this action-state.
<action-state id="initAdminsActionState">
<transition to="boardSubflow"/>
<on-exit>
<evaluate expression="boardFlowController.initAdmins(enterprise)" result="flowScope.admins" />
</on-exit>
</action-state>

Webflow binding failure

I'm trying to add a Forgot Password path to an existing view. I created a new view, action, model bean, and some states in my webflow. Instead of seeing the view, I keep getting the error java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'forgotPassword' available as request attribute. I know the bean exists, and it should be visible. I think I set up the webflow properly, but I'm not 100% sure. Does anybody know what I might be doing wrong?
casLoginView.jsp:
Forgot Password
login-webflow.xml:
<var name="credentials" class="org.jasig.cas.authentication.principal.UsernamePasswordCredentials" />
<var name="forgotPasswordBean" class="com.mycompany.authentication.ForgotPasswordBean" />
<view-state id="viewLoginForm" view="casLoginView" model="credentials">
<binder>
<binding property="username" />
<binding property="password" />
</binder>
<on-entry>
<set name="viewScope.commandName" value="'credentials'" />
</on-entry>
<transition on="submit" bind="true" validate="true" to="realSubmit">
<evaluate expression="authenticationViaFormAction.doBind(flowRequestContext, flowScope.credentials)" />
</transition>
<transition on="forgotPassword" bind="false" validate="false" to="forgotPasswordView"/>
</view-state>
<view-state id="forgotPasswordView" view="myForgotPasswordView.jsp" model="forgotPasswordBean">
<binder>
<binding property="username" required="true"/>
</binder>
<transition on="submit" to="forgotPassword"/>
</view-state>
<action-state id="forgotPassword">
<evaluate expression="forgotPasswordAction.submit(flowScope.forgotPasswordBean)" />
<transition on="success" to="newPasswordSentView"/>
<transition on="forbidden" to="forgotPasswordForbiddenView"/>
<transition on="error" to="forgotPasswordView"/>
</action-state>
<end-state id="newPasswordSentView" view="myNewPasswordSentView" />
<end-state id="forgotPasswordForbiddenView" view="forgotPasswordForbiddenView" />
Your <form:form ... > tag should reference the correct bean. Your configuration mentions a forgotPasswordBean and not a forgotPassword one.
Either your form object should reference the correct bean
<form:form modelAttribute="forgotPasswordBean" ... >
Or you should rename the bean in your webflow configuration (including all the references to it).
<var name="forgotPassword" class="com.mycompany.authentication.ForgotPasswordBean" />

Dynamic Transitions in Spring WebFlow

Does anybody know if it's possible to define dynamic transitions in a Spring Web Flow definition?
Example 1 - using a properties file:
<action-state id="createSubscription" >
<evaluate expression="myvar" />
<transition on="$[test.result.valid]" to="subscribeUser-successResponse" />
<transition to="subscribeUser-exceptionResponse" />
</action-state>
Example 2 - using the value of the variable itself:
<action-state id="createSubscription" >
<evaluate expression="myvar" />
<transition to="$[myvar]" />
</action-state>
It's not mandatory, but could help to design more generic flows.
Thanks in advance to everyone.
You can do definitely do for transition "to".
Suppose flow xml has some action and view state as:
<action-state id="createSubscription">
<evaluate expression="myAction.prepareNextState(flowScope.formBean)"/>
<transition to="${flowScope.formBean.displayNextState}">
</action-state>
<view-state id="someView" view="someView" model="formBean">
...
</view-state>
and myAction class with prepareNextState method is as:
public class MyAction implements Serializable{
....
public void prepareNextState(FormBean formBean){
//displayNextState is a String field in FormBean
formBean.setDisplayNextState("someView");
}
....
}
This way we can define generic transitions for transition "to".

Invalidating or discarding history is not working after state validation in Spring Web Flow 2

I've configured the backtracking in my flow in the following way:
<view-state id="step1" model="step1Model" >
<transition on="next" to="step2" history="invalidate">
</transition>
</view-state>
<view-state id="step2" model="step2Model" >
<transition on="next" to="xxxx" history="invalidate">
</transition>
</view-state>
Also, I'm using the programatic validation implementing a model validate method in the same way you can read in the documentation (link).
The backtracking configuration is working properly when the model validation doesn't have any errors, in this case if you press the browser back button the flow is reloaded how it's expected.
The problem is when the model has some errors the validation doesn't let you go to the next step, in this case if you press the browser back button you can see a browser error page "Document Expired. This document is no longer available".
Any idea about this?
Thanks, Gerardo.
Problem is that you invalidate history on each step.
It would be better if you use end-state to clean history.
<view-state id="step1" model="step1Model" >
<transition on="next" to="step2">
</transition>
</view-state>
<view-state id="step2" model="step2Model" >
<transition on="next" to="end">
</transition>
</view-state>
<end-state id="end" />

Categories