Hi,

I've finally had time to finalize some changes to the control flow  
layer, which should make it more usable. I've also wrote a simple  
application that makes use of these changes and shows how the control  
flow layer is supposed to be used. I also plan to write some real  
documentation on how to use the control flow layer. Before then  
however, I'll write here some quick thoughts on how this works. At some  
point I'll take these and transform them into a real document.

This is a long description describing potentially important stuff for  
you, so you may want to read through it with a fresh mind. So grab a  
cup of strong coffee before you start.

Model-View-Controller
=====================

With the control flow architecture in place a Cocoon Web application is  
split in three different conceptual layers, using the well-known  
Model-View-Controller (MVC) pattern:

- Model: the business logic, e.g. Java classes which implements the  
meat of your application.

- View: the collection of the XML pages and XSLT stylesheets that give  
a visual representation to your business objects. In Cocoon the pages  
used in the View layer can be simple XML files or XSP pages that use  
the JXPath logicsheet. More on this later.

- Controller, which is coordinating the sequence of pages being sent to  
the client browser, based on the actions taken by the user. In Cocoon,  
the Controller is a specialized engine which uses a modified Rhino  
JavaScript implementation which supports continuations as first class  
objects. From a user (really a developer) perspective, the Controller  
is nothing else than a collection of JavaScript files running on the  
server side.

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 Controller 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 Controller, 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 Controller. 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 Controller.

Once here, the Controller 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  
JXPath elements to retrieve values from the context objects passed by  
the Controller.

The JXPath elements mirror similar XSLT constructions, except that  
instead of operating on an XML document, operate on a Java or  
JavaScript object. The JXPath logicsheet has constructs like jpath:if,  
jpath:choose, jpath:when, jpath:otherwise, jpath:value-of and  
jpath:for-each, which know how to operate on hierarchies of nested Java  
objects. Historically the namespace is called "jpath" instead of  
"jxpath", we'll probably change it to the latter before the next major  
release.

A special instruction, jpath:continuation returns the id of the  
continuation that restarts the processing from the last point. It can  
actually retrieve ids of earlier continuations, which represent  
previous stopped points, but I'm not discussing about this here to keep  
things simple.

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 Controller 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 I've just checked in CVS:

<http://cvs.apache.org/viewcvs.cgi/xml-cocoon2/src/webapp/samples/flow/ 
examples/prefs/>

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.

-- 

This is all I have to say for the moment on this topic.

The thing I'm going to work on next is a user feedback for  
documentation which uses this MVC pattern. Jeff Turner and I are  
planning to use this system as the documentation system for Anteater.  
For this I want to use OJB (http://jakarta.apache.org/ojb/) to map  
database tables to Java objects, so I can implement a clean Model  
layer. This is a more realistic example, which will hopefully showcase  
the ease of use of this MVC approach.

Future plans include writing a WikiWiki application and a Weblog tool  
using the same patterns. I think these would be real killer  
applications for Cocoon with MVC.

As usually, I appreciate any comments and feedback you have on the  
above.

Best regards,
-- 
Ovidiu Predescu <[EMAIL PROTECTED]>
http://webweavertech.com/ovidiu/weblog/ (Weblog)
http://www.geocities.com/SiliconValley/Monitor/7464/ (Apache, GNU,  
Emacs ...)


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, email: [EMAIL PROTECTED]

Reply via email to