Results 1 to 6 of 6

Thread: Beans with Request Scope in AbstractXmlFlowExecutionTests

  1. #1

    Default Beans with Request Scope in AbstractXmlFlowExecutionTests

    Hello,

    I posted a question a few days ago here:
    http://forum.springframework.org/showthread.php?t=50400
    but didn't get any responses, so I'll try to communicate the issue a little more clearly (I realize my last post was confusing) and hopefully someone knows how to overcome my problem.

    I have a Spring bean in my web application that has a scope of request:

    Code:
    	<bean id="flowStateTracker"
    		class="com.fidelity.shares.web.controller.support.FlowStateTracker"
    		scope="request">
    		<aop:scoped-proxy />
    	</bean>
    This bean's values are being set/get in a FlowExecutionListenerAdapter during each request:

    Code:
    public class FlowStateListener extends FlowExecutionListenerAdapter {
    
    	private FlowStateTracker flowStateTracker;
    
    ...
    
    	public void requestSubmitted(RequestContext context) {
    		try {
    			if (context.getActiveFlow() != null) {
    				flowStateTracker.setCurrentFlow(context.getActiveFlow().getId());
    				flowStateTracker.setCurrentState(context.getCurrentState()
    						.getId());
    			}
    		}
    		catch (java.lang.IllegalStateException iies) {
    			// do nothing - we are not currently in a flowStateTracker so can't call the validate method anyway
    			log
    					.warn(
    							"Could not retrieve current flowStateTracker.  This is probably okay, hence the warning message.",
    							iies);
    		}
    	}
    The FlowStateTracker is then injected into my Flow's Validator for use in commons-validator integration.

    Everything works great when I load the app up in Tomcat, but I get the following error in my AbstractXmlFlowExecutionTests:

    org.springframework.webflow.engine.ActionExecution Exception: Exception thrown executing [AnnotatedAction@1fe2493 targetAction = com.fidelity.shares.web.controller.ServiceProvider ElectionFormAction@d2883b, attributes = map['method' -> 'bindAndValidate']] in state 'election' of flow 'serviceProviderElection-flow' -- action execution attributes were 'map['method' -> 'bindAndValidate']'; nested exception is java.lang.IllegalStateException: No Scope registered for scope 'request'
    at org.springframework.webflow.engine.ActionExecutor. execute(ActionExecutor.java:64)
    at org.springframework.webflow.engine.support.ActionT ransitionCriteria.test(ActionTransitionCriteria.ja va:84)
    at org.springframework.webflow.engine.support.Transit ionCriteriaChain.test(TransitionCriteriaChain.java :71)
    at org.springframework.webflow.engine.Transition.canE xecute(Transition.java:182)
    at org.springframework.webflow.engine.Transition.exec ute(Transition.java:195)
    at org.springframework.webflow.engine.TransitionableS tate.onEvent(TransitionableState.java:107)
    at org.springframework.webflow.engine.Flow.onEvent(Fl ow.java:534)
    at org.springframework.webflow.engine.impl.RequestCon trolContextImpl.signalEvent(RequestControlContextI mpl.java:205)
    at org.springframework.webflow.engine.impl.FlowExecut ionImpl.signalEvent(FlowExecutionImpl.java:202)
    at org.springframework.webflow.test.execution.Abstrac tFlowExecutionTests.signalEvent(AbstractFlowExecut ionTests.java:243)
    at org.springframework.webflow.test.execution.Abstrac tFlowExecutionTests.signalEvent(AbstractFlowExecut ionTests.java:195)
    at com.fidelity.shares.web.controller.ServiceProvider ElectionFlowIntegrationTest.testElectionOnNextShow Summary(ServiceProviderElectionFlowIntegrationTest .java:99)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Nativ e Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Native MethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(De legatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:585)
    at org.springframework.test.context.junit4.SpringTest Method.invoke(SpringTestMethod.java:198)
    at org.springframework.test.context.junit4.SpringMeth odRoadie.runTestMethod(SpringMethodRoadie.java:274 )
    at org.springframework.test.context.junit4.SpringMeth odRoadie$2.run(SpringMethodRoadie.java:207)
    at org.springframework.test.context.junit4.SpringMeth odRoadie.runBeforesThenTestThenAfters(SpringMethod Roadie.java:254)
    at org.springframework.test.context.junit4.SpringMeth odRoadie.runWithRepetitions(SpringMethodRoadie.jav a:234)
    at org.springframework.test.context.junit4.SpringMeth odRoadie.runTest(SpringMethodRoadie.java:204)
    at org.springframework.test.context.junit4.SpringMeth odRoadie.run(SpringMethodRoadie.java:146)
    at org.springframework.test.context.junit4.SpringJUni t4ClassRunner.invokeTestMethod(SpringJUnit4ClassRu nner.java:151)
    at org.junit.internal.runners.JUnit4ClassRunner.runMe thods(JUnit4ClassRunner.java:51)
    at org.junit.internal.runners.JUnit4ClassRunner$1.run (JUnit4ClassRunner.java:44)
    at org.junit.internal.runners.ClassRoadie.runUnprotec ted(ClassRoadie.java:27)
    at org.junit.internal.runners.ClassRoadie.runProtecte d(ClassRoadie.java:37)
    at org.junit.internal.runners.JUnit4ClassRunner.run(J Unit4ClassRunner.java:42)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestR eference.run(JUnit4TestReference.java:38)
    at org.eclipse.jdt.internal.junit.runner.TestExecutio n.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRu nner.runTests(RemoteTestRunner.java:460)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRu nner.runTests(RemoteTestRunner.java:673)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRu nner.run(RemoteTestRunner.java:386)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRu nner.main(RemoteTestRunner.java:196)
    Caused by: java.lang.IllegalStateException: No Scope registered for scope 'request'
    at org.springframework.beans.factory.support.Abstract BeanFactory.getBean(AbstractBeanFactory.java:282)
    at org.springframework.beans.factory.support.Abstract BeanFactory.getBean(AbstractBeanFactory.java:170)
    at org.springframework.aop.target.SimpleBeanTargetSou rce.getTarget(SimpleBeanTargetSource.java:33)
    at org.springframework.aop.framework.Cglib2AopProxy$D ynamicAdvisedInterceptor.getTarget(Cglib2AopProxy. java:662)
    at org.springframework.aop.framework.Cglib2AopProxy$D ynamicAdvisedInterceptor.intercept(Cglib2AopProxy. java:612)
    at com.fidelity.shares.web.controller.support.FlowSta teTracker$$EnhancerByCGLIB$$75beca47.getCurrentFlo wAndState(<generated>)
    at com.fidelity.shares.web.form.ElectionFormValidator .validate(ElectionFormValidator.java:45)
    at org.springframework.webflow.action.FormAction.doVa lidate(FormAction.java:881)
    at org.springframework.webflow.action.FormAction.bind AndValidate(FormAction.java:513)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Nativ e Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Native MethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(De legatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:585)
    at org.springframework.webflow.util.DispatchMethodInv oker.invoke(DispatchMethodInvoker.java:99)
    at org.springframework.webflow.action.MultiAction.doE xecute(MultiAction.java:133)
    at org.springframework.webflow.action.AbstractAction. execute(AbstractAction.java:192)
    at org.springframework.webflow.engine.AnnotatedAction .execute(AnnotatedAction.java:146)
    at org.springframework.webflow.engine.ActionExecutor. execute(ActionExecutor.java:59)
    ... 34 more
    I think I know the root of the problem. In my AbstractXmlFlowExecutionTests test case, I override the following:

    Code:
    	@Override
    	protected FlowServiceLocator createFlowServiceLocator() {
    		ApplicationContext context = new ClassPathXmlApplicationContext(
    				new String[] {"classpath:/spring/allTestContexts.xml"});
    
    		FlowDefinitionRegistry registry = new FlowDefinitionRegistryImpl();
    
    		DefaultFlowServiceLocator locator = new DefaultFlowServiceLocator(
    				registry, context);
    
    		XmlFlowRegistrar registrar = new XmlFlowRegistrar(locator);
    
    		if (this.getSubFlowNames() != null) {
    			for (int i = 0; i < this.getSubFlowNames().length; i++) {
    				registrar.addResource(createFlowDefinitionResource(
    						this.flowDir, this.getSubFlowNames()[i]));
    			}
    		}
    		registrar.registerFlowDefinitions(registry);
    
    		return locator;
    	}
    I do this in order to run integration tests vs unit tests. I think the issue is around the following piece of code:

    Code:
    ApplicationContext context = new ClassPathXmlApplicationContext(
    				new String[] {"classpath:/spring/allTestContexts.xml"});
    The ClassPathXmlApplicationContext is not "web-aware" and thus does not know what "Request" or "Session" scope is. It looks like I might need to instead instantiate a XmlWebApplicationContext instead, but that seems to complain about needing a ServletContext.

    My question is: how can I test my request-scope dependent validator? Is there another way I can load my contexts so that they are web aware?

    Thanks for your time and help!
    Leo

  2. #2
    Join Date
    Sep 2004
    Location
    Leuven, Belgium
    Posts
    1,853

    Default

    I saw your earlier post but haven't been able to investigate yet -- it's still on my TODO list .

    But indeed, SWF 1.x sees the RequestContext as an internal artifact and doesn't expose it to views.

    Erwin

  3. #3

    Default

    Thanks for your reply, Erwin.

    I also noticed this post:

    http://forum.springframework.org/sho...452#post168452

    It seems related, but so far hasn't completely solved my problem.

    I look forward to your input and appreciate that you're probably a VERY BUSY guy! Your assistance over the past year has been outstanding.

    Thanks,
    Leo

  4. #4

    Default

    I should also mention...if this is something that can be easily bypassed by upgrading to SWF 2.0 (beta or not), then we can certainly do that. Our product is still in the development phase and will not be released until next year.

  5. #5
    Join Date
    Mar 2008
    Posts
    3

    Default

    see the solution I posted here http://forum.springframework.org/sho...564#post168564

    Marnix

  6. #6

    Default

    Thanks Marnix. That got me a little farther. I'm now receiving an error message:
    org.springframework.webflow.engine.ActionExecution Exception: Exception thrown executing [AnnotatedAction@70856b targetAction = com.fidelity.shares.web.controller.ServiceProvider ElectionFormAction@533246, attributes = map['method' -> 'bindAndValidate']] in state 'election' of flow 'serviceProviderElection-flow' -- action execution attributes were 'map['method' -> 'bindAndValidate']'; nested exception is com.fmr.commons.cs203.validator.struts:E_CS203NoFo rmFoundException - The validation rules in the application cannot find a validation form called null.null
    at org.springframework.webflow.engine.ActionExecutor. execute(ActionExecutor.java:64)
    at
    ...
    so I think at this point I better explain what I'm trying to do.

    I'm trying to integrate Commons Validator into SWF without requiring our developers to explicitly inject and call the Spring Modules validator themselves. Developers should only need to add forms in their custom-forms.xml using a naming standard of <flow-id>.<flow-state-id> in order to validate using commons validator:

    Code:
    <form-validation>
    	<formset>
    		<form name="serviceProviderElection-flow.election">
    		...
    		</form>
    	</formset>
    </form-validation>
    to validate my next transition:

    Code:
    	<view-state id="election"
    		view="content/serviceProviderElection/election">
    		<render-actions>
    			<action bean="serviceProviderElectionFormAction"
    				method="setupForm" />
    		</render-actions>
    
    		<transition on="next" to="summary">
    			<action bean="serviceProviderElectionFormAction"
    				method="bindAndValidate">
    			</action>
    		</transition>
    
    		<transition on="previous" to="landing">
    			<action bean="serviceProviderElectionFormAction"
    				method="bind" />
    		</transition>
    	</view-state>
    in a flow named "serviceProviderElection-flow".


    In order to do this, I've created a base validator, with the following implementation:

    Code:
    	@SuppressWarnings("unchecked")
    	public void validate(Object target, Errors errors) {
    		// Set the name of the form we wish to validate the bean against and validate it
    		springModulesValidator.setFormName(flowStateTracker
    				.getCurrentFlowAndState());
    		springModulesValidator.validate(target, errors);
    	}
    FlowStateTracker is an object I created in order to store the current flow's id and state id:

    Code:
    public class FlowStateTracker {
    
    	private String currentState;
    	private String currentFlow;
    
    	public String getCurrentState() {
    		return currentState;
    	}
    
    	public void setCurrentState(String currentState) {
    		this.currentState = currentState;
    	}
    
    	public String getCurrentFlow() {
    		return this.currentFlow;
    	}
    
    	public void setCurrentFlow(String currentFlow) {
    		this.currentFlow = currentFlow;
    	}
    	
    	public String getCurrentFlowAndState(){
    		return this.currentFlow + "." + this.currentState;
    	}
    
    }
    The FlowStateTracker gets populated by a FlowExecutionListener I've created:

    Code:
    public class FlowStateListener extends FlowExecutionListenerAdapter {
    
    	private FlowStateTracker flowStateTracker;
    	private Log log = LogFactory.getLog(getClass());
    
    	public FlowStateTracker getFlowStateTracker() {
    		return flowStateTracker;
    	}
    
    	public void setFlowStateTracker(FlowStateTracker flowStateTracker) {
    		this.flowStateTracker = flowStateTracker;
    	}
    
    	public void requestSubmitted(RequestContext context) {
    		try {
    			if (context.getActiveFlow() != null) {
    				flowStateTracker.setCurrentFlow(context.getActiveFlow().getId());
    				flowStateTracker.setCurrentState(context.getCurrentState()
    						.getId());
    			}
    		}
    		catch (java.lang.IllegalStateException iies) {
    			// do nothing - we are not currently in a flowStateTracker so can't call the validate method anyway
    			log
    					.warn(
    							"Could not retrieve current flowStateTracker.  This is probably okay, hence the warning message.",
    							iies);
    		}
    	}
    
    }
    So the idea is that for each request, if it is in a state, the listener populates the FlowStateTracker with the current flow and state id so that it is available for use in my base validator.

    The FlowStateListener is wired into my flow executor. My FlowStateTracker is wired into my FlowStateListener and into my base validator. My SpringModulesValidator is wired into my base validator.

    Both the FlowStateTracker and SpringModulesValidator are set to request scope in order to ensure that when the tracker is set in the listener, the values are not changed before being used in the base validator.

    Code:
    	<!-- Listener that is used to set the value of the current state -->
    	<bean id="flowStateListener"
    		class="com.fidelity.shares.web.controller.support.FlowStateListener">
    		<!-- a reference to the proxied 'stateTracker' bean -->
    		<property name="flowStateTracker" ref="flowStateTracker" />
    	</bean>
    
    	<!-- A HTTP request-scoped bean exposed as a proxy. 
    		This request scoped object is used to store the current state of the flow -->
    	<bean id="flowStateTracker"
    		class="com.fidelity.shares.web.controller.support.FlowStateTracker"
    		scope="request">
    		<aop:scoped-proxy />
    	</bean>
    
    	<bean id="springModulesValidator"
    		class="org.springmodules.validation.commons.ConfigurableBeanValidator"
    		scope="request">
    		<property name="validatorFactory" ref="validatorFactory" />
    		<aop:scoped-proxy />
    	</bean>
    
    	<!-- Validator factory used by spring modules validator -->
    	<bean id="validatorFactory"
    		class="org.springmodules.validation.commons.DefaultValidatorFactory">
    		<property name="validationConfigLocations">
    			<list>
    				<value>classpath:cs203/custom-forms.xml</value>
    				<value>
    					classpath:com/fmr/commons/cs203/rules/spring/validator-rules.xml
    				</value>
    			</list>
    		</property>
    	</bean>
    
    	<!-- Launches new flow executions and resumes existing executions -->
    	<flow:executor id="flowExecutor" registry-ref="flowRegistry">
    		<flow:execution-listeners>
    			<flow:listener ref="flowStateListener" />
    		</flow:execution-listeners>
    	</flow:executor>
    
    	<bean abstract="true" id="electionFormValidator"
    		class="com.fidelity.shares.web.form.ElectionFormValidator">
    		<property name="flowStateTracker" ref="flowStateTracker" />
    		<property name="springModulesValidator" ref="springModulesValidator"/>
    	</bean>
    All of this works just groovily when I launch the application in Tomcat, but fails when I run my integration tests. Here are the overridden methods in my tests:

    Code:
    	@Autowired
    	protected FlowExecutionListener flowStateListener;
    
    	@Override
    	protected FlowServiceLocator createFlowServiceLocator() {
    		ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
    				new String[] {"classpath:/spring/allTestContexts.xml"});
    		
    		context.getBeanFactory().registerScope("request", new MockRequestScope());
    		
    		this.setFlowExecutionListener(this.flowStateListener);
    				
    		FlowDefinitionRegistry registry = new FlowDefinitionRegistryImpl();
    
    		DefaultFlowServiceLocator locator = new DefaultFlowServiceLocator(
    				registry, context);
    
    		XmlFlowRegistrar registrar = new XmlFlowRegistrar(locator);
    
    		if (this.getSubFlowNames() != null) {
    			for (int i = 0; i < this.getSubFlowNames().length; i++) {
    				registrar.addResource(createFlowDefinitionResource(
    						this.flowDir, this.getSubFlowNames()[i]));
    			}
    		}
    		registrar.registerFlowDefinitions(registry);
    
    		return locator;
    	}
    And here's the MockRequestScope object I created:

    Code:
    public class MockRequestScope implements Scope {
    
    	Map<String, Object> map;
    
    	
    	public MockRequestScope() {
    		super();
    		
    		map = new HashMap<String, Object>();
    	}
    
    	/**
    	 * @see org.springframework.beans.factory.config.Scope#get(java.lang.String,
    	 *      org.springframework.beans.factory.ObjectFactory)
    	 */
    	public Object get(String name, ObjectFactory objectFactory) {
    		Object scopedObject = map.get(name);
    		
    		if (scopedObject == null) {
    			scopedObject = objectFactory.getObject();
    			map.put(name, scopedObject);
    		}
    		
    		return scopedObject;
    	}
    
    	/**
    	 * @see org.springframework.beans.factory.config.Scope#getConversationId()
    	 */
    	public String getConversationId() {
    		return null;
    	}
    
    	/**
    	 * @see org.springframework.beans.factory.config.Scope#registerDestructionCallback(java.lang.String,
    	 *      java.lang.Runnable)
    	 */
    	public void registerDestructionCallback(String name, Runnable callback) {
    		//Nothing yet
    	}
    
    	/**
    	 * @see org.springframework.beans.factory.config.Scope#remove(java.lang.String)
    	 */
    	public Object remove(String name) {		
    		Object scopedObject = map.get(name);
    		if (scopedObject != null) {
    			map.remove(name);
    			return scopedObject;
    		}
    		return null;
    	}
    
    }
    Now I would think that I wouldn't need to explicitly call this.setFlowExecutionListener(this.flowStateListen er) as I'm including all the beans mentioned above in "classpath:/spring/allTestContexts.xml", but I've done so per your suggestions. The FlowExecutionListener is now getting triggered, however, it seems I am losing the value of FlowStateTracker once it gets to the validator.

    I'm open to approaching this problem another way. I really just care about getting commons validator integrated in a way that's unobtrusive to the app developers.

    Thanks!
    Leo

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •