190
Taming Cocoon ApacheCon Europe 2005 Gianugo Rabellino

Taming Cocoon

  • Upload
    samira

  • View
    64

  • Download
    0

Embed Size (px)

DESCRIPTION

Taming Cocoon. ApacheCon Europe 2005 Gianugo Rabellino. Agenda. Introduction Installing Cocoon Dissecting Cocoon Management and configuration A sitemap tour Cocoon components and blocks Understanding continuations and flow Dealing with forms Patterns, best practices, pitfalls. - PowerPoint PPT Presentation

Citation preview

Page 1: Taming Cocoon

Taming Cocoon

ApacheCon Europe 2005Gianugo Rabellino

Page 2: Taming Cocoon

Agenda

Introduction Installing Cocoon Dissecting Cocoon Management and configuration A sitemap tour Cocoon components and blocks Understanding continuations and flow Dealing with forms Patterns, best practices, pitfalls

Page 3: Taming Cocoon

Introduction: web developer nightmares

out.println(“<table border=\”0\”>\n\t<tr>\n\t\t” + “<td align=\”left\”><font size=\”+1\”>” + request.getParameter(“lousyjob”) + “Does your boss know you\'re typing HTML?” + “</td>\n\t</tr>\n</table>\n”);

Page 4: Taming Cocoon

Nightmare: managing HTML

Your IDE can't deal with HTML Dreamweaver can't deal with business logic Period. Oh, and JSPs suck. Big time.

“Separate concerns” - Edsger Dijkstra - 1974

Page 5: Taming Cocoon

Nightmare: handling state

HTTP is stateless (did you notice already?) Where am I now? Restart required Sessions == overpopulated mess of dictionaries

Page 6: Taming Cocoon

Nightmare: managing user input

Validation hell Conversion ordeal Binding torture The infamous Back button, oh my!

Page 7: Taming Cocoon

There has to be a better way

“Never let a man do what a machine can do for him”

Blaise Pascal

Page 8: Taming Cocoon

Apache Cocoon

A fast-paced overview

Page 9: Taming Cocoon

Apache Cocoon: facts and figures

Origins Started by the Italian student Stefano Mazzocchi

Redesign of Apache.org Frustrated by the limitations of HTML Wanted to use emerging technologies (XML/XSL)

Today Now one of the most important Apache projects Incorporates technologies from various project Just under 2000 registered on all mailing lists Includes major companies such as HP, IBM

Page 10: Taming Cocoon

Tucking web devs in, kissing them goodnight

Page 11: Taming Cocoon

10.000ft overview

Page 12: Taming Cocoon

URI space decoupling Componentized resource assembling Datasource connections and decoupling Stateful applications

Key Cocoon concepts

Page 13: Taming Cocoon

URI space decoupling Sitemap

Componentized resource assembling Pipelines

Datasource connections and decoupling Source interface and implementations (file, URL,

database, WebDAV, XMLDB...) Stateful applications

Web continuations

And their implementation...

Page 14: Taming Cocoon

Think <servlet-mapping> on steroids Pattern freaks think FrontController Uses pluggable algorithms:

Wildcard Regexp Your own

Matching the whole HTTP environment: Request URI/parameters/attributes Session attributes Cookies, context, etc...

Welcome to the sitemap

URI space decoupling: the sitemap

Page 15: Taming Cocoon

<map:match pattern="news/*" type="wildcard"> <map:generate src="newsfeeds/{1}.xml" type="file"/> <map:transform src="news2html.xsl" type="xslt"/> <map:serialize type="html"/> </map:match> <map:match pattern="products/*" type="wildcard"> <map:generate src="products/infos/product_{1}.xml"

type="file"/> <map:transform src="products2html.xsl" type="xslt"/> <map:serialize type="html"/> </map:match>

A sitemap example

Page 16: Taming Cocoon

Task: find the total number of unique hosts visiting the home page of your site Windows solution:

#include <stdlib.h>...

Unix solution:$ grep index.html access.log | awk '{print $2 }' | sort |

uniq | wc -l

Welcome to the pipeline: specialized components glued together

Resource assembling: the pipeline

Page 17: Taming Cocoon

Two simple rules: Data stream is based on SAX events Pipelines start with one Generator (and only one), have 0-

n Transformers, end with one (and only one) Serializer Generators: adapters from the outside world to SAX

events Transformers: SAX event manglers Serializers: adapters from SAX events to the outside

world

The Cocoon pipeline

Page 18: Taming Cocoon

<map:match pattern="*.html">

<map:generate src=”xdocs/{1}.xml"/>

<map:transform src=“style/xdocs2html.xsl”/>

<map:serialize/>

</map:match>

A pipeline example

Page 19: Taming Cocoon

Virtualization layer over stream-based data sources Provides a java.io.File like interface for a number of

data sources Think URLs on steroids

Datasource virtualization

Page 20: Taming Cocoon

The Source interface

Page 21: Taming Cocoon

<map:match pattern="news/*" type="wildcard"> <map:generate src="webdav://repo/newsfeeds/{1}.xml" type="file"/> <map:transform src="xmldb://localhost/db/news2html.xsl"

type="xslt"/> <map:serialize type="html"/> </map:match> <map:match pattern="products/*" type="wildcard"> <map:generate src="cocoon://products/infos/product_{1}.xml"/> <map:transform src="resource://products2html.xsl" type="xslt"/> <map:serialize type="html"/> </map:match>

A datasource example

Page 22: Taming Cocoon

The sitemap decouples URI space from physical resources

The pipeline decouples results from the way they're built

XML decouples content from presentation The source abstraction decouples data sources

from physical locations

Result: full Separation of Concerns

(and you ain’t seen nothing yet...)

Summing it up

Page 23: Taming Cocoon

The next revolution

Web Continuations

Page 24: Taming Cocoon

Web applications are easy!

Page 25: Taming Cocoon

Webapps are finite state machines Upon every click, processing starts from the beginning Again, and again, and again

But wait, this is stateless HTTP

Page 26: Taming Cocoon

A typical web application clutter

Page 27: Taming Cocoon

“A continuation is an entire set of closures that make up a point of execution”

“A continuation is an object that, for a given point in your program, contains a snapshot of the stack trace, including all the local variables, and the program counter”

Hmmm... so what?

What are continuations?

Page 28: Taming Cocoon

var cart;var user;

function checkout(){ while(user == null) { cocoon.sendPageAndWait("login.html");

user = UserRegistry.getUser(cocoon.request.get("name")); } cocoon.sendPageAndWait("shippingAddress.html", {who: user});

var address = cocoon.request.get("address"); cocoon.sendPageAndWait("creditCard.html");

var creditCard = cocoon.request.get("creditCard"); cocoon.sendPageAndWait("confirmOrder.html");

EnterpriseSystem.placeOrder(user, cart, address, creditCard); cocoon.sendPage("orderPlaced.html");}

A flowscript example: where the magic happens

Page 29: Taming Cocoon

– Contents of a continuation● Stack of function calls● Value of local variables

Most often a lightweight object

Creating a continuation does not halt a thread !!

– A continuation object is associated with a unique identifier available to the view Later used to "resurrect" it

What are continuations, again?

Page 30: Taming Cocoon

saved continuationsvar cart;var user;

function checkout(){ while(user == null) { cocoon.sendPageAndWait("login.html");

user = UserRegistry.getUser(cocoon.request.get("name")); } cocoon.sendPageAndWait("shippingAddress.html", {who: user});

var address = cocoon.request.get("address"); cocoon.sendPageAndWait("creditCard.html");

var creditCard = cocoon.request.get("creditCard"); cocoon.sendPageAndWait("confirmOrder.html");

EnterpriseSystem.placeOrder(user, cart, address, creditCard); cocoon.sendPage("orderPlaced.html");}

Flowscript example, revisited

Page 31: Taming Cocoon

var cart;var user;function checkout(){ while(user == null) { cocoon.sendPageAndWait("login.html");

user = UserRegistry.getUser(cocoon.request.get("name")); } cocoon.sendPageAndWait("shippingAddress.html", {who: user});

var address = cocoon.request.get("address"); cocoon.sendPageAndWait("creditCard.html");

var creditCard = cocoon.request.get("creditCard"); cocoon.sendPageAndWait("confirmOrder.html");

EnterpriseSystem.placeOrder(user, cart, address, creditCard); cocoon.sendPage("orderPlaced.html");}

Don't fear the Back button anymore!

Continuations tree

Page 32: Taming Cocoon

– Continuations give control back to the server We always know "where" the browser is

– Allow sophisticated flow screens No need for state automata

– Increase security and robustness Forbids direct access to form submission URLs

Handling of "back" and "new window"

Summing it up

Page 33: Taming Cocoon

Managing user input

A word about forms

Page 34: Taming Cocoon

MVC based form framework Provides:

Strongly-typed data: a date field is a java.util.Date. Automatically.

Validation: declarative. Handled for you. Automatically. Binding: to objects (EJB, POJOs...) or to XML, your choice.

Automatically.

Cocoon Forms: nightmares solved!

Page 35: Taming Cocoon

Separation of Concerns in forms: Model: data typing, validation rules View: widget presentation

selections could be drop down lists or checkboxes Controller:

flowscript (continuations based) custom actions (à la Struts) – not recommended

Data binding: declarativeJXPath based

Cocoon Forms, a functional view

Page 36: Taming Cocoon

Powerful widget library Fields, aggregated fields, dates, selectors, trees and

repeaters AJAX compliant infrastructure

Boosts perceived performance Integrated add-ons

HTML editor calendar pop-ups dynamic double selection lists

Cocoon Forms: a rich framework

Page 37: Taming Cocoon

Configure, don't code Continuations make webapp development a breeze Powerful form frameworks ease managing user input Separation of Concerns brings maintainability

... and they lived happily ever after.

Summing it up: RAD development

Page 38: Taming Cocoon

Installing Cocoon

Finding your way through compilation and blocks

Page 39: Taming Cocoon

Downloading Cocoon

http://cocoon.apache.org Subversion for the latest version:

http://svn.apache.org/repos/asf/cocoon/branches/BRANCH_2.1.Xhttp://svn.apache.org/repos/asf/cocoon/trunk … or even WebDAV

Page 40: Taming Cocoon

Preparing Cocoon

Task list: Edit local.build.properties

Exclude unneeded and heavy tasks (e.g. javadocs / documentation)

Edit local.blocks.propertiesBeware of dependencies

All done, now run:

$ ./build.sh (Unix)

C\> build.bat (Windows)

Page 41: Taming Cocoon

Testing the install

Embedded Jetty: Run

$ ./cocoon.sh servlet (Unix)

C\> cocoon.bat servlet (Windows)

Point your browser to http://localhost:8888/

Other servlet engines: Run$ ./build.sh war (Unix)

C\> build.bat war (Windows)

Deploy dist/cocoon*.war

Page 42: Taming Cocoon

Beware the classloader

Cocoon uses its own version of Rhino (Javascript engine)

Might clash with vendor-provided ones (e.g. Websphere/BEA)

Make sure that your appserver is configured to run Cocoon in an isolated classloader (as per servlet spec): ask your vendor to fix his bug if it doesn’t work

If it doesn’t work, use the “paranoid” block and the ParanoidCocoonServlet (but you shouldn’t)

Mind redirects on WebSphere!

Page 43: Taming Cocoon

Eclipse integration

Run:$ ./build.sh eclipse-project (Unix)

C\> build.bat eclipse-project (Windows)

Inside Eclipse: New Java Project Navigate to $COCOON_HOME

Your project will appear automagically!

Page 44: Taming Cocoon

Files you want to know

Main sitemap:$COCOON_HOME/build/webapp/sitemap.xmap

Log configuration:$COCOON_HOME/build/webapp/WEB-INF/logkit.xconf

Component configuration:$COCOON_HOME/build/webapp/WEB-INF/cocoon.xconf

Mount table:$COCOON_HOME/mount-table.xml

Page 45: Taming Cocoon

Management and configuration

Handling Cocoon in production

Page 46: Taming Cocoon

Configuring Cocoon

Web Application Configuration Cocoon runs as a servlet Configuration controlled by web.xml Find it in the WEB-INF directory

Most important entry Name and location of the actual configuration file

Default: WEB-INF/cocoon.xconf

<!--

This parameter points to the main configuration file for Cocoon. Note that the path is specified in absolute notation but it will be resolved relative to the servlets webapp context path

-->

<init-param>

<param-name>configurations</param-name>

<param-value>/WEB-INF/cocoon.xconf</param-value>

</init-param>

Page 47: Taming Cocoon

Configuring Cocoon

cocoon.xconf XML format Contains Avalon component configuration But not (!!) the sitemap components

They are in the sitemap cocoon.xconf rarely needs changing

Moving from test to productionReplacing the XML parser

Page 48: Taming Cocoon

Configuring Cocoon

cocoon.xconf

<?xml version="1.0"?>

<cocoon version="2.0">

<parser class="org.apache.cocoon.components.parser.XercesParser"/>

<hsqldb-server class="org.apache.cocoon.components.hsqldb.ServerImpl"

pool-max="1" pool-min="1">

<parameter name="port" value="9002"/>

<parameter name="silent" value="true"/>

<parameter name="trace" value="false"/>

</hsqldb-server>...

</cocoon>

Page 49: Taming Cocoon

cocoon.xconf No namespace Each component defined inside <cocoon> Logical names matched to implementations

e.g. parser; hsqldb-server Configuration using <parameter>

Contains name-value pairs pool-min and pool-max

We will look at those later (pooling)

Configuring Cocoon

Page 50: Taming Cocoon

Configuring Cocoon

cocoon.xconf Another important piece of information

Location and name of the sitemap

<sitemap file="context://sitemap.xmap"

check-reload="yes"/>

ReloadingTriggers on incoming requestSynchronous

• New sitemap will be generated and then handles the request

What do you do if an error occurs ............

Page 51: Taming Cocoon

Configuring Cocoon

LogKit Configuration Each component in Cocoon logs using LogKit There are five log levels

DEBUGINFOWARNINGERRORFATAL_ERROR

In testing: DEBUG In production: ERROR or FATAL_ERROR Location of the LogKit configuration file

Is in the web.xml:<init-param> <param-name>logkit-config</param-name> <param-value>/WEB-INF/logkit.xconf</param-value></init-param>

Page 52: Taming Cocoon

Configuring Cocoon

LogKit Configuration Consists of several parts

Factories for logging targets• Implementations are not "hard-wired"• Defines components that are to receive the log

messagesTargets

• Configured with Factory• And file name, output format, size/rotation information

Categories• Actual "receiver" of the log messages• Configured with (multiple) targets• And log level

Page 53: Taming Cocoon

Configuring Cocoon

LogKit Configuration<logkit> <factories> <factory type="cocoon" class="org.apache.cocoon.util.log.CocoonTargetFactory"/> </factories>  <targets> <cocoon id="cocoon"> <filename>${context-root}/WEB-INF/logs/cocoon.log</filename> <format type="cocoon"> %7.7{priority} %{time} [%8.8{category}] (%{uri}) %{thread}/%{class:short}: %{message}\n%{throwable} </format> <append>true</append> <rotation type="revolving" init="1" max="4"> <or> <size>100m</size> <time>01:00:00</time> </or> </rotation> </cocoon> <filter/> </targets>  <categories> <category name="cocoon" log-level="DEBUG"> <log-target id-ref="cocoon"/> <log-target id-ref="filter"/> </category> </categories></logkit>

Page 54: Taming Cocoon

Configuring Cocoon

LogKit Configuration (Normally) No need to change the setting

Apart from log-levelsNot reflected automaticallyTouch cocoon.xconf

DEBUG logfiles can become very largeHard drives have a limited capacity And slow performance down

Default level: ERROR for all categories

Page 55: Taming Cocoon

Dissecting Cocoon

A sitemap tour

Page 56: Taming Cocoon

Cocoon anatomy

Page 57: Taming Cocoon

Cocoon physiology

Page 58: Taming Cocoon

The sitemap disemboweled

The sitemap contains: Component definitions and configurations Views Resources Action sets Flow Pipelines

… all neatly packaged as a namespaced XML file

<map:sitemap xmlns:map="http://xml.apache.org/cocoon/sitemap/1.0"> <map:components/> <map:views/> <map:resources/> <map:action-sets/> <map:flow/> <map:pipelines/></map:sitemap>

Page 59: Taming Cocoon

Sitemap components

Configured as children of the <map:components> section Contains:

Generators Transformers Serializers Readers Matchers Selectors Actions Pipes …and their configuration

Each section declaration can have a default component

Page 60: Taming Cocoon

Sitemap components Tour

Generator, Transformers and Serializers are typical Cocoon components for pipelines

Readers are used for binary resources Think of a “collapsed” Generator and Serializer

Matchers are used to route requests Selectors implement branching (if… then… else) logic Actions implement business logic based switching (old

fashioned, flow is now the preferred way) Pipes define different pipeline implementation (which

differ mainly for caching policies)

Examples will follow

Page 61: Taming Cocoon

A word about Actions

Actions are reusable snippets of business logic Actions wrap pipeline snippets Actions can return either:

null: the pipeline snippet is skipped A Map containing business values, which can be reused in

the pipeline snippet

<map:act type="clear-cache"><map:generate src="status" type="status"/><map:transform src="context://stylesheets/system/status2html.xslt">  <map:parameter name="contextPath" value="{request:contextPath}"/></map:transform><map:serialize/>

</map:act>

Page 62: Taming Cocoon

Sitemap components example

<map:components>  <map:generators/>  <map:transformers default="xslt">     <map:transformer logger="sitemap.transformer.xslt"           name="xslt" pool-max="32"              src="org.apache.cocoon.transformation.TraxTransformer">        <use-request-parameters>false</use-request-parameters>                     <use-session-parameters>false</use-session-parameters>               <use-cookie-parameters>false</use-cookie-parameters>               <xslt-processor-role>xalan</xslt-processor-role>               <check-includes>true</check-includes>

     </map:transformer>  </map:transformers>   <map:serializers/>  <map:readers/>  <map:actions/>  <map:pipes/></map:components>

Page 63: Taming Cocoon

Tip: reusing components

Components can be re-defined with different configurations, Names have to be unique

<map:transformer logger="sitemap.transformer.xalan" name="xalan" pool-max="32" src="org.apache.cocoon.transformation.TraxTransformer">      <use-request-parameters>false</use-request-parameters>      <use-session-parameters>false</use-session-parameters>      <use-cookie-parameters>false</use-cookie-parameters>      <xslt-processor-role>xalan</xslt-processor-role>      <check-includes>true</check-includes>    </map:transformer>

    <map:transformer logger="sitemap.transformer.xsltc" name="xsltc" pool-max="32" src="org.apache.cocoon.transformation.TraxTransformer">      <use-request-parameters>false</use-request-parameters>      <use-session-parameters>false</use-session-parameters>      <use-cookie-parameters>false</use-cookie-parameters>      <xslt-processor-role>xsltc</xslt-processor-role>      <check-includes>true</check-includes>    </map:transformer>

Page 64: Taming Cocoon

Understanding views

Views are “exit points” in pipeline processing Original motivation: semantic search Current use: mainly debug Configuration:

name: unique identifier from-label: explicit exit point from-position: implicit (automatic) exit point

first: right after the generatorlast: right before the serializer

Activated by cocoon-view=view-name

Warning! Views are not inherited by subsitemaps Warning! Views can be a security concern

Page 65: Taming Cocoon

More on views: placing labels

Labels are set using the label attribute on either: A Generator or Transformer declaration in the

components section (will work anywhere) A Generate or Transform directive in a pipeline (specific

to the pipeline) An Aggregate or Part directive (more on this later)

Page 66: Taming Cocoon

View examples

Defining views: <map:views>

    <map:view from-label="content" name="content">      <map:serialize type="xml"/>    </map:view>

    <map:view from-label="content" name="pretty-content">      <map:transform src="stylesheets/system/xml2html.xslt"/>      <map:serialize type="html"/>    </map:view>  </map:views>

Defining labels: <map:generator label="content" logger="sitemap.generator.file"

name="file" pool-max="32”

src="org.apache.cocoon.generation.FileGenerator"/>

Page 67: Taming Cocoon

Resources

Reusable pipeline fragments Useful for repetitive tasks Will be (mostly) replaced by Virtual Sitemap

Components

Warning! Resources are not inherited by subsitemaps

Page 68: Taming Cocoon

A resource example

Defining a resource: <map:resources>

   <map:resource name="simple-page2html">     <map:transform src="context://samples/common/style/xsl/html/simple-page2html.xsl">       <map:parameter name="contextPath" value="{request:contextPath}"/>       <map:parameter name="servletPath" value="{request:servletPath}"/>       <map:parameter name="sitemapURI" value="{request:sitemapURI}"/>       <map:parameter name="file" value="{file}"/>       <map:parameter name="remove" value="{../0}"/>     </map:transform>    </map:resource> </map:resources>

Calling a resource<map:call resource="simple-page2html">

<map:parameter name="file" value="forms/form1_success.xsp"/></map:call>

Page 69: Taming Cocoon

Action-sets

Define a set of actions to be executed as a unit of work either: Unconditionally (upon every invocation) When explicitely called, using a cocoon-action

parameter

<map:action-sets>  <map:action-set name="shop-actions">    <map:act type="session-invalidator" action="logoff"/>    <map:act type="session-validator"/> <!-- Always executed -->    <map:act type="cart-add" action="addItem"/>    <map:act type="cart-remove" action="removeItem"/>    <map:act type="cart-remove-all" action="removeAll"/>    <map:act type="cart-update" action="updateQty"/>    <map:act type="order-add" action="addOrder"/>    <map:act type="order-verify" action="verifyOrder"/>    <map:act type="navigator" src="{1}"/> <!-- Always executed -->

  </map:action-set></map:action-sets>

Page 70: Taming Cocoon

Declaring flow scripts

Flow is a new way to insert page flow control in your application (much more on that later)

Scripts and/or classes are declared in the flow section of the sitemap

<map:flow language="javascript">

<map:script src="calc.js"/>

</map:flow>  

<map:flow language="java"><map:script

src="org.apache.cocoon.samples.flow.java.CalculatorFlow"/><map:script src="org.apache.cocoon.samples.flow.java.FormFlow"/><map:script

src="org.apache.cocoon.samples.flow.java.PersistenceFlow"/>

</map:flow>

Page 71: Taming Cocoon

The pipelines section

The “heart” of Cocoon, where things get done Contains 1+ <map:pipeline> declarations Any pipeline section represents a logical division Different pipelines serve different purposes:

Visibility: internal-only pipelines can only be just using the cocoon: protocol;

Caching, as pipelines can be: non-cached (always executed) cached delegating validity to the pipeline components

(executed if nothing has changed in the pipeline) cached beforehand using the expires directive (executed

only once during the validity period)

Page 72: Taming Cocoon

Cocoon pipelines dissected

Pipelines are “recipes” building resources SAX events travel through the pipeline The TLA is GTS: Generator, Transformer, Serializer Generators are adapters from the outside world to SAX

events Transformers are SAX filters Serializers are the opposite of Generators, adapting

SAX events to the outside world The obligatory diagram:

Page 73: Taming Cocoon

What’s in a pipeline?

Well, components in action: from nouns to verbs

Content production: generate [- transform ] - serialize aggregate - part [ - part …] read

Branching: match select act call

Page 74: Taming Cocoon

So what’s the sitemap, again?

Central switchboard (or FrontController, if you like patterns)

Contains component declarations (yes, even business components)

Locates (match) and builds (pipelines) the final result In most cases, it’s the only file you’ll need to touch

Page 75: Taming Cocoon

A typical request cycle in Cocoon

1. The environment is checked for the proper pipeline to use (normally via matching/selecting)

2. The pipeline is evaluated:1. Sitemap components are looked up via type references

or default assignment2. The pipeline is setup.

3. The pipeline is executed

Warning: no dynamic sitemap routing!

Page 76: Taming Cocoon

Zen and the art of matching

Page 77: Taming Cocoon

A perfectly valid pipeline example

<map:pipeline>

<map:generate src=”hello.xml" type="file"/>

<map:transform src=”hello2html.xsl" type="xslt"/>

<map:serialize type="html"/>

</map:pipeline>

Would match any request and greet the user … which is probably not what you want

Page 78: Taming Cocoon

Matchers kick in

Matchers associate the actual environment to a pipeline<map:pipeline>

<map:match pattern=”hello” type="wildcard">

<map:generate src=”hello.xml" type="file"/>

<map:transform src=”hello2html.xsl" type="xslt"/>

<map:serialize type="html"/>

</map:match>

<map:pipeline>

Hit http://localhost:8888/hello

Page 79: Taming Cocoon

Semantic problem

Warning, pitfall ahead! Formally a pipeline is everything included in

<map:pipeline> Colloquially, though, a pipeline is normally the G-T-S part

<map:pipeline> <map:match pattern=”hello” type="wildcard"> <map:generate src=”hello.xml" type="file"/> <map:transform src=”hello2html.xsl" type="xslt"/> <map:serialize type="html"/> </map:match> <map:match pattern=”goodbye” type="wildcard"> <map:generate src=”goodbye.xml" type="file"/> <map:transform src=”bye2html.xsl" type="xslt"/> <map:serialize type="html"/> </map:match><map:pipeline>

Page 80: Taming Cocoon

Why the fuss?

A pipeline, formally, is everything that starts with a Generator and ends with a Serializer

This sample, then, is perfectly valid:

1:<map:pipeline> 2: <map:match pattern=”hello” type="wildcard"> 3: <map:generate src=”hello.xml" type="file"/> 4: </map:match> 5: <map:match pattern=”goodbye” type="wildcard"> 5: <map:generate src=”goodbye.xml" type="file"/> 7: </map:match> 8: <map:transform src=”greeting2html.xsl" type="xslt"/> 9: <map:serialize type="html"/>10:<map:pipeline>

Page 81: Taming Cocoon

To make things further complicated…

This one is valid as well: 1:<map:pipeline>

2: <map:match pattern=”hello” type="wildcard">

3: <map:generate src=”hello.xml" type="file"/>

4; <map:transform src=”greeting2html.xsl" type="xslt"/>

5: <map:serialize type="html"/>

6: </map:match>

7: <map:match pattern=”goodbye” type="wildcard">

8: <map:generate src=”goodbye.xml" type="file"/>

9: </map:match>

10: <map:transform src=”greeting2html.xsl" type="xslt"/>

11: <map:serialize type="html"/>

12:<map:pipeline>

Page 82: Taming Cocoon

Argh! Where is my aspirin?

… but this one could not be:

1:<map:pipeline>

2: <map:generate src=”hello.xml" type="file"/>

3: <map:match pattern=”hello” type="wildcard">

4: <map:generate src=”hello.xml" type="file"/>

5: <map:transform src=”greeting2html.xsl" type="xslt"/>

6: <map:serialize type="html"/>

7: </map:match>

8: <map:match pattern=”goodbye” type="wildcard">

9: <map:generate src=”goodbye.xml" type="file"/>

10: </map:match>

11: <map:transform src=”greeting2html.xsl" type="xslt"/>

12: <map:serialize type="html"/>

13:<map:pipeline>

Page 83: Taming Cocoon

Bottom line

Cocoon will process the sitemap and try to build a G-[T]-S pipeline

Cocoon will stop processing when a Serializer is met Beware the “Generator already set” errors! You can’t have a “Serializer already set” error

Page 84: Taming Cocoon

Understanding matchers

Matchers will “route” a user request through Cocoon pipelines

Matchers are evaluated in order: first match wins Matchers are available for the whole environment,

matching on: Request URI Request parameters Request Attributes Session Attributes Cookies … and more

Matchers are pluggable: write your own!

Page 85: Taming Cocoon

Using matchers

A basic example:

     <map:match pattern="">      <map:generate src="welcome.xml"/>      <map:transform src="welcome.xslt"/>      <map:serialize type="xhtml"/>    </map:match>

Captures an empty URI

Page 86: Taming Cocoon

Wildcard-based matching

Wildcard match part of the URI using special chars (*/**) A single asterisk matches everything up to the first forward

slash  <map:match pattern=”welcome-*">

      <map:generate src="welcome.xml"/>      <map:transform src="welcome.xslt"/>      <map:serialize type="xhtml"/>    </map:match>

Matches http://localhost:8888/welcome-friend A double asterisk matches everything     <map:match pattern=”welcome**">

      <map:generate src="welcome.xml"/>      <map:transform src="welcome.xslt"/>      <map:serialize type="xhtml"/>    </map:match>

Matches http://localhost:8888/welcome/friend

Page 87: Taming Cocoon

Capturing matcher results

Captured parts of a URI are available for later use Ordered list, denoted by position number in curly

braces Starts at 1  <map:match pattern=”welcome-*">

      <map:generate src="welcome-files/{1}.xml"/>      <map:transform src="welcome.xslt"/>      <map:serialize type="xhtml"/>    </map:match>

Matches http://localhost:8888/welcome-friend Uses welcome-files/friend.xml as the generator input

Can be arbitrarily complex: <map:match pattern="linkstatus/*/*/**">

     <map:generate src="http://{1}:{2}/{3}" type="linkstatus"/>     <map:transform src="stylesheets/system/linkstatus2html.xslt"/>     <map:serialize/>   </map:match>

Page 88: Taming Cocoon

Nesting matchers

Matchers can be nested:  <map:match pattern=”welcome-**">

<map:match pattern=”friend-*">

       <map:generate src="welcome-files/ {1}.xml"/>

</map:match>

<map:match pattern=”foe-*">

       <map:generate src=”goaway/{1}.xml"/>

</map:match>       <map:transform src="welcome.xslt"/>      <map:serialize type="xhtml"/>    </map:match>

Page 89: Taming Cocoon

Nesting matchers: capturing parent results

The matched expression on the child matcher doesn’t take into account the part already matched

“Parent” captured expressions are available with a tree navigation syntax:

  <map:match pattern=”welcome-**"> <map:match type=”host-matcher” pattern=”localhost">        <map:generate src=”local-files/{../1}.xml"/> </map:match> <map:match type=”host-matcher” pattern=”*.mydomain.com">        <map:generate src=”remote-files/{1}/{../1}.xml"/> </map:match>

      <map:transform src="welcome.xslt"/>      <map:serialize type="xhtml"/>    </map:match>

Page 90: Taming Cocoon

Dealing with Sources

Page 91: Taming Cocoon

The Source abstraction

Abstracts a stream based data source Sub interfaces define write mode and tree-like

navigation (à la java.io.File) URL-like syntax (with some bastardization)

Page 92: Taming Cocoon

The Source interface

Page 93: Taming Cocoon

Available sources

Remote sources: URLSource: deals with every protocol available for java.net.URL FileSource: manages local files WebDAVSource: interoperates with WebDAV repositories XMLDBSource: connects to NXDs BlobSource: uses databases blobs

“Meta” sources: SitemapSource: uses Cocoon pipelines as data stream sources ResourceSource: grabs data from classpath ContextSource: accessed streams from the webapp context ModuleSource: converts modules into sources CachedSource: decorates sources adding passive caching

Page 94: Taming Cocoon

The Sitemap Source

Calls cocoon pipelines Weirdo:

When used in a generator or transformer, the called pipeline serializer will be ignored

When used in a reader, the serializer will be honored More weirdos: URL bastardization

cocoon:/ will call a pipeline starting from the current sitemap

cocoon:// will call a pipeline starting from the root sitemap

Page 95: Taming Cocoon

Cocoon Components Tour

A journey through Generators, Transformers, Serializers, Readers,

Matchers, Sub-sitemaps, Modules, Error handling and more

Page 96: Taming Cocoon

Components Tour

Cocoon has roughly 400 components: 66 Generators 65 Transformers 23 Serializers 10 Readers 37 Matchers 31 Selectors 88 Actions 59 Input/Output Modules

And you might want to write your own No way to cover them all… we’ll see the most useful

Page 97: Taming Cocoon

Useful stuff: FileGenerator

Actually it’s a SourceGenerator: can deal with every Source data stream

… which includes cocoon:// URIs Probably the most used generator in Cocoon

Page 98: Taming Cocoon

Useful stuff: Directory Traversal

Operate on TraversableSources (directories) Provide an XML listing of the requested resources Available components:

DirectoryGeneratorImageDirectoryGeneratorMP3DirectoryGenerator

TraversableGenerator TraversableSourceDescriptionGenerator XPathTraversableGenerator

Page 99: Taming Cocoon

Useful stuff: more generators

JXTemplateGenerator: inspired by JSTL, more on this later

RequestGenerator: XMLized Request object

SessionAttributeGenerator: streams an object stored in session as XML

StreamGenerator: reads and streams XML from a request InputStream (e.g. POST requests)

HttpProxyGenerator: accesses an XML stream over HTTP

HTMLGenerator: grabs HTML from a source, converts to XHTML using jTidy and streams it away

Page 100: Taming Cocoon

Useful stuff: TraxTransformer

Can use either (and concurrently): TRAX Xalan Saxon

Relevant configuration parameters:

<use-request-parameters>true|false</use-request-parameters><use-session-parameters>true|false</use-session-parameters><use-cookie-parameters>true|false</use-cookie-parameters><xslt-processor-role>trax|xalan|xsltc</xslt-processor-role><check-includes>true</check-includes>

Beware caching impact!

Page 101: Taming Cocoon

Useful stuff: I18NTransformer

Incredibly useful for dictionaries and localization Golden rule: use it Relevant configuration:

 <map:transformer name="i18n" logger="sitemap.transformer.i18n" src="org.apache.cocoon.transformation.I18nTransformer"><catalogues default="messages">  <catalogue id="messages" name="messages" location="translations"/>  <catalogue id="menu" name="menu" location="translations"/>  <catalogue id="tiered" name="messages">     <location>translations/tiered</location>     <location>translations</location>  </catalogue></catalogues><cache-at-startup>true</cache-at-startup>

</map:transformer>

Page 102: Taming Cocoon

Dissecting I18NTransformer

Resolving catalogue files: name, id: unique identifiers and base names for files location: a source to be prepended to the base names Locale will be appended, in full or short form (en_US or en

as a fallback). Finally, “.xml” will complete the file name

So: <catalogue id="messages" name="messages” location="translations"/>

Assuming “en_US” as locale, the transformer will look for:

translations/messages_en_US.xml

translations/messages_en.xml

translations/messages.xml

Page 103: Taming Cocoon

I18N catalog files

<catalogue xml:lang="en">

<message key="Apache Cocoon i18n Samples">Apache Cocoon i18n Samples</message><message key="Samples">Samples</message><message key="Introduction">Introduction</message><message key="Static (XML)">Static (XML)</message><message key="Dynamic (XSP)">Dynamic (XSP)</message><message key="Sitemap source">Sitemap source</message><message key="Locales">Locales</message><message key="Documentation">Documentation</message><message key="i18n transformer docs"><![CDATA[<I18ntransformer> docs]]></message><message key="i18n transformer Javadoc"><![CDATA[<I18ntransformer> Javadoc]]></message><message key="LocaleAction Javadoc"><![CDATA[<LocaleAction> Javadoc]]></message><message key="Credits">Credits</message><message key="Konstantin Piroumian">Konstantin Piroumian</message><message key="Many others...">Many others...</message>

</catalogue>

Page 104: Taming Cocoon

Using the I18NTransformer

From then you can happily translate:

Elements<title>

  <i18n:text>titletext</i18n:text></title>

Attributes<para title="first" name="article" i18n:attr="title name">

Dates<i18n:date-time pattern="MEDIUM"/>

Numbers<i18n:number type="percent" src-locale="en" value="1.2"/>

Page 105: Taming Cocoon

Useful stuff: CIncludeTransformer

Dynamically inserts content in XML streams Reacts to elements in the http://apache.org/cocoon/include/1.0

namespace <page

  xmlns:cinclude="http://apache.org/cocoon/include/1.0">  <title>Hello</title>  <content>    <para>This is my first Cocoon page!</para>    <cinclude:include src="include.xml" element="included"/>  </content></page>    

Can be cached as well: <map:transform type="cinclude">

      <map:parameter name="expires" value="600"/> </map:transform>

  <cinclude:cached-include src="include.xml"/>

Page 106: Taming Cocoon

Useful stuff: SourceWritingTransformer

Useful to write XML data to (Writeable) Sources during pipeline execution e.g., save a file

Reacts to elements in the http://apache.org/cocoon/source/1.0 namespace

Typically used with XSLT building the source:* stuff Read the Javadocs for details <page>

  <source:write create="true”

xmlns:source="http://apache.org/cocoon/source/1.0">    <source:source>file://tmp/test.write</source:source>    <source:fragment>      <title>a title</title>      <content>       ...      </content>    </source:fragment>  </source:write></page>

Page 107: Taming Cocoon

Useful stuff: XMLSerializer

Outputs, well, XML. Configurable encodings and doctypes:

<map:serializer logger="sitemap.serializer.xhtml"

mime-type="application/xhtml+xml" name="xhtml11”

src="org.apache.cocoon.serialization.XMLSerializer"><doctype-public>-//W3C//DTD XHTML 1.1//EN</doctype-public><doctype-system>

http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd

</doctype-system>

<encoding>UTF-8</encoding>

</map:serializer>

Page 108: Taming Cocoon

Useful stuff: FOPSerializer

Uses Apache FOP to produce: PDF PS PCL RTF

<map:serializer logger="sitemap.serializer.fo2pdf" mime-type="application/pdf" name="fo2pdf"

src="org.apache.cocoon.serialization.FOPSerializer">      <user-config>WEB-INF/fop-config.xml</user-config>      <set-content-length>true</set-content-length>

</map:serializer>

<map:serializer logger="sitemap.serializer.fo2ps" mime-type="application/postscript" name="fo2ps"

src="org.apache.cocoon.serialization.FOPSerializer"/>

<map:serializer logger="sitemap.serializer.fo2pcl" mime-type="vnd.hp-PCL" name="fo2pcl"

src="org.apache.cocoon.serialization.FOPSerializer"/>    

Page 109: Taming Cocoon

Useful stufff: SVGSerializer

Takes SVG as input, produces JPEG/PNG as output Uses Apache Batik

<map:serializer logger="sitemap.serializer.svg2jpeg"

mime-type="image/jpeg" name="svg2jpeg" src="org.apache.cocoon.serialization.SVGSerializer">   <parameter name="quality" type="float" value="0.9"/>

</map:serializer>

<map:serializer logger="sitemap.serializer.svg2png"

mime-type="image/png" name="svg2png”

src="org.apache.cocoon.serialization.SVGSerializer"/>

    

Page 110: Taming Cocoon

Useful stuff: ImageReader

Will serve images (surprise!) The kick: performing width, height, ratio adjustments

(great for thumbnails)

      <map:match pattern="image-size-*">

 <map:read type="image" src="logo.jpg"> <map:parameter name="width" value="{1}"/> <map:parameter name="height" value="{1}"/> </map:read>

</map:match>

Page 111: Taming Cocoon

Sub sitemaps

Sitemaps could become overpopulated and messy (the Perl effect)

Solution: modularize using sub-sitemaps From the main sitemap match the parent path and

mount a sitemap underneath:    <map:match pattern="xsp/**">

     <map:mount uri-prefix="xsp" src="xsp.xmap" />   </map:match>

Wildcard substitution, of course, works: <map:match pattern="~*/**">

<map:mount src="/home/{1}/public_html/" uri-prefix="~{1}"/>

 </map:match>  

Page 112: Taming Cocoon

Input/Output Modules

Modules create generic components and plug actual input/output at runtime

Example: grab a request parameter and use it in the sitemap

<map:match pattern=”welcome">

       <map:generate src=”local-files/{request-param:file}.xml"/>

      <map:transform src="welcome.xslt"/>      <map:serialize type="xhtml"/>    </map:match>

Again: no dynamic routing possible Modules are evaluated during the pipeline setup phase

Page 113: Taming Cocoon

Useful modules: GlobalInputModule

Allows to configure global variables in the sitemap, with runtime substitution

Helps writing maintainable code <map:component-configurations>

    <global-variables>      <staging>webdav://localhost/webdav/step1/</staging>    </global-variables>  </map:component-configurations>

  <map:pipeline>

    <map:match pattern="repo/">      <map:generate type="traverse" src="{global:staging}repo/">        <map:parameter name="depth" value="1"/>      </map:generate>      <map:serialize type="xml"/>    </map:match>

   </map:pipeline>

Page 114: Taming Cocoon

Useful modules: XMLFileInputModule

Allows XPath queries on external resources Helps maintainability: configuration will be outside the

sitemap Configured in cocoon.xconf

<component-instance    class="org.apache.cocoon.components.modules.input.XMLFileModule"  logger="core.modules.xml" name=”settings">     <file src="cocoon://${project.mount}/configuration/test.xhtml"         reloadable="false" cacheable="true"/></component-instance>

… <map:generate type="traverse" src="{settings:/html/head/title}">

Page 115: Taming Cocoon

When things go wrong: error handling

Error conditions (exception) can be catched in the pipeline

Each pipeline can have a handle-errors section Error handling is inherited NotifyingGenerator provides errors in XML format ExceptionSelector allows branching according to the

exception

Page 116: Taming Cocoon

Error handling explained<map:selector logger="sitemap.selector.exception" name="exception" src="org.apache.cocoon.selection.ExceptionSelector"> <exception class="o.a.c.ResourceNotFoundException" name="not-found"/>       <exception class="o.a.c.c.flow.InvalidContinuationException" name="invalid-continuation"/></map:selector>

  <map:handle-errors> <map:select type="exception">

  <map:when test="not-found">     <map:generate type="notifying"/>     <map:transform src="stylesheets/system/error2html.xslt”/>     <map:serialize status-code="404"/>  </map:when>

  <map:when test="invalid-continuation">      <map:generate type="notifying"/>      <map:transform src="stylesheets/system/error2html.xslt">      <map:serialize status-code="404"/>  </map:when>

  <map:otherwise>      <map:generate type="notifying"/>      <map:transform src="stylesheets/system/error2html.xslt">      <map:serialize status-code="500"/>  </map:otherwise></map:select>

</map:handle-errors>

Page 117: Taming Cocoon

Flowscript

The magic of web continuations

Page 118: Taming Cocoon

Introduction JavaScript features View layer Putting it all together Session variables Managing continuations

Overview

Page 119: Taming Cocoon

Need for flow control What is "flow control" ?

Control of transition between pagesDriven by application logic

Aren't actions enough ?Yes, but they require state managementQuickly becomes complex, hard to understand and to

maintain

Introduction

Page 120: Taming Cocoon

Flow script : what is it ? Simple and effective way to glue together business

logic, presentation and page flow Uses scripts written in JavaScript (server-side)

a simple scripting languagecan implement the most complex use cases

Why JavaScript ?Simpler than Java, although powerfulIntegrates well with JavaWell-known in the web worldAllows faster roundtrips (save and reload)Supports continuations

Introduction

Page 121: Taming Cocoon

Flow script example

var cart;var user;

function checkout(){ while(user == null) { cocoon.sendPageAndWait("login.html");

user = UserRegistry.getUser(cocoon.request.get("name")); } cocoon.sendPageAndWait("shippingAddress.html", {who: user});

var address = cocoon.request.get("address"); cocoon.sendPageAndWait("creditCard.html");

var creditCard = cocoon.request.get("creditCard"); cocoon.sendPageAndWait("confirmOrder.html");

EnterpriseSystem.placeOrder(user, cart, address, creditCard); cocoon.sendPage("orderPlaced.html");}

var cart;var user;

function checkout(){ while(user == null) { cocoon.sendPageAndWait("login.html");

user = UserRegistry.getUser(cocoon.request.get("name")); } cocoon.sendPageAndWait("shippingAddress.html", {who: user});

var address = cocoon.request.get("address"); cocoon.sendPageAndWait("creditCard.html");

var creditCard = cocoon.request.get("creditCard"); cocoon.sendPageAndWait("confirmOrder.html");

EnterpriseSystem.placeOrder(user, cart, address, creditCard); cocoon.sendPage("orderPlaced.html");}

Introduction

Page 122: Taming Cocoon

JavaScript vs. Java detour If you know Java, you already know JavaScript !

Well, mostly. JavaScript is dynamically typed (variables don’t have

types, values do) Prototype-based inheritance as opposed to class-

based inheritance Objects are extensible at runtime : add or remove

properties and methods

Introduction

Page 123: Taming Cocoon

cocoon.sendPage() cocoon.sendPage invokes the output page (view)

with two argumentsThe view URL, relative to current sitemapA context Map made available to the view

Can contain Java or JavaScript objects

cocoon.sendPage("view.html") is like redirecting to "cocoon:/view.html"

Control then comes back to the script Should normally terminate

cocoon.sendPage("checkout.html", {user: loggedUser, email: address});

cocoon.sendPage("checkout.html", {user: loggedUser, email: address});

Calling the view

Page 124: Taming Cocoon

cocoon.sendPageAndWait() Similar to cocoon.sendPage

Invoke the view with a context object The script is suspended after the view is generated

the whole execution stack saved in a continuation object

Flow between pages becomes sequential code No more complicated state automata

Calling the view

Page 125: Taming Cocoon

What is it ? Contents of a continuation

Stack of function callsValue of local variables Most often a lightweight object

Creating a continuation does not halt a thread !! A continuation object is associated with a unique

identifier available to the view Later used to "resurrect" it

Continuations

Page 126: Taming Cocoon

Sample flow script revisited

saved continuations

var cart;var user;

function checkout(){ while(user == null) { cocoon.sendPageAndWait("login.html");

user = UserRegistry.getUser(cocoon.request.get("name")); } cocoon.sendPageAndWait("shippingAddress.html", {who: user});

var address = cocoon.request.get("address"); cocoon.sendPageAndWait("creditCard.html");

var creditCard = cocoon.request.get("creditCard"); cocoon.sendPageAndWait("confirmOrder.html");

EnterpriseSystem.placeOrder(user, cart, address, creditCard); cocoon.sendPage("orderPlaced.html");}

var cart;var user;

function checkout(){ while(user == null) { cocoon.sendPageAndWait("login.html");

user = UserRegistry.getUser(cocoon.request.get("name")); } cocoon.sendPageAndWait("shippingAddress.html", {who: user});

var address = cocoon.request.get("address"); cocoon.sendPageAndWait("creditCard.html");

var creditCard = cocoon.request.get("creditCard"); cocoon.sendPageAndWait("confirmOrder.html");

EnterpriseSystem.placeOrder(user, cart, address, creditCard); cocoon.sendPage("orderPlaced.html");}

Continuations

Page 127: Taming Cocoon

What is JXTemplate ? An XML template language inspired by JSTL Doesn't allow code, but only access to context

variables More simple, more secure

Flow values are provided as variables :

2 expression languages : Jexl and JXPath

<jx:forEach var="item" items="${cart.items}"> <p>Name: <jx:out value="${item.name}"/></p></jx:forEach>

<jx:forEach var="item" items="${cart.items}"> <p>Name: <jx:out value="${item.name}"/></p></jx:forEach>

<a href="kont/${continuation.id}">Continue</a><a href="kont/${continuation.id}">Continue</a>

View layer : JXTemplate

Page 128: Taming Cocoon

Jexl JSTL & Velocity's expression language

JavaBean property navigation language Property navigation using "."

Expressions enclosed in ${…}

More suited to Java objects

You are calling from ${request.remoteHost}You are calling from ${request.remoteHost}

http://jakarta.apache.org/commons/jexl/

View layer : JXTemplate

Page 129: Taming Cocoon

JXPath XPath on abitrary object graphs

XML documents, but also JavaBeansExpressions enclosed in #{…}

Equally suited to XML documents and Java objects

You are calling from #{$request/remoteHost}You are calling from #{$request/remoteHost}

http://jakarta.apache.org/commons/jxpath/

View layer : JXTemplate

Page 130: Taming Cocoon

The sitemap<map:flow language="JavaScript"> <map:script src="store.js"/></map:flow>

<map:pipelines> <map:pipeline> <map:match pattern="checkout/"> <map:call function="checkout"/> </map:match>

<map:match pattern="*.html"/> <map:generate src="{1}.jx"/> <map:transform src="page2html.xsl"/> <map:serialize/> </map:match>

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

<map:flow language="JavaScript"> <map:script src="store.js"/></map:flow>

<map:pipelines> <map:pipeline> <map:match pattern="checkout/"> <map:call function="checkout"/> </map:match>

<map:match pattern="*.html"/> <map:generate src="{1}.jx"/> <map:transform src="page2html.xsl"/> <map:serialize/> </map:match>

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

Ressurect a continuation

Ressurect a continuation

Called by the flow

Called by the flow

Call a flow function

Call a flow function

Putting it all together

Page 131: Taming Cocoon

Recap Controller is composed of flow scripts written in

JavaScriptuse sendPageAndWait() to send a response and

temporarily suspend execution Views are regular pipeline with access to flow data

JPath XSP tag libraryJXTemplate generator

Model : your Java business logic Sitemap glues everything together

Putting it all together

Page 132: Taming Cocoon

Access to the environment "request", "response", "session " and "context"

properties "parameter" : sitemap parameters

Access to the framework Logging :"log" property

cocoon.log.debug("Hi there"); Getting components

cocoon.getComponent("org.apache.excalibur.xml.Parser")

cocoon.releaseComponent(parser)

The "cocoon" global object

Page 133: Taming Cocoon

Script modularization JavaScript has no "import" feature

cocoon.load("resource://other/script.js")

Page flow control cocoon.sendPage(), cocoon.sendPageAndWait()

Internal calls to the sitemap cocoon.redirectTo("foo.html")

External redirect sent to the browser

The "cocoon" global object

Page 134: Taming Cocoon

Global scope = session scope Global variables are attached to the session

Saved across top-level function invocationsSpecific to each user

Removes most of the needs for session attributes !

Session variables

Page 135: Taming Cocoon

Example

var user = null;

function login() { while (user == null) { sendPageAndWait("login.html"); user = UserRegistry.getUser( cocoon.request.getParameter("name"), cocoon.request.getParameter("password") ); }}

function placeOrder() { login(); Accounting.placeOrder(user); sendPage("orderPlaced.html");}

function logout() { user = null; sendPage("bye.html");}

var user = null;

function login() { while (user == null) { sendPageAndWait("login.html"); user = UserRegistry.getUser( cocoon.request.getParameter("name"), cocoon.request.getParameter("password") ); }}

function placeOrder() { login(); Accounting.placeOrder(user); sendPage("orderPlaced.html");}

function logout() { user = null; sendPage("bye.html");}

Shows the login screen

only if needed

Shows the login screen

only if needed

Won't pass through if not

logged !

Won't pass through if not

logged !

Just clear user info to log out

Just clear user info to log out

Session variables

Page 136: Taming Cocoon

var cart;var user;function checkout(){ while(user == null) { cocoon.sendPageAndWait("login.html");

user = UserRegistry.getUser(cocoon.request.get("name")); } cocoon.sendPageAndWait("shippingAddress.html", {who: user});

var address = cocoon.request.get("address"); cocoon.sendPageAndWait("creditCard.html");

var creditCard = cocoon.request.get("creditCard"); cocoon.sendPageAndWait("confirmOrder.html");

EnterpriseSystem.placeOrder(user, cart, address, creditCard); cocoon.sendPage("orderPlaced.html");}

var cart;var user;function checkout(){ while(user == null) { cocoon.sendPageAndWait("login.html");

user = UserRegistry.getUser(cocoon.request.get("name")); } cocoon.sendPageAndWait("shippingAddress.html", {who: user});

var address = cocoon.request.get("address"); cocoon.sendPageAndWait("creditCard.html");

var creditCard = cocoon.request.get("creditCard"); cocoon.sendPageAndWait("confirmOrder.html");

EnterpriseSystem.placeOrder(user, cart, address, creditCard); cocoon.sendPage("orderPlaced.html");}

Continuation trees Browser "back" or "new window"

Managing continuations

Page 137: Taming Cocoon

Continuation trees Browser "back" : the previous path is lost No fear : a continuation is lightweight

Reference to the parent continuation Local variables since the parent continuation

Browser "new window"Creates a new branch Allows "what if ?" navigation in the application

Managing continuations

Page 138: Taming Cocoon

Expiring continuations Manual expiration :

sendPageAndWait() returns its continuation k k.invalidate() invalidates the continuation and its

subtree

Again, avoids complicated state management Automatic expiration

An inactive continuation expires after a delay

var k = sendPageAndWait("start.html");...BusinessService.commit();// Cannot go back againk.invalidate();

var k = sendPageAndWait("start.html");...BusinessService.commit();// Cannot go back againk.invalidate();

Managing continuations

Page 139: Taming Cocoon

Flow script Gives control back to the server

We always know "where" the browser is Allows sophisticated flow screens

No need for state automata Increases security and robustness

Forbids direct access to form submission URLs Handling of "back" and "new window"

Conclusion

Page 140: Taming Cocoon

Flow script debugger Activated in cocoon.xconf

Development tools

Page 141: Taming Cocoon

Dealing with forms

The powerful Cocoon forms framework

Page 142: Taming Cocoon

Introduction

The need for form handling Cocoon started as a publication framework

Many pages, limited user feedback Content was mostly written "outside"

Evolution towards a general-purpose web framework Published content has to be managed Used for more and more data-centric applications

Need for good form handling featuresVarious attempts before Cocoon Forms…

Page 143: Taming Cocoon

Cocoon Forms principles

Main requirements Strong typing and formatting

A date input will give a java.util.Date Support for localized input formats

No requirement for a form bean The form has its own data model

Easy and safe binding to the data model Only when the all inputs are valid No direct link from request to application data

Strong separation of form definition and styling Extensibility

Everything is a component

Page 144: Taming Cocoon

Cocoon Forms principles

The big picture

Page 145: Taming Cocoon

Cocoon Forms principles

The form object model Composed of "widgets"

Represents "something" that appears in the formCan read, parse and validate itselfCan output its XML representation

public interface Widget { public Widget getParent(); public void setParent(Widget widget); public Widget getWidget(String id); public String getId(); public String getFullyQualifiedId(); public void readFromRequest(FormContext formContext); public Object getValue(); public void setValue(Object object); public boolean validate(FormContext formContext);

public void generateSaxFragment(ContentHandler contentHandler, Locale locale); public void generateLabel(ContentHandler contentHandler);}

public interface Widget { public Widget getParent(); public void setParent(Widget widget); public Widget getWidget(String id); public String getId(); public String getFullyQualifiedId(); public void readFromRequest(FormContext formContext); public Object getValue(); public void setValue(Object object); public boolean validate(FormContext formContext);

public void generateSaxFragment(ContentHandler contentHandler, Locale locale); public void generateLabel(ContentHandler contentHandler);}

Widget hierarchy

Naming

Parsing and

validation

XML output

Page 146: Taming Cocoon

Cocoon Forms principles

Form definition overview

<fd:form xmlns:fd="http://apache.org/cocoon/forms/1.0#definition"> <fd:widgets> <fd:field id="name" required="true"> <fd:label>Name:</fd:label> <fd:datatype base="string"> <fd:validation> <fd:length min="2"/> </fd:validation> </fd:datatype> </fd:field>

<fd:field id="email" required="true"> <fd:label>Email address:</fd:label> <fd:datatype base="string"> <fd:validation> <fd:email/> </fd:validation> </fd:datatype> </fd:field> …/… <fd:widgets></fd:form>

<fd:form xmlns:fd="http://apache.org/cocoon/forms/1.0#definition"> <fd:widgets> <fd:field id="name" required="true"> <fd:label>Name:</fd:label> <fd:datatype base="string"> <fd:validation> <fd:length min="2"/> </fd:validation> </fd:datatype> </fd:field>

<fd:field id="email" required="true"> <fd:label>Email address:</fd:label> <fd:datatype base="string"> <fd:validation> <fd:email/> </fd:validation> </fd:datatype> </fd:field> …/… <fd:widgets></fd:form>

Page 147: Taming Cocoon

Cocoon Forms principles

Form template overview Embeds widget references in target markup

<html xmlns:ft="http://apache.org/cocoon/forms/1.0#template"> <head> <title>Registration form</title> </head> <body> <h1>Registration</h1> <ft:form-template action="registration" method="POST"> <ft:widget-label id="name"/> <ft:widget id="name"/> <br/> <ft:widget-label id="email"/> <ft:widget id="email"/> <br/> …/… <input type="submit"/> </ft:form-template> </body></html>

<html xmlns:ft="http://apache.org/cocoon/forms/1.0#template"> <head> <title>Registration form</title> </head> <body> <h1>Registration</h1> <ft:form-template action="registration" method="POST"> <ft:widget-label id="name"/> <ft:widget id="name"/> <br/> <ft:widget-label id="email"/> <ft:widget id="email"/> <br/> …/… <input type="submit"/> </ft:form-template> </body></html>

Page 148: Taming Cocoon

Cocoon Forms principles

Resulting output

Page 149: Taming Cocoon

The form definition file

Widgets Standard widgets

<fd:form> : the main form widget<fd:field> : "atomic" input field<fd:booleanfield> : boolean input<fd:mutivaluefield> : multiple selection in a list<fd:repeater> : collection of widgets<fd:output> : unmodifiable widget<fd:action> : action button<fd:tree> : a tree-shaped widget

They're all defined in cocoon.xconf Add your own if needed

Page 150: Taming Cocoon

The form definition file

The <fd:field> widget Definition overview

The label can contain abitrary markup

Including i18n references

<fd:field id="..." required="true|false"> <fd:label>...</fd:label> <fd:datatype base="..."> [...] </fd:datatype> <fd:selection-list> [...] </fd:selection-list></fd:field>

<fd:field id="..." required="true|false"> <fd:label>...</fd:label> <fd:datatype base="..."> [...] </fd:datatype> <fd:selection-list> [...] </fd:selection-list></fd:field>

<fd:label>Your <b>name</b></fd:label><fd:label>Your <b>name</b></fd:label>

<fd:label> <i18n:text key="name-field-label"/></fd:label>

<fd:label> <i18n:text key="name-field-label"/></fd:label>

Page 151: Taming Cocoon

The form definition file

Defining the data type of a field Mandatory "base" type

Defines the Java type"string", "long", "decimal", "date", "boolean"

Pluggable components : add your own ! Optional conversion and validation

<fd:datatype base="..."> <fd:convertor> [...] </fd:convertor> <fd:validation> [...] </fd:validation></fd:datatype>

<fd:datatype base="..."> <fd:convertor> [...] </fd:convertor> <fd:validation> [...] </fd:validation></fd:datatype>

Parsing and

formatting

Validation

Page 152: Taming Cocoon

The form definition file

Data type parsing and formatting Each base type has a set of converters

Pluggable components : add your own ! Example : date's "formatting" converter

based on java.text.SimpleDateFormat locale-dependent patterns

<fd:datatype base="date"> <fd:convertor type="formatting"> <fd:patterns> <fd:pattern>yyyy-MM-dd</fd:pattern> <fd:pattern locale="en">MM/dd/yyyy</fd:pattern> <fd:pattern locale="fr">dd/MM/yyyy</fd:pattern> <fd:pattern locale="nl-BE">dd/MM/yyyy</fd:pattern> <fd:pattern locale="de">dd.MM.yyyy</fd:pattern> </fd:patterns> </fd:convertor></fd:datatype>

<fd:datatype base="date"> <fd:convertor type="formatting"> <fd:patterns> <fd:pattern>yyyy-MM-dd</fd:pattern> <fd:pattern locale="en">MM/dd/yyyy</fd:pattern> <fd:pattern locale="fr">dd/MM/yyyy</fd:pattern> <fd:pattern locale="nl-BE">dd/MM/yyyy</fd:pattern> <fd:pattern locale="de">dd.MM.yyyy</fd:pattern> </fd:patterns> </fd:convertor></fd:datatype>

Page 153: Taming Cocoon

The form definition file

Data type validation A validation rule checks value validity

length, range, regexp, creditcard, assert, email Pluggable components : add your own !

A datatype can have several validation rules Example : email input field

<fd:field id="email"> <fd:datatype base="string"> <fd:validation> <fd:length max='100'/> <fd:email> <fd:failmessage>Not a valid email address!</fd:failmessage> </fd:email> </fd:validation> </fd:datatype></fd:field>

<fd:field id="email"> <fd:datatype base="string"> <fd:validation> <fd:length max='100'/> <fd:email> <fd:failmessage>Not a valid email address!</fd:failmessage> </fd:email> </fd:validation> </fd:datatype></fd:field>

Page 154: Taming Cocoon

The form definition file

Selection lists Provide enumerations to the user

List of items having a valueOptional item label

Selection lists can be external and dynamic

<fd:field name="OS"> <fd:datatype base="string"/> <fd:selection-list> <fd:item value="Linux"/> <fd:item value="Windows"/> <fd:item value="Mac OS"/> <fd:item value="Solaris"/> <fd:item value="other"> <fd:label><i18n:text key="other"/></fd:label> </fd:item> </fd:selection-list></fd:field>

<fd:field name="OS"> <fd:datatype base="string"/> <fd:selection-list> <fd:item value="Linux"/> <fd:item value="Windows"/> <fd:item value="Mac OS"/> <fd:item value="Solaris"/> <fd:item value="other"> <fd:label><i18n:text key="other"/></fd:label> </fd:item> </fd:selection-list></fd:field>

<fd:selection-list src="cocoon:/build-list.xml"> <fd:selection-list src="cocoon:/build-list.xml">

Page 155: Taming Cocoon

The form definition file

The <fd:booleanfield> widget Boolean values

Usually rendered as a checkbox

<fd:booleanfield id="somebool">

<fd:label>Put me <em>on</em> or <em>off</em>.</fd:label>

</fd:booleanfield>

<fd:booleanfield id="somebool">

<fd:label>Put me <em>on</em> or <em>off</em>.</fd:label>

</fd:booleanfield>

Page 156: Taming Cocoon

The form definition file

The <fd:multivaluefield> widget Allows the selection of several values

The value is an Object[] Requires a <fd:selection-list>

<fd:multivaluefield id="drinks">

<fd:label>Indicate your 2 most preferred drinks:</fd:label> <fd:datatype base="string"> <fd:validation> <fd:value-count exact="2"/> </fd:validation> </fd:datatype>

<fd:selection-list> <fd:item value="Maes"/> <fd:item value="Jupiler"/> <fd:item value="Leffe"/> <fd:item value="Hoegaarden"/> <fd:item value="Coca Cola"/> </fd:selection-list>

</fd:multivaluefield>

<fd:multivaluefield id="drinks">

<fd:label>Indicate your 2 most preferred drinks:</fd:label> <fd:datatype base="string"> <fd:validation> <fd:value-count exact="2"/> </fd:validation> </fd:datatype>

<fd:selection-list> <fd:item value="Maes"/> <fd:item value="Jupiler"/> <fd:item value="Leffe"/> <fd:item value="Hoegaarden"/> <fd:item value="Coca Cola"/> </fd:selection-list>

</fd:multivaluefield>

Page 157: Taming Cocoon

The form definition file

The <fd:repeater> widget Repeats a number of child widgets

Used to manage collections, tables, etc.

<fd:repeater id="contacts">

<fd:field id="firstname"> <fd:label>Firstname</fd:label> <fd:datatype base="string"/> </fd:field>

<fd:field id="lastname"> <fd:label>Lastname</fd:label> <fd:datatype base="string"/> </fd:field>

</fd:repeater>

<fd:repeater id="contacts">

<fd:field id="firstname"> <fd:label>Firstname</fd:label> <fd:datatype base="string"/> </fd:field>

<fd:field id="lastname"> <fd:label>Lastname</fd:label> <fd:datatype base="string"/> </fd:field>

</fd:repeater>

Page 158: Taming Cocoon

The form definition file

The <fd:output> widget Read-only widget

Provides formatting featuresBut doesn't read its value from the request

<fd:output id="shipping-date"> <fd:label> <i18n:text key="shipping-date.label"/> </fd:label> <fd:datatype base="date"> <fd:convertor type="formatting" style="short"/> </fd:datatype></fd:output>

<fd:output id="shipping-date"> <fd:label> <i18n:text key="shipping-date.label"/> </fd:label> <fd:datatype base="date"> <fd:convertor type="formatting" style="short"/> </fd:datatype></fd:output>

Page 159: Taming Cocoon

The form definition file

The <fd:action> widget An action button other than standard "submit"

Allows various actions to be taken on the form "action-command" defines the event name

<fd:repeater id="contacts"> <fd:field id="firstname"> <fd:label>Firstname</fd:label> <fd:datatype base="string"/> </fd:field> <fd:field id="lastname"> <fd:label>Lastname</fd:label> <fd:datatype base="string"/> </fd:field> <fd:booleanfield id="select"/></fd:repeater>

<fd:action id="add" action-command="add-contact"> <fd:label>Add contact</fd:label></fd:action>

<fd:action id="remove" action-command="remove-selected-contacts"> <fd:label>Remove selected contacts</fd:label></fd:action>

<fd:repeater id="contacts"> <fd:field id="firstname"> <fd:label>Firstname</fd:label> <fd:datatype base="string"/> </fd:field> <fd:field id="lastname"> <fd:label>Lastname</fd:label> <fd:datatype base="string"/> </fd:field> <fd:booleanfield id="select"/></fd:repeater>

<fd:action id="add" action-command="add-contact"> <fd:label>Add contact</fd:label></fd:action>

<fd:action id="remove" action-command="remove-selected-contacts"> <fd:label>Remove selected contacts</fd:label></fd:action>

Page 160: Taming Cocoon

The form template

The big picture (again)

Page 161: Taming Cocoon

The form template

Role of the Cocoon FormsTransformer "Expand" all ft: elements

<html xmlns:ft="http://apache.org/cocoon/forms/1.0#template"> <head> <title>Registration form</title> </head> <body> <h1>Registration</h1> <ft:form-template action="registration" method="POST"> <ft:widget-label id="name"/> <ft:widget id="name"/> <br/> <ft:widget-label id="email"/> <ft:widget id="email"/> <br/> …/… <input type="submit"/> </ft:form-template> </body></html>

<html xmlns:ft="http://apache.org/cocoon/forms/1.0#template"> <head> <title>Registration form</title> </head> <body> <h1>Registration</h1> <ft:form-template action="registration" method="POST"> <ft:widget-label id="name"/> <ft:widget id="name"/> <br/> <ft:widget-label id="email"/> <ft:widget id="email"/> <br/> …/… <input type="submit"/> </ft:form-template> </body></html>

<html xmlns:ft="http://apache.org/cocoon/forms/1.0#instance"> <head> <title>Registration form</title> </head> <body> <h1>Registration</h1> <fi:form-template action="registration" method="POST"> Name: <fi:field id="name"> <fi:label>Name:</fi:label> <fi:value>Cocoon</fi:value> </fi:field> <br/> Email address: <fi:widget id="email"> <fi:label>Email address:</fi:label> <fi:value>foo</fi:value> <fi:validation-message> Invalid email address </fi:validation-message> </fi:widget> <br/> …/… <input type="submit"/> </fi:form-template> </body></html>

<html xmlns:ft="http://apache.org/cocoon/forms/1.0#instance"> <head> <title>Registration form</title> </head> <body> <h1>Registration</h1> <fi:form-template action="registration" method="POST"> Name: <fi:field id="name"> <fi:label>Name:</fi:label> <fi:value>Cocoon</fi:value> </fi:field> <br/> Email address: <fi:widget id="email"> <fi:label>Email address:</fi:label> <fi:value>foo</fi:value> <fi:validation-message> Invalid email address </fi:validation-message> </fi:widget> <br/> …/… <input type="submit"/> </fi:form-template> </body></html>

Validation failed

Page 162: Taming Cocoon

The form template

Role of the FormsTransformer Expand all "ft" elements in their "fi" counterpart

  ft = Cocoon Forms template   fi = Cocoon Forms instance

Output of the transformer goes to stylingProvided : HTML stylingOther stylings are possible (e.g. WML) Cocoon Forms does not hardcode the presentation !

Page 163: Taming Cocoon

The form template

The <ft:form-template> element Setup the transformer context

Retrieve the Form object from the environment

The <ft:widget-label> element Copies the content of <fd:label>

Used to separate label output from the full widget

Page 164: Taming Cocoon

The form template

The <ft:widget> element Produces the corresponding widget instance

Markup depends on the actual widgetFor fields : <fi:label>, <fi:value>, <fi:selection-list>

<ft:widget> can contain styling informationDrives the styling stylesheet Contents of <fi:styling> depends on the styling !

<ft:widget id="fourchars"> <!-- particular styling for the enumeration --> <fi:styling list-type="listbox" listbox-size="4"/></ft:widget>

<ft:widget id="fourchars"> <!-- particular styling for the enumeration --> <fi:styling list-type="listbox" listbox-size="4"/></ft:widget>

Page 165: Taming Cocoon

The form template

The <ft:repeater-widget> element Iterates on the contents of a <fd:repeater>

Use to build e.g. tablesProduces indexed names for each iteration

<ft:repeater-widget-label> Gets the label of a child of the repeater

Useful for table headers

Page 166: Taming Cocoon

The form template

The <ft:repeater-widget> element

<table> <tr> <th> <ft:repeater-widget-label id="contacts" widget-id="firstname"/> </th> <th> <ft:repeater-widget-label id="contacts" widget-id="email"/> </th> </tr> <ft:repeater-widget id="contacts"> <tr> <td> <ft:widget id="firstname"/> </td> <td> <ft:widget id="email"/> </td> </tr> </ft:repeater-widget></table>

<table> <tr> <th> <ft:repeater-widget-label id="contacts" widget-id="firstname"/> </th> <th> <ft:repeater-widget-label id="contacts" widget-id="email"/> </th> </tr> <ft:repeater-widget id="contacts"> <tr> <td> <ft:widget id="firstname"/> </td> <td> <ft:widget id="email"/> </td> </tr> </ft:repeater-widget></table>

<table> <tr> <th>Name</th> <th>Email address</th> </tr> <tr> <td> <fi:widget id="contacts.0.firstname"> <fi:label>Name</fi:label> <fi:value>Harry</fi:value> </fi:widget> </td> <td> <fi:widget id="contacts.0.email"> <fi:label>Email address</fi:label> <fi:value>[email protected]</fi:value> </fi:widget> </td> </tr> <tr> <td> <fi:widget id="contacts.1.firstname"> <fi:label>Name</fi:label> <fi:value>Anakin</fi:value> </fi:widget> </td> <td> <fi:widget id="contacts.1.email"> <fi:label>Email address</fi:label> <fi:value>[email protected]</fi:value> </fi:widget> </td> </tr></table>

<table> <tr> <th>Name</th> <th>Email address</th> </tr> <tr> <td> <fi:widget id="contacts.0.firstname"> <fi:label>Name</fi:label> <fi:value>Harry</fi:value> </fi:widget> </td> <td> <fi:widget id="contacts.0.email"> <fi:label>Email address</fi:label> <fi:value>[email protected]</fi:value> </fi:widget> </td> </tr> <tr> <td> <fi:widget id="contacts.1.firstname"> <fi:label>Name</fi:label> <fi:value>Anakin</fi:value> </fi:widget> </td> <td> <fi:widget id="contacts.1.email"> <fi:label>Email address</fi:label> <fi:value>[email protected]</fi:value> </fi:widget> </td> </tr></table>

Page 167: Taming Cocoon

Built in HTML styling

The provided stylesheets  »forms-field-styling.xsl"

Only styles basic inputs (fields & actions) many styling variants

   »forms-advanced-field-styling.xsl »Advanced group layout fieldsets, tab panels, overlapping panels

Page 168: Taming Cocoon

Built in HTML styling

Field styling Basic styling : html input <fi:styling type="…">

"password" <input type="password">"hidden" <input type="hidden">"textarea" <textarea>"date" date popup

Page 169: Taming Cocoon

Built in HTML styling

Fields with a <selection-list> Basic styling : drop-down menu <fi:styling type="…">

"listbox" <select><option>…"radio" <input type="radio">

• Additional "orientation" attribute (horizontal/vertical)

<multivaluefield> Basic styling : check-boxes

Page 170: Taming Cocoon

Built in HTML styling

<fi:group> Instance-only widget providing high-level styling

No corresponding <fd:> nor <ft:>Contains items that will be laid out

<fi:group> <fi:label>Profile header</fi:label> <fi:styling type="fieldset" layout="columns"/> <fi:items> <ft:widget id="revision"/> <ft:widget id="identification"/> <ft:widget id="name"/> <ft:widget id="author"/> <ft:widget id="classID"/> <ft:widget id="releaseDate"> <fi:styling type="date"/> </ft:widget> <ft:widget id="additional-info"/> </fi:items></fi:group>

<fi:group> <fi:label>Profile header</fi:label> <fi:styling type="fieldset" layout="columns"/> <fi:items> <ft:widget id="revision"/> <ft:widget id="identification"/> <ft:widget id="name"/> <ft:widget id="author"/> <ft:widget id="classID"/> <ft:widget id="releaseDate"> <fi:styling type="date"/> </ft:widget> <ft:widget id="additional-info"/> </fi:items></fi:group>

Page 171: Taming Cocoon

Built in HTML styling

<fi:group> styling Contents rendering

"layout" attribute : "columns" Automatic 2-column layout of widgets and labels

type="fieldset"

layout="columns"

<fi:group> <fi:label>Profile header</fi:label> <fi:styling type="fieldset" layout="columns"/> <fi:items> <ft:widget id="revision"/> [...] </fi:items></fi:group>

<fi:group> <fi:label>Profile header</fi:label> <fi:styling type="fieldset" layout="columns"/> <fi:items> <ft:widget id="revision"/> [...] </fi:items></fi:group>

Page 172: Taming Cocoon

Built in HTML styling

<fi:group> styling Container rendering

"type" attribute : "fieldset", "tabs", "choice" Tabs defined with CSS

type="choice"

type="tabs"

Note : items in "tabs" and "choice" are most often subgroups

Page 173: Taming Cocoon

Binding : linking forms to application data

An additional binding definition file Associates widget names to XPath expressions on the

data modelExample : binding to an XML document

<fb:context xmlns:fb="http://apache.org/cocoon/forms/1.0#binding" xmlns:fd="http://apache.org/cocoon/forms/1.0#definition" path="/data/user" >

<fb:value id="email" path="email" readonly="true"/>

<fb:value id="number" path="number/@value"> <fd:convertor datatype="long"/> </fb:value>

<fb:value id="choose" path="choose/@value"> <fd:convertor datatype="boolean"/> </fb:value></fb:context>

<fb:context xmlns:fb="http://apache.org/cocoon/forms/1.0#binding" xmlns:fd="http://apache.org/cocoon/forms/1.0#definition" path="/data/user" >

<fb:value id="email" path="email" readonly="true"/>

<fb:value id="number" path="number/@value"> <fd:convertor datatype="long"/> </fb:value>

<fb:value id="choose" path="choose/@value"> <fd:convertor datatype="boolean"/> </fb:value></fb:context>

Set the context of included

paths

Associates a widget to a

path

Read-only

widget

Binding convertor (XML

is text)

Page 174: Taming Cocoon

Usage in flow script

The form.js library Provides a Form class

Constructor takes a form definition file Method Form.showForm() to display the form

Does not return until validation is ok !! Internal loop on sendPageAndWait()

function edit_header() { var form = new Form("forms/profile-header-def.xml"); form.showForm("view-profile-header.html", {foo: bar});

var revision-date = form.getModel().releaseDate; ... sendDialog("Thanks a lot");}

function edit_header() { var form = new Form("forms/profile-header-def.xml"); form.showForm("view-profile-header.html", {foo: bar});

var revision-date = form.getModel().releaseDate; ... sendDialog("Thanks a lot");}

Application data for the

view

Getting form data

Page 175: Taming Cocoon

Usage in flow script

Advanced features Business-level validation

Because not all constraints can be in the definition file Knowing the <fd:action> that was hit

function edit_header() { var limit = Application.getLimit(); var form = new Form("forms/profile-header-def.xml"); form.validator = function(form) { var dateWidget = form.getWidget("releaseDate"); if (dateWidget.getValue() > limit) { dateWidget.setValidationError("Out of limits"); return false; } return true; }

form.showForm("view-profile-header.html", {foo: bar}); if (form.submitID == 'cancel') { sendDialog("Action cancelled"); } else { var revision-date = form.getModel().releaseDate; ... sendDialog("Thanks a lot"); }}

function edit_header() { var limit = Application.getLimit(); var form = new Form("forms/profile-header-def.xml"); form.validator = function(form) { var dateWidget = form.getWidget("releaseDate"); if (dateWidget.getValue() > limit) { dateWidget.setValidationError("Out of limits"); return false; } return true; }

form.showForm("view-profile-header.html", {foo: bar}); if (form.submitID == 'cancel') { sendDialog("Action cancelled"); } else { var revision-date = form.getModel().releaseDate; ... sendDialog("Thanks a lot"); }}

Form is redisplayed if result is

false

Test of the submit button

Page 176: Taming Cocoon

Usage in flow script

Advanced features Binding form and application data

function edit_header() { var data = Application.getData(); var form = new Form("forms/profile-header-def.xml");

form.createBinding("forms/profile-header-binding.xml"); form.load(data);

form.showForm("view-profile-header.html", {foo: bar});

form.save(data);

sendDialog("Thanks a lot");}

function edit_header() { var data = Application.getData(); var form = new Form("forms/profile-header-def.xml");

form.createBinding("forms/profile-header-binding.xml"); form.load(data);

form.showForm("view-profile-header.html", {foo: bar});

form.save(data);

sendDialog("Thanks a lot");}

Page 177: Taming Cocoon

Putting it all together : the sitemap

HTTP method selection GET method : start the flow POST method : continue Advantages

The form's action is "" (submit to the same URL)Continuation management is hidden

<map:match pattern="edit_*.html"> <map:select type="method"> <!-- GET : start the flow for this screen --> <map:when test="GET"> <map:call function="editor_{1}"/> </map:when> <!-- POST (form submission) : continue the flow --> <map:when test="POST"> <map:call continuation="{request-param:continuation-id}"/> </map:when> </map:select></map:match>

<map:match pattern="edit_*.html"> <map:select type="method"> <!-- GET : start the flow for this screen --> <map:when test="GET"> <map:call function="editor_{1}"/> </map:when> <!-- POST (form submission) : continue the flow --> <map:when test="POST"> <map:call continuation="{request-param:continuation-id}"/> </map:when> </map:select></map:match>

Page 178: Taming Cocoon

Putting it all together : the sitemap

View pipeline Use of JXTemplate generator

Allows the use of application data passed to showForm()

Or just a plain file generator, using the template

<map:match pattern="view-*.html"> <map:generate type="jxtemplate" src="forms/{1}-tmpl.xml"/> <map:transform type="forms"/> <map:transform type="i18n"/> <map:transform type="xslt" src="resources/editor-layout.xsl"/> <map:serialize type="html"/></map:match>

<map:match pattern="view-*.html"> <map:generate type="jxtemplate" src="forms/{1}-tmpl.xml"/> <map:transform type="forms"/> <map:transform type="i18n"/> <map:transform type="xslt" src="resources/editor-layout.xsl"/> <map:serialize type="html"/></map:match>

<map:match pattern="view-*.html"> <map:generate type="file" src="forms/{1}-tmpl.xml"/> <map:transform type="forms"/> <map:transform type="i18n"/> <map:transform type="xslt" src="resources/editor-layout.xsl"/> <map:serialize type="html"/></map:match>

<map:match pattern="view-*.html"> <map:generate type="file" src="forms/{1}-tmpl.xml"/> <map:transform type="forms"/> <map:transform type="i18n"/> <map:transform type="xslt" src="resources/editor-layout.xsl"/> <map:serialize type="html"/></map:match>

Page 179: Taming Cocoon

Conclusion

A powerful form frameworkRich datatypes and validation rulesEasy extension to specific needs

Flow script makes it really easy Future work

Client-side validation Better event handling system Overall stabilization

Detailed documentation on http://wiki.cocoondev.org/Wiki.jsp?page=CForms

Page 180: Taming Cocoon

Summary

Good for complex / multi forms Widget Oriented Approach

Custom validation rules Dependencies Strongly type data

Separation of declarations and templates Modular Reuse

Business Integration via Flow I18n Support

Page 181: Taming Cocoon

Tales from the operation room

8 simple rules for building a manageable Cocoon applications

Page 182: Taming Cocoon

Cocoon and manageability

Cocoon is a huge beast to master Things tend to get out of hands quickly The real Cocoon advantage is about increased

manageability So plan for it

Page 183: Taming Cocoon

Plan, plan, plan

Don’t start coding, start sketching Customize the Cocoon environment:

Be ruthless in excluding blocks Have a solid build system Use mount-table wisely Do version control local.build.properties and local.blocks.properties

Page 184: Taming Cocoon

Manage configuration files

You will need to modify configuration files: Use Cocoon’s own XConfTool to patch Cocoon

configurations Separate configuration (patch) files by functionality Have further configuration files to overcome the current

Cocoon blocks limitations (why do you need JMS to access WebDAV?)

Don’t trust components in Cocoon’s main sitemap, declare them again

Page 185: Taming Cocoon

Relocation is not an option

Users might install your application in a number of ways: Directly from the webapp root From a context path As a sub-sitemap

Resource resolving needs to take this into account: Use a mount-point variable

<map:generate src="{global:mount-point}/some/where.xml">

Perform calculations (upcoming Cocoon versions)

<map:generate src="{request:sitemapURIPrefix}/some/where.xml">

Page 186: Taming Cocoon

Samples are just samples

Don’t rely on them for any functionality

Import the required resources, don’t link to them

This is especially true for Cocoon Forms Newer Cocoon versions have Forms resources packaged

in a jar file However, XSLTs are still included in samples

Page 187: Taming Cocoon

Separate application and user concerns

In most applications, users are supposed to manage configuration files

Keep user-accessible resources separated: Client-side CSS, JS, images Consider xsl:import (with performance in mind) as

override mechanism

Page 188: Taming Cocoon

Package and ship

Ideally, package everything in a jar file

Yes, even sitemaps: <map:mount src=”resource://com/pnetx/app/sitemap.xmap"/>

Don’t forget: Error management (map:handle-errors) Error logging (logkit.xconf)

Page 189: Taming Cocoon

Wrapping up

Long journey, and bumpy ride Yet barely scratched the surface More Cocoon presentations during ApacheCon:

DE1348: Tools for Content Management and Publishing in Apache: An Overview

DE1258: Single Source Publishing with Apache Forrest DE1221: Cocoon - One Hour Portal DE1244: Developing Enterprise Web Applications with

Cocoon and Spring DE1284: Creating Print on Demand solutions with Cocoon,

FOP, and Lucene DE1272: Powering High-volume web sites with

Lenya/Cocoon and mod_cache

Page 190: Taming Cocoon

Thank you!

Questions?