November 27, 2005

Maven and Cocoon

(This is a draft of a document I hope to add to the Cocoon docs. Yes, it's only Maven 1.x at the moment. Yes, it's only unix-based at the moment. Feedback welcome...)

Luminas have been using Maven and Cocoon on various projects recently. There's relatively poor documentation "out there" on how to do so - a reference to Maven on the Cocoon wiki, some details on building and deploying Cocoon with Maven.

Why would you want to use Maven, and the Maven Cocoon plugin? Maven is a great tool for helping you develop projects, and can ease the task of managing and downloading dependencies, building, and generating project documentation. It can also do much more, take a look at the list of Maven 1 plugins to get an idea of the possibilities.

The maven cocoon plugin helps by managing the build properties and blocks properties files in Cocoon for you, and makes the Cocoon build a part of your overall project build. It can also help you by preparing a deployable version of Cocoon.

The maven-cocoon-plugin was developed by Otego.

I assume you've installed Maven (version 1 currently for this) and Cocoon (2.1.7 or later), and got both working. The first thing you need to do is create a place to work and generate the appropriate directory hierarchy for your application. Assuming a fictional project name of 'macaroon', this is what you do:

mkdir macaroon
cd macaroon

Now we create the skeleton directories, using the Maven application generator plugin:

maven genapp

(If you have not yet used the genapp plugin, maven will automatically download the relevant dependencies, e.g. commons-jelly-tags-interaction.)

Enter a project template to use: [default]
(hit return)
Please specify an id for your application: [app]
macaroon
Please specify a name for your application: [Example Application]
Macaroon
Please specify the package for your application: [example.app
uk.co.luminas.macaroon
build:start:

genapp:
[copy] Copying 1 file to ~/macaroon/src/java/uk/co/luminas/macaroon
[copy] Copying 3 files to ~/macaroon/src/test/uk/co/luminas/macaroon
[copy] Copying 1 file to ~/macaroon
[copy] Copying 2 files to ~/macaroon
BUILD SUCCESSFUL
Total time: 27 seconds

Great! We have a set of empty directories to work on. Next, we need to fill out the project descriptor project.xml, or at least update the template with content of our own. A sample project descriptor might be:

<?xml version="1.0" encoding="UTF-8"?>
<project>
<pomVersion>3</pomVersion>
<id>macaroon</id>
<name>Macaroon</name>
<currentVersion>1.0</currentVersion>
<organization>
<name>Luminas Limited</name>
<url>http://www.luminas.co.uk/</url>
<logo>http://www.luminas.co.uk/images/luminas_187x100.png</logo>
</organization>
<inceptionYear>2005</inceptionYear>
<package>uk.co.luminas.macaroon</package>
<description>A demonstration project showing how to use maven and cocoon together.</description>
<shortDescription>How to use maven with cocoon.</shortDescription>
<dependencies/>
<build>
<sourceDirectory>src/java</sourceDirectory>
<unitTestSourceDirectory>src/test</unitTestSourceDirectory>
<unitTest>
<includes>
<include>**/*Test.java</include>
</includes>
</unitTest>
<resources>
<resource>
<directory>src/conf</directory>
<includes>
<include>*.properties</include>
</includes>
</resource>
</resources>
</build>
</project>

Next we need to create our maven build file, maven.xml, with instructions on how to build the project. A sample maven build file might be:

<project default="usage"
xmlns:ant="jelly:ant"
xmlns:maven="jelly:maven"
xmlns:j="jelly:core"
>

<goal name="usage" description="Prints usage information">
<ant:echo></ant:echo>
<ant:echo>===============================================================================</ant:echo>
<ant:echo></ant:echo>
<ant:echo>Maven Goals:</ant:echo>
<ant:echo> install : build and install all required components and settings into Cocoon</ant:echo>
<ant:echo> uninstall : uninstall all components, resetting Cocoon as it was</ant:echo>
<ant:echo> build-cocoon : builds Cocoon specifically for this project</ant:echo>
<ant:echo> clean : clean the repository from generated artifacts</ant:echo>
<ant:echo></ant:echo>
<ant:echo>===============================================================================</ant:echo>
<ant:echo>NB. All targets require that you have set the environment variable $COCOON_HOME</ant:echo>
<ant:echo>COCOON_HOME: ${env.COCOON_HOME}</ant:echo>
<ant:echo>===============================================================================</ant:echo>
</goal>

<!-- Get property values from the environment and reset any properties -->
<preGoal name="build:start">
<ant:property environment="env"/>
<ant:property file="${basedir}/local.build.properties"/>
</preGoal>

<!-- install dependencies and mount the webapp into the target Cocoon -->
<goal name="install" prereqs="clean,build-cocoon">
<attainGoal name="cocoon:install-deps"/>
<attainGoal name="cocoon:mount"/>
</goal>

<!-- uninstall dependencies and mount the webapp into the target Cocoon -->
<goal name="uninstall">
<attainGoal name="cocoon:uninstall-deps"/>
<attainGoal name="cocoon:unmount"/>
</goal>

<!-- Executes the 'clean:clean' goal on all sub projects -->
<goal name="clean">
<attainGoal name="clean:clean"/>
<ant:delete dir="${basedir}/target"/>
</goal>

<!-- Convenience goal for less typing -->
<goal name="build-cocoon">
<attainGoal name="cocoon:build-cocoon"/>
</goal>

<!-- ==================== -->
<!-- Cocoon-related tasks -->
<!-- ==================== -->
<!-- Collects resources needed for proper self containment of the application -->
<postGoal name="cocoon:install-deps">
<j:set var="targetInstallationDir" value="${env.COCOON_HOME}/build/webapp"/>
<attainGoal name="copy-additional-resources"/>
</postGoal>

<!-- Collects resources needed for proper self containment of the application -->
<postGoal name="cocoon:build-package-dir">
<j:set var="targetInstallationDir" value="${maven.build.dir}/cocoon/${pom.artifactId}/WEB-INF/classes"/>
<attainGoal name="copy-additional-resources"/>
</postGoal>

<!-- Collects resources needed for proper self containment of the application -->
<goal name="copy-additional-resources">
</goal>

</project>

Now, typing maven will give you instructions on the maven goals available.

build:start:

usage:
[echo]
[echo] ===============================================================================
[echo]
[echo] Maven Goals:
[echo] install : build and install all required components and settings into Cocoon
[echo] uninstall : uninstall all components, resetting Cocoon as it was
[echo] build-cocoon : builds Cocoon specifically for this project
[echo] clean : clean the repository from generated artifacts
[echo]
[echo] ===============================================================================
[echo] NB. All targets require that you have set the environment variable $COCOON_HOME
[echo] COCOON_HOME: /Users/savs/Development/cocoon-2.1.8
[echo] ===============================================================================
BUILD SUCCESSFUL
Total time: 4 seconds
Finished at: Sat Nov 26 17:44:22 GMT 2005

The next task is to add suitable entries to project.properties to control for example what blocks will be required by your application, and where to retrieve dependencies from. This is done as follows:

maven.repo.remote = http://project.otego.com/maven,http://www.ibiblio.org/maven
maven.cocoon.blocks.list = ajax,databases,forms,serializers,xsp

Now that the repo is added, download the cocoon plugin:

maven plugin:download -DgroupId=maven -DartifactId=maven-cocoon-plugin -Dversion=1.0.10

Let's try building Cocoon:

maven build-cocoon

You should see that just our selected blocks are built into Cocoon:

(...)
[exec] cocoon-block-ajax-compile:
(...)
[exec] cocoon-block-forms-compile:
(...)
[exec] cocoon-block-serializers-compile:
(...)
BUILD SUCCESSFUL
Total time: 1 minutes 5 seconds

If you need to build your project against Cocoon, add the Cocoon dependency to the project.xml file:

<dependencies>
<dependency>
<groupId>cocoon</groupId>
<artifactId>cocoon</artifactId>
<version>2.1.7</version>
<type>jar</type>
</dependency>
</dependencies>

Now when you type maven build-cocoon you will see that Maven downloads Cocoon:

Attempting to download cocoon-2.1.7.jar.
1265K downloaded

Where to put your cocoon app files? I suggest the following layout:

mkdir -p src/cocoon/app/content
mkdir src/cocoon/app/flow
mkdir src/cocoon/app/forms
mkdir src/cocoon/app/messages
mkdir -p src/cocoon/app/resources/images
mkdir src/cocoon/app/resources/css
mkdir src/cocoon/app/stylesheets
touch src/cocoon/app/sitemap.xmap

A common patch to make to Cocoon might be to add a datasource (of course in real life you'd need a patch to add the driver to web.xml too).

Create directory structure for patches:
mkdir -p src/cocoon/install-patches
mkdir -p src/cocoon/uninstall-patches

Create the patch file 'datasources.xconf' in install-patches:

<?xml version="1.0"?>
<xconf xpath="/cocoon/datasources" unless="jdbc[@name='macaroon']">
<jdbc name="macaroon">
<pool-controller min="5" max="10"/>
<dburl>jdbc:postgresql://localhost/macaroon</dburl>
<user></user>
<password></password>
</jdbc>
</xconf>

... and create the unpatch file 'datasources.xconf' in uninstall-patches:
<?xml version="1.0"?>
<xconf xpath="/"
remove="/cocoon/datasources/jdbc[@name='macaroon']"></xconf>

Next time you build your application, you should see the above being patched into Cocoon:

[cocoon-xpatch] Processing: macaroon/src/cocoon/install-patches/datasources.xconf
[cocoon-xpatch] Writing: cocoon-2.1.8/build/webapp/WEB-INF/cocoon.xconf

But wait - you need the Postgres JDBC driver in order for it to work. Not a problem, simply add it to the project.xml as an additional dependency. To make sure it gets copied into Cocoon's lib directory, use cocoon.deploy:

<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>8.0-312.jdbc3</version>
<type>jar</type>
<properties>
<cocoon.deploy>true</cocoon.deploy>
</properties>
</dependency>

Coming up: building a distributable war file.

Posted by savs at November 27, 2005 4:59 PM
Comments

*sigh* sometimes ecto really annoys me. Where'd all those extra line spacings come from?!

Posted by: Andrew Savory at November 27, 2005 5:00 PM