Announcement Announcement Module
Collapse
No announcement yet.
Action Stored in Scope - StatefulBeanInvokingAction Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Action Stored in Scope - StatefulBeanInvokingAction

    Have been going through the available kinds of actions. My requirement is to be able to invoke a method on a particular bean that i have stored in a particular scope in my action state. I have noticed that there is a StatefulBeanInvokingAction which talks of something similar. But from what i understand, it forces me to delegate the instance creation of the bean to the Spring Context. I would like to do something as follows:

    Code:
    <action-state id="xxxAction1">
        <action bean="xxxAction1Bean" 
            method="xxxMeth1()" 
            result-name="xxxAction2Bean"
            result-scope="flow" />
        ...
        ...
    </action-state>
    ...
    ...
    <action-state id="xxxAction2">
        <action bean="xxxAction2Bean" [or may be "${xxxAction2Bean}"]
            method="xxxMeth2()" />
        ...
        ...
    </action-state>
    Notice that in the above code the xxxAction2Bean was got as a result from executing action 1 and was stored in scope. More generally it could come from anywhere.

    Thx in advance.

  • #2
    Do i need to extend AbstractBeanInvokingAction and override the getBean() method? In that case, how do i hook it into the XML configuration?

    Comment


    • #3
      Yes this is a limitation now. To get this to work now you'd need to customize two things: BeanInvokingActionFactory and DefaultFlowServiceLocator. Steven Devijer has actually already done this for a project using aspects--I'll get him to post what he did.

      In the future we will support this.

      Keith

      Comment


      • #4
        Thx. That would be very helpful.

        Comment


        • #5
          The code below is a proof of concept more than anything else to invoke arbitrary methods on objects in the flow scope. The BeanInvokingActionFactoryAspect will check it a bean is available in the Spring BeanFactory with a given name and if not will assume the object comes from the flow scope.

          It depends on Spring 2.0 and will not work with older versions. I've used it with 2.0 RC1 and RC2 releases.

          I basically wrote two aspects, one to extend the functionality in BeanInvokingActionFactory and one to fix a problem in BaseFlowServiceLocator.

          The first aspect extends the functionality in BeanInvokingActionFactory:

          Code:
          package com.interface21.cert.web.swf.builder;
          
          import org.springframework.beans.factory.BeanFactory;
          import org.springframework.webflow.Action;
          import org.springframework.webflow.action.MethodResultSpecification;
          import org.springframework.webflow.action.ResultObjectBasedEventFactory;
          import org.springframework.binding.method.MethodSignature;
          import org.springframework.binding.convert.ConversionService;
          import org.aspectj.lang.ProceedingJoinPoint;
          import org.aspectj.lang.annotation.Aspect;
          import org.aspectj.lang.annotation.Around;
          import com.interface21.cert.web.swf.action.FlowRetrievingBeanInvokingAction;
          
          @Aspect
          public class BeanInvokingActionFactoryAspect {
          
              @Around(
                  value = "execution(* createBeanInvokingAction(..)) " +
                  "&& args(beanName, beanFactory, methodSignature, methodResultSpecification, conversionService, ..) " +
                  "&& target(org.springframework.webflow.builder.BeanInvokingActionFactory)",
                  argNames = "beanName, beanFactory, methodSignature, methodResultSpecification, conversionService")
              public Object retrieveObjectFromFlowScopeIfNotAvaibleInBeanFactory(
                  ProceedingJoinPoint joinPoint,
                  String beanName,
                  BeanFactory beanFactory,
                  MethodSignature methodSignature,
                  MethodResultSpecification methodResultSpecification,
                  ConversionService conversionService) throws Throwable {
          
                  if (beanFactory.containsBean(beanName)) {
                      return joinPoint.proceed();
                  } else {
                      return createFlowScopeRetrievingAction(beanName, methodSignature, methodResultSpecification, conversionService);
                  }
              }
          
              public Action createFlowScopeRetrievingAction(String beanName, MethodSignature methodSignature, MethodResultSpecification methodResultSpecification, ConversionService conversionService) {
                  FlowRetrievingBeanInvokingAction action = new FlowRetrievingBeanInvokingAction(methodSignature, beanName);
                  action.setMethodResultSpecification(methodResultSpecification);
                  action.setConversionService(conversionService);
                  action.setResultEventFactory(new ResultObjectBasedEventFactory());
                  return action;
              }
          }
          This is the FlowRetrievingBeanInvokingAction class:

          Code:
          package com.interface21.cert.web.swf.action;
          
          import org.springframework.webflow.action.AbstractBeanInvokingAction;
          import org.springframework.webflow.RequestContext;
          import org.springframework.binding.method.MethodSignature;
          
          public class FlowRetrievingBeanInvokingAction extends AbstractBeanInvokingAction {
          
              private String beanName;
          
              public FlowRetrievingBeanInvokingAction(MethodSignature methodSignature, String beanName) {
                  super(methodSignature);
                  this.beanName = beanName;
              }
          
              private String getBeanName() {
                  return beanName;
              }
          
              protected Object getBean(RequestContext context) throws Exception {
                  return context.getFlowScope().get(getBeanName());
              }
          }
          The second aspect fixes a problem with the isAction() method in BaseFlowServiceLocator. This one is a true hack since I'm using Spring AOP (not Aspectj) and I can't decorate BaseFlowServiceLocator directly:

          Code:
          package com.interface21.cert.web.swf.registry;
          
          import org.aspectj.lang.annotation.Aspect;
          import org.aspectj.lang.annotation.Around;
          import org.aspectj.lang.ProceedingJoinPoint;
          import org.springframework.webflow.registry.FlowRegistry;
          import org.springframework.webflow.builder.FlowServiceLocator;
          import org.springframework.aop.support.NameMatchMethodPointcutAdvisor;
          import org.springframework.aop.framework.ProxyFactory;
          import org.springframework.beans.factory.BeanFactoryAware;
          import org.springframework.beans.factory.BeanFactory;
          import org.aopalliance.intercept.MethodInterceptor;
          import org.aopalliance.intercept.MethodInvocation;
          
          @Aspect
          public class XmlFlowRegistrarAspect implements BeanFactoryAware {
          
              private BeanFactory beanFactory;
          
              public void setBeanFactory(BeanFactory beanFactory) {
                  this.beanFactory = beanFactory;
              }
          
              @Around(
                  value = "execution(* registerFlows(..)) " +
                      "&& args(flowRegistry, flowServiceLocator) " +
                      "&& target(org.springframework.webflow.registry.XmlFlowRegistrar)",
                  argNames = "flowRegistry, flowServiceLocator"
              )
              public Object interceptRegisterFlowsMethodAndDecorateFlowServiceLocator(ProceedingJoinPoint joinPoint, FlowRegistry flowRegistry, FlowServiceLocator flowServiceLocator) throws Throwable {
                  return joinPoint.proceed(new Object[] { flowRegistry, decorateFlowServiceLocator(flowServiceLocator) });
              }
          
              protected FlowServiceLocator decorateFlowServiceLocator(FlowServiceLocator flowServiceLocator) {
          
                  NameMatchMethodPointcutAdvisor pointcutAdvisor = new NameMatchMethodPointcutAdvisor();
                  pointcutAdvisor.setAdvice(new BeanFactoryContainsBeanNameMethodInterceptor());
                  pointcutAdvisor.setMappedName("isAction");
          
                  ProxyFactory pf = new ProxyFactory(flowServiceLocator);
                  pf.addAdvisor(pointcutAdvisor);
          
                  return (FlowServiceLocator) pf.getProxy();
              }
          
              public class BeanFactoryContainsBeanNameMethodInterceptor implements MethodInterceptor {
                  public Object invoke(MethodInvocation methodInvocation) throws Throwable {
                      String beanName = (String)methodInvocation.getArguments()[0];
          
                      if (XmlFlowRegistrarAspect.this.beanFactory.containsBean(beanName)) {
                          return Boolean.TRUE;
                      } else {
                          return Boolean.FALSE;
                      }
                  }
              }
          }
          Finally, I brought these two aspects together with some extra configuration in a Spring 2.0 XML file:

          Code:
          <?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:util="http://www.springframework.org/schema/util"
          	   xmlns:aop="http://www.springframework.org/schema/aop"
          	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
                 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
          
              <aop:aspectj-autoproxy proxy-target-class="true"/>
          
              <bean class="com.interface21.cert.web.swf.builder.BeanInvokingActionFactoryAspect"/>
          
              <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
                  <property name="customTargetSourceCreators">
                      <bean class="org.springframework.aop.framework.autoproxy.target.LazyInitTargetSourceCreator"/>
                  </property>
              </bean>
          
              <bean id="beanInvokingActionFactory" class="org.springframework.webflow.builder.BeanInvokingActionFactory" lazy-init="true"/>
          
              <bean id="examinationFlow" class="org.springframework.webflow.registry.XmlFlowRegistryFactoryBean">
                  <lookup-method name="getFlowRegistrar" bean="xmlFlowRegistrar"/>
                  <property name="flowLocations">
                      <list>
                          <!-- location of flow definition files here -->
                      </list>
                  </property>
                  <property name="beanInvokingActionFactory" ref="beanInvokingActionFactory"/>
              </bean>
          
              <bean id="xmlFlowRegistrar" class="org.springframework.webflow.registry.XmlFlowRegistrar"/>
          
              <bean class="com.interface21.cert.web.swf.registry.XmlFlowRegistrarAspect"/>
          </bean>
          Hope this helps. It's a hack I've put together in a couple of hours. There are a number of issues I had to cater for which is why the solution is so elaborate.

          Steven

          Comment

          Working...
          X