Solprovider Lenya Kongregate Registration for Free Flash Games and Chat with solprovider

Useful Commands for XMAPs

This article explains useful functions that can be handled by XMAPs and integrated into XSL. There are several other basic tutorials available; this just describes what I found most useful.

Cocoon refers to XMAP files as "Sitemaps". Since "sitemap" has a different ubiquitous definition for the web, this website refers to them as XMAPs. They are files with the extension ".xmap" containing XML with the top-level tag of <map:sitemap>.

Each XMAP contains many elements, but almost all customization is handled by "Pipelines" with the occasional "Resource". Almost anything added to other elements requires calling Java. Some elements may already exist; some may be written for your site; but everything outside the pipelines and resources require programming outside the XMAP and XSL. The one useful exception is creating Forms and calling JavaScript functions from an XMAP, which is explained in the chapter Flow


Every XMAP has a section formatted:
<!-- MATCHES -->

Many people use unnecessary </map:pipeline><map:pipeline> pairs. There are only two reasons to have multiple tags.

The first is to change caching:
<map:pipeline type="caching">
<map:pipeline type="noncaching">

Cocoon likes to cache everything. Try setting the pipeline to noncaching if your results are not updating correctly.

The second reason is handling errors.

You can only have one <map:handle-errors> per <map:pipeline>. Multiple <map:pipeline> allow each <map:match> to have its own error handling.
<map:match></map:match> (Additional matches use the same error handling.)

... Another pipeline with different error handling.

An error during processing immediately jumps to the error handling, discarding the current work. <map:handle-errors> must be a complete pipeline (defined next), such as a <map:generate/><map:serialize type="html"/> sequence.

Error handling can branch based on the error:
<map:select type="exception">
<map:when test="document-does-not-exist"></map:when>

See Cocoon's <map:handle-errors> and examples in publication-sitemap.xmap in the default pub.


While the <map:pipeline> tag exists, we refer to the process of responding to a request as a "Pipeline", even though the Pipelines usually refers to just the <map:match> elements. The <map:match> tags are unnecessary if there is only one process.

A normal Pipeline has four parts:
1. A pattern. It could match a URL, called from another pipeline by name, or be for handling errors.
2. Get some content. This is usually a Generator or an Aggregator.
3. (Optional) Transform the content using XSL, or pass control elsewhere.
4. Serialize the content into either XML or HTML. The final pipeline should return HTML; all others should return XML for further processing.
The tags are:
<map:match pattern="myPattern">
<map:generate src="someFile"/>
<map:serialize type="xml"/>

Generator and Aggregators

The Generator is the simplest method for getting data. The basic choices are:
<map:generate src="someFile"/>
<map:generate src="file:///someFile"/>
<map:generate src="cocoon:/nameOfMatchInThisXMAP"/>
<map:generate src="cocoon://nameOfMatchInAnyXMAP"/>
<map:generate src="http://urlOfExternalContent"/>

Aggregators are used to combine data from several sources into one XML file for further processing. The tags are similar to the Generator:
<map:aggregate element="rootElementOfNewContent">
<map:part src="file:///someFile"/>
<map:part src="cocoon:/nameOfMatchInThisXMAP"/>
<map:part src="cocoon://nameOfMatchInAnyXMAP"/>
<map:part src="http://urlOfExternalContent"/>

Most XMAPs have:
<map:generators default="file">
so the
<map:part src="file:///someFile"/>
can be just:
<map:part src="someFile"/>

There are other protocols, such as for getting data from a database. Research them if needed.

When using the file protocol to get the results of an XSP, add:
to both generators and map:parts so the XSP is processed, otherwise you are adding the XML of the XSP.

Type can have other values.
"request" returns XML describing the request.
"directory" returns XML describing the contents of a directory and its subdirectories.
See Generators for more details.

There are additional parameters allowed for map:part:
<map:part src="..." strip-root="true" element="newRootElement" ns="fakePathToNewNamespace" prefix="newNamespace"/>
strip-root="true"Remove the top-level element.
element="newRootElement"Add a new top-level element.
ns="fakePathToNewNamespace" prefix="newNamespace"Add a namespace prefix to all elements except the top level.

<map:aggregate element="newRootElement">
<map:part src="test.xml" strip-root="true" element="newPartElement" ns="newNS" prefix="newPrefix"/>

<?xml version="1.0" encoding="UTF-8"?>
<First something="good"/>
<Second><More>Second Data</More></Second>

<?xml version="1.0" encoding="UTF-8"?>
<newPrefix:newPartElement xmlns:newPrefix="newNS">
<newPrefix:First something="good"/>
<newPrefix:Second><newPrefix:More>Second Data</newPrefix:More></newPrefix:Second>

If you want to transform one part being aggregated before aggregation, and these parameters cannot do it, then do the transformation in another pipeline and call that pipeline from the part:
<map:match pattern="getTransformedPage">
<map:generate src="myPage" />
<map:transform src="xslt/fixMyPage.xsl">
<map:serialize type="xml" />
<map:match pattern="**.html">
<map:aggregate element="cmsbody">
<map:part src="cocoon:/getTransformedPage"/>
<map:part src="content/{1}.xml"/>
<map:serialize type="html" />


The tag is:
<map:transform src="xslt/myXslt.xsl">
<map:parameter name="myParameter" value="someValue"/>

Parameters are used in the XSLT by adding at the top:
<xsl:param name="myParameter"/>
and using:
<xsl:value-of select="$myParameter"/>
<TAG VALUE="{$myParameter}/>
where needed. See Variables for example code.


To tell the browser to go somewhere else, use the RedirectTo command:
<map:redirect-to uri="REQUIRED: new url" session="false" global="false" permanent="false"/>

This is usually used to append "index.html" to the server's root because Lenya did not handle that automatically. I am not certain if this is bad design in Lenya, or if Cocoon makes it difficult to fix the Request. Lenya creates PageEnvelope, but then processes the Request without modification. There is a RequestWrapper class where the override would be simple.

Binary Files

To read any file and send it to the visitor, use this:
<map:read type="resource" src="path/finame.ext" mime-type="text/css">

It reads the Resource (a file), and wraps it with the appropriate HTML headers including the specified mimie-type. It immediately stops all other processing, meaning no <map:serialize> is needed. This is often used for Assets, and CSS and JS files.

IF Statements

There are several useful IF statements. The first requires Java to create an Action. An example would be:
<map:act type="authenticator">
<map:redirect-to uri="LoggedInPage"/>

If the Action returns true, Lenya does what is within the tags. There is no ELSE. There is no NOT. Redirection to other pipelines must be used to work around these limitations. (Do not blame the Lenya devs; it is the Cocoon devs who were not thinking.)

Useful without Java is the Selector:
<map:select type="parameter">
<map:parameter name="parameter-selector-test" value="{request-param:myParameter}"/>
<map:when test="myValue">
<!-- Do something -->
<!-- Do something else -->

This allows the IF-ELSEIF-ELSEIF-ELSE format by adding map:whens. You can set any parameter. The above gets "myParameter" from the querystring of the request, and checks it for "myValue". You can use any variable available. A list of useful variable is available here. Also see the official documentation.


The Resources section contains subroutines for use in pipelines. Anything repeated in two Pipelines in the same XMAP should be turned into a Resource.

<map:resource name="SerializeXML">
<map:serialize type="xml" />

Then use this line in a pipeline:
<map:call resource="SerializeXML"/>

A resource can contain any code used in a pipeline (between the map:match tags), and a call to the resource is replaced by that code. This is very useful for maintenance because code that would be repeated can be kept in one place.

Passing Control to another XMAP

You can pass control to another XMAP using:
<map:mount src="otherXMAP.xmap" check-reload="true" reload-method="synchron" uri-prefix="newprefix-"/>
src="otherXMAP.xmap"REQUIRED: Filename of other XMAP
check-reload="true"Check if other XMAP has been modified
reload-method="synchron"If other XMAP was modified, reload now. Otherwise it be reloaded for the next request, and this request will use the cached version.
uri-prefix="newprefix"Add a prefix for matching.

The uri-prefix tells Cocoon where to start the match earlier. The uri-prefix must exist in the match. It will error "The current URI doesn't start with given prefix "If the uri-prefix does not exist in the match. uri-prefix="" means use the entire match. The new XMAP must contain matches for whatever is sent.
<map:match pattern="test/**">
<map:mount uri-prefix="test" src="test.xmap" check-reload="true" reload-method="synchron"/>

To pass variables to another XMAP, use a generator to change the match:
<map:generate src="cocoon:/mount/myVariable1/myVariable2"/>
<map:serialize type="html"/>

<map:match pattern="mount/**">
<map:mount uri-prefix="" src="another.xmap"/>

In this example, another.xmap must have match="mount/*/*", and can use the variables {1} and {2}.


XMAPs have access to JXPath functions using the SessionModule. This is very useful for string manipulation. <A HREF="">Click here for the official documentation.</A>

You can nest functions and variables:

substring-before() and substring-after() are the most useful functions:
{session:substring-before('mylongstring','long')} returns "my"
{session:substring-after('mylongstring','long')} returns "string"
These return the substring based on the first occurrence of the second parameter.
Usefulness is limited by having no method for retrieving later occurrences. There is no substring-after-last('/aa/bb/cc', '/') to return "cc".
Nesting them is limited because the empty string is returned if the second parameter is not found. Using the empty string as the second parameter is undefined. I may test sometime to discover if it returns the entire string or the empty string.

Substring might be useful, but this is limited by the lack of index-of('mystring', 'str'). The syntax is:
{session:substring('mylongstring',4)} returns "ngstring"
{session:substring('mylongstring',1, 5)} returns "mylon"
substring()'s second parameter is the starting position. The first character is 1.
The optional third parameter is the number of characters in the result. If the parameter is missing, or the string ends with less characters, all of the string after the starting position is returned. substring(something, 1) always returns the entire string.

<< DocumentNesting >>

Contact Solprovider
Paul Ercolino