Cocoon 2.2 Without Tears

May 25, 2010
Hugh Sparks
http://www.csparks.com/c22without.xhtml

Introduction

In ancient times, simple peasants were able to start the cocoon webapp and just leave it running on their servers more-or-less forever. They could edit sitemaps, flowscript, and other content and see the changes right before their eyes using an ordinary browser. To add new content, they simply copied it to the server!


Primitive webapp development

This article show how to configure cocoon 2.2 so you can have the same experience. You won't have to deploy blocks, learn (too much) about the Spring Framework, or deal with Maven (more than once.)

I should add that I have nothing against cocoon blocks or Spring: Blocks allow servlets to be created without java programing, an amazing idea. And Spring lets you create, configure, and interconnect blocks along with servlets from any source. This is real progress of the most visionary sort.

The methods described here will not prevent you from using blocks with your cocoon webapp. They will simply become optional.


"They didn't use blocks..."

A quick word for new cocoon users: This page describes an unusual way of using cocoon 2.2. Consequently, it is best appreciated by former users of cocoon 2.1 or people who have already come up against frustrations using 2.2. If you've never used cocoon, I'm amazed you'd bother to read this far, but I strongly suggested you visit the Apache cocoon site and learn to go straight before it's too late.

Related work

This Cocoon 2.2 application was inspired by Vadim Gritsenko's paper Using Cocoon 2.2 the Classic Way

I wasn't smart enough to get Mr. Gritsenko's demonstration working with a recent version of Cocoon, but the method described here is even simpler because it depends on editing only two files created by the standard webapp archetype.

I should also mention the cocoon Reloading Class Loader, implemented by the cocoon-maven-plugin. This is a tremendous asset for cocoon users and accomplishes almost everything promised here with no fuss. The drawback is that it is useful only before deployment: It is a developer's tool that relieves some of the annoyance of having all content buried inside of blocks. But I prefer to have my content editable right where it sits on the webserver. I'm such a klutz that I often find mistakes after deploying my website. In fact, I'm embarassed to admit, I usually have a browser and text editor open side by side while I hack away and refresh the page to see if I've got it right.


"They didn't like Maven..."

Download

The following sections show how to construct this webapp starting with the published archetype. But if you want to try out the idea with no effort, you can download a copy here.

To run the demo, you'll need maven and java jdk installed. Unpack the archive and from a command window inside, enter:

	mvn install jetty:run 

To see the test page, aim the browser at:

	http://localhost:8888 

Create a webapp from an archetype

This website will no doubt fall out of date, so you should visit the cocoon project to find the most recent version numbers for blocks and archetypes.

Even if you don't plan to use Maven in your development, I suggest that you install it just to get the correct and current webapp structure installed.

Navigate to the directory above the location where you want to create the directory for your project and enter this command: (Windows users should substitute the "^" symbol instead of "\" for the line continuations.)

You will, of course, substitute whatever you like for myDomain, myApplication, and myVersion. The myVersion parameter should take the form of a decimal literal such as 1.0 or 1.2.3, etc.

	mvn archetype:create \
	    -DarchetypeGroupId=org.apache.cocoon \
	    -DarchetypeArtifactId=cocoon-22-archetype-webapp \
	    -DarchetypeVersion=1.0.0 \
	    -DgroupId=myDomain.com \
	    -DartifactId=myApplication \
	    -Dversion=myVersion 

At this point, you have a working webapp that doesn't do anything. If you add blocks as illustrated by the cocoon tutorial, this webapp will dispatch them. But our goal is to have a webapp that doesn't require blocks, so we have to add some components.


"He didn't use Spring..."

Add dependencies to the pom

In the top level directory of your project, edit the pom file:

	myApplication/pom.xml 

Adding these dependencies will give you the familiar cocoon sitemap components:

	...
	<dependencies>
	<dependency>
		<groupId>org.apache.cocoon</groupId>
		<artifactId>cocoon-core</artifactId>
	</dependency>
	<dependency>
		<groupId>org.apache.cocoon</groupId>
		<artifactId>cocoon-servlet-service-components</artifactId>
	</dependency>
	</dependencies>  

You may want other components. I generally add, for example:

	cocoon-template-impl
	cocoon-flowscript-impl
	cocoon-forms-impl
	cocoon-samples-style-default 

You will notice there are no version elements in the dependencies. I generally take the "package deal" and use the cocoon-core-modules parent element at the top level of my pom:

	<parent>
		<groupId>org.apache.cocoon</groupId>
		<artifactId>cocoon-core-modules</artifactId>
		<version>6-SNAPSHOT</version>
	</parent> 

To get an appropriate version for the parent element, you can browse the cocoon 2.2 truck using svn or you can see it online using the Maven repository browser.


"They wrote applications with flowscript"

Configure the application context

Edit the application context:

	myApplication/src/main/webapp/WEB-INF/applicationContext.xml 

In the <beans> opening tag, add the servlet namespace definition:

	xmlns:servlet="http://cocoon.apache.org/schema/servlet" 

In the same file, right after the <avalon:bridge/> element add your servlet setup:

	<bean id="myDomain.com.myApplication" class="org.apache.cocoon.sitemap.SitemapServlet">
		<servlet:context mount-path="" context-path="file:///${webroot}"/>
	</bean> 

If you're going to use cforms, you need to add a >connections< element inside the context element:

	<servlet:context mount-path="" context-path="file:///${webapp.root}">
		<servlet:connections>
			<entry key="ajax" value-ref="org.apache.cocoon.ajax.impl.servlet"/>
			<entry key="forms" value-ref="org.apache.cocoon.forms.impl.servlet"/>
			<entry key="style-default" value-ref="org.apache.cocoon.samples.style.default.servlet"/>
		</servlet:connections>
	</servlet:context> 

Define the webapp root directory

The perceptive reader may have noticed a Spring placeholder "${webapp.root} used as the value of the context-path attribute. The value of this variable should be the absolute path to the directory that contains your top-level sitemap and the servlet's WEB-INF directory.

To define this variable, Spring provides a nice automatic mechanism. Edit the web.xml file created by the archetype and add the following elements right before the first <listener> element already in the file:

	myApplication/src/main/webapp/WEB-INF/web.xml:
	
	<listener>
		<listener-class>org.springframework.web.util.WebAppRootListener</listener-class>
	</listener> 

"They wanted instant gratification..."

Enable support for blocks

Again edit the file:

	myApplication/src/main/webapp/WEB-INF/web.xml

Add the following >listener< element anywhere inside the top-level web-app element after the elements defined in the previous section:

	<listener>
		<description>Declare a context listener that installs all blocks.</description>
		<listener-class>org.apache.cocoon.blockdeployment.BlockDeploymentServletContextListener</listener-class>
	</listener> 

Configure cocoon interactive features

One of the most productive aspects of Cocoon is the ability to edit sitemaps, change content, modify flowscript and see the results immediately by refreshing the browser page.

To get this behavior from your webapp, create a core.properties file:

	org.apache.cocoon.reloading=true
	org.apache.cocoon.reloading.sitemap=true
	org.apache.cocoon.reload-delay.sitemap=5000
	org.apache.cocoon.reloading.config=true
	org.apache.cocoon.reloading.flow=true 

Create the necessary directories and put the file here:

	myApplication/src/main/webapp/WEB-INF/cocoon/properties/core.properties 

There are more elaborate ways to handle this so you can have different properties for different running modes, but this simple setup will give you the old behavior.


"He didn't use Java..."

Build the webapp

        cd myApplication 
        mvn clean install  

Deploy the webapp

In the myApplication/target directory you will find a directory and a war file:

	myApplication-1.0/
	myApplication-1.0.war 

To keep this page reasonably short, I'll cover deploying with Apache Tomcat. Jetty users will find the procedure very similar.

If you're using Tomcat locally, you can deploy the webapp by copying the whole myApplication-1.0 directory to the tomcat webapps directory.

If tomcat is running on a remote server, you can use the Tomcat Manager to upload the .war file or copy it to the remote webapps directory by any convenient means.

I prefer to simply copy the WEB-INF and META-INF directories by hand or via ftp to "webapps/cocoon" in the tomcat installtion directory. This allows me to update the servlet without worrying about my content being erased by automatic deployment.

As suggested above, you can also change the name of the directory to something more concise like "cocoon", rather than "myApplication-1.0", which might change if you bump the version number.

This is how the setup looks on the server:

        tomcat/webapps/cocoon
                WEB-INF
                META-INF 

Add your content

Add your sitemap and any other content from your old cocoon 2.1 world to the cocoon directory:

        tomcat/webapps/cocoon
                WEB-INF
                sitemap.xmap
                ... 
		Your content 

Start tomcat if necessary and visit:

        http://localhost:8080/cocoon 

Most things will now work the way they did in the "old days." You only deploy the cocoon webapp once. You manage your content by copying it to appropriate subdirectories on the server. Sitemaps, form definitions, flowscript, web pages, etc., can be modified by editing them in place and the changes will be immediately visible.


Happy Cocoon users

Integration with the Apache web server

I like to keep all content files in directories below the apache server root directory. By overlapping cocoon so it has the same root, content can be processed by the server, scripting languages, cocoon, or other servlets.

To do this, I copy the WEB-INF and META-INF directories from the cocoon webapp to the root directory of the web server. On some linux systems, for example, this would be the directory "/var/www/html".

The apache configuration file contains these proxy definitions:

	ProxyPass        /cocoon http://localhost:8081/cocoon
	ProxyPassReverse /cocoon http://localhost:8081/cocoon 

In the tomcat/webapps directory, I make "cocoon" a symbolic link that points to the apache root directory.

With this setup, apache handles most of the static content of my site. But any links that use the trigger "cocoon" go through tomcat and then cocoon. (For example, this site.)

I have many pages that simply need processing with xslt. They have extensions ".xhtml" which apache recognizes with rewrite rules:

	<VirtualHost *:80>
		RewriteCond %{REQUEST_URI} !(^/cocoon/.*)
		RewriteRule (.*)\.xhtml(.*)$ cocoon/$1.$2$3 [P]
	</VirtualHost> 

The sitemap recognizes this extension and applies some simple processing:

	<map:match pattern="**.xhtml">
                <map:generate src="{1}.xml"/>
                <map:transform src="stylesheet.xsl"/>
                <map:serialize/>
	</map:match> 

Using blocks

All you have to do is drop them into the webapp lib directory. Using tomcat, they go here:

	tomcat/webapps/cocoon/WEB-INF/lib 

You'll have to restart the servlet for the blocks to be noticed. I wish someone could show me a way to use the rcl with this setup! It refuses to install with maven if it's not part of a block.

You can also add a dependency for your block to the webapp pom. In that case, you have to build and redeploy the whole thing.

References

Acknowledgments

I got essential help for this project from many contributors on the cocoon mailing list. I want to particularly acknowledge the contributions of Michel Erard who showed me how to configure webapps to process sitemaps and the tireless patience of Grzegors Kossakowski.

Other Credits

Suggestions and complaints

I've been using cocoon for the last 5 years to publish my website, but I'm very far from satisfied with my understanding. If you have suggestions or complaints, I'm eager to improve. hugh@csparks.com