Results 1 to 9 of 9

Thread: flow inheritance

  1. #1
    Join Date
    Mar 2006
    Location
    Sydney
    Posts
    27

    Arrow flow inheritance

    I'd like to be able to reuse features of flows in parent-child relationship. For example, I need to check authorisation at the start of each flow - if there is a problem then transition to a logon subflow-state. I'm trying to implement this using exception-handlers, but this doesn't have a neat way of resuming where the exception occurred. If there were parent flows, then they could describe common flow funcationality that a child flow could extend or modify - in my case checking for authorisation. Basically, I want a way to declare a bunch of predefined states - and a way to get at the start-state.

  2. #2
    Join Date
    Aug 2004
    Location
    Melbourne, FL
    Posts
    2,794

    Default

    Jamie,

    Could you elaborate on what you mean here:
    I'm trying to implement this using exception-handlers, but this doesn't have a neat way of resuming where the exception occurred.
    ... specifically the part about "neat way of resuming where the exception occured" -- what are you looking for there exactly?

    Regarding flow inheritence, that sounds like an interesting feature. Do you think you could take a stab at producing a flow definition demonstrating what you'd ideally like to see there?

    Thanks,

    Keith
    Keith Donald
    Core Spring Development Team

  3. #3
    Join Date
    Mar 2006
    Location
    Sydney
    Posts
    27

    Default for example

    I should be a bit clearer

    I'm writing a VoiceXML banking application, and I'm evaluating the use of WebFlow as our state machine. It looks like a good fit so far since voice interactions are highly sequential and there are no browser navigation surprises.

    I have some cases where global handling of events is necessary - I'd rather not duplicate the handling of these events in every flow. The two main cases I have in mind are user authorisation, and help handling. Authorisation should be checked before each flow is begun, but I'd like to define that behaviour in one place, then reuse it for every flow in the system (more than 100 flows I'm guessing). Next, I'd like to handle that event in one place - (e.g transition to a logon flow), then return to the original flow and resume.

    The solution I had in mind is similar to the <bean parent="parentBean"> syntax we are familar with - but modified for flows. There is a problem with start-state semantics that needs to be thought through. The basic idea is that all states from the parent are just "copied down" to the child - then the child flow can transition to any parent flow. The child can also redefine any parent state.

    Here is a possible flow defn for a "parent" whose _flowId=authorisation:

    Code:
    <flow start-state="check">
    
    	<action-state id="check">
    		<action bean="action"/>
    		<transition on="yes" to="finish"/>
    		<transition on="no" to="logon"/>	
    	</action-state>
    
    	<subflow-state id="logon" flow="logon">
    		<transition on="yes" to="finish"/>
    		<transition on="no" to="exit"/>
    	</subflow-state>
    
    	<end-state id="finish"/>
    	<end-state id="exit"/>
    
    	<import resource="beans.xml"/>
    
    </flow>
    And here is a "child" flow (_flowId=balance). My expectation is that the parent's start-state happens first, then one of it's end-state's (in this case "finish") is mapped to the child's start-state.

    Code:
    <flow parent="authorisation" map-event="finish" start-state="displaySelect">
    
        <view-state id="displaySelect" view="balance/select">
            <transition on="submit" to="process">
    		<action bean="formAction" method="bind"/>
    	</transition>
        </view-state> 
    
        <action-state id="process">
            <action bean="formAction"/>
            <transition on="success" to="displayBalance"/>
    	<transition on="error" to="finish"/>
        </action-state>
    
        <view-state id="displayBalance" view="balance/balance">
            <transition on="submit" to="finish"/>
        </view-state> 
    
        <end-state id="finish"/>
    
        <import resource="beans.xml"/>
    
    </flow>
    These semantics are similar to VoiceXML <catch> tags that are copied from application scope, to form scope, then finally to field scope.

    However, instead of all this extra syntactic sugar - can I do this with an exception handling mechanism? - so that I throw a StateException when the user tries to access flow functions - perhaps by using AOP pointcuts to advise all action beans with the authorisation-check, then handle the exception by initiating the logon sub-flow, then resume in the state where the exception occurred. Or can exceptions only be handled in the flow that generated them?

    As a final unrelated point, VoiceXML doesn't like the _underscore at the start of it's ECMA script variables. What is the best way to redefine the parameter names for _flowExecutionKey and _eventId?

    Regards,
    Jamie

  4. #4
    Join Date
    Aug 2004
    Location
    Melbourne, FL
    Posts
    2,794

    Default

    Hey Jamie,

    Great post.

    I think the inheritence mechanism you're proposing has merit and should be considered more carefully. Let's see, first off, if we can meet your requirements with the existing feature set:

    ... can I do this with an exception handling mechanism? - so that I throw a StateException when the user tries to access flow functions - perhaps by using AOP pointcuts to advise all action beans with the authorisation-check, then handle the exception by initiating the logon sub-flow, then resume in the state where the exception occurred. Or can exceptions only be handled in the flow that generated them?
    Exactly what I was thinking as I read your response. I would recommend at least trying the following, initially:
    - Attaching a flow execution listener to the flows you wish to secure with this logon check, performing the check before the flow is allowed to start (see the sessionStarting callback)
    - Throwing a EnterStateVetoException (which is a StateException) if the authorisation precondition check fails, which an exception handler attached at the flow-level can use to transition to a login subflow.

    Now this doesn't entirely solve your problem--the listener modularlizes the security check; however, you still need that exception-hander and login subflow-state definition for every flow you wish to have this capability, because indeed, a StateException can only be handled in the flow that generated them (and there is no support for any inheritence yet).

    I'm trying to think of other options here, besides some form of flow inheritence...

    If we were to add an exception handling capability at the flow executor level (up one level from the flow itself), on a SecurityException a handler there could launch an independent login flow (not in any way associated with the flow that failed to start) which on completion could redirect back to the flow that failed the first time around (which would cause a new flow execution to launch--basically restart behavior). This seems doable, so long as the flow to redirect back to is parameterizable (which it can be).

    I'm not sure an externally-driven 'handle exception/resume' capability for the same flow execution makes sense, where the flow throws an exception, some external handler catches that and then tries to manipulate the flow execution by signaling an event against it--that to me seems to violate flow encapsulation...

    So in my mind I see two options:
    -Some form of flow inheritence, where a flow definition can be constructed to handle these common cases with minimal duplication. In this case the flow is fully responsible for responding to its own failure scenarios, which I like (it just inherits base behavior from a parent flow).

    - An exception handling capability at the flow executor level, where a flow that throws an unhandled exception can be invalidated, and then a new flow can be launched which on completion can redirect to a new instance of the flow that failed, to "try it again". The important point here is this would launch three independent flow executions: the first one that fails, the next one to login, and the final one to try the one that failed again (starting over). Note: this option is likely already possible by relying on an external exception handler like a Spring MVC HandlerExceptionResolver--it is just not built into SWF at present.

    I think both options are viable...

    As a final unrelated point, VoiceXML doesn't like the _underscore at the start of it's ECMA script variables. What is the best way to redefine the parameter names for _flowExecutionKey and _eventId?
    Interesting you're not the first person to mention dislike the use of _ in the parameter names (we use that to indicate it's an "internal system parameter", not an application parameter). Any particular reason why (just curious)? In any case, the parameter names are fully configurable via the FlowExecutorArgumentExtractor, settable at the FlowController level.

    Keith
    Keith
    Last edited by Keith Donald; Mar 6th, 2006 at 06:56 PM.
    Keith Donald
    Core Spring Development Team

  5. #5
    Join Date
    Mar 2006
    Location
    Sydney
    Posts
    27

    Default

    Keith,

    Thanks for the post.

    So given the following comment...

    Now this doesn't entirely solve your problem--the listener modularlizes the security check; however, you still need that exception-hander and login subflow-state definition for every flow you wish to have this capability, because indeed, a StateException can only be handled in the flow that generated them (and there is no support for any inheritence yet).
    ... it looks like i'd have to duplicate a fair amount of code around the place. I'm favouring the inheritance mechanism more and more

    I mentioned two cases where global handling would come in handy - the other was help. I'd like to emit a help event from any view state in any flow, and it be handled in a consistent way - including restarting a flow.

    What about the ability to restart a calling flow (not necessary the flow that generated the event) i.e i'd like some kind of chained handling of events that proceeds up the calling-flow chain. Either that, or I remember where I've been and transition to them as normal - but that would involve monitoring the state machine and recording it's state - which it does internally anyway - why duplicate that?


    - An exception handling capability at the flow executor level, where a flow that throws an unhandled exception can be invalidated, and then a new flow can be launched which on completion can redirect to a new instance of the flow that failed, to "try it again".
    This exception mechanism seems to be a way to mimic events. I'd rather call an event an event.

    Would it be possible to do some kind of post processing on the flowRegistry that sets up all this authorisation/help event plumbing. i.e get all the flows in the registry and rewire them with help/authorisation?

    About VoiceXML. To submit a form you put:

    Code:
    <submit namelist="accountNumber accessCode _eventId _flowExecutionId" next="webflow.form"/>
    But the ECMA variables referenced in the namelist attribute are not interpretted by the VoiceXML browser if they have _underscore - the browser throws an error "Event::error.semantic, Message: variable name violates naming convention". A another solution is put them in the next attribute:

    Code:
    <submit namelist="customerNumber accessCode" next="webflow.form?_eventId=submit&amp;_flowExecutionKey=_s2C04C94F-CE49-FF44-D618-D19D9F74E116_c914892B8-BD98-7C8E-B4F3-F9E2DA295F5D"/>
    but then you can't submit different events from the same tag - since you might have the events generated dynamically from your grammar (i.e. known only on the client side during a recognition).

    Regards,
    Jamie

  6. #6
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    21

    Default

    Quote Originally Posted by Keith Donald
    Interesting you're not the first person to mention dislike the use of _ in the parameter names (we use that to indicate it's an "internal system parameter", not an application parameter). Any particular reason why (just curious)? In any case, the parameter names are fully configurable via the FlowExecutorArgumentExtractor, settable at the FlowController level.

    Keith
    Yah .. I was one of them, and the VXML interpreter's underscore "reserved namespace" was also the reason for me :-)

    SWF is a good match to voice state machines, thanks!

  7. #7
    Join Date
    Aug 2004
    Location
    Melbourne, FL
    Posts
    2,794

    Default

    I mentioned two cases where global handling would come in handy - the other was help. I'd like to emit a help event from any view state in any flow, and it be handled in a consistent way - including restarting a flow.
    Checkout Flow 'global transitions' (see the <global-transition> element)...

    What about the ability to restart a calling flow (not necessary the flow that generated the event) i.e i'd like some kind of chained handling of events that proceeds up the calling-flow chain.
    You can restart a flow from a view-state or end-state by using the "flowRedirect:${flowId}" form for the view attribute.

    Either that, or I remember where I've been and transition to them as normal - but that would involve monitoring the state machine and recording it's state - which it does internally anyway - why duplicate that?
    Currently a FlowExecution doesn't hold onto a detailed history table of where it's been--it tracks its current state and session stack of course, and also remembers the last event and last transition, but does not track more than that (and the additional history information it does track is only kept on a per request basis). It is expected an external FlowExecutionListener would capture conversation history and save it out to some store for later access--this is not yet supported out of the box however (though it is straightforward to attach a custom listener implementation).

    Regarding of post-processing of registered flow definitions: I would consider using a custom FlowRegistrar, perhaps an extension of XmlFlowRegistrar that does the processing you need after parsing a Flow definition but before registering it. I'll have a look and see if such a post processing hook can be made more explicit.

    Thanks,

    Keith
    Keith Donald
    Core Spring Development Team

  8. #8
    Join Date
    Sep 2004
    Posts
    346

    Default Is it also possible to consider inheritance of actions?

    for instance below action states are essentially the same except for id but the state id is meaningful to our processes to determine things like where failure occured. So what I'm talking about is a mechanism similar to the inheritance mechanism of normal spring beans?
    --------------------------------
    Current:
    <action-state id="Initial">
    <action bean="enrollmentSystemRouteCreateUnitOfWorkActivit y"/>
    <transition on="UnitOfWorkCreated"
    to="enrollmentSystemRouteDeleteOldRowsActivity"/>
    <transition on="NoMoreWork"
    to="enrollmentSystemRouteCommitWorkActivity"/>
    </action-state>

    <action-state id="DoNotCommitWork">
    <action bean="enrollmentSystemRouteCreateUnitOfWorkActivit y"/>
    <transition on="UnitOfWorkCreated"
    to="enrollmentSystemRouteDeleteOldRowsActivity"/>
    <transition on="NoMoreWork"
    to="enrollmentSystemRouteCommitWorkActivity"/>
    </action-state>

    -------------------------------

    Proposal

    <action-state id="Initial">
    <action bean="enrollmentSystemRouteCreateUnitOfWorkActivit y"/>
    <transition on="UnitOfWorkCreated"
    to="enrollmentSystemRouteDeleteOldRowsActivity"/>
    <transition on="NoMoreWork"
    to="enrollmentSystemRouteCommitWorkActivity"/>
    </action-state>

    <action-state id="DoNotCommitWork" parent="Initial"/>

  9. #9

    Default

    What *I* would like to be able to do is the following:

    File1:
    Code:
    <flow start-state="check">
    
    	<action-state id="check">
    		<action bean="action"/>
    		<transition on="yes" to="finish"/>
    		<transition on="no" to="logon"/>	
    	</action-state>
    
    	<subflow-state id="logon" flow="logon">
    		<transition on="yes" to="finish"/>
    		<transition on="no" to="exit"/>
    	</subflow-state>
    
    	<end-state id="finish"/>
    	<end-state id="exit"/>
    
    	<import resource="beans.xml"/>
    
    </flow>
    And then in another file be able to "extend" it (i.e. adding a middle action in the flow). (in case an explanation is needed, this would allow me to create 'base' flows packaged in a jar file and let customers extend them whenever needed.)

    Code:
    <flow start-state="check" refid="original_flow_id">
    
    	<action-state id="check">
    		<!-- override this transition -->
    		<transition on="no" to="customLogonPreStep"/>	
    	</action-state>
    
    	<subflow-state id="customLogonPreStep" flow="...">
    		<transition on="continue" to="logon">
    	</subflow-state>
    	
    </flow>
    Again, not sure about the syntax or how it can be achieved, but the idea is to go through the customLogonPreStep subflow before going to the Logon state. The remaining of the flow is still the same.

    This has the added benefit that I can change the first file definition, redistribute the new jar with that file and the customer's modification will be kept intact (of course, if the referenced names did not change).

    Any idea if this could be implemented?

    PS: I'm not an active webflow user, just evaluating it and I *really* need this functionality to allow for extensibility...

    Best Regards

Posting Permissions

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