May 25, 2010
Hugh Sparks
http://www.csparks.com/c22without.xhtml
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!
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.
A quick word for new cocoon users:
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.
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
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.
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.
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>
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>
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>
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.
cd myApplication mvn clean install
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 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.
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>
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.
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.
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