cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Stefano Mazzocchi <stef...@apache.org>
Subject [RT] Flowmaps
Date Sun, 16 Jun 2002 15:52:38 GMT
Last time I wrote an RT about these things, the flowmap wasn't
implemented. Today it's working, but there are things that I would like
to change.

This RT is to start a discussion that will hopefully lead to a coherent
and community-driven design on what the flowmap engine will look like.

First of all, since there is no clear howto about the current flowmap
and I need to give you context in order to make you partecipate in the
discussion, I'll write a basic explaination of what's going on.

                              - oooo -

How Ovidiu implemented the flowmap concept
------------------------------------------

First of all, do a 'cvs checkout' of HEAD if you haven't done so. I'm
referring to the samples that you find in 

 /xml-cocoon2/src/webapp/samples/flow/examples/calc/

which is a sample about a very simple web application that implements a
calculator using the flowmap.

First of all, I'll outline the 'procedural flow logic' of this
application using highly-pseudo code:

 1) tell me the first operand
 2) tell me the second operand
 3) tell me the operation to apply
 4) here is your result

You'll see how much similar to the actual implementing code this will
look like. This is probably already a great advantage over anything that
we had before.

Let us look at the sitemap:

<map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">

  [skipping component declaration]

  <map:resources>
    <map:resource name="flow">
      <map:script src="calc.js"/>
    </map:resource>
  </map:resources>

  <map:pipelines>
    <map:pipeline>
      <map:match pattern="kont/*">
        <map:continue with="{1}"/>
      </map:match>

      <map:match pattern="">
        <map:call function="calculator">
          <map:parameter name="prefix"
value="/samples/flow/examples/calc/"/>
        </map:call>
      </map:match>

      <map:match pattern="*.html">
        <map:generate src="{1}.xsp" type="serverpages"/>
        <map:serialize/>
      </map:match>
    </map:pipeline>
  </map:pipelines>
</map:sitemap>

So, Ovidiu added three concepts that weren't found in the previous
sitemap implementations:

 1) a new "map:script" component connects a sitemap resource with a
flowmap (which is here implemented as a javascript program)

 2) a new attribute "map:call/@function" allows you to pass control from
the sitemap to the flowmap, calling the function defined in the
attribute and passing the nested "map:parameter" to the function as a
regular call.

 3) a "map:continue" element indicates that the flow must be continued
with the continuation passed in the attribute "@with".

Let's look at the flowmap:

  var prefix;

  function calculator(uriPrefix)
  {
    prefix = uriPrefix;
    var a = getNumber("a");
    var b = getNumber("b", a);
    var op = getOperator(a, b);

    if (op == "plus")
      sendResult(a, b, op, a + b);
    else if (op == "minus")
      sendResult(a, b, op, a - b);
    else if (op == "multiply")
      sendResult(a, b, op, a * b);
    else if (op == "divide")
      sendResult(a, b, op, a / b);
    else
      sendResult("Error: Unkown operator!");
  }

the 'calculator' function is the one that is called from the sitemap. As
you can see, the procedural logic of your flow is *very well* described:
I'm sure that it would takes a few minutes for anybody to understand
what your application is doing.

This is *NOT* possible with any other FSM-based approach.

If you think about it, in Cocoon 1.x we used PI to encode state
transitions in every stage, then we identified this as a problem and we
centralized it into the sitemap: the location where you can understand
what your URI does in a central and confortable location.

Here we are doing it again, but instead of contralizing declarative
client/server behavior, we are centralizing procedural web-app flow.

But let's keep looking into the flowmap:

   function getNumber(name, a, b)
   {
1     var uri = prefix + "getNumber" + name.toUpperCase() + ".html";
2     sendPage(uri, { "a" : a, "b" : b });
3     return parseFloat(cocoon.request.getParameter(name));
   }

This function collects the number required by the flow:

line 1: the URI of the resource to use is created
line 2: the URI is sent to the client, along with some parameters in a
map
line 3: a float is returned from the cocoon request parameter named with
the given name

Anybody who ever wrote a webapp would think we are nuts: line 2
generates a response and line 3 reads a request. But here is where the
flowmap gets magic, there are bunch of things that you don't see
happening.

So, let's look at that function from what really happens behind the
lines, let us suppose we call

  var a = getNumber("a");

then

a) the sitemap is called to process a the URI 'getNumberA.html'
b) the sitemap matches with this matcher

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

c) the sitemap executes the "getNumberA.xsp", which is given by

<xsp:page
  language="java"
  xmlns:xsp="http://apache.org/xsp"
  xmlns:jpath="http://apache.org/xsp/jpath/1.0"
>

<document>
 <body>
  <s1 title="Calculator">
   <form>
    <xsp:attribute name="action">
     <xsp:expr>"kont/" + <jpath:continuation/></xsp:expr>
    </xsp:attribute>

    <p>Enter value of <strong>a</strong>: 
          <input type="text" name="a"/></p>
    <input type="submit" name="submit" value="Enter"/>
   </form>
  </s1>
 </body>
</document>
</xsp:page>

where the "jpath" logicsheet is used to obtain the "continuation" of the
current flow and is then encoded in the URI called.

So, when the users hits 'submit', Cocoon will receive a request for an
hypotetical URI "kont/39849834983498", then Cocoon will match it with:

      <map:match pattern="kont/*">
        <map:continue with="{1}"/>
      </map:match>

and resurrect the flow logic from where it was left. So, again, between

   function getNumber(name, a, b)
   {
1     var uri = prefix + "getNumber" + name.toUpperCase() + ".html";
2     sendPage(uri, { "a" : a, "b" : b });
3     return parseFloat(cocoon.request.getParameter(name));
   }

line 2 and line 3, several things happen

 1) control is given to the sitemap
 2) the sitemap produces a response which encodes the continuation
 3) this response is sent to the client
 4) the client acts with the response
 5) cocoon receives a request for a continuation-decoding URI
 6) the flowmap is called with the given continuation (think of it as a
starting point)

If you think of the above as a command line environment, line 2 gives
you the dialog where to enter the number and line 3 uses the number
returned from the user.

[the rest of the flowmap is easy to understand if you understood so far,
so I'll skip it]

                              - oooo -

The problems with this approach
-------------------------------

First of all, let me say that I consider the above concept the biggest
advancement in server-side web technology since servlets.

This design not only makes it a breeze to implement MVC, but it shows
you how natural and clear things become once you separate the procedural
flow, from the declarative serving of resources.

At the same time, I think there are a number of issues that we might
solve before freezing the concept in a beta release:

1) the semantics to call a flowmap from a sitemap are too implicit:
users must assume to much from what they read (ie self-readability of
the concept is very poor).

2) the concept of continuations is not transparent, there is concern
overlap between the view designers and the sitemap managers since the
view designers must be aware of the URI location of the
continuation-decoding URI.

[NOTE: it is *not* required that you use continuations for your flow
logic, you can use whatever means you have to control state as you did
previously, such as REST-like passing style, sessions or cookies]

3) there is currently only one way to implement the MVC "view" and that
forces you to use XSP. This might not be a performance problem, but it
might become a usability problem.

Let's talk about each one of them independently.

[NOTE: Giacomo and I spent a full evening and the next day (during
"Italy vs. Mexico"! consider that!) talking about these things, so the
above reflects design concepts of both of us]

More explicit sitemap markup
----------------------------

Here is what I think would be a much better approach to connect a
sitemap and a flowmap:

<map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">

  <map:flowmaps default="calculator">
   <map:flowmap name="calculator" src="calc.js" language="javascript"/>
  </map:flowmaps>
  
  <map:pipelines>
   <map:pipeline>
    <map:match pattern="">
     <map:call function="calculator('prefix','/kont')"/>
    </map:match>

    <map:match pattern="kont/*">
     <map:call with-continuation="{1}"/>
    </map:match>

    <map:match pattern="*.html">
     <map:generate src="{1}.xsp" type="serverpages"/>
     <map:serialize/>
    </map:match>
   </map:pipeline>
  </map:pipelines>

</map:sitemap>

There are a couple of major differences:

 1) a flowmap isn't a sitemap resource, but it's something else. The
above semantics reflect this.

 2) you can have more than one flowmap in a sitemap (then you can define
a default one or use a 'flowmap' attribute in "map:call" to identify
which flowmap you are calling)

[of course, in order to accomplish this, the implementation must
completely isolate the different flowmaps and its global variables]

 3) the "map:call" element is used to call sitemap resources (pipelines)
and flowmap resources (functions), an attribute is used to indicate the
behavior that should be used and it's consistent with the rest of the
sitemap.

 4) the "map:call" element can use both nested map:parameters or
directly using the 'function(param, param)' syntax inside the attribute.
This makes it more readable in some cases.

Making continuations transparent
--------------------------------

I personally don't have a clear view of the usability of this concept
myself: I like it very much, it's clear and consistent and it's similar
to the session concept.

The only thing is that we might provide a
"ContinuationURLEncodingTransformer" of some sort to separate the
concern of those who write the forms and those who write the sitemap,
even if, passing the 'kont/' prefix as I showed above allows to keep the
URI space totally self-contained in the sitemap.

I'd be happy to hear more comments in this area (Daniel?)

More ways to get flowmap data
-----------------------------

Currently, in order to have access to flowmap data (parameters or
continuations), you need to use the XSP or write java code yourself (I'm
not even sure the latest is possible, Ovidiu?)

I'd like to have at least a few others:

 1) XSTL
 2) Velocity
 3) JSP?

I don't know how hard it is to implement them and I'd like to have
suggestions on how to implement at least the first one (the problem with
velocity is that its output must be parsed, unlike XSP, so Velocity
templates are inherently slower than a compiled XSP, but at least they
are easier to understand and to use for many, expecially HTML
designers).

Using JSP as an MVC view makes sense in those cases where a tool is
required for HTML designers to connect to the parameters passed to the
JSP. I personally don't care but others might.

                              - oooo -

Ok, almost there.

There is only one big thing missing: when I thought originally at the
flowmap concept, I wanted it to be interchangeable with the sitemap, now
I've changed my mind and I think it makes sense to have a sitemap always
up front, no matter how 'procedural' your web application is (might even
have a single URI for the whole thing and handle everything inside the
flowmap)

At the same time, when I was explaining the flowmap concept to the guys
at SwissRisk, we came up with an idea [gosh, forgot the name of the guy
who suggested me to think in that direction, hope he is subscribed and
makes himself known!].

Consider this flowmap call

   sendPage("hello.html");

this means:

  1) ask the sitemap to redirect to "hello.html"
  2) send the output to the client
  3) wait for a request to make me continue

Now, suppose we do this

   callPipeline("hello.html", input, output);

where we mean:

 1) call the internal "hello.html" URI connecting the input and output
that I give you.
 2) come back here without sending anything to the client

<drum-roll/>

VOILA'! We have the ability to use serializers to write on disk, without
even touching the sitemap!

So, consider this sitemap:

<map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">

  <map:flowmaps default="editor">
   <map:flowmap name="editor" src="editor.js" language="javascript"/>
  </map:flowmaps>
  
  <map:pipelines>

    <map:pipeline internal="true">
      <map:match pattern="save2disk">
        <map:generate type="stream">
          <map:parameter name="content" value="content"/>
        </map:generate>
        <map:serialize type="xml"/>
      </map:match>
    </map:pipeline>

    <map:pipeline>
      <map:match pattern="save">
       <map:call function="save2disk()"/>
      </map:match>
    </map:pipeline>

  </map:pipelines>
</map:sitemap>

and your flowmap is something like

function save2disk() {
	OutputStream output = new FileOutputStream("/usr/local/docs");
	callPipeline("save",cocoon.request, output);
}

it would be *THAT* easy!

[NOTE: the 'input' and 'output' parameters of the callPipeline() method
will have to be carefully choosen, here I'm just making this up as an
example, don't take this as a complete proposal, but just a way to
sparkle discussion]

                              - oooo -

There are a few important issues with the flowmap:

 1) documentation must be provided on the FOM (flowmap object model),
sort of a DOM for flowmaps. Currently, objects like 'cocoon', 'log' and
others are provided, but there is no documentation presenting this

 2) mappings between java and javascript: I don't know how much coupling
can be done between java and javascript in the flowmap, this must be
documented more.

                              - oooo -

Ok, please, send your comments.

Ciao.

-- 
Stefano Mazzocchi      One must still have chaos in oneself to be
                          able to give birth to a dancing star.
<stefano@apache.org>                             Friedrich Nietzsche
--------------------------------------------------------------------


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


Mime
View raw message