spring webflow: check attribute value and execute action - java

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>

Related

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>

Spring Web Flow - Integrate Javascript confirm for a decision

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>

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

webflow2 is not finding my java code (controller)

I am trying to work a webflow project but I keep getting the following error that it cant find my controller?
Here is my flow code:
<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="member" class="org.uftwf.enrollment.domain.Member" />
<decision-state id="checkIsInPending">
<if test="FlowActions.isInPending()" then="endStateMemeberPending" else="name" />
</decision-state>
<view-state id="name" view="enrollment1.jsp">
<transition on="submit" to="SSNonFile" />
<transition on="cancel" to="endState" bind="false" />
</view-state>
<action-state id="SSNOnFile">
<evaluate expression="FlowActions.isSSNOnFile(member)" />
<transition on="SUCCESS" to="endStateNoSSN" />
<transition on="FAIL" to="isMemeber" />
</action-state>
<action-state id="isMemeber">
<evaluate expression="FlowActions.isMemeber(member)" />
<transition on="SUCCESS" to="endStateMemeberExists" />
<transition on="FAIL" to="isDeceased" />
</action-state>
<action-state id="isDeceased">
<evaluate expression="FlowActions.isDeceased(member)" />
<transition on="SUCCESS" to="endStateMemeberExists" />
<transition on="FAIL" to="address" />
</action-state>
<view-state id="address" view="enrollment2.jsp">
<transition on="submit" to="endStateSuccess" />
<transition on="cancel" to="name" bind="false" />
</view-state>
<view-state id="preview" model="customer">
<transition on="cancel" to="name" />
<transition on="accept" to="endStateSuccess">
<evaluate expression="FlowActions.addCustomer(member)" />
</transition>
</view-state>
<!-- End state -->
<end-state id="endStateMemberDeceased" view="unsuccessful.jsp" />
<end-state id="endStateNoSSN" view="noSSN.jsp" />
<end-state id="endStateMemeberExists" view="exists.jsp" />
<end-state id="endStateMemeberPending" view="pending.jsp" />
<end-state id="endStateSuccess" view="success.jsp" />
</flow>
Here is my controller code:
package org.uftwf.enrollment.controller.swf;
import static org.apache.log4j.Logger.getLogger;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.uftwf.enrollment.domain.Member;
import org.uftwf.enrollment.service.TestService;
#Component
public class FlowActions {
#Autowired
private TestService testService;
private static final Logger LOGGER = getLogger(FlowActions.class);
public void addCustomer(Member customer) {
// testService.saveCustomer(customer);
}
public boolean isInPending()
{
LOGGER.debug("in side isInPending()");
return false;
}
public void isSSNOnFile(Member customer)
{
}
public void isMemeber(Member customer)
{
}
public void isDeceased(Member customer)
{
}
}
Crazy error. my project cant find controller with webflow..
SEVERE: Servlet.service() for servlet [spring] in context with path [/enrollment] threw exception [Request processing failed; nested exception is org.springframework.webflow.execution.FlowExecutionException: Exception thrown in state 'checkIsInPending' of flow 'enroll'] with root cause
org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Field or property 'FlowActions' cannot be found on object of type 'org.springframework.webflow.engine.impl.RequestControlContextImpl'
Here is my root-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<import resource="mvc.xml" />
<import resource="flow.xml" />
<import resource="domain.xml" />
<context:component-scan base-package="org.uftwf.enrollment.controller" />
</beans>
You should be referencing the bean flowActions, not the class FlowActions. Try changing the case of the initial 'f'.
E.g.
<if test="flowActions.isInPending()" then="endStateMemeberPending" else="name" />

Categories