cocoon-docs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From stev...@outerthought.org
Subject [WIKI-UPDATE] FinishingFlow RT LenyaDemo GeneralizedFlow MarcPortier Fri Jul 4 16:00:06 2003
Date Fri, 04 Jul 2003 14:00:07 GMT
Page: http://wiki.cocoondev.org/Wiki.jsp?page=FinishingFlow , version: 13 on Fri Jul  4 13:55:20
2003 by ReinhardPoetz

+ 
+ *sorting order of parameters\\
+  [http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=105732398423404&w=2]
+ 


Page: http://wiki.cocoondev.org/Wiki.jsp?page=RT , version: 10 on Fri Jul  4 13:56:51 2003
by MarcPortier

+ * __[GeneralizedFlow]__: some random thoughts placing the 'Flow' in a larger picture of
web application development.
+ 


Page: http://wiki.cocoondev.org/Wiki.jsp?page=LenyaDemo , version: 13 on Fri Jul  4 13:13:37
2003 by AndreasKuckartz

- 2003.07.03: Works again
+ 2003.07.03: Works again\\
?                        ++



Page: http://wiki.cocoondev.org/Wiki.jsp?page=GeneralizedFlow , version: 1 on Fri Jul  4 13:55:29
2003 by MarcPortier

New page created:
+ The goal of this writing is 
+ * to smash down and make public some thoughts floating around between [SylvainWallez] and
[MarcPortier] following these discussions on cocoon-dev recently:
+ ** [http://marc.theaimsgroup.com/?t=105588670600005&r=1&w=2] and 
+ ** [http://marc.theaimsgroup.com/?t=105587166400013&r=1&w=2]
+ * to fuel a discussion on how Cocoon can be extended to control webapps
+ * to put down some bigger picture aswell as some naming and definitions
+ * propose a design for one and the other
+ 
+ Before getting there we will have
+ * catalogued 3 types of Controllers for WebApps
+ * said something about the use of HttpSessions
+ * given some opinions on Actions
+ * provided some challenging ways of using flowscripts
+ 
+ 
+ Basically we got off-list for some social talk and found ourselves exchanging loads of yepyep's
on shared thinking about flowscripts. Before we new it this led to a far better understanding
of both flowscripts and how they relate to some other types of interaction-management. 
+ 
+ 
+ !1. InteractionState
+ Uhm, an other type of interaction management? Where is it?
+ In your mind: It basically sits in realizing that server side state in webapps can be concidered
as temporary resources. (URI's with dunamic resource-id's in there)
+ ''(please read more about it in
+ * Pier's description - [http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=105589388328188&w=2]
+ * the xreporter documentation, since it is how the flow is managed over there. - [http://xreporter.cocoondev.org]
+ * more general similar concepts in many of the ReST related stuff - [http://internet.conveyor.com/RESTwiki/moin.cgi/FrontPage]
+ )''
+ 
+ In some detail: 
+ Look at a specific use case of your webapplication: one that consists of a number of end-user-interaction-steps
taking the end user via a number of screens through the complete use case.
+ 
+ Then ''Starting'' this use-case can be seen as: creating a server side 'context' for this
interaction.
+   Let us call that context the "InteractionInstance".
+   And provide a unique identifier to it that can be encoded into URI's on the server.
+   (Like this there is some addressable temporary resource which is in fact holding the server
side state of your application)
+ 
+ And finally ''Continuing'' this use case is then easily achieved by calling upon URI's that
are holding this identifier.
+ 
+ ''(Pier's description perfectly clears out why this is different then using the session
context for controlling your state)''
+ 
+ As the biggest benefits for this kind of working you could
+ # have one end user interact in two parallel disctinct InteractionInstances of the same
use case without the data messing up
+ # have a way to share (publish) these interaction URI's between different end users so more
then one can interact with the same instance (think chatroom or helpdesk assisted form completion)
+ 
+ 
+ {{{
+ Browsers are requesting URI's. 
+ URI's are something between names and addresses pointing to resources.  
+ These resources are at each moment in time in some kind of 'state'.
+ Dynamic applications kind of implicitely indicate there should be something 
+ as temporary resources.
+ }}}
+ ''(most of this should sound a bit ReSTy - see [http://internet.conveyor.com/RESTwiki/moin.cgi/FrontPage])''
+ 
+ 
+ 
+ !2. WebContinuations rule
+ The important eye opener Sylvain brought at this time is that flowscript continuations inside
Cocoon today effectively allow for this state management model:
+ 
+ In fact:
+ 
+ ''Starting'' a flow-interaction currently in Cocoon is like creating a temporary resource
that captures (freezes) the state of your flowscript (== interaction description) in a WebContinuation.
+ 
+ indeed: the sitemap will match a URI that maps to the logical name of the usecase you want
to engage, and you start that with <map:call function="..." />
+ 
+ 
+ ''Continuing'' the flow_interaction then actually becomes calling back into that frozen
WebContinuation state to continue where it was left of
+ 
+ indeed: the remainder of the end user flow is directed to a *.kont URI that maps to the
previously created WebContinuation with a <map:call continutation="{1}" />
+ 
+ 
+ So there is only a slight difference in implementation: 
+ While the previous section spoke of ONE temporary resource grabbing the current state of
a 'InteractionInstance' for the complete duration of the use case...
+ the WebContinuation approach is allocating a different (unique) temporary resource for awaiting
each end-user click during the complete flow.
+ Conceptually (and from a user perspective) there is hardly any distinction between both.
+ 
+ As Sylvain put it here ([http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=105601203401382&w=2])
:
+ (slightly rephrased) 
+ {{{ The (complete) continuation tree *is* actually the "interaction instance". }}}
+ 
+ 
+ Remark: It is to be noted that local variables inside a flowscript are frozen into the WebContinuation
by reference, and not by value!  This means that all distinct continuations inside one continuation
tree (that is: inside parallelly started interaction sequences) are in fact pointing to the
same values at all times.
+ (explicitely for the interaction scheme where 2 distinct end users engage in one flow instance,
this is an important note to make)
+ 
+ 
+ 
+ 
+ !3. Limit transitions
+ Mind though that this equivalence only works in one direction. InteractionState approach
is a special case of the WebContinuation approach (not vice versa) 
+ 
+ Mathematically speaking we could write it down like this
+ {{{
+   lim WebContinuation = InteractionInstance
+     number_of_sendPageAndWaits_in_the_flow_script -> 1
+ }}}
+ Indeed. Suppose your complete flowscript (including subroutines called) gets written down
so it only holds one
+ occurance of the sendPageAndWait() function (probably nested in a while loop).  Then all
the continuations in the complete tree will be pointing to the same local variables (by ref)
and to the same flow-position to go back to. As such they become functionally equivalent.
+ We might even consider in this case to invalidate each continuation as soon as the sendPageAndWait()
returns, then the only noticeable difference between the two cases is that the WebContinuation
approach will slide in different ID's in the URI upon each request.
+ 
+ 
+ The assumption of one sendPageAndWait per flow script might sound as a big limitation, however
checking up on some of the current examples:
+ - numberguessing: http://wiki.cocoondev.org/Wiki.jsp?page=GettingStartedWithFlow
+ - linotype (if you do not consider the login/security): http://cvs.apache.org/viewcvs.cgi/cocoon-2.1/src/blocks/linotype/samples/flow.js?rev=1.5&content-type=text/vnd.viewcvs-markup
+ - petstore (some, not all, of the functions there): http://cvs.apache.org/viewcvs.cgi/cocoon-2.1/src/scratchpad/webapp/samples/petstore/flow/petstore.js?rev=1.13&content-type=text/vnd.viewcvs-markup
+ 
+ we see the same pattern recurring.
+ 
+ 
+ 
+ When looking at these limits or boundaries of usage there is one more transition we could
make:  
+ {{{
+   lim InteractionInstance  = Stateless FlowController
+     number_of_interaction_clicks_per_instance -> 1
+ }}}
+ we end up in the case of classical servlet programming, where there is no need to remember
any state.
+ So we don't really need a temporary resource to be refered to and each request-response
cycle is just independently dealt with. (adding ultimate scalability)
+ 
+ Some of us might not see this as genuine webapp-programming, but there really remains a
great deal of nice things that can be covered by this easy mechanism.  In fact starting again
from WebContinuations, this is the special case where the first thing the flow-script will
encounter is a mere sendPage(). (notWait)
+ 
+ 
+ 
+ We end up listing 3 separate models for controlling flow:
+ # WebContinuations (each interaction-click creates another temporary (state-) resource on
the server)
+ # InteractionInstances (there is one temporary resource keeping track of the last (or current)
state of the interaction on the server)
+ # Stateless FlowControl (there is only some 'static' context and no state variables to take
into account during the application flow)
+ 
+ All of these could hook into the sitemap in a similar way since from that point of view
they are doing the same things for the sitemap:
+ # Offering a set of meaningful (temporary) URI's pointing back to next state-transitions
in the interaction flow.
+ # Offering a resulting dataBean (bean-dict) with resulting data that can be merged into
the publication line
+ # Selecting the pipeline to use for presenting the answer to the end-user.
+ 
+ 
+ Remark: notice that even the stateless flowcontroller gets to maybe even dynamically provide
a set of next links (ie. controlling the flow)... to aid immagination here are some cases
where that will occur
+ * the session is assembling a growing xlink linkbase as the end user wades through your
site
+ * a database table lists over time different 'news' or 'alert' items that need to be linked
from the resulting page
+ * an LDAP server stores user-specific links (e.g those accessible or his/her preferences)
+ * a specific workflow engine backend is actually making all the decissions, your stateless
flowcontroller is just the gateway to that service.
+ 
+ 
+ 
+ Given this 'special case of' understanding we think that:
+ If we smash down an API for the WebContinuations then it is very likely that we have passed
stages of abstraction where the special cases could be handled directly.
+ 
+ Or put differently: 
+ WebContinuations are obviously on the top of the foodchain here. However:
+ * the need to have a complete continuation tree managed in the more specialized case of
InteractionState
+ * or the need to hook up your stateless flowcontrollers inside WebContinuations (and currently
also javascript) 
+ will at best be seen as an arbitrary (random) hurdle for a developer to take.
+ While one could rate that as 'bad enough' there could even be some criticism on the effect
of the additional server-side objects and layers passed on the overall performance and scalability.
+ 
+ Positively: catching these 3 ways of working under one umbrella will leave the publication
pipelines in all cases very similar if not the same.
+ As such there will be a gained knowledge re-use and forehoped a more hasardless transition
of our web applications from one model to the other...
+ 
+ 
+ 
+ 
+ !4. Events
+ One thing the InteractionInstance approach seem better at is handling random-end-user-navigation
throught the flowscript.
+ 
+ Mind you that the current continuations-based implementation of the flow would at first
glance give you the impression that there is only ONE next link to be followed.  And thus
leave the impatient flow investigator with the understanding that it cannot control a more
random or multi-path end-user interaction with the application.
+ After all, making use of the continuations paradigm will require all interaction links to
carry the continuation-id and will thus, through the <map:call continuation="{1}" />
all point to the exact same place in the script.
+ 
+ Let us take up the challenge.  Assuming one of these typical UI's that show this multipath
behaviour:
+ 1. the tabbed-wizard:\\
+ *suppose for this case a very classic wizard-like UI hooking up several stages of screens
to be filled in.
+ *typically the navigation allows for stepping with next> <previous links through the
various screens.
+ *this 'tabbed-wizard' would allow direct links to each of the logically named sub-stages

+ 
+ 2. the datarecord browser\\
+ * a typical record-detail browsing page would have the following links
+ ** next-record, 
+ ** [[input-box-for-id] + goto-record-button,
+ ** pevious-record, 
+ ** save-changes, 
+ ** create-new, 
+ ** delete-current
+ 
+ ''(In fact, again linotype offers a nice example in its edit(..) function at [http://cvs.apache.org/viewcvs.cgi/cocoon-2.1/src/blocks/linotype/samples/flow.js?rev=1.5&content-type=text/vnd.viewcvs-markup])''
+ We can easily end up writing a flowscript that handles these multiple events and paths through
which the end-user might take your application, in the more general case it will look somewhat
like:
+ 
+ {{{
+ while (!finished){
+   sendPageAndWait("view.html");
+   // following gets a <map:parameter> from the <map:call continuation>
+   String command = cocoon.parameters["command"]; 
+   if (command == "save") {
+     doSave();
+   } else if (command == "other") {
+     doOther();
+   } 
+   finished = // some logical function of having saved or something else...
+ }
+ }}}
+ 
+ where the choosen 'action' got somehow encoded into the URI (and extracted by the sitemap)
+ As such we have effectively written an event-handling loop for dealing with the end-user
interaction.
+ 
+ 
+ {{{
+ Inside a web-application URI's are not as much requesting resources as they are communicating
end-user-selected events.
+ }}}
+ 
+ 
+ Having declared the notion of events we might just as well slide in the notion of eventhandlers
and maybe introduce a notation like:
+ {{{
+  handlers = {"save": doSave, "other": doOther};
+  sendPageAndWait(view.html);
+  handleCommand(handlers); 
+ }}}
+ The publishing pipelines now will need some mechanism to encode these 'save' and 'other'
events into into call-back URI's (with temporary URI) 
+ 
+ And honesty requires to indicate we haven't seen the light on what the clean way for doing
that would really be.
+ 
+ [Sylvain|SylvainWallez] suggested that this could just be put down in the bean-dict object
to get it working without revolutionising current investment and untill we gain some experience
with what this will actually be used for...
+ 
+ Indeed: It is quite unclear if this 'set-of-next-flow-links' is to be separated into a possible
third argument (of a possible overloaded version) of the sendPageAndWait method:
+ 
+ {{{
+   handlers = {save: doSave, other: doOther};
+   bean_dict = {data : x, message : say };
+   sendPageAndWait(view.html, bean_dict, buildUriMapForEvents(handlers)); 
+   handleCommand(handlers);
+ }}}
+ 
+ could easily be handled by
+ 
+ {{{
+   handlers = {save: doSave, other: doOther};
+   bean_dict = {data : x, message : say, flow-links: buildUriMapForEvents(handlers)};
+   sendPageAndWait(view.html, bean_dict);
+   handleCommand(handlers);
+ }}}
+ 
+ Finally, if we could assume this, we might even allow the jxtransformer to react on more
meaningful flow-indicating parameters then only the #{$continuation/id} construct
+ (as in : <form action="#{$continuation/id}.kont" method="POST"> ) 
+ 
+ this specific one could get a name detached of the continuation implementation by calling
it flow.next, and likewise the named events could get a flow.save, flow.other,  etc. etc.
+ 
+ 
+ We have to think some more on this encoding of 'events' cause people would want to encode
the next links in very variant (even deviant) ways: form/@action as above, but equally some
input[[type='submit']/@value and surely a/@href, possibly even clientside-javascript{location=..}
+ 
+ 
+ 
+ !5. Names, Definitions and Design Proposal
+ 
+ Now, off for a question: How do you control your webapp?\\
+ As a developer? You don't!  The web is purely re-active. The end-user (browser) is in charge
at all times by the mere pull nature of the web. It's his/her click (incoming HTTPRequest)
that is deciding what's up next.
+ The smart developer however immediately understands that dynamically generated HTML, showing
an abundance of seducing widgets is in fact exposing only a very limited list of sensible
'next' actions the end user can engage upon. 
+ Indeed it is only declaring those URI's to be followed by the end user.
+ 
+ This "Providing the set of possible links" is precisely what the Cocoon FLOW (in more general
terms) is about.  
+ 
+ Here is how Flow deciding components could be mapped onto some interfaces and their interrelation.
+ 
+ {{{
+ +-------------------+      <<provides>>       +---------------------+
+ | FlowDataBean      |<------\       /-------->| FlowEventLinks      |
+ +-------------------+       |       |         +---------------------+
+                             |       |
+                             |       |
+                        +----------------+
+                        | FlowController | <|--------------\
+                        +----------------+                 |
+                                ^                          |
+                               /_\                         |
+                                |                          |
+                                |                          |
+              <<uses>>  +----------------+  <<instantiates>>
+           /----------  |   FlowEngine   |----------\      |
+           |            +----------------+          |      |
+           |                                        |      |
+           V                                        V      |
+ +-------------------+    <<holds>>            +---------------------+
+ | FlowStateCtrlMngr |------------------------>| FlowStateController |
+ +-------------------+                         +---------------------+
+ }}}
+ 
+ * __FlowController__  is a thing that 
+ # selects a pipe, 
+ # can set in the environment/objectmodel a FlowDataBean (bean-dict) ready to be mixed into
the pipe, 
+ # can declare FlowEventLinks for having call backs into the FlowController user customisation
should include easy writing their own FlowControllerImpl (ActionNG) for stateless arbitrary
control logic
+ 
+ * __FlowEngine__ extends FlowController 
+ **depicts implementations of the FlowController that will create temporary resources that
are tied to unique URI's and are managed by FlowStateControllerManager
+ **has two foreseen inplementations WebContinuationsFlowEngine and InteractionInstanceFlowEngine
+ 
+ 
+ * __FlowStateControllerManager__ is a cache (manager) of FlowStateController instances
+ 
+ * __FlowStateController__ extends FlowController
+ ** depicts implementations of the FlowController that are stateful, tied to a unique idenifier
and 
+ ** has two foreseen implementations WebContinuation and InteractionInstance
+ 
+ * __FlowEventLinks__ is an object that enlists the guided next links an end-user should
use in this application.  Each of these event-links will map onto a separate unique uri.
+ 
+ * __FlowDataBean__ is a simple JXPath accessible object that holds 'resulting' data from
the executed arbitrary logic (function of the current bean-dict)
+ 
+ 
+ Hooking into current discussions:
+ 
+ * the FOM (as being discussed on the list recently) in fact resembles the arguments and
instance-variables that are accessible inside the WebContinuationFlowEngine and should be
made accessible to the js-flowsctipts
+ 
+ * Flow as such has been tied to WebContinuations inside the Cocoon community.  If this small
document made any sense at all, we should try to avoid this name-coupling in future? 
+ 
+ 
+ 
+ !6. faq
+ 
+ __Q:__ Why don't we just use Actions for the stateless flow control?
+ 
+ A very good question indeed. 
+ In fact looking at it from the WebContinuations angle there is quite little distinction
to be made between
+ 
+ {{{
+          <map:call function="fn_name" /> and <map:act type="start-flow" src="fn_name"
/>
+ or even  <map:call continuation="{1}" /> and <map:act type="cont-flow" src="{1}"
/>
+ }}}
+ 
+ Actions however have been quite largely seen as 'bad', a stigma they probably got while
the community extended the original Action interface so it could be a haven for 'arbitrary
control logic'.
+ Having said that, we believe that current webapps still have room for a more classic 'stateless
flow control'.
+ If we now try to hook in arbitrary logic for the FlowControl, we should envision it having
a simple mechanism for doing stateless stuff.
+ 
+ Rather then wanting us to write up the flow with the map:act equivalent we would hope for
a flow-umbrella API that allows for stateless implementations as well.  Small changes in the
wording of the sitemap-elements and attributes could achieve a lot:
+ {{{
+ call/@function becomes:   <map:call controller=".." />
+ call/@continuation:       <map:call state=".." /> -- not to be used for stateless
controllers
+ }}}
+ 
+ Also check the remark in section 3 for a couple of examples where even these stateless implementations
could be setting FlowEventLinks and a FlowDataBean (without them needing to be Actions, so
we can have Actions become what they were intended for?)
+ 
+ 
+ 
+ __Q:__ Why do you hardly mention sessions in this story?
+ 
+ Binding server side state to the infamous sessions (often cookie based) is an orthogonal
way of working which is obviously accessible to any of the 3 mechanisms we have catalogued.
+ 
+ As became clear in the story of Pier the session is in fact a kind of store (Map) tied to
the user-agent-instance (the one remembering the cookie) as such it declares a clear context
to store anything a developer would want.
+ (That of course includes Developers of FlowEngine Implementations)
+ 
+ 
+ 
+ !7. How could development look like
+ The danger of any example is that narrows the reasoning too much down to a specific case.
+ This specific case one could argue will best fit one implementation or the other.
+ The hope is that we could avoid this argumentation all together and just see how 'different'
things can fit in.
+ 
+ Suppose the classic guestbook. For some reason we split up the 'make-your-entry' process
in 3 steps.
+ The following 3 simple jx-pages could be grabbing the pages the end user will see.
+ 
+ {{{
+ entry-part1.jx
+ <form action="#{$flow/next}">
+   Name:       <input name="name"  value="#{name}"  />
+   Email:      <input name="email" value="#{email}" />
+   WebAddress: <input name="www"   value="#{www}"   />
+   <input type="submit" name="ok"/>
+ </form>
+ 
+ entry-part2.jx
+ <form action="#{$flow/next}" />
+   Entry: 
+    <textarea name="entry">#{entry}</textarea>
+ </form>
+ 
+ entry-part3.jx
+ <h1>Preview your entry</h1>
+   Your details: <a href="#{$flow/change-name}"> Change those </a>
+   <a href="mailto:#{email}>#{name}</a> from <a href="#{www}">#{www}</a>
+ 
+   Entry: <a href="#{$flow/change-entry}"> Change that </a>
+   <p>#{entry}</p>
+ }}}
+ 
+ above pages get published through some:
+ {{{
+ <map:match pattern="guestbook/entry/part/*" />
+   <map:generate type="jx" src="entry-part{1}.jx" />
+   <map:serialize />
+ </map:match>
+ }}}
+ 
+ obviously there will be some thx.html and guestbook.html pages to complete the show.
+ 
+ 
+ ''Solution 1 - WebContinuations.''
+ 
+ {{{
+ <map:flow>
+ <!-- where flowengine-type="js-continuations" -->
+      <map:script src="guestbook-flow.js"/>
+ </map:flow>
+ 
+ <map:pipeline>
+   <map:match pattern="**/*.kont">
+     <map:call state="{2}"/>
+   </map:match>
+ 
+   <map:match pattern="guestbook-entry">
+     <map:call controller="make_entry()" />
+   </map:match>
+ </map:pipeline>
+ 
+ 
+ // simple version if we're not using the 'named links' stepping back in the flow
+ function make_entry() {
+   var name  = "(your name here)";
+   var email = "your.name@here";
+   var www   = "http://there";
+   var entry = "(your guestbook entry)";
+ 
+   var i = 1;
+   while (i<=3){
+     var dataBean = {name: name, email: email, www: www, entry: entry};
+     sendPageAndWait('guestbook/entry/part/' + i, dataBean, flowLinks );
+     i++;
+   }
+   // actual code writing the entry to file obviously...
+   sendPage('guestbook/thx.html');
+ }
+ 
+ }}}
+ 
+ 
+ ''Solution 2 - InteractionState''
+ 
+ {{{
+ <map:flow>
+ <!-- where flowengine-type="InteractionState" -->
+     <map:class="com.some.cocoon.interactions.GuestBookEntry" name="GuestBookEntry"/>
+ </map:flow>
+ 
+ <map:pipeline>
+   <map:match pattern="**/*.kont">
+     <map:call state="{2}"/>
+   </map:match>
+ 
+   <map:match pattern="guestbook-entry">
+     <map:call controller="GuestBookEntry" />
+   </map:match>
+ </map:pipeline>
+ 
+ 
+ // the following must be doable with a lot less code
+ // (by making this class his own eventHandler probably), 
+ // Having it as explicit as here however pushes me to think some classes like this
+ // could be build from XML config files, maybe slide in some scripting in there 
+ // and end up with something that could be called FSP: FlowScriptingPages?
+ package com.some.cocoon.interactions;
+ 
+ import..;
+ 
+ public class GuestBookEntry 
+ implements FlowStateController, LogEnabled, Serviceable, ..  {
+ 
+     SimpleFlowEventLinks flowEvents = new SimpleFlowEventLinks(super.getStateRef());
+     FlowDataBean dataBean = new RequestFillableDataBean(); 
+     String showStage = "guestbook/entry/part/1";
+ 
+     public GuestBookEntry  {  
+         flowEvents.add("change-name", new FlowEventHandler(){
+             public boolean handleEvent() {
+                 GuestBookEntry.this.showStage = "guestbook/entry/part/1";
+                 GuestBookEntry.this.flowEvents.setNextEvent("change-entry");
+                 return FlowStateController.CONTINUE;
+             }
+         });
+ 
+         flowEvents.add("change-entry", new FlowEventHandler(){
+             public boolean handleEvent(){
+                 GuestBookEntry.this.showStage = "guestbook/entry/part/2";
+                 GuestBookEntry.this.flowEvents.setNextEvent("check");
+                 return FlowStateController.CONTINUE;
+             }
+         });       
+ 
+         flowEvents.add("check", new FlowEventHandler(){
+             public boolean handleEvent() {
+                 GuestBookEntry.this.showStage = "guestbook/entry/part/3";
+                 GuestBookEntry.this.flowEvents.setNextEvent("final");
+                 return FlowStateController.CONTINUE;
+             }
+         });
+ 
+         flowEvents.add("final", new FlowEventHandler(){
+             public boolean handleEvent() {
+                 GuestBookEntry.this.showStage = "guestbook/thx.html";
+                 // actual code to save the entry somewhere
+                 return FlowStateController.END;
+             }
+         });
+ 
+        flowEvents.setNextEvent("change-entry");
+     }
+ 
+ 
+     public boolean controlFlow(FlowRequest request, FlowResponse response) {
+         dataBean.updateDataFromRequest(request);
+         boolean isFinished = flowLinks.handleEvent(request.getEventName())
+         response.setPage(this.showStage);
+         response.setDataBean(this.dataBean);
+         response.setFlowLinks(this.flowEvents.getLinks());
+         return isFinished;
+     }
+ }
+ 
+ }}}
+ 
+ 
+ 
+ ''Solution 3 - Stateless ''
+ 
+ {{{
+ <map:flow>
+ <!-- should there be no flowengine here?  -->     
+   <map:class="GuestBookChangeName"  name="guestbook-change-name"/>
+   <map:class="GuestBookChangeEntry" name="guestbook-change-entry"/>
+   <map:class="GuestBookCheck"       name="guestbook-check"/>
+   <map:class="GuestBookProcess"     name="guestbook-process"/>
+ </map:flow>
+ 
+ <map:pipeline>
+   <map:match pattern="guestbook/change-name" >
+     <map:call controller="guestbook-change-name" />
+   </map:match>
+   <map:match pattern="guestbook/change-entry" >
+     <map:call controller="guestbook-change-entry" />
+   </map:match>
+   <map:match pattern="guestbook/check" >
+     <map:call controller="guestbook-check" />
+   </map:match>
+   <map:match pattern="guestbook/process" >
+     <map:call controller="guestbook-process" />
+   </map:match>
+ </map:pipeline>
+ }}}
+ 
+ with the quite cumbersome four different classes...
+ public class GuestBookChangeName 
+ extends FlowController {...}
+ 
+ which would easily be compared with classic servlet (not so smart) coding we have left behind
us some time ago.
+ Biggest difference to those servlets still would be that these controllers only decide and
prepare, but have separated out the actual compilation and styling of the return HTML (a huge
benefit if you attack this from a servlet background IMHO).
+ 
+ And while we mentioned 'not so smart' there is no real need to look down on this approach:
+ The use for stateless stuff like this is very likely to be useful when your back-end has
all the power and we practically just need to provide some simple gateway, in which case we
reduce all this to 
+ 
+ {{{
+ <map:flow>
+   <map:flowcontroller class="GuestBookGateWay" name="guestbook-gw"/>
+ </map:flow>
+ 
+ <map:pipeline>
+   <map:match pattern="guestbook/*" >
+     <map:call controller="guestbook-gw" >
+       <map:parameter name="event" value="{1}" />
+     </map:call>
+   </map:match>
+ </map:pipeline>
+ }}}
+ 
+ Where the controller could just read the parameter, and use it to decide which back-end
service to call.
+ Backend-return just holds all of flowLinks and flowDataBean that might needs to be wrapped,
mapped or translated...
+ 
+ 
+ !8. leftovers
+ apart from all the code to actually do this (and that do-experience modifying more then
just some details), the biggest thing to hope for on short terms is some challenging remarks
on this
+ 
+ some of what we realise already:
+ 
+ 
+ * If we really want these FlowController instances be accessible by more then one person
at a time we will need\\ 
+ 1. some kind of locking on them (since they _are_ statefull)\\
+ 2. some access control management as well. (since the session will not do it for us)
+ 
+ 
+ * To be useable in practice the InteractionState approach might require dynamic loading
(possibly even compilation?) of the implementation classes
+ 
+ 
+ * left unclear how we actually best encode the event in the URI, having some dislike to
the ?event= approach, but can't put it down yet.  I remember some remark about how matching
**/*.kont was really easy, and I should check upon the greadiness of the * matcher to see
if some **/*.kont/** would work where {3} would catch the event-subresource-path
+ * related: the nature of the FlowEventLinks might not be really a map of URIs:  Which events
the controller can respond to is a part of his interface, and the publishing pipelines (matching
that contract) should just decide how they encode that themselves... only thing that remains
then is that 'logic' might want to indicate which state-transitions are 'allowed' or 'enabled'
at any given moment in time (ie in a particular state of the interaction)... so it ends up
more of a UI thingy then. So just passing back a list of 'allowed links now' would do.
+ * another idea came from [Sylvain|SylvainWallez] to load off the uri-encoding issue to the
LinkRewriter: <a href="flow-event:event-name"> maybe...
+ 
+ 
+ * without wanting to re-open the discussion on short notice (and thus jeopardize some implicit
release planning) this might lead to reconsidering that one sitemap would like to have more
then one flowControllerEngine active inside one sitemap.  No real opinions yet, but it seems
to be opening a logic 'expectation' after enhancing the reuse of publishing pipelines across
the different programming models.
+ 
+ 
+ * and some old mindspin that pops up again: the mount/processer similarity in all of this:
+ 
+ what to think about something that could dynamically mount the same subsitemap.xmap file
as separate sitemap-instances on dynamically generated sub-resource-id's?
+ 
+ something like uri at /whatever/mount-name/** creates a dynamic ID  and uses it to \\
+   - add some sort of dynamic mount point at /whatever/mount-name/MOUNT-ID.DYN \\
+   - redirects the browser to it with /whatever/mount-name/MOUNT-ID.DYN/{1} \\
+ after that all requests to /whatever/mount-name/MOUNT-ID.DYN/** are handled by a separate
instance of that sub-sitemap?
+ 
+ only thing left would be to allow this subsitemap to declare room for some member-variables
in his own context.
+ (ie allowing the sitemap to have the same kind of attribute-holding possibilities as session
or request contexts)
+ 
+ parent sitemap could look like: 
+ {{{
+ <map:match pattern="mount-name/*.DYN/**">
+   <map:mount instance="{1}" uri-prefix="mount-name/{1}.DYN"/>
+ </map:match>
+ 
+ <map:match pattern="mount-name/**">
+   <map:act type="dynamount" >
+     <map:redirect src="mount-name/{mount-id}.DYN/{1}/>
+   </map:act>
+ </map:match>
+ }}}
+ 
+ the subsitemap in guestbook/sitemap.xmap would just have some simple pipelines exchanging
objects with the sitemap's own context... leaving sub-uri management as we know it today.
+ 
+ 
+ * all the things we haven't thought of yet.
+ 


Page: http://wiki.cocoondev.org/Wiki.jsp?page=MarcPortier , version: 11 on Fri Jul  4 13:49:26
2003 by MarcPortier

+ * [GeneralizedFlow]



Mime
View raw message