Announcement Announcement Module
Collapse
No announcement yet.
SWF2+MVC: can't get bound attribs to stick; model re-instantiating? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • SWF2+MVC: can't get bound attribs to stick; model re-instantiating?

    I'm trying (using JVM 1.5, Spring 2.5.6, MVC 2.5.6, Webflow 2.0.7) to build a rather simple flow that accepts search criteria, queries a datasource, and forks based on the number of results, finally producing a single chosen result (see attached diagram).

    I can get the initial view to bind to the singleton model, transit to the query action, run the query and fork based on the result count. The query results state is stored back into the model, but on subsequent views the model appears to be reinitialized, or even re-instantiated.

    I'm not smart enough to know why, and the reference documentation hasn't helped, nor have the varied but often contradictory examples available on-line. Any nudge in the right direction would be much appreciated!
    Russ

    Here's the servlet config:
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    	<!-- Maps request URL to controller -->
    	<bean id="defaultController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />
    
    	<bean id="urlMapping"
    		class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    		<property name="mappings">
    			<props>
    				<prop key="/">defaultController</prop>
    				<prop key="/studentsearch">studentSearchController</prop>
    			</props>
    		</property>
    	</bean>
    	
    	<!-- Maps logical view names to physical JSP views -->
    	<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    		<property name="viewClass">
    			<value>org.springframework.web.servlet.view.JstlView</value>
    		</property>
    		<property name="prefix" value="/WEB-INF/classes/jsp/" />
    		<property name="suffix" value=".jsp" />
    	</bean>
    
    	<!-- Maps request paths to webflows in the flowRegistry -->	
    	<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
    	    <property name="flowExecutor" ref="flowExecutor" />
    	</bean>
    
    	<webflow:flow-executor id="flowExecutor" />
    	
    	<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
    	    <property name="order" value="0"/>
    	    <property name="flowRegistry" ref="flowRegistry"/>
    	</bean>	
    
    	<webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices" >
    		<webflow:flow-location path="/WEB-INF/webflows/studentsearch-flow.xml" id="studentsearch" />
    		<webflow:flow-location-pattern value="/WEB-INF/webflows/*-flow.xml" />
    	</webflow:flow-registry>
    
    	<!-- Uses jspViewResolver in webflows -->	
    	<webflow:flow-builder-services id="flowBuilderServices" view-factory-creator="mvcViewFactoryCreator" development="true" />
    
    	<bean id="mvcViewFactoryCreator" class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator">
        	<property name="viewResolvers" ref="jspViewResolver"/>
        	<property name="useSpringBeanBinding" value="true" />
        	<property name="messageCodesResolver" ref="exceptionResolver" />
    	</bean>
    
    ->[lotsa resolvers]
    
    	<!-- Configuration for student search function -->
    	<bean id="studentSearchController" class="org.springframework.webflow.mvc.servlet.FlowController">
    		<property name="flowExecutor" ref="flowExecutor"/>
    	</bean>
    
    	<bean id="studentSearchModel" class="edu.berkeley.lsisys.advgsrvc.search.StudentSearchModel" init-method="init" />
    
    	<bean id="studentSearchValidator" class="edu.berkeley.lsisys.advgsrvc.search.StudentSearchValidator"/>
       
    </beans>
    Here's the model:
    Code:
    package edu.berkeley.lsisys.advgsrvc.search;
    ->[imports]
    public class StudentSearchModel implements Serializable {
    ->[lotsa stuff]
    
        public void init() {
            set.clear();
    ->[set strings to empty, floats to 0F]
        }
    
    ->[lotsa getters & setters]
    
        public boolean pickFromSet() {
            for (BFStudentDto student : set) {
                if (student.getStuId().equals(stuId)) {
    ->[set model state from matching data transfer object]
    	        return true;
                }
            }
            return false;
        }
    
        public boolean query() {
            set.clear();
            try {
                set.addAll((BFStudentQuery.getList(new String[]{stuId, nameGiven, nameMiddle, nameSur, email})));
                if (FormatUtils.isNonEmpty(stuId)) {
                    pickFromSet();
                }
                return true;
            } catch (Exception e) {
                LOG4J.error("query() threw exception: " + e.getMessage(), e);
                return false;
            }
        }
    }
    Here's the flow:
    Code:
    	<var name="studentSearchModel" class="edu.berkeley.lsisys.advgsrvc.search.StudentSearchModel" />
    
    	<view-state id="enterCriteria" view="search/stuEnterCriteria" model="studentSearchModel" >
    		<binder>
    			<binding property="email" />
    			<binding property="nameGiven" />
    			<binding property="nameMiddle" />
    			<binding property="nameSur" />
    			<binding property="stuId" />
    		</binder>
    		<transition on="continue" to="basedOnSearchResults" />
    	</view-state>
    
    	<action-state id="basedOnSearchResults"  >
    		<evaluate expression="studentSearchModel.query()" />
    		<evaluate expression="studentSearchModel.getSet().size().compareTo(1).toString()" />
    		<transition on="1" to="pickList" />
    		<transition on="0" to="confirm" />
    		<transition on="-1" to="enterData" />
    	</action-state>
    
    	<view-state id="pickList" view="search/stuPickList"  model="studentSearchModel" >
    		<binder>
    			<binding property="set" />
    			<binding property="stuId" />
    		</binder>
    		<transition on="startover" to="enterCriteria" />
    		<transition on="success" to="filterPicked" />
    	</view-state>
    	
    	<action-state id="filterPicked"  >
    		<evaluate expression="studentSearchModel.pickFromSet()" />
    		<transition to="confirm" />
    	</action-state>
    
    	<view-state id="enterData" view="search/stuEnterData"  model="studentSearchModel" >
    		<binder>
    			<binding property="email" />
    ->[lotsa bindings]
    			<binding property="ugAdmityear" />
    		</binder>
    		<transition on="startover" to="enterCriteria"/>
    		<transition on="continue" to="confirm"/>
    	</view-state>
    
    	<view-state id="confirm" view="search/stuConfirm"  model="studentSearchModel" >
    		<binder>
    			<binding property="acadStanding" />
    ->[lotsa bindings]
    			<binding property="phoneUpdate" />
    		</binder>
    		<transition on="startover" to="enterCriteria"/>
    		<transition on="backtolist" to="pickList"/>
    	</view-state>
    
    </flow>
    The enterCriteria view:
    Code:
    <f:form id="studentEnterCriteriaForm" commandName="studentSearchModel" method="post" >	
    	<h3>To schedule an advising appointment, please enter one of the following:</h3>
    	<s:hasBindErrors name="studentSearchModel">  
    		<div id="error">  
    			<c:forEach items="${errors.allErrors}" var="error">  
    				<s:message code="${error.code}" text="${error.defaultMessage}"/> <br /> 
    			</c:forEach>  
    		</div>  
    		<hr />
    	</s:hasBindErrors>
    	<label for="stuId" class="mainlabel">Student ID:</label>
    	<input type="text" id="stuId" name="stuId" size="8" maxlength="8"/>
    	<hr />
    	<fieldset id="name_fs">
    		<label for="name_fs" class="mainlabel">or Name:</label>
    		<input type="text" id="nameGiven" name="nameGiven" size="13" maxlength="25" /> 
    		<input type="text" id="nameMiddle" name="nameMiddle" size="12" maxlength="25" /> 
    		<input type="text" id="nameSur" name="nameSur" size="18" maxlength="25" />
    		<label for="name_fs" class="sublabel">(First, Middle, Last)</label>
    	</fieldset>
    	<hr />
    	<label for="email" class="mainlabel">or Email:</label>
    	<input type="text" id="email" name="email" size="50" maxlength="50" /> 
    	<hr />
    	<br />
    	<label for="continue" class="mainlabel">&nbsp;</label>
    	<button name="_eventId_continue" value="continue" type="submit" >&nbsp;Continue&nbsp;</button>
    	<input type="hidden" name="_flowId" value="studentsearch" />
    </f:form>
    And finally one of the results views (the pickList):
    Code:
    <f:form id="studentPickListForm" commandName="studentSearchModel" method="post" >	
    	<h3>These student match your criteria, please click on one to continue:</h3>
     	<s:hasBindErrors name="studentSearchModel">  
    		<font color="red">  
    			<c:forEach items="${errors.allErrors}" var="error">  
    				<s:message code="${error.code}" text="${error.defaultMessage}"/> <br /> 
    			</c:forEach>  
    		</font>  
    		<hr />
    	</s:hasBindErrors>
    	<c:forEach items="${set}" var="stu"> 
    		<button class="pickListButton" id="stu${stu.stuId}" name="stuId" value="${stu.stuId}" type="submit" ><c:out value="${stu.stuId}"/>&nbsp;&nbsp;<c:out value="${stu.nameFull}"/>&nbsp;&nbsp;<c:out value="${stu.educLevel}"/></button><br />
    	</c:forEach>
    	<hr />
    	<br />
    	<button name="_eventId_startover" value="startover" type="submit" >&nbsp;Start over&nbsp;</button>
    	<input type="hidden" name="_flowId" value="studentsearch" />
    </f:form>
    Last edited by areyouesses; Mar 3rd, 2010, 12:38 AM.

  • #2
    For starters your configuration you are configuring a FlowHandlerAdapter and a FlowController, use 1 of those not both.

    Your flow you are using 1 do-it-all object, it queries, it holds state, it contains logic not really OO.

    In your jsp you aren't binding anything you are using plain input tags where you should use form tags.

    Comment


    • #3
      Absolutely right, Marten... I got confused mapping the use of AbstractWizardControllers to this first foray into SWF and combined the model and controller.

      Shall I consider the flow itself the controller when using the FlowHandlerAdapter, but depend on MVC binding and view resolution when using the FlowController?

      (BTW, when using AbstractWizardControllers, plain input tags with attrib-matching names do indeed bind, not true under SWf?)

      Comment


      • #4
        Shall I consider the flow itself the controller when using the FlowHandlerAdapter, but depend on MVC binding and view resolution when using the FlowController?
        You shouldn't use the FlowController, view resolution and binding is handled by spring webflow, it delegates to the Spring MVC stuff but it is handled for you so you don't need the controller.

        (BTW, when using AbstractWizardControllers, plain input tags with attrib-matching names do indeed bind, not true under SWf?)
        It depends if you include the name of the form object it works, but why make it so hard, the form tags are there for a reason and they make it a lot easier.

        Comment

        Working...
        X