Chapter 5. Wicket Integration

5.1. Introduction

Apache Wicket has become on of the most popular web frameworks of these days. Especially developers with a strong Java background benefit from its Java-centric approach because all object-oriented features can be applied. This results in highly reusable code.

On the other side Cocoon implementing the pipe/filter pattern has its merits in the field of generating resources in different output formats.

The Cocoon-Wicket integration module bridges between those two web application frameworks in order to use the strenghts of both. This integration supports the integration of Cocoon into Wicket as well as the integration of Wicket into Cocoon.

Note: This is not an introduction into Apache Wicket. This documentation explains to the experienced Wicket user what needs to be done to integrate Cocoon 3 into a Wicket application.

5.2. Integrate Cocoon into Wicket

The integration of Cocoon into Wicket is available in several ways:

Whatever approach is chosen, the first step is adding cocoon-wicket and all its transitive dependencies to your project's classpath:

<dependency>
  <groupId>org.apache.cocoon.wicket</groupId>
  <artifactId>cocoon-wicket</artifactId>
  <version>3.0.0-alpha-2</version>
</dependency>

5.2.1. Mount a Cocoon sitemap

Mounting in the context of Wicket means a class implementing IRequestTargetUrlCodingStrategy is added to a Wicket web application. This interface is implemented by CocoonSitemap:

import org.apache.cocoon.wicket.target.CocoonSitemap;
import org.apache.wicket.protocol.http.WebApplication;

public class SomeWebApplication extends WebApplication {

    @Override
    protected void init() {
        ...
        this.mount(new CocoonSitemap("/sitemap", "/sitemap.xmap.xml"));                  (1)
        ...
    }
}
1

The first parameter is the mount path which is a part of the request URI that should be handled by CocoonSitemap. The second parameter is the location of the sitemap relativ to the servlet context.

Additionally you have to make sure that all Spring bean definitions provided by the Cocoon modules are loaded into the web application's Spring application context. Cocoon's own bean definitions are located in META-INF/cocoon/spring/*.xml.

The simplest solution for this task is referring to the Cocoon Spring Configurator in your main Spring application context, which is usually located in [servlet-context-base-directory]/WEB-INF/applicationContext.xml. It will automatically load all bean definitions located in META-INF/cocoon/spring/*.xml of all libraries on the classpath. The Cocoon Spring Configurator documentation contains further details.

Note that the Spring Configurator is one of the transitive dependencies of cocoon-wicket.

That's it! Everything else is the same as using Cocoon 3 outside of Wicket except that the servlet:/ protocol won't work in this environment.

5.2.2. Mount a Cocoon pipeline

NOTE: This hasn't been implemented yet!

Mounting a Cocoon pipeline follows the same idea as mounting a whole sitemap. However, it's only a single pipeline that is added to Wicket's URI path and that this can be done without having to write any XML.

All that needs to be done is subclassing from org.apache.cocoon.wicket.AbstractCocoonPipeline and implementing its addComponents method:

import com.mycompany.MyCocoonPipeline;
import org.apache.wicket.protocol.http.WebApplication;

public class SomeWebApplication extends WebApplication 

    @Override
    protected void init() {
        ...
        this.mount(new MyCocoonPipeline("/my-pipeline"));                                (1)
        ...
    }
}
1

The only parameter is the path where the pipeline should be be mounted by Wicket.

In MyCocoonPipeline all that needs to be done is subclassing from org.apache.cocoon.wicket.AbstractCocoonPipeline and implementing its abstract method addComponents:

package com.mycompany;
import org.apache.cocoon.wicket.AbstractCocoonPipeline;
import org.apache.wicket.protocol.http.WebApplication;

public class MyCocoonPipeline extends 
    org.apache.cocoon.wicket.AbstractCocoonPipeline<SAXPipelineComponent>

    @Override
    protected void addComponents() {                                                     (1)
        this.addComponent(new FileGenerator(this.getClass().getResource("test.xml")));
        this.addComponent(new XSLTTransformer(this.getClass().getResource("test.xsl")));        
        this.addComponent(new XMLSerializer());        
    }
}
1

Add all pipeline components that are required.

5.2.3. CocoonSAXPipeline Wicket component

The third alternative of using Cocoon in Wicket is adding a Cocoon pipeline as WebComponent. This is as simple as instantiating CocoonSAXPipeline and adding all generators and transformers that are required:

import org.apache.cocoon.pipeline.NonCachingPipeline;
import org.apache.cocoon.sax.SAXPipelineComponent;
import org.apache.cocoon.sax.component.StringGenerator;
import org.apache.cocoon.sax.component.XSLTTransformer;
import org.apache.cocoon.wicket.CocoonSAXPipeline;
import org.apache.wicket.markup.html.WebPage;
      
    public class Homepage extends WebPage {

    public Homepage() {
        CocoonSAXPipeline pipeline = new CocoonSAXPipeline("cocoon-pipeline-component",  (1)
                new NonCachingPipeline<SAXPipelineComponent>());
        pipeline.addComponent(new StringGenerator("<b>hello, Cocoon!</b>"));             (2)
        pipeline.addComponent(new XSLTTransformer(
                this.getClass().getResource("transform.xslt")));
        this.add(pipeline);
    }
}
1

Instantiate the component

2

Adding SAX pipeline components

The pipeline's result is added to the HTML produced by this page. This is the reason why only generators and transformers can be added to this component because the pipeline is always serialized as XHTML. An XHTMLSerializer is added implicitly to each pipeline.

5.3. Integrate Wicket into Cocoon

Note: The integration of Wicket into Cocoon os mostly a proof of concept. It is experimental and has neither been optimized nor tested yet.

The integration of Wicket into Cocoon means that the output of Wicket is added to the content stream of a pipeline. The currently available solution is a reader but alternatively a generator or a transformer would offer an even more alternatives.

As pointed out for the Wicket-Cocoon integration, the first step in every case is adding cocoon-wicket and all its transitive dependencies to your project's classpath:

<dependency>
  <groupId>org.apache.cocoon.wicket</groupId>
  <artifactId>cocoon-wicket</artifactId>
  <version>3.0.0-alpha-2</version>
</dependency>

5.3.1. Wicket reader

By using the Wicket reader a servlet request is referred to a Wicket web application. The current implementation expects exactly one Wicket web application being available as Spring bean:

<beans>
  <bean id="wicketWebapp" 
    class="com.mycompany.MyWicketWebapp"/>
</beans>

By adding the bean definition file as resource into META-INF/cocoon/spring the Wicket web application bean will be loaded automatically.

Then the Wicket reader has be to used in the sitemap:

<map:sitemap>
  <map:pipelines>    
    <map:pipeline type="noncaching">
      <map:match wildcard="my-wicket-app/**">
        <map:read type="wicket" base-path="/my-wicket-app" />                            (1)
      </map:match>                                                                       (2)
    </map:pipeline>
  </map:pipelines>
</map:sitemap>
1

Use a ** wildcard to match all requests that start with my-wicket-app.

2

The name of the reader is wicket. It's also required to define the base path so that Wicket can calclulate realtive URLs correctly.