Announcement Announcement Module
Collapse
No announcement yet.
Attribute Output Mappings to Request Scope? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Attribute Output Mappings to Request Scope?

    Hi Keith!

    Awesome work on Web Flow! Here's a comment/question for you.

    I noticed that the implementation of the ParameterizableFlowAttributeMapper prohibits one from returning a value from a sub-flow to the super-flow's request scope:

    Code:
    	public void mapSubflowOutput(RequestContext context) {
    		if (this.outputMapper != null) {
    			// map from request context to parent flow scope
    			this.outputMapper.map(context,
    					context.getFlowExecutionContext().getActiveSession().getParent().getScope(),
    					getMappingContext(context));
    		}
    	}
    This prevents:

    Code:
       <attribute-mapper>
             <output value="someObject" as="$&#123;requestScope.subFlowObject&#125;"/>
       </attribute-mapper>

    I'm not sure if this is a design choice intended to encourage proper subflow usage, or if I'm just misinformed on this point. Nevertheless, I'd like to make a case for allowing parent flows to map sub-flow data back to the request scope.

    Consider the case where the we'd like to store a reference to the sub-flow's form backing object in a collection on the super-flow's form backing object. Mapping the sub-flow's formbacking object as a request scoped object means that we don't have that object lingering around in the super-flow's scope.

    Allowing the sub-flow's form backing object to stick around in the flow scope of the super-flow appears to defy the expected state of the super-flow after the sub-flow's object has been added to the collection. As you are aware, objects that should only last as long as the request go into the request scope. In this example, an argument could certainly be made that the sub-flow's object should not live in the context of the super-flow's scope.

    As it stands now, the action method that handles adding the sub-flow's object to the form backing object in the super-flow must manually remove the object from flow scope after adding it to the collection.

    Code:
    public Event addSubFlowObjectToCollection&#40;RequestContext ctx&#41; &#123;
         Object subFlowObject = ctx.getFlowScope&#40;&#41;.get&#40;"subFlowObject"&#41;;
         &#40;&#40;MyObject&#41; getFormObject&#40;ctx&#41;&#41;.addSubFlowObjectToCollection&#40;subFlowObject&#41;;
         ctx.getFlowScope&#40;&#41;.remove&#40;subFlowObject&#41;;
    &#125;
    This seems a little odd to me, and could be avoided by allowing subflow mappings to wind up in the super flow's request scope. So what do you think?

    -Alex

  • #2
    This is intentional. The request scope is 'scoped at the level of a client request', so it's basically independent of flows. What this means is that if a request moves a flow execution from a parent flow to a subflow the request scope will remain the same, i.e. it is shared between the parent and sub flow. Same thing when a request moves a flow execution from a sub flow back up to the parent flow.

    Given the fact that the parent and sub flow have access to the same request scope there is no use for mapping in this case: if the sub flow puts an object in request scope before it ends, it will still be there when the parent flow resumes and your code could just do this:

    Code:
    public Event addSubFlowObjectToCollection&#40;RequestContext ctx&#41; &#123; 
         Object subFlowObject = ctx.getRequestScope&#40;&#41;.get&#40;"subFlowObject"&#41;; 
         &#40;&#40;MyObject&#41; getFormObject&#40;ctx&#41;&#41;.addSubFlowObjectToCollection&#40;subFlowObject&#41;; 
    &#125;
    Erwin

    Comment


    • #3
      Originally posted by klr8
      Given the fact that the parent and sub flow have access to the same request scope there is no use for mapping in this case: if the sub flow puts an object in request scope before it ends, it will still be there when the parent flow resumes
      Hi Erwin,

      Thanks for the explanation. You mention that there is no use for mapping sub-flow request scope to super-flow request scope. I agree on this point. I am only concerned with mapping the flow-scope of the sub-flow to the request-scope of the super-flow.

      As it stands now, the sub-flow's action must place a flow-scoped form backing object into the request scope when the sub-flow completes. I'd argue that using a mapping to accomplish that task is a more desirable approach.

      Consider the following example.


      Code:
      /**
       * Does all the dirty work of building an instance of MyDomainObject. 
       */
      public class BuildMyDomainObjectAction extends FormAction &#123;
      
          public ComponentAction&#40;&#41; &#123;
              setFormObjectName&#40;"myDomainObject"&#41;;
              setFormObjectScope&#40;ScopeType.FLOW&#41;;
              setFormObjectClass&#40;MyDomainObject.class&#41;;
          &#125;
      
          // ...
      
          public Event submit&#40;RequestContext context&#41; throws Exception &#123;
              // Notice that we aren't explicitly adding the myDomainObject
              // instance to the request scope here.  It is implied that 
              // consumers of this action's flow will retrieve the instance
              // in a fashion that is consistent with how it will be used.
              // Some super-flows may require the instance in the request
              // scope, and some may require the instance in the flow scope.
              // Super-flows may also want this instance to be stored
              // in their request scope with a name that is relevant to that flow.
              return success&#40;&#41;;
          &#125;
      &#125;
      Code:
          <subflow-state id="buildCollectionElement" flow="myDomainObjectFlow">
              <attribute-mapper>
                 <output value="myDomainObject" as="$&#123;requestScope.collectionElement&#125;/>
              </attribute-mapper>
              <transition on="success" to="addCollectionElement" />
          </subflow-state>
      Code:
      /**
       * Builds an instance of MyParentObjectAction.  This process involves
       * launching a sub-flow to add new elements to the parent's collection.
       */
      public class ParentObjectAction extends FormAction &#123;
      
          public ParentObjectActionAction&#40;&#41; &#123;
              setFormObjectName&#40;"parentObject"&#41;;
              setFormObjectScope&#40;ScopeType.FLOW&#41;;
              setFormObjectClass&#40;MyParentDomainObject.class&#41;;
          &#125;
      
          /**
           *  Retrieves a MyDomainObject instance named
           *  "collectionElement" from the request scope and adds it
           *  to this flow's MyParentObjectAction's collection.
           */
          public Event addCollectionElement&#40;RequestContext ctx&#41; throws Exception &#123;
              MyDomainObject collectionElement = &#40;MyDomainObject&#41; ctx.getRequestScope&#40;&#41;.get&#40;"collectionElement"&#41;;
              &#40;&#40;MyParentDomainObject&#41; getFormObject&#40;ctx&#41;&#41;.getCollection&#40;&#41;.add&#40;
                   collectionElement 
              &#41;;
              return success&#40;&#41;;
          &#125;
      &#125;
      In this case, we've alleviated the need for the sub-flow to assume that the super-flow will be using the myDomainObject in the request scope. That responsibility has been delegated instead to the attribute mapper. This eliminates the sub-flow's orthagonal responsibility of correctly naming and placing its form backing object in the request scope as required by the super flow.

      As a side effect, our super-flow's action knows only that an incoming collection element will be named "collectionElement" and that it'll be appropriately accessible from the request scope. If we have any problems with retrieving this value, we know right where to look: the attribute mappings!

      Thanks for taking the time to consider my rebuttal! =)

      -Alex

      Comment


      • #4
        Mmm, those are interesting arguments you make. Right now ParameterizableFlowAttributeMapper maps the request context to a new map while doing 'input mapping', and maps the request context to parent flow scope during 'output mapping'. I think I a agree with your arguments that making output mapping to the request context possible could maybe clarify the contract of the subflow and make it easier to use by parent flows.

        That being said, how can we implement this?

        1) You could just do a custom FlowAttributeMapper implementation i.s.o. ParameterizableFlowAttributeMapper.
        2) Enhancing the ParameterizableFlowAttributeMapper is not so easy since we need to avoid situations like:
        <output value="foo" as="${flowExecutionContext.activeSession.parent.sc ope.bar}"/>
        But I guess an OGNL alias could be used to work around that.

        I suggest you post a RFE on JIRA with your ideas on the subject.

        Erwin

        Comment


        • #5
          http://opensource2.atlassian.com/pro.../browse/SWF-31

          Comment

          Working...
          X