blob: 4c892635da02c5c94c04eaed6cfee25a98e483a0 [file] [log] [blame]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Advanced Control Flow</title>
<link href="http://purl.org/DC/elements/1.0/" rel="schema.DC">
<meta content="Ovidiu Predescu" name="DC.Creator">
</head>
<body>
<h1>Using Cocoon's Control Flow</h1>
<p>
The general flow of actions in an application which uses the control flow
is as described below.
</p>
<p>
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:</p>
<ul>
<li>
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.
</li>
<li>
To invoke a top level JavaScript function in the Control Flow, you use
the <a href="sitemap.html#callFunction"><span class="codefrag">&lt;map:call&nbsp;function="function-name"/&gt;</span></a>
construction.
</li>
<li>
To restart the computation of a previously stopped function, you use
the <a href="sitemap.html#callContinuation"><span class="codefrag">&lt;map:call&nbsp;continuation="..."/&gt;</span></a> construction.
This restarts the computation saved in a continuation object
identified by the string value of the <span class="codefrag">continuation</span> 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.
</li>
</ul>
<p>
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...
</p>
<p>
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.
</p>
<p>
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 one of the
<a href="api.html#sendPageAndWait"><span class="codefrag">cocoon.sendPageAndWait()</span></a> or <a href="api.html#sendPage"><span class="codefrag">cocoon.sendPage()</span></a> 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.
</p>
<p>
The second argument to <span class="codefrag">cocoon.sendPageAndWait()</span> and
<span class="codefrag">cocoon.sendPage()</span> 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.
</p>
<p>
The page specified by the URL is processed by the sitemap, using the
normal sitemap rules. The simplest case is a <a href="views.html">generator</a> followed by
an XSLT transformation and a serializer. This page generation is part of
the View layer. To process a page you can make use of several
Cocoon <a href="views.html">generators</a> to retrieve values from the context objects passed by the
Control Flow.
</p>
<p>
Going back to the <span class="codefrag">cocoon.sendPageAndWait()</span> and
<span class="codefrag">sendPage()</span> 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,
<span class="codefrag">cocoon.sendPage()</span> 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
<span class="codefrag">&lt;map:call&nbsp;function="..."/&gt;</span>.
</p>
<p>
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.
</p>
<h2>Basic usage</h2>
<p>
As hinted in the previous section, an application using Cocoon's MVC
approach is composed of three layers:</p>
<ul>
<li>
A JavaScript controller which implements the interaction with the
client
</li>
<li>
The business logic model which implements your application
</li>
<li>
The <a href="views.html">page templates</a>, which describe the content of the pages, and XSLT
stylesheets which describe the look of the content.
</li>
</ul>
<p>
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.
</p>
<p>
An example of such an application is the user login and preferences
sample
</p>
<p>
This application is composed of four top-level JavaScript functions:</p>
<ul>
<li>
<span class="codefrag">login</span>,</li>
<li>
<span class="codefrag">registerUser</span>,</li>
<li>
<span class="codefrag">edit</span> and</li>
<li>
<span class="codefrag">logout</span>.</li>
</ul>
<p>
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.
</p>
<p>
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 <span class="codefrag">cocoon.sendPage()</span> 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.
</p>
</body>
</html>