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" <[EMAIL PROTECTED]> To: "Daniel Fagerstrom" <[EMAIL PROTECTED]> Cc: <[EMAIL PROTECTED]> 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 <[EMAIL PROTECTED]> > http://www.geocities.com/SiliconValley/Monitor/7464/ (GNU, Emacs, other stuff) > > On Mon, 15 Apr 2002 00:00:54 +0200, "Daniel Fagerstrom" <[EMAIL PROTECTED]> 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: [EMAIL PROTECTED] > For additional commands, email: [EMAIL PROTECTED] > --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, email: [EMAIL PROTECTED]