You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cocoon.apache.org by Ovidiu Predescu <ov...@apache.org> on 2002/04/15 09:55:44 UTC

Re: [RT] Forms and wizards (was: RE: HEADS UP - cocoon form handling (long!!))

I think the same approach can be achieved with the control flow layer
without having to store any "full" continuations on the server.

You just need to map the requests to <map:call function="..."/> and
implement the appropriate JavaScript functions on the server side to
check the arguments, save them in the session object, and send back
the approapriate page. Something like this, although I might have left
out things you mention for brevity purposes:

sitemap.xmap:

  <map:resource>
    <map:script src="survey.js"/>
  </map:resource>

  <map:match pattern="cocoonSurvey/*.html">
    <map:call function="{1}"/>
  </map:match>

  <map:match pattern="internal/*.html" internal-only="true">
    <map:generate src="{1}.xsp" type="serverpages"/>
    <map:transform .../>
    <map:serialize/>
  </map:match>


survey.js:

function start()
{
  databaseFillSessionWithData();
  sendPageAndContinue("internal/personal.html", {"continue": "personal"});
}

function personalPost()
{
  storeRequestArgumentsToSession();
  sendPageAndContinue("internal/system.html", {"continue": "system"});
}

function system()
{
  storeRequestArgumentsToSession();
  var page;

  if (cocoon.session["platform"] == "linux")
    page = "internal/linuxDetails.html";
  else if (cocoon.session["platform"] == "macosx")
    page = "internal/macoxDetails.html";
  else ...

  sendPageAndContinue(page, {"continue": "last"});
}

function databaseFillSessionWithData()
{
  /*
   Call to a Java object to retrieve the user's data from a
   database. The user identification is obtained from the request. The
   result of calling the Java object is a hash table, another Java
   object, anything containing the information for the user. This info
   is placed in the session object.
   */
}

function storeRequestArgumentsToSession()
{
  for (var argument in cocoon.request.arguments) 
    cocoon.session[argument] = cocoon.request.arguments[session];
}



As you see in the above example, the functions above have no
connection between them, there's no shared variable or anything which
they update, other than the session object. In fact updating
JavaScript global variables would not make any difference in the next
request, because there's no continuation stored on the server that
contains the modified value.

The sendPageAndContinue() function is very similar with sendPage()
currently available in Schecoon, except that it doesn't stop the
execution of the thread after sending the page to the user, and it
doesn't save any continuation on the server side. Similarly with
sendPage(), it takes as argument an object which is made available to
the Cocoon pipeline. The XSP generator in my example makes use of it
to set the value of the "action" attribute in the HTML form, which
links to the next page. The mechanism is similar to the one described
by you in your example. This is in fact what is commonly referred to
as Continuation Passing Style, where the continuation is passed to the
next execution block to make use of.

The approach here and the one you describe are similar in
functionality and shortcomings. The above version is much cleaner IMO,
as it removes the flow of the application from the sitemap. The syntax
is much easier to read, and since we use a full programming language,
there's no need to modify the sitemap engine to add new constructs as
needs evolve. Just write a new JavaScript function, or create a Java
class which you can call from the control layer. Also I think it's a
lot easier for new users to come up to speed on such an approach,
compared to the similar sitemap-based implementation.

Of course the same scenario might be implemented in the control flow
layer using full continuations, in which case the implementation would
be a little different, since everything could be written in one
function.

As you notice, there are some differences in how a developer thinks
when describing the flow using a continuations based approach and when
the classical one is used. But you're free to choose the option that
fits you best, the control flow layer gives you this flexibility. You
can even mix and match in the same implementation to use continuations
for one portion, and the session state for the rest.

Regards,
-- 
Ovidiu Predescu <ov...@apache.org>
http://www.geocities.com/SiliconValley/Monitor/7464/ (GNU, Emacs, other stuff)

On Mon, 15 Apr 2002 00:00:54 +0200, "Daniel Fagerstrom" <da...@swipnet.se> wrote:

> Berin Loritsch wrote:
> > Ivelin Ivanov wrote:
> <snip/>
> > > Would you scetch an example of a non-trivial app which does not use
> > > JavaBeans?
> <snip/>
> > I know I am side-stepping your question, however I have not run into
> > a situation where I needed beans at all.  So your assumption that every
> > non-trivial application needs beans is not valid.
> <snip/>
> > What you invariably run into in the Cocoon world if you use beans is
> > a double mapping: DB to bean, and bean to schema.  That approach does
> > not scale well at all.  Not to meantion there are unnecessary
> > conversions that can be the source of problems.  KISS (Keep It
> > Simple Stupid).
> >
> > Keep your memory lean.  Beans don't let that happen--or they force
> > you to be too smart for your own good.
> 
> I find Ivelins and Torstens work on form handling very promising, but I
> share Berins (and some other commenter), concern that they are maybe trying
> to find answers to some unnecessarily complicated problems.
> 
> I'll discuss form handling an multipage form handling (wizards), and try to
> give some proposals on how to _not_ solve some of open questions and issues
> discussed in
> http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=101834333817122&w=2. While
> at it, I'll also share some provocative thoughts about continuations and
> show some heavy use of the hammer anti pattern ;)
> 
> One page forms
> --------------
> 
> We start with a simple one page form. Say that we want to collect some data
> about a user of our system. Typically we want the form to be partially
> filled in with some default data or something that the user already have
> filled in. So we start with something like:
> 
> <map:match pattern="userForm.html">
>   <map:generate src="userDefault.xml"/>
>   <map:transform src="userForm.xsl"/>
>   <map:serialize/>
> </map:match>
> 
> Here the input data is xml (I guess I don't have to argue about why that is
> a good idea ;) ), it might be directly from a file, from a db, from
> JavaBeans or whatever you might prefer. "userForm.xsl" is a simple
> stylesheet that creates a partially filled in html form from its input, (you
> can use Velocity or some other template generator instead if you like). To
> make the connection between the xml input data and the form field names
> simple we use xpaths as field names as is done in XForms (Ivelin, Torsten
> and Konstantin do the same). The form stylesheet will have fields like this:
> 
> <input type="text" name="/user/name" value="{/user/name}"/>
> 
> As these form stylesheets are rather boring to write, we let our computer do
> the work:
> 
> <map:match pattern="*Form.xsl">
>   <map:generate src="{1}Form.xform"/>
>   <map:transform src="xForm2xsl.xsl"/>
>   <map:serialize type="xml"/>
> </map:match>
> 
> We can describe our form in terms of some appropriate subset of the form
> control part of XForms or maybe in some home brewed form describing
> language. "xForm2xsl.xsl" is a stylesheet that takes an Xform description as
> input and creates another stylesheet that in turns takes the default data as
> input and creates an html form. Konstantin have submitted something similar:
> http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=101429833513052&w=2 a while
> ago. He stays closer to the XForms standard, which contains the instance as
> a part of the xforms document, IMHO the XForms standard mixes concerns by
> doing so. Anyway by follow the XForms way of doing things Konstantin don't
> have to write xslt generating xslt, but have instead to use some extension
> functions (evaluate).
> 
> Ok, now we can use our automatically generated form stylesheet:
> 
> <map:match pattern="userForm.html">
>   <map:generate src="userDefault.xml"/>
>   <map:transform src="cocoon:/userForm.xsl?continue=userPost.html"/>
>   <map:serialize/>
> </map:match>
> 
> We also use a "poor mans continuation" trick, by sending the url that the
> form is supposed to post to, as a request parameter to the form stylesheet
> generator. This makes the page flow explicit in the sitemap as it should be.
> 
> Time to take care of the data posted from the form:
> 
> Input handling
> --------------
> 
> Even if we would prefer xml data in the post, it will probably take some
> time before that is the usual case. By using xpaths as field names we have
> at least an implicit description of an xml document. So what we need to do
> is the following:
> * Create an xml document from the request parameters
> * Validate the input data
>   Invalid -> Resend the partially filled in form with error messages
> * Store or use the data
> * Send a success page or the next form
> 
> This can look like:
> 
> <map:match pattern="userPost.html">
>   <map:generate type="xrequest"/>
>   <map:transform type="schematron" src="user.sch"/>
>   <map:match type="pipe-state" test="fail">
>     <map:transform src="cocoon:/userForm.xsl?continue=userPost.html"/>
>     <map:serialize/>
>   </map:match>
>   <map:transform type="write" method="overwrite" src="dbxml:/user"/>
>   <map:transform type="read" src="success.html"/>
>   <map:serialize/>
> </map:match>
> 
> * The "xrequest" generator builds an input xml document from the (xpath)
> request-parameters.
> * The "schematron" transformer validates the input data, set some
> "pipe-state" request attribute to "success" or "fail" and give some kind of
> report about the errors.
> * The "pipe-state" matcher is pipe state aware (cf. the recent discussions
> about pipe aware selectors), i.e. it depends on the state of the
> "pipe-state" request parameter as it is _after_ the execution of the
> validator.
> * If the validation failed the input is piped to the form stylesheet and
> sent back to the user. The form stylesheet also have rules for rendering the
> error report.
> * If the validation succeeds the data is sent to a store of some kind.
> * A success report is generated.
> 
> <design-issues>
> In the above example collection of the data and storing of the data
> (population of the instance) are separated while I&T combine them. There are
> so many possible storages for instance data e.g.: beans and dom in session
> and request attributes, files, relational db:s, xml db:s, business objects
> and so on, that it seem overwhelming to create a common "instance" interface
> for them all, better just put the data in the pipe and let the storage of
> it, be someone others concern.
> 
> I&T also discuss the indirect vs. direct population problem and proposes to
> use direct population of instances (cf. the link above for details). The
> example above uses the indirect approach, but could easily be made direct by
> giving a template instance as a "src" parameter. We have used designs like
> the one above for nearly a year in the company I work for and have never had
> any problems with indirect population, IMHO this is a concern for the
> storage model.
> 
> I think the result of our recent "pipe-awareness" discussion is that the
> success or failure of a validation transformer should be put in some "meta"
> parameter, probably in a request attribute. We still need to find a good way
> to report the details of the errors. One possibilities is: having a separate
> result document with pairs of xpaths and error messages, (that explains what
> was wrong at that position). Another: annotating the document with error
> attributes at the faulty elements, e.g.
> 
> <foo>
>   ...
>    <bar err:error="not a number">qwerty</bar>
> </foo>
> 
> The first variant is more general and elegant, and the second is much more
> easy to use as input for a stylesheet. I prefer the second one :)
> 
> The "write" transformer is mainly a thought experiment, it is like tee in
> unix. It does the same thing as the WritableSourceTransformer but the source
> name is in the sitemap instead than in the input document. I placed the
> "method" attribute in it to indicate that if we want to make things like xml
> db:s writable sources, we have to find a way to describe what kind of update
> method we want to use.
> 
> The "read" transformer doesn't care about its input and just puts the
> content of its src attribute in the pipeline.
> 
> In many cases, there is no already written source or writable source for our
> storage and we have to work a little bit harder, e.g. by writing own
> mappings between e.g. xml and relational db:s or xml and java beans. There
> are some tools that can create at least part of the mapping from some
> scheme, e.g. Castor and XML-DBMS http://www.rpbourret.com/xmldbms/.
> </design-issues>
> 
> Wizards
> -------
> 
> Here we define multi page forms (or wizards) as building one xml document
> from several form pages.
> 
> I&T suggests that the structure of the instance and the views should be as
> decoupled as possible:
> 
> >       [------------instance-----------]
> >HTML: [-----view1-----][-----view2----]
> >WML:  [--view1--][--view2--][--view3--]
> 
> and also that validation could be done at still other substructures that not
> necessarily are connected to the views (cf their paragraph about views and
> phases).
> 
> IMO one can simplify the problem considerably by deciding that the view
> always are non-overlapping sub trees of the instance, and that we have one
> scheme for each view. I know that this is not a completely generic solution,
> but after all we probably did some modeling when we designed our instance.
> The sub trees probably describes conceptually different areas, so hopefully
> we can reuse this thinking by couple the views to these different areas. If
> on the other hand the instance is based on a lousy model, why don't build a
> new one for our wizard, then we can always use xslt to transform our view
> model to the instance model.
> 
> Time for some code:
> 
> <map:resource name="form">
>   <map:transform src="cocoon:/{query}Form.xsl?continue={query}Post.html"/>
>   <map:serialize/>
> </map:resource>
> 
> <map:resource name="filled-form">
>   <map:transform type="read" src="session:/{wizard}/{query}"/>
>   <map:call name="form">
>     <map:parameter name="query" value="{query}"/>
>   </map:call>
> </map:resource>
> 
> <map:resource name="store">
>   <map:generate type="xrequest"/>
>   <map:transform type="schematron" src="{query}.sch"/>
>   <map:match type="pipe-state" test="fail">
>     <map:call name="form">
>       <map:parameter name="query" value="{query}"/>
>     </map:call>
>   </map:match>
>   <map:transform type="write" src="session:/{wizard}/{query}"/>
> </map:match>
> 
> Here I mainly restate our earlier code in terms of parameterized resources,
> so that I don't have to repeat myself all the time. The "wizard" parameter
> is for the root element of the xml document, and query is for the sub
> documents and views. We store everything in a xml document in a session
> attribute while filling in the queries in the wizard session.
> 
> As an example we do a simple survey for cocoon users:
> 
> <map:match pattern="cocoonSurvey/**">
>   <map:parameter name="wizard" value="cocoonSurvey"/>
> 
>   <map:match pattern="**/start.html">
>     <map:act type="write">
>       <map:parameter name="from" src="dbxml:/cocoonSurvey[@id='default']"/>
>       <map:parameter name="to" src="session:/cocoonSurvey"/>
>     </map:act>
>     <map:call name="filled-form">
>       <map:parameter name="query" value="personal"/>
>     </map:call>
>   </map:match>
> 
>   <map:match pattern="**/personalPost.html">
>     <map:call name="store">
>       <map:parameter name="query" value="personal"/>
>     </map:call>
>     <map:call name="filled-form">
>       <map:parameter name="query" value="system"/>
>     </map:call>
>   </map:match>
> 
>   <map:match pattern="**/systemPost.html">
>     <map:call name="store">
>       <map:parameter name="query" value="system"/>
>     </map:call>
>     <map:select type="xpath">
>       <map:when test="/system/platform[.='linux']">
>         <map:call name="filled-form">
>           <map:parameter name="query" value="linuxDetails"/>
>         </map:call>
>       </map:when>
>       <map:when test="/system/platform[.='windows']">
>         <map:call name="filled-form">
>           <map:parameter name="query" value="windowsDetails"/>
>         </map:call>
>       </map:when>
>       <!-- ... -->
>     </map:select>
>   </map:match>
> 
>   <map:match pattern="**/linuxDetailsPost.html">
>   <!-- ... -->
>   </map:match>
> 
>   <map:match pattern="**/windowsDetailsPost.html">
>   <!-- ... -->
>   </map:match>
> 
>   <map:match pattern="**/lastPost.html">
>     <map:call name="store">
>       <map:parameter name="query" value="last"/>
>     </map:call>
>     <map:act type="write">
>       <map:parameter name="from" src="session:/cocoonSurvey"/>
>       <map:parameter name="to" src="dbxml:/cocoonSurvey[@id='1234']"/>
>     </map:act>
>     <map:transform type="read" src="success.html"/>
>     <map:serialize/>
>   </map:match>
> </map:match>
> 
> In this example we fill the xml structure that we put in the session, with
> default data from a db in the beginning, and store the result in a db in the
> end. We use a pipe-aware selector to choose between several paths in the
> wizard, depending on the last answer, (we could achieve even larger
> flexibility by making choices based on xpath expressions applied on the
> session data). Note that the flow control is based on the same continuation
> trick as we used in the "one page form" example. The value of the "continue"
> parameter is used for the submit button. Also note that we can use the back
> and forward button of our browser as much as we want. As soon as you push
> the submit button and your data is valid, the data for that page is stored,
> and as soon you push the refresh button on a form page, its current content
> will be filled in.
> 
> A problem is that if one first say that one is a linux user and submit the
> linux data, and the go back and say that one is a windows user, and fill in
> the windows data, booth the windows and the linux data continue to be
> stored. To handle this problem further mechanisms are needed.
> 
> <design-issues>
> >From a component point of view there is not much new in the wizard compared
> to the one page form. We use a writeable session source that is mainly a
> non-existing repackaging of functionality already in the
> SunShineTransformer, we could have used that instead, and the same applies
> to the "write" action. We also use a pipe-content-aware selector besides the
> pipe-state-aware selector.
> 
> As a consequence of that we always store a page before showing a new the
> pages will get misleading names, booth the linuxDetails and the
> widowsDetails form page will have the url: systemPost.html. This can be
> handled by having obscure names like large numbers so that no one notices,
> or by using redirect, which is considered bad. Are there other alternatives
> in the http protocol? Something like an internal redirect?
> </design-issues>
> 
> Continuations
> -------------
> 
> Why do I keep using the term "continuation" for the trick of sending the
> address of the next page as an argument to the current generated page? Isn't
> a continuation an object that contains the whole current state of the
> program? Actually booth descriptions are true, it all depends on what's in
> your program language.
> 
> The sitemap together with the continue parameter is a program language
> although a quite small one. It contains selection: select and match, and it
> contains global variables: session and request attributes, writable
> resources and so on, and by using the continuation parameter we introduces a
> goto construction. Thus: a small programming language. In such a small
> language a continuation is just a program pointer - in the sitemap case: an
> url. The control structures in structured programming: sequence, selection
> and repetitions can be translated to goto selection (and the other way
> around). So it would be rather easy to translate the structured programming
> concepts mentioned to a sitemap as the one above.
> 
> If we extend our language with (possibly recursive) functions, we need to
> take care of a call stack of program pointers also. This stack of uri:s
> could be stored in a continuation object or in a hidden form field. If we
> introduce local variables in our language we need to put these in the
> continuation object as well. Local variables introduces the possibility for
> "what if" scenarios, i.e. that you can have several independent instances of
> the same form page. For some kind of webapps: e.g. shopping carts and
> checkout sequences, this kind of behavior is IMHO harmful, (cf discussion
> between Ovidiu and me:
> http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=101856443021128&w=2,
> http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=101858926504890&w=2,
> http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=101861458122598&w=2,
> http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=101870096719351&w=2). I
> would propose that in a large class of webapps you don't need more than the
> continue parameter described above. Another aspect is of course that the
> sitemap with continuation parameter sucks as a web application programming
> language. So you would anyway need a better language for describing
> complicated flow, but I still wonder if something as powerful as
> continuations with local variables is needed.
> 
> Using the hammer anti-pattern
> -----------------------------
> 
> After all the recent discussion about what you not are supposed to do in the
> sitemap I can not help to feel like provoking a little ;)
> 
> Principle: "Everything can and should be done in the sitemap" ;)
> 
> As we saw in the examples above we have always to combine the handling of
> the input of one form with the construction of the next form. This is
> because we need to have a match statement that surrounds the two. It would
> be more natural to combine the form generation with its input handler:
> 
> <map:resource name="form-handling">
>   <map:call name="filled-form">
>     <map:parameter name="query" value="{query}"/>
>   </map:call>
>   <fm:label name="{query}Post.html"/>
>   <map:call name="store">
>     <map:parameter name="query" value="{query}"/>
>   </map:call>
> </map:resource>
> 
> Here some kind of environment is supposed to detect that a serialize is
> followed of a generate, and make a continuation available. "fm:label" is a
> way to give a name to the continuation if one don't want an automatic one.
> 
> Here is our example again:
> 
> <map:match pattern="cocoonSurvey/**">
>   <map:parameter name="wizard" value="cocoonSurvey"/>
> 
>   <fm:sequence uri-prefix="cocoonSurvey">
> 
>     <fm:label name="start.html"/>
>     <map:act type="write">
>       <map:parameter name="from" src="dbxml:/cocoonSurvey[@id='default']"/>
>       <map:parameter name="to" src="session:/cocoonSurvey"/>
>     </map:act>
> 
>     <map:call name="form-handling">
>       <map:parameter name="query" value="personal"/>
>     </map:call>
> 
>     <map:call name="form-handling">
>       <map:parameter name="query" value="system"/>
>     </map:call>
> 
>     <fm:select type="xpath">
>       <fm:when test="/system/platform[.='linux']">
>         <map:call name="form-handling">
>           <map:parameter name="query" value="linuxDetails"/>
>         </map:call>
>       </fm:when>
>       <fm:when test="/system/platform[.='windows']">
>         <map:call name="filled-form">
>           <map:parameter name="query" value="windowsDetails"/>
>         </map:call>
>       </fm:when>
>       <!-- ... -->
>     </fm:select>
> 
>     <!-- ... -->
> 
>     <map:act type="write">
>       <map:parameter name="from" src="session:/cocoonSurvey"/>
>       <map:parameter name="to" src="dbxml:/cocoonSurvey[@id='1234']"/>
>     </map:act>
> 
>     <map:transform type="read" src="success.html"/>
>     <map:serialize/>
> 
>   </fm:sequence>
> </map:match>
> 
> The new construction "fm:sequence" handles the url as "map:mount" it also
> automates continuation handling by analyzing its content pipeline components
> and making an url to each generator be available in a continuation parameter
> for the components earlier in the pipe up to the next generator.
> 
> Implementation
> --------------
> 
> If we exclude the last section what do we need to implement if we would like
> to have what I describe above? Not much actually, we need the "xrequest"
> generator, a validation transformer and pipe aware selectors. It could be
> nice to have an "xForm2xsl.xsl" stylesheet also, but it is not necessary,
> one can write form stylesheets manually also. The read and write
> transformers and the new writable sources are not necessary at all there are
> already transformers that do the same work. But maybe not as smooth in the
> proposed framework.
> 
> Torsten have already implemented most of the functionality of an "xrequest"
> generator but as an action, it would be fairly easy to repackage it as a
> generator, I have an implementation that I can donate "as is", although it
> would need some more polishing.  Torsten and Ivelin have also implemented
> some different variants of validation transformers. And I contributed a
> prototype implementation of pipe-aware selection some while ago. Booth would
> need some small adjustments to work together as described above.
> 
> The great unsolved problems
> ---------------------------
> 
> There are probably tons of unsolved problems, but two particularly tricky
> are:
> * Order restrictions: E.g. A user cannot go back after having committed a
> certain page.
> * There might be several paths from the first to the last page in the
> wizard, only the data submitted in pages along the path that was chosed in
> the end should be stored in the end. An instance of this problem was
> described in the end of the "wizard" section.
> 
> I have no solution to these problems, but I think that they become easier to
> solve if there is a simple and explicit connection, between forms and
> submitted data.
> 
> 
> That's more than enough ;)
> 
> What do you think?
> 
> /Daniel Fagerstrom

---------------------------------------------------------------------
To unsubscribe, e-mail: cocoon-dev-unsubscribe@xml.apache.org
For additional commands, email: cocoon-dev-help@xml.apache.org


Re: [RT] Forms and wizards (was: RE: HEADS UP - cocoon form handling (long!!))

Posted by Mats Norén <ma...@alma.nu>.
Ovidiu,
I have some stupid user questions again ;)

I´ve been snooping around in your code and I´m wondering which way
do you feel is the simplest way of integrating Java components into
schecoon?

Would it be possible in an easy way to reuse some of the actions already
present in C2.
For instance the old database actions or maybe the new ones?

And for my last question, can I send parameters from the sitemap to the
function I´m invoking:

<map:call function="function({1})"/>

or maybe

<map:call function="function()">
    <map:param name="a" value="{1}"/>
</map:call>

In the full continuation method I don´t see the need to send any parameters
but in the method you described below I need a  way to pass on information
encoded in the uri and not from the request parameters.
For example:

sitemap.xmap:

  <map:resource>
    <map:script src="detail.js"/>
  </map:resource>

  <!-- a detail contains detailed information about the object with id
{2} -->
  <!-- {1} action to perform, {2} id of detail. -->
  <map:match pattern="detail/*/*.html">
    <map:call function="{1}">
        <map:parameter name="id" value="{2}"/>
    </map:call>
  </map:match>

detail.js:

function show(id)
{
     // the page at uri uses the ESQL taglib to retrieve information about
an object from DB
     sendPageAndContinue("show_detail_with_id", {"edit": "edit", "new" :
"new"});
}

function edit(id)
{
  // show edit view
  sendPageAndContinue("show_edit_view_for_id", {"update": "update", "delete"
: "delete"});
}

function update(id)
{
  getParametersFromRequest();
  writeInformationToDB
  show(id);
}

function new()
{
      id = incrementCounterInDB();
      createEmptyRecordInDB(id);
      edit(id);
}

function delete(id)
{
    deleteFromDBDetailWithId(id);
    showPageAndContinue("show_list_of_objects_uri")
}

/Regards Mats


----- Original Message -----
From: "Ovidiu Predescu" <ov...@apache.org>
To: "Daniel Fagerstrom" <da...@swipnet.se>
Cc: <co...@xml.apache.org>
Sent: Monday, April 15, 2002 9:55 AM
Subject: Re: [RT] Forms and wizards (was: RE: HEADS UP - cocoon form
handling (long!!))


> I think the same approach can be achieved with the control flow layer
> without having to store any "full" continuations on the server.
>
> You just need to map the requests to <map:call function="..."/> and
> implement the appropriate JavaScript functions on the server side to
> check the arguments, save them in the session object, and send back
> the approapriate page. Something like this, although I might have left
> out things you mention for brevity purposes:
>
> sitemap.xmap:
>
>   <map:resource>
>     <map:script src="survey.js"/>
>   </map:resource>
>
>   <map:match pattern="cocoonSurvey/*.html">
>     <map:call function="{1}"/>
>   </map:match>
>
>   <map:match pattern="internal/*.html" internal-only="true">
>     <map:generate src="{1}.xsp" type="serverpages"/>
>     <map:transform .../>
>     <map:serialize/>
>   </map:match>
>
>
> survey.js:
>
> function start()
> {
>   databaseFillSessionWithData();
>   sendPageAndContinue("internal/personal.html", {"continue": "personal"});
> }
>
> function personalPost()
> {
>   storeRequestArgumentsToSession();
>   sendPageAndContinue("internal/system.html", {"continue": "system"});
> }
>
> function system()
> {
>   storeRequestArgumentsToSession();
>   var page;
>
>   if (cocoon.session["platform"] == "linux")
>     page = "internal/linuxDetails.html";
>   else if (cocoon.session["platform"] == "macosx")
>     page = "internal/macoxDetails.html";
>   else ...
>
>   sendPageAndContinue(page, {"continue": "last"});
> }
>
> function databaseFillSessionWithData()
> {
>   /*
>    Call to a Java object to retrieve the user's data from a
>    database. The user identification is obtained from the request. The
>    result of calling the Java object is a hash table, another Java
>    object, anything containing the information for the user. This info
>    is placed in the session object.
>    */
> }
>
> function storeRequestArgumentsToSession()
> {
>   for (var argument in cocoon.request.arguments)
>     cocoon.session[argument] = cocoon.request.arguments[session];
> }
>
>
>
> As you see in the above example, the functions above have no
> connection between them, there's no shared variable or anything which
> they update, other than the session object. In fact updating
> JavaScript global variables would not make any difference in the next
> request, because there's no continuation stored on the server that
> contains the modified value.
>
> The sendPageAndContinue() function is very similar with sendPage()
> currently available in Schecoon, except that it doesn't stop the
> execution of the thread after sending the page to the user, and it
> doesn't save any continuation on the server side. Similarly with
> sendPage(), it takes as argument an object which is made available to
> the Cocoon pipeline. The XSP generator in my example makes use of it
> to set the value of the "action" attribute in the HTML form, which
> links to the next page. The mechanism is similar to the one described
> by you in your example. This is in fact what is commonly referred to
> as Continuation Passing Style, where the continuation is passed to the
> next execution block to make use of.
>
> The approach here and the one you describe are similar in
> functionality and shortcomings. The above version is much cleaner IMO,
> as it removes the flow of the application from the sitemap. The syntax
> is much easier to read, and since we use a full programming language,
> there's no need to modify the sitemap engine to add new constructs as
> needs evolve. Just write a new JavaScript function, or create a Java
> class which you can call from the control layer. Also I think it's a
> lot easier for new users to come up to speed on such an approach,
> compared to the similar sitemap-based implementation.
>
> Of course the same scenario might be implemented in the control flow
> layer using full continuations, in which case the implementation would
> be a little different, since everything could be written in one
> function.
>
> As you notice, there are some differences in how a developer thinks
> when describing the flow using a continuations based approach and when
> the classical one is used. But you're free to choose the option that
> fits you best, the control flow layer gives you this flexibility. You
> can even mix and match in the same implementation to use continuations
> for one portion, and the session state for the rest.
>
> Regards,
> --
> Ovidiu Predescu <ov...@apache.org>
> http://www.geocities.com/SiliconValley/Monitor/7464/ (GNU, Emacs, other
stuff)
>
> On Mon, 15 Apr 2002 00:00:54 +0200, "Daniel Fagerstrom"
<da...@swipnet.se> wrote:
>
> > Berin Loritsch wrote:
> > > Ivelin Ivanov wrote:
> > <snip/>
> > > > Would you scetch an example of a non-trivial app which does not use
> > > > JavaBeans?
> > <snip/>
> > > I know I am side-stepping your question, however I have not run into
> > > a situation where I needed beans at all.  So your assumption that
every
> > > non-trivial application needs beans is not valid.
> > <snip/>
> > > What you invariably run into in the Cocoon world if you use beans is
> > > a double mapping: DB to bean, and bean to schema.  That approach does
> > > not scale well at all.  Not to meantion there are unnecessary
> > > conversions that can be the source of problems.  KISS (Keep It
> > > Simple Stupid).
> > >
> > > Keep your memory lean.  Beans don't let that happen--or they force
> > > you to be too smart for your own good.
> >
> > I find Ivelins and Torstens work on form handling very promising, but I
> > share Berins (and some other commenter), concern that they are maybe
trying
> > to find answers to some unnecessarily complicated problems.
> >
> > I'll discuss form handling an multipage form handling (wizards), and try
to
> > give some proposals on how to _not_ solve some of open questions and
issues
> > discussed in
> > http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=101834333817122&w=2.
While
> > at it, I'll also share some provocative thoughts about continuations and
> > show some heavy use of the hammer anti pattern ;)
> >
> > One page forms
> > --------------
> >
> > We start with a simple one page form. Say that we want to collect some
data
> > about a user of our system. Typically we want the form to be partially
> > filled in with some default data or something that the user already have
> > filled in. So we start with something like:
> >
> > <map:match pattern="userForm.html">
> >   <map:generate src="userDefault.xml"/>
> >   <map:transform src="userForm.xsl"/>
> >   <map:serialize/>
> > </map:match>
> >
> > Here the input data is xml (I guess I don't have to argue about why that
is
> > a good idea ;) ), it might be directly from a file, from a db, from
> > JavaBeans or whatever you might prefer. "userForm.xsl" is a simple
> > stylesheet that creates a partially filled in html form from its input,
(you
> > can use Velocity or some other template generator instead if you like).
To
> > make the connection between the xml input data and the form field names
> > simple we use xpaths as field names as is done in XForms (Ivelin,
Torsten
> > and Konstantin do the same). The form stylesheet will have fields like
this:
> >
> > <input type="text" name="/user/name" value="{/user/name}"/>
> >
> > As these form stylesheets are rather boring to write, we let our
computer do
> > the work:
> >
> > <map:match pattern="*Form.xsl">
> >   <map:generate src="{1}Form.xform"/>
> >   <map:transform src="xForm2xsl.xsl"/>
> >   <map:serialize type="xml"/>
> > </map:match>
> >
> > We can describe our form in terms of some appropriate subset of the form
> > control part of XForms or maybe in some home brewed form describing
> > language. "xForm2xsl.xsl" is a stylesheet that takes an Xform
description as
> > input and creates another stylesheet that in turns takes the default
data as
> > input and creates an html form. Konstantin have submitted something
similar:
> > http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=101429833513052&w=2 a
while
> > ago. He stays closer to the XForms standard, which contains the instance
as
> > a part of the xforms document, IMHO the XForms standard mixes concerns
by
> > doing so. Anyway by follow the XForms way of doing things Konstantin
don't
> > have to write xslt generating xslt, but have instead to use some
extension
> > functions (evaluate).
> >
> > Ok, now we can use our automatically generated form stylesheet:
> >
> > <map:match pattern="userForm.html">
> >   <map:generate src="userDefault.xml"/>
> >   <map:transform src="cocoon:/userForm.xsl?continue=userPost.html"/>
> >   <map:serialize/>
> > </map:match>
> >
> > We also use a "poor mans continuation" trick, by sending the url that
the
> > form is supposed to post to, as a request parameter to the form
stylesheet
> > generator. This makes the page flow explicit in the sitemap as it should
be.
> >
> > Time to take care of the data posted from the form:
> >
> > Input handling
> > --------------
> >
> > Even if we would prefer xml data in the post, it will probably take some
> > time before that is the usual case. By using xpaths as field names we
have
> > at least an implicit description of an xml document. So what we need to
do
> > is the following:
> > * Create an xml document from the request parameters
> > * Validate the input data
> >   Invalid -> Resend the partially filled in form with error messages
> > * Store or use the data
> > * Send a success page or the next form
> >
> > This can look like:
> >
> > <map:match pattern="userPost.html">
> >   <map:generate type="xrequest"/>
> >   <map:transform type="schematron" src="user.sch"/>
> >   <map:match type="pipe-state" test="fail">
> >     <map:transform src="cocoon:/userForm.xsl?continue=userPost.html"/>
> >     <map:serialize/>
> >   </map:match>
> >   <map:transform type="write" method="overwrite" src="dbxml:/user"/>
> >   <map:transform type="read" src="success.html"/>
> >   <map:serialize/>
> > </map:match>
> >
> > * The "xrequest" generator builds an input xml document from the (xpath)
> > request-parameters.
> > * The "schematron" transformer validates the input data, set some
> > "pipe-state" request attribute to "success" or "fail" and give some kind
of
> > report about the errors.
> > * The "pipe-state" matcher is pipe state aware (cf. the recent
discussions
> > about pipe aware selectors), i.e. it depends on the state of the
> > "pipe-state" request parameter as it is _after_ the execution of the
> > validator.
> > * If the validation failed the input is piped to the form stylesheet and
> > sent back to the user. The form stylesheet also have rules for rendering
the
> > error report.
> > * If the validation succeeds the data is sent to a store of some kind.
> > * A success report is generated.
> >
> > <design-issues>
> > In the above example collection of the data and storing of the data
> > (population of the instance) are separated while I&T combine them. There
are
> > so many possible storages for instance data e.g.: beans and dom in
session
> > and request attributes, files, relational db:s, xml db:s, business
objects
> > and so on, that it seem overwhelming to create a common "instance"
interface
> > for them all, better just put the data in the pipe and let the storage
of
> > it, be someone others concern.
> >
> > I&T also discuss the indirect vs. direct population problem and proposes
to
> > use direct population of instances (cf. the link above for details). The
> > example above uses the indirect approach, but could easily be made
direct by
> > giving a template instance as a "src" parameter. We have used designs
like
> > the one above for nearly a year in the company I work for and have never
had
> > any problems with indirect population, IMHO this is a concern for the
> > storage model.
> >
> > I think the result of our recent "pipe-awareness" discussion is that the
> > success or failure of a validation transformer should be put in some
"meta"
> > parameter, probably in a request attribute. We still need to find a good
way
> > to report the details of the errors. One possibilities is: having a
separate
> > result document with pairs of xpaths and error messages, (that explains
what
> > was wrong at that position). Another: annotating the document with error
> > attributes at the faulty elements, e.g.
> >
> > <foo>
> >   ...
> >    <bar err:error="not a number">qwerty</bar>
> > </foo>
> >
> > The first variant is more general and elegant, and the second is much
more
> > easy to use as input for a stylesheet. I prefer the second one :)
> >
> > The "write" transformer is mainly a thought experiment, it is like tee
in
> > unix. It does the same thing as the WritableSourceTransformer but the
source
> > name is in the sitemap instead than in the input document. I placed the
> > "method" attribute in it to indicate that if we want to make things like
xml
> > db:s writable sources, we have to find a way to describe what kind of
update
> > method we want to use.
> >
> > The "read" transformer doesn't care about its input and just puts the
> > content of its src attribute in the pipeline.
> >
> > In many cases, there is no already written source or writable source for
our
> > storage and we have to work a little bit harder, e.g. by writing own
> > mappings between e.g. xml and relational db:s or xml and java beans.
There
> > are some tools that can create at least part of the mapping from some
> > scheme, e.g. Castor and XML-DBMS http://www.rpbourret.com/xmldbms/.
> > </design-issues>
> >
> > Wizards
> > -------
> >
> > Here we define multi page forms (or wizards) as building one xml
document
> > from several form pages.
> >
> > I&T suggests that the structure of the instance and the views should be
as
> > decoupled as possible:
> >
> > >       [------------instance-----------]
> > >HTML: [-----view1-----][-----view2----]
> > >WML:  [--view1--][--view2--][--view3--]
> >
> > and also that validation could be done at still other substructures that
not
> > necessarily are connected to the views (cf their paragraph about views
and
> > phases).
> >
> > IMO one can simplify the problem considerably by deciding that the view
> > always are non-overlapping sub trees of the instance, and that we have
one
> > scheme for each view. I know that this is not a completely generic
solution,
> > but after all we probably did some modeling when we designed our
instance.
> > The sub trees probably describes conceptually different areas, so
hopefully
> > we can reuse this thinking by couple the views to these different areas.
If
> > on the other hand the instance is based on a lousy model, why don't
build a
> > new one for our wizard, then we can always use xslt to transform our
view
> > model to the instance model.
> >
> > Time for some code:
> >
> > <map:resource name="form">
> >   <map:transform
src="cocoon:/{query}Form.xsl?continue={query}Post.html"/>
> >   <map:serialize/>
> > </map:resource>
> >
> > <map:resource name="filled-form">
> >   <map:transform type="read" src="session:/{wizard}/{query}"/>
> >   <map:call name="form">
> >     <map:parameter name="query" value="{query}"/>
> >   </map:call>
> > </map:resource>
> >
> > <map:resource name="store">
> >   <map:generate type="xrequest"/>
> >   <map:transform type="schematron" src="{query}.sch"/>
> >   <map:match type="pipe-state" test="fail">
> >     <map:call name="form">
> >       <map:parameter name="query" value="{query}"/>
> >     </map:call>
> >   </map:match>
> >   <map:transform type="write" src="session:/{wizard}/{query}"/>
> > </map:match>
> >
> > Here I mainly restate our earlier code in terms of parameterized
resources,
> > so that I don't have to repeat myself all the time. The "wizard"
parameter
> > is for the root element of the xml document, and query is for the sub
> > documents and views. We store everything in a xml document in a session
> > attribute while filling in the queries in the wizard session.
> >
> > As an example we do a simple survey for cocoon users:
> >
> > <map:match pattern="cocoonSurvey/**">
> >   <map:parameter name="wizard" value="cocoonSurvey"/>
> >
> >   <map:match pattern="**/start.html">
> >     <map:act type="write">
> >       <map:parameter name="from"
src="dbxml:/cocoonSurvey[@id='default']"/>
> >       <map:parameter name="to" src="session:/cocoonSurvey"/>
> >     </map:act>
> >     <map:call name="filled-form">
> >       <map:parameter name="query" value="personal"/>
> >     </map:call>
> >   </map:match>
> >
> >   <map:match pattern="**/personalPost.html">
> >     <map:call name="store">
> >       <map:parameter name="query" value="personal"/>
> >     </map:call>
> >     <map:call name="filled-form">
> >       <map:parameter name="query" value="system"/>
> >     </map:call>
> >   </map:match>
> >
> >   <map:match pattern="**/systemPost.html">
> >     <map:call name="store">
> >       <map:parameter name="query" value="system"/>
> >     </map:call>
> >     <map:select type="xpath">
> >       <map:when test="/system/platform[.='linux']">
> >         <map:call name="filled-form">
> >           <map:parameter name="query" value="linuxDetails"/>
> >         </map:call>
> >       </map:when>
> >       <map:when test="/system/platform[.='windows']">
> >         <map:call name="filled-form">
> >           <map:parameter name="query" value="windowsDetails"/>
> >         </map:call>
> >       </map:when>
> >       <!-- ... -->
> >     </map:select>
> >   </map:match>
> >
> >   <map:match pattern="**/linuxDetailsPost.html">
> >   <!-- ... -->
> >   </map:match>
> >
> >   <map:match pattern="**/windowsDetailsPost.html">
> >   <!-- ... -->
> >   </map:match>
> >
> >   <map:match pattern="**/lastPost.html">
> >     <map:call name="store">
> >       <map:parameter name="query" value="last"/>
> >     </map:call>
> >     <map:act type="write">
> >       <map:parameter name="from" src="session:/cocoonSurvey"/>
> >       <map:parameter name="to" src="dbxml:/cocoonSurvey[@id='1234']"/>
> >     </map:act>
> >     <map:transform type="read" src="success.html"/>
> >     <map:serialize/>
> >   </map:match>
> > </map:match>
> >
> > In this example we fill the xml structure that we put in the session,
with
> > default data from a db in the beginning, and store the result in a db in
the
> > end. We use a pipe-aware selector to choose between several paths in the
> > wizard, depending on the last answer, (we could achieve even larger
> > flexibility by making choices based on xpath expressions applied on the
> > session data). Note that the flow control is based on the same
continuation
> > trick as we used in the "one page form" example. The value of the
"continue"
> > parameter is used for the submit button. Also note that we can use the
back
> > and forward button of our browser as much as we want. As soon as you
push
> > the submit button and your data is valid, the data for that page is
stored,
> > and as soon you push the refresh button on a form page, its current
content
> > will be filled in.
> >
> > A problem is that if one first say that one is a linux user and submit
the
> > linux data, and the go back and say that one is a windows user, and fill
in
> > the windows data, booth the windows and the linux data continue to be
> > stored. To handle this problem further mechanisms are needed.
> >
> > <design-issues>
> > >From a component point of view there is not much new in the wizard
compared
> > to the one page form. We use a writeable session source that is mainly a
> > non-existing repackaging of functionality already in the
> > SunShineTransformer, we could have used that instead, and the same
applies
> > to the "write" action. We also use a pipe-content-aware selector besides
the
> > pipe-state-aware selector.
> >
> > As a consequence of that we always store a page before showing a new the
> > pages will get misleading names, booth the linuxDetails and the
> > widowsDetails form page will have the url: systemPost.html. This can be
> > handled by having obscure names like large numbers so that no one
notices,
> > or by using redirect, which is considered bad. Are there other
alternatives
> > in the http protocol? Something like an internal redirect?
> > </design-issues>
> >
> > Continuations
> > -------------
> >
> > Why do I keep using the term "continuation" for the trick of sending the
> > address of the next page as an argument to the current generated page?
Isn't
> > a continuation an object that contains the whole current state of the
> > program? Actually booth descriptions are true, it all depends on what's
in
> > your program language.
> >
> > The sitemap together with the continue parameter is a program language
> > although a quite small one. It contains selection: select and match, and
it
> > contains global variables: session and request attributes, writable
> > resources and so on, and by using the continuation parameter we
introduces a
> > goto construction. Thus: a small programming language. In such a small
> > language a continuation is just a program pointer - in the sitemap case:
an
> > url. The control structures in structured programming: sequence,
selection
> > and repetitions can be translated to goto selection (and the other way
> > around). So it would be rather easy to translate the structured
programming
> > concepts mentioned to a sitemap as the one above.
> >
> > If we extend our language with (possibly recursive) functions, we need
to
> > take care of a call stack of program pointers also. This stack of uri:s
> > could be stored in a continuation object or in a hidden form field. If
we
> > introduce local variables in our language we need to put these in the
> > continuation object as well. Local variables introduces the possibility
for
> > "what if" scenarios, i.e. that you can have several independent
instances of
> > the same form page. For some kind of webapps: e.g. shopping carts and
> > checkout sequences, this kind of behavior is IMHO harmful, (cf
discussion
> > between Ovidiu and me:
> > http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=101856443021128&w=2,
> > http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=101858926504890&w=2,
> > http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=101861458122598&w=2,
> > http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=101870096719351&w=2). I
> > would propose that in a large class of webapps you don't need more than
the
> > continue parameter described above. Another aspect is of course that the
> > sitemap with continuation parameter sucks as a web application
programming
> > language. So you would anyway need a better language for describing
> > complicated flow, but I still wonder if something as powerful as
> > continuations with local variables is needed.
> >
> > Using the hammer anti-pattern
> > -----------------------------
> >
> > After all the recent discussion about what you not are supposed to do in
the
> > sitemap I can not help to feel like provoking a little ;)
> >
> > Principle: "Everything can and should be done in the sitemap" ;)
> >
> > As we saw in the examples above we have always to combine the handling
of
> > the input of one form with the construction of the next form. This is
> > because we need to have a match statement that surrounds the two. It
would
> > be more natural to combine the form generation with its input handler:
> >
> > <map:resource name="form-handling">
> >   <map:call name="filled-form">
> >     <map:parameter name="query" value="{query}"/>
> >   </map:call>
> >   <fm:label name="{query}Post.html"/>
> >   <map:call name="store">
> >     <map:parameter name="query" value="{query}"/>
> >   </map:call>
> > </map:resource>
> >
> > Here some kind of environment is supposed to detect that a serialize is
> > followed of a generate, and make a continuation available. "fm:label" is
a
> > way to give a name to the continuation if one don't want an automatic
one.
> >
> > Here is our example again:
> >
> > <map:match pattern="cocoonSurvey/**">
> >   <map:parameter name="wizard" value="cocoonSurvey"/>
> >
> >   <fm:sequence uri-prefix="cocoonSurvey">
> >
> >     <fm:label name="start.html"/>
> >     <map:act type="write">
> >       <map:parameter name="from"
src="dbxml:/cocoonSurvey[@id='default']"/>
> >       <map:parameter name="to" src="session:/cocoonSurvey"/>
> >     </map:act>
> >
> >     <map:call name="form-handling">
> >       <map:parameter name="query" value="personal"/>
> >     </map:call>
> >
> >     <map:call name="form-handling">
> >       <map:parameter name="query" value="system"/>
> >     </map:call>
> >
> >     <fm:select type="xpath">
> >       <fm:when test="/system/platform[.='linux']">
> >         <map:call name="form-handling">
> >           <map:parameter name="query" value="linuxDetails"/>
> >         </map:call>
> >       </fm:when>
> >       <fm:when test="/system/platform[.='windows']">
> >         <map:call name="filled-form">
> >           <map:parameter name="query" value="windowsDetails"/>
> >         </map:call>
> >       </fm:when>
> >       <!-- ... -->
> >     </fm:select>
> >
> >     <!-- ... -->
> >
> >     <map:act type="write">
> >       <map:parameter name="from" src="session:/cocoonSurvey"/>
> >       <map:parameter name="to" src="dbxml:/cocoonSurvey[@id='1234']"/>
> >     </map:act>
> >
> >     <map:transform type="read" src="success.html"/>
> >     <map:serialize/>
> >
> >   </fm:sequence>
> > </map:match>
> >
> > The new construction "fm:sequence" handles the url as "map:mount" it
also
> > automates continuation handling by analyzing its content pipeline
components
> > and making an url to each generator be available in a continuation
parameter
> > for the components earlier in the pipe up to the next generator.
> >
> > Implementation
> > --------------
> >
> > If we exclude the last section what do we need to implement if we would
like
> > to have what I describe above? Not much actually, we need the "xrequest"
> > generator, a validation transformer and pipe aware selectors. It could
be
> > nice to have an "xForm2xsl.xsl" stylesheet also, but it is not
necessary,
> > one can write form stylesheets manually also. The read and write
> > transformers and the new writable sources are not necessary at all there
are
> > already transformers that do the same work. But maybe not as smooth in
the
> > proposed framework.
> >
> > Torsten have already implemented most of the functionality of an
"xrequest"
> > generator but as an action, it would be fairly easy to repackage it as a
> > generator, I have an implementation that I can donate "as is", although
it
> > would need some more polishing.  Torsten and Ivelin have also
implemented
> > some different variants of validation transformers. And I contributed a
> > prototype implementation of pipe-aware selection some while ago. Booth
would
> > need some small adjustments to work together as described above.
> >
> > The great unsolved problems
> > ---------------------------
> >
> > There are probably tons of unsolved problems, but two particularly
tricky
> > are:
> > * Order restrictions: E.g. A user cannot go back after having committed
a
> > certain page.
> > * There might be several paths from the first to the last page in the
> > wizard, only the data submitted in pages along the path that was chosed
in
> > the end should be stored in the end. An instance of this problem was
> > described in the end of the "wizard" section.
> >
> > I have no solution to these problems, but I think that they become
easier to
> > solve if there is a simple and explicit connection, between forms and
> > submitted data.
> >
> >
> > That's more than enough ;)
> >
> > What do you think?
> >
> > /Daniel Fagerstrom
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: cocoon-dev-unsubscribe@xml.apache.org
> For additional commands, email: cocoon-dev-help@xml.apache.org
>


---------------------------------------------------------------------
To unsubscribe, e-mail: cocoon-dev-unsubscribe@xml.apache.org
For additional commands, email: cocoon-dev-help@xml.apache.org


RE: [RT] Forms and wizards (was: RE: HEADS UP - cocoon form handling (long!!))

Posted by Daniel Fagerstrom <da...@swipnet.se>.
Ovidiu Predescu wrote:
<snip/>
> The approach here and the one you describe are similar in
> functionality and shortcomings. The above version is much cleaner IMO,
> as it removes the flow of the application from the sitemap.
Yes, from a flow perspective they do about the same thing. From an input
handling perspective they do not, the main topic in my post was how to
handle XML-input, and your example don't. Handling XML-input might not be an
important use of Cocoon right now for most people. But with the growing
popularity of SOAP, XML-RPC, WebDAV, and more specialized XML-based
protocols, I think it will be an important use of Cocoon, or at least it can
be if we find it worthwhile to provide good tools for it. In my experience
form handling becomes much cleaner if one transform the form input to xml as
the first step while handling input. We have build webapps along the lines I
described for nearly a year at the company I work for, we have also tried
the action based approach, and it is so much more productive to use
XML-input.

Ok, suppose that we want to use XML input, then we might want to do thing
like: validate it, transform it to another format, store it, present it,
send it to an application and so on. Many of these operations have XML both
as input and output, it is of course possible to glue such operations
together in Java or JavaScript or to use Actions, but IMO it is much cleaner
to connect the operations together as a pipeline. By doing this we can also
reuse all the existing pipeline components in cocoon, and one can manage
fairly complex input handling without needing to write any program code. Do
you think it would be possible to connect "input"-pipelines to Schecoon?

> The syntax
> is much easier to read, and since we use a full programming language,
> there's no need to modify the sitemap engine to add new constructs as
> needs evolve. Just write a new JavaScript function, or create a Java
> class which you can call from the control layer.
Ever heard about FS ;) The great strength with Cocoon as a publishing
framework is that it uses a few, very carefully chosen concepts, and by
using these few concepts one can easily build sites that would be so much
harder to build in e.g. JSP. Why should we use the opposite strategy and
give the users full flexibility but no guiding in the flow handling case?
Isn't our task to develop the concepts that makes building webapps easy, I
found it hard to believe that the best answer is: introduce a full
programming language.

I believe that continuations is one of the concepts that are going to make
webapps easy, but I think we have to be very careful about what kind of
continuations we should use.

I think the best way to keep focus on what is important and avoiding the
temptations of FS, is to evaluate the new ideas against realistic use cases.
In our earlier discussion, we evaluated how to use continuations for a
shopping site. In the shopping site examples there are (at least) two
classes of user interactions: managing the shopping cart and the checkout
sequence. In booth cases we found that using full continuations would lead
to unwanted behavior of the application. After all, having several parallel
instances of the shopping basket with different content would be rather
confusing. I would also be frustrating if you, if you go back and change
some data in the first page in a checkout sequence, after having filled in
the rest, and find that everything you wrote in the rest of the pages are
gone. You solved this by adding shared global variables, and the
sendPageAndContinue function and continuation passing style.

Now I wonder, where are the realistic and important use cases where we
really need the full continuation machinery and all the extra complexiy and
storage requirements that it introduces?

> Also I think it's a
> lot easier for new users to come up to speed on such an approach,
> compared to the similar sitemap-based implementation.
Maybe, when I taught about continuations in computer science courses a
number of years ago, I never got the impression that people found
continuations easy to grasp ;), maybe it was just a defect in my teaching
skills.

> Of course the same scenario might be implemented in the control flow
> layer using full continuations, in which case the implementation would
> be a little different, since everything could be written in one
> function.
Please show how, and describe how to make it possible to go back and make
changes in early pages without losing the content of the ones after.

> As you notice, there are some differences in how a developer thinks
> when describing the flow using a continuations based approach and when
> the classical one is used. But you're free to choose the option that
> fits you best, the control flow layer gives you this flexibility. You
> can even mix and match in the same implementation to use continuations
> for one portion, and the session state for the rest.
So if I write "what if" scenarios I can do it in a really elegant and neat
way. And if I write more conventional webapps, I should by all means avoid
storing important data in local variables and I have to program continuation
passing style, which is less neat. Is this really the right priority order
between the use cases?

/Daniel Fagerstrom



---------------------------------------------------------------------
To unsubscribe, e-mail: cocoon-dev-unsubscribe@xml.apache.org
For additional commands, email: cocoon-dev-help@xml.apache.org


Re: [RT] Forms and wizards (was: RE: HEADS UP - cocoon form handling (long!!))

Posted by Diana Shannon <te...@mac.com>.
Ovidiu Predescu wrote:

> Also I think it's a
> lot easier for new users to come up to speed on such an approach,
> compared to the similar sitemap-based implementation.

I think this is a crucial point. At first, I was too invested in my 
"sitemap on steroids" to begin to appreciate the freedom and elegance of 
Ovidiu's flow map. Try to think back what it might be like if you were 
learning Cocoon for the first time. When you hold up many of the 
informative examples (on this list to date) of the two approaches, 
Schecoon would be so much more compelling for a newbie, at least in my 
opinion. (And, I don't think the fact it requires Javascript is a 
hurdle, given the likelihood of most people's prior experience on the 
client-side.) In my early experiences with the sitemap, I really 
resented having to sift around code to understand/remember how this or 
that compiled action worked -- even if I had programmed it myself. I had 
to build lots of pipelines before I could abstract the problem down to 
the correct and elegant few that I actually needed. It took me a week to 
get my wizard working satisfactorily (ok, I may be a slow learner, but 
this was before the public efforts of others on this list). With the 
flow map, prototyping/experimenting is a delight. I just feel I spend 
more time on the problem I'm trying to solve, not the implementation. 
Now that I've played with Schecoon, returning to a sitemap-only approach 
feels, well, burdensome.

Diana


---------------------------------------------------------------------
To unsubscribe, e-mail: cocoon-dev-unsubscribe@xml.apache.org
For additional commands, email: cocoon-dev-help@xml.apache.org