Return-Path: Delivered-To: apmail-xml-cocoon-cvs-archive@xml.apache.org Received: (qmail 87355 invoked by uid 500); 24 May 2003 18:51:28 -0000 Mailing-List: contact cocoon-cvs-help@xml.apache.org; run by ezmlm Precedence: bulk Reply-To: cocoon-dev@xml.apache.org list-help: list-unsubscribe: list-post: Delivered-To: mailing list cocoon-cvs@xml.apache.org Received: (qmail 87344 invoked by uid 500); 24 May 2003 18:51:27 -0000 Delivered-To: apmail-cocoon-2.1-cvs@apache.org Received: (qmail 87340 invoked from network); 24 May 2003 18:51:27 -0000 Received: from icarus.apache.org (208.185.179.13) by daedalus.apache.org with SMTP; 24 May 2003 18:51:27 -0000 Received: (qmail 11937 invoked by uid 1544); 24 May 2003 18:51:26 -0000 Date: 24 May 2003 18:51:26 -0000 Message-ID: <20030524185126.11936.qmail@icarus.apache.org> From: coliver@apache.org To: cocoon-2.1-cvs@apache.org Subject: cvs commit: cocoon-2.1/src/documentation/xdocs/userdocs/flow api.xml jpath.xml jxtemplate.xml sitemap.xml using.xml velocity.xml views.xml book.xml continuations.xml how-does-it-work.xml X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N coliver 2003/05/24 11:51:26 Modified: src/documentation/xdocs/userdocs/flow book.xml continuations.xml how-does-it-work.xml Added: src/documentation/xdocs/userdocs/flow api.xml jpath.xml jxtemplate.xml sitemap.xml using.xml velocity.xml views.xml Log: improve flowscript docs (still sucks though, unfortunately) Revision Changes Path 1.3 +7 -0 cocoon-2.1/src/documentation/xdocs/userdocs/flow/book.xml Index: book.xml =================================================================== RCS file: /home/cvs/cocoon-2.1/src/documentation/xdocs/userdocs/flow/book.xml,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- book.xml 25 Mar 2003 13:53:56 -0000 1.2 +++ book.xml 24 May 2003 18:51:26 -0000 1.3 @@ -14,5 +14,12 @@ + + + + + + + 1.2 +3 -3 cocoon-2.1/src/documentation/xdocs/userdocs/flow/continuations.xml Index: continuations.xml =================================================================== RCS file: /home/cvs/cocoon-2.1/src/documentation/xdocs/userdocs/flow/continuations.xml,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- continuations.xml 25 Mar 2003 13:43:33 -0000 1.1 +++ continuations.xml 24 May 2003 18:51:26 -0000 1.2 @@ -35,13 +35,13 @@ var a, b, operator; sendPageAndWait("getA.html"); - a = request.getParameter("a"); + a = cocoon.request.get("a"); sendPageAndWait("getB.html"); - b = request.getParameter("b"); + b = cocoon.request.get("b"); sendPageAndWait("getOperator.html"); - operator = request.getParameter("op"); + operator = cocoon.request.get("op"); try { if (operator == "plus") 1.2 +1 -1 cocoon-2.1/src/documentation/xdocs/userdocs/flow/how-does-it-work.xml Index: how-does-it-work.xml =================================================================== RCS file: /home/cvs/cocoon-2.1/src/documentation/xdocs/userdocs/flow/how-does-it-work.xml,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- how-does-it-work.xml 25 Mar 2003 13:43:33 -0000 1.1 +++ how-does-it-work.xml 24 May 2003 18:51:26 -0000 1.2 @@ -35,7 +35,7 @@

Since continuations are objects, you can also store them in a database, for really long-lived session, just like you do with session beans.

- + 1.1 cocoon-2.1/src/documentation/xdocs/userdocs/flow/api.xml Index: api.xml ===================================================================
Advanced Control Flow

Cocoon provides a JavaScript API to manage control flow based on an extended version of Mozilla Rhino that supports continuations.

void sendPage([String] uri, [Object] bean)

Passes control to the Cocoon sitemap to generate the output page.

uri is the relative URL of the page to be sent back to the client.

bean is a context object which can be accessed inside this page toextract various values and place them in the generated page.

WebContinuation sendPageAndWait([String] uri, [Object] bean, [Number] timeToLive)

Passes control to the Cocoon sitemap to generate the output page.

The flow script is suspended after the page is generated and the whole execution stack saved in a the WebContinuation object returned from this function.

uri is the relative URL of the page to be sent back to the client. bean is a context object which can be accessed inside this page to extract various values and place them in the generated page.

timeToLive is the time to live for the continuation created.

The return value is the continuation object.

The cocoon object represents the current Cocoon sitemap and provides the following readonly properties:

The current Cocoon request:

[org.apache.cocoon.environment.Request] request

The current Cocoon response:

[org.apache.cocoon.environment.Response] response

The current Cocoon session:

[org.apache.cocoon.environment.Session] session

The current Cocoon application context:

[org.apache.cocoon.environment.Context] context

The current Cocoon environment:

[org.apache.cocoon.environment.Environment] environment

The current Sitemap's component manager:

[org.apache.avalon.framework.component.ComponentManager] componentManager

Any parameters passed to the script by the Sitemap:

[Array] parameters

The cocoon object also provides the following functions:

[Boolean] process([String] uri, [Object] bizData, [java.io.OutputStream] stream)

Call the Cocoon sitemap for the given URI, sending the output of the eventually matched pipeline to the specified outputstream.

uri is the URI for which the request should be generated.

bizData is the business data object to be made available to the forwarded pipeline

stream is an OutputStream where the output should be written to.

Returns the result from the Cocoon processor.

void createSession()

Associate the current set of JavaScript global variables with the user's session. This means that all invocations from the sitemap of a JavaScript function (using the <map:call function="...">), will share global JavaScript variables.

void removeSession()

Dissociate global JavaScript variables from the user's session.

void load([String] uri)

Load the JavaScript script specified by uri. The Cocoon source resolver is used to resolve uri.

1.1 cocoon-2.1/src/documentation/xdocs/userdocs/flow/jpath.xml Index: jpath.xml ===================================================================
Advanced Control Flow

The JPath Logic Sheet is an XSP logic sheet that allows you to access data from a Cocoon Flowscript in an XSP page and inject it into a Cocoon pipeline. It provides a set of tags (similar to the those defined by XSLT) that allow you to iterate over Java collections (and Java or JavaScript arrays) and to test for the presence of optional or alternate bean properties. It is based on Apache JXPath.

The JPath tags are defined in the namespace

http://apache.org/xsp/jpath/1.0

The if tag allows the conditional execution of its body according to value of a test attribute:

<if test="XPathExpression"> body </if>

Example: <jpath:if test="cart/numberOfItems = 0"> Your cart is empty </jpath:if>

The choose tag performs conditional block execution by the embedded when sub tags. It renders the body of the first when tag whose test condition evaluates to true. If none of the test conditions of nested when tags evaluate to true, then the body of an otherwise tag is evaluated, if present:

<choose> <when test="XPathExpression"> body </when> <otherwise> body </otherwise> </choose>

Example: <choose> <when test="not(user/loggedIn)"> You're not logged in </when> <otherwise> You're already logged in </otherwise> </choose>

The value-of tag evaluates an expression and outputs the result of the evaluation:

<value-of select="XPathExpression"/>

Example: <value-of select="cart/numberOfItems">

The forEach tag allows you to iterate over a collection of objects:

<forEach select="XPathExpression"> body </forEach>

When using XPath expressions within for-each the current element is the context node and can be referenced with:

.

Example: <for-each select="cart/cartItems[position() <= $count]"> <td><value-of select="./productId"></td> </forEach>

The continuation tag returns the id of the current web continuation of your Flowscript. You can refer to previous continuations by supplying the level attribute. Zero is the current level, -1 refers to the previous continuation, and so on.

<continuation [level="Number"]/>

Example: <xsp:attribute name="action">"kont/"+<jpath:continuation><xsp:attribute>

1.1 cocoon-2.1/src/documentation/xdocs/userdocs/flow/jxtemplate.xml Index: jxtemplate.xml ===================================================================
Advanced Control Flow

The JXTemplate Generator is a page template processor that allows you to inject data from Java and JavaScript objects passed by a Cocoon Flowscript into a Cocoon pipeline. It provides a set of tags (similar to the JSTL core tags) that allow you to iterate over Java collections (and Java or JavaScript arrays) and to test for the presence of optional or alternate bean properties, as well as embedded expressions to specify conditions and to access the properties of objects. The JXTemplate Generator gets its name from the embedded expression languages it supports, namely Apache JXPath and Apache JeXl.

The JXTemplateGenerator supports two embedded expression languages: Jexl and JXPath. Apache Jexl provides an extended version of the expression language of the JSTL. Apache JXPath provides an interpreter of the XPath expression language that can apply XPath expressions to graphs of Java objects of all kinds: JavaBeans, Maps, Servlet contexts, DOM etc, including mixtures thereof.

Having an embedded expression language allows a page author to access an object using a simple syntax such as

<site signOn="${accountForm.signOn}">

Embedded Jexl expressions are contained in ${}

Embedded JXPath expressions are contained in #{}

The referenced objects may be Java Beans, DOM, JDOM, or JavaScript objects from a Flowscript. In addition the following implicit objects are available as both JXPath and Jexl variables in a template:

request
The Cocoon current request
response
The Cocoon response associated with the current request
session
The Cocoon session associated with the current request
context
The Cocoon context associated with the current request
parameters
Any parameters passed to the generator in the pipeline

Jexl Example: The content type of the current request is ${request.contentType}

JXPath Example: The content type of the current request is #{request/contentType}

The current Web Continuation from the Flowscript is also available as a variable named continuation. You would typically access its id: <form action="${continuation.id}">

You can also reach previous continuations by using the getContinuation() function:

<form action="${continuation.getContinuation(1).id}" >

or using an XPath expression:

<form action="#{getContinuation($continuation)/id}" >

The JXTemplateGenerator tags are defined in the namespace

http://apache.org/cocoon/templates/jx/1.0

The template tag defines a new template:

<t:template xmlns:t="http://apache.org/cocoon/templates/jx/1.0"> body </t:template>

The import tag allows you to include another template within the current template. The content of the imported template is compiled and will be executed in place of the import tag:

<import uri="URI" [context="Expression"]/>

The Cocoon source resolver is used to resolve uri. If context is present, then its value is used as the context for evaluating the imported template, otherwise the current context is used.

The set tag creates a local alias of an object. The var attribute specifies the name of a variable to assign the object to. The value attribute specifies the object (defaults to body if not present):

<set var="Name" [value="Value"]> [body] </set>

If used within a macro definition (see below) variables created by set are only visible within the body of the macro.

Jexl Example: <set var="greeting" value="Hello ${user}"/> ${greeting}

JXPath Example: <set var="greeting" value="Hello #{user}"/> #{$greeting}

The if tag allows the conditional execution of its body according to value of a test attribute:

<if test="Expression"> body </if>

Jexl Example: <if test="${cart.numberOfItems == 0}"> Your cart is empty </if>

JXPath Example: <if test="#{cart/numberOfItems = 0}"> Your cart is empty </if>

The choose tag performs conditional block execution by the embedded when sub tags. It renders the body of the first when tag whose test condition evaluates to true. If none of the test conditions of nested when tags evaluate to true, then the body of an otherwise tag is evaluated, if present:

<choose> <when test="Expression"> body </when> <otherwise> body </otherwise> </choose>

Jexl Example: <choose> <when test="${!user.loggedIn}"> <set var="label" value="Log in"> </when> <otherwise> <set var="label" value="Log out"> </otherwise> </choose>

JXPath Example: <choose> <when test="#{not(user/loggedIn)}"> <set var="label" value="Log in"> </when> <otherwise> <set var="label" value="Log out"> </otherwise> </choose>

The out tag evaluates an expression and outputs the result of the evaluation:

<out value="Expression"/>

Jexl Example: <out value="${cart.numberOfItems}">

JXPath Example: <out value="#{cart/numberOfItems}">

The forEach tag allows you to iterate over a collection of objects:

<forEach [var="Name"] [items="Expression"] [begin="NumExpr"] [end="NumExpr"] [step="NumExpr"]> body </forEach>

The items attribute specifies the list of items to iterate over. The var attribute specifies the name of a variable to hold the current item. The begin attribute specifies the element to start with (0 = first item, 1 = second item, ...). If unspecified it defaults to 0. The end attribute specifies the item to end with (0 = first item, 1 = second item, ...). If unspecified it defaults to the last item in the list. Every step items are processed (defaults to 1 if step is absent). Either items or both begin and end must be present.

An alternate form of forEach is supported for convenience when using XPath (since you can specify the selection criteria for the collection using XPath itself):

<forEach select="XPathExpression"> body </forEach>

When using XPath expressions within forEach the current element is the context node and can be referenced with:

#{.}

Jexl Example: <forEach var="item" items="${cart.cartItems} begin="${start}" end="${count-start}" step="1""> <td>${item.productId}</td> </forEach>

JXPath Example: <forEach items="#{cart/cartItems[position() <=$count]}}> <td>#{./productId}</td> </forEach>

The macro tag allows you define a new custom tag.

<macro name="Name" [targetNamespace="Namespace"]> <parameter name="Name" [optional="Boolean"] [default="Value"]/> body </macro>

For example:

<c:macro name="d"> <tr><td></td></tr> </c:macro>

The tag being defined in this example is <d> and it can be used like any other tag:

<d/>

However, when this tag is used it will be replaced with a row containing a single empty data cell.

When such a tag is used, the attributes and content of the tag become available as variables in the body of the macro's definition, for example:

<c:macro name="tablerows"> <c:parameter name="list"/> <c:parameter name="color"/> <c:forEach var="item" items="${list}"> <tr><td bgcolor="${color}">${item}</td></tr> </c:forEach> </c:macro>

The parameter tags in the macro definition define formal parameters, which are replaced with the actual attribute values of the tag when it is used. The content of the tag is also available as a special variable ${content}.

Assuming you had this code in your flowscript:

var greatlakes = ["Superior", "Michigan", "Huron", "Erie", "Ontario"];

sendPage(uri, {greatlakes: greatlakes});

and a template like this:

<table> <tablerows list="${greatlakes}" color="blue"/> </table>

When the tablerows tag is used in this situation the following output would be generated:

<table> <tr><td bgcolor="blue">Superior</td></tr> <tr><td bgcolor="blue">Michigan</td></tr> <tr><td bgcolor="blue">Huron</td></tr> <tr><td bgcolor="blue">Erie</td></tr> <tr><td bgcolor="blue">Ontario</td></tr> </table>
1.1 cocoon-2.1/src/documentation/xdocs/userdocs/flow/sitemap.xml Index: sitemap.xml ===================================================================
Advanced Control Flow

The Cocoon sitemap includes several elements to specify interactions with your Flowscripts

The flow element defines a Flowscript interpreter for a sitemap. The language attribute specifies the target programming language. Currently the only supported language is "JavaScript". Its embedded script elements allow you to specify the files that make up the flow for this sitemap. Each script element specifies the URI of a script that will be compiled and executed when this Sitemap is created. The src attribute specifies the URI of the script.

+ ]]>

The call element allows you to call a top-level function in your Flowscript or to invoke an existing continuation.

If the function attribute is present, then the Sitemap will invoke a top-level function defined in your Flowscript. The function attribute specifies the name of the function. Zero or more nested parameter elements may be provided to pass arguments to the function.

* ]]>

If the continuation attribute is present, then the Sitemap will invoke an existing continuation of your Flowscript. The continuation attribute specifies the unique id of the continuation.

]]>

1.1 cocoon-2.1/src/documentation/xdocs/userdocs/flow/using.xml Index: using.xml ===================================================================
Advanced Control Flow

The general flow of actions in an application which uses the control flow is as described below.

The request is received by Cocoon and passed to the sitemap for processing. In the sitemap, you can do two things to pass the control to the Control Flow layer:

  • You can invoke a JavaScript top-level function to start processing a logically grouped sequences of pages. Each time a response page is being sent back to the client browser from this function, the processing of the JavaScript code stops at the point the page is sent back, and the HTTP request finishes. Through the magic of continuations, the execution state is saved in a continuation object. Each continuation is given a unique string id, which could be embedded in generated page, so that you can restart the saved computation later on.
  • To invoke a top level JavaScript function in the Control Flow, you use the <map:call function="function-name"/> construction.
  • To restart the computation of a previously stopped function, you use the <map:continue with="..."/> construction. This restarts the computation saved in a continuation object identified by the string value of the with attribute. This value could be extracted in the sitemap from the requested URL, from a POST or GET parameter etc. When the computation stored in the continuation object is restarted, it appears as if nothing happened, all the local and global variables have exactly the same values as they had when the computation was stopped.

Once the JavaScript function in the control layer is restarted, you're effectively inside the Control Flow. Here you have access to the request parameters, and to the business logic objects. The controller script takes the appropriate actions to invoke the business logic, usually written in Java, creating objects, setting various values on them etc...

When the business logic is invoked, you're inside the Model. The business logic takes whatever actions are needed, accessing a database, making a SOAP request to a Web service etc. When this logic finishes, the program control goes back to the Control Flow.

Once here, the Control Flow has to decide which page needs to be sent back to the client browser. To do this, the script can invoke either the sendPage or the sendPageAndContinue functions. These functions take two parameters, the relative URL of the page to be sent back to the client, and a context object which can be accessed inside this page to extract various values and place them in the generated page.

The second argument to sendPage and sendPageAndContinue is a context object, which can be a simple dictionary with values that need to be displayed by the View. More generally any Java or JavaScript object can be passed here, as long as the necessary get methods for the important values are provided.

The page specified by the URL is processed by the sitemap, using the normal sitemap rules. The simplest case is an XSP generator followed by an XSLT transformation and a serializer. This page generation is part of the View layer. If an XSP page is processed, you can make use of several Cocoon generators to retrieve values from the context objects passed by the Control Flow.

Going back to the sendPage and sendPageAndContinue functions, there is a big difference between them. The first function will send the response back to the client browser, and will stop the processing of the JavaScript script by saving it into a continuation object. The other function, sendPageAndContinue will send the response, but it will not stop the computation. This is useful for example when you need to exit a top-level JavaScript function invoked with <map:call function="..."/>.

The above explains how MVC could be really achieved in Cocoon with the control flow layer. Note that there is no direct communication between Model and View, everything is directed by the Control Flow by passing to View a context object constructed from Model data. In a perfect world, XSP should have only one logicsheet, the JXPath logicsheet. There should be no other things in an XSP page that put logic in the page (read View), instead of the Model. If you don't like XSP, and prefer to use JSP or Velocity, the JXPath logicsheet equivalents should be implemented.

Basic usage

As hinted in the previous section, an application using Cocoon's MVC approach is composed of three layers:

  • A JavaScript controller which implements the interaction with the client
  • The business logic model which implements your application
  • The XSP pages, which describe the content of the pages, and XSLT stylesheets which describe the look of the content.

In more complex applications, the flow of pages can be thought of smaller sequences of pages which are composed together. The natural analogy is to describe these sequences in separate JavaScript functions, which can then be called either from the sitemap, can call each other freely.

An example of such an application is the user login and preferences sample

This application is composed of four top-level JavaScript functions:

  • login,
  • registerUser,
  • edit and
  • logout.

The entry level point in the application can be any of these functions, but in order for a user to use the application, (s)he must login first. Once the user logs in, we want to maintain the Java User object which represents the user between top-level function invocations.

If the script does nothing, each invocation of a top-level function starts with fresh values for the global variables, no global state is preserved between top-level function invocations from the sitemap. In this sample for example, the login function assigns to the global variable user the Java User object representing the logged in user. The edit function trying to operate on this object would get a null value instead, because the value is not shared by default between these top-level function invocations.

To solve the problem, the login and registerUser functions have to call the cocoon.createSession() method, which creates a servlet session and saves the global scope containing the global variables' value in it. Next time the user invokes one of the four top-level functions, the values of the global variables is restored, making sharing very easy.

Even if you don't need complex control flow in your application, you may still choose to use the MVC pattern described above. You can have top- level JavaScript functions which obtain the request parameters, invoke the business logic and then call sendPageAndContinue to generate a response page and return from the computation. Since there's no continuation object being created by this function, and no global scope being saved, there's no memory resource being eaten. The approach provides a clean way of separating logic and content, and makes things easy to follow, since you have to look at a single script to understand what's going on.

1.1 cocoon-2.1/src/documentation/xdocs/userdocs/flow/velocity.xml Index: velocity.xml ===================================================================
Advanced Control Flow

If called from a Flowscript, the Cocoon Velocity Generator provides access to the immediate properties of the context object passed to sendPage and sendPageAndWait. In that case, the current WebContinuation is also available as a variable named $continuation You would typically access its id:

     <form action="$continuation.id">
  

You can also reach previous continuations by using the getContinuation() function:

      <form action="$continuation.getContinuation(1).id" >
  

In addition the following implicit objects are always available in the Velocity context:

request (org.apache.cocoon.environment.Request)
The current Cocoon request
response (org.apache.cocoon.environment.Response)
The Cocoon response associated with the current request
session (org.apache.cocoon.environment.Session)
The Cocoon session associated with the current request
context (org.apache.cocoon.environment.Context)
The Cocoon context associated with the current request
parameters (org.apache.avalon.framework.parameters.Parameters)
Any parameters passed to the generator in the pipeline

1.1 cocoon-2.1/src/documentation/xdocs/userdocs/flow/views.xml Index: views.xml ===================================================================
Advanced Control Flow

The second argument to sendPage and sendPageAndWait is a context object, which can be a simple dictionary with values that need to be displayed by the View. More generally any Java or JavaScript object can be passed here, as long as the necessary get methods for the important values are provided.

The page specified by the URL is processed by the sitemap, using the normal sitemap rules.

Several generators are provided that allow you to access the context object and inject its values into a pipeline.