Sitemap components in Cocoon 2.2

For the cocoon 2.1 project I did last year, I wrote a few components in Java (mostly Generators and one InputModule). It’s a bit of a pain because it’s built on the out-of-date and intrusive avalon framework. Anyway the end result is that you can write things in your cocoon sitemap like:

  <map:match pattern="foo">
    <map:generate type="foo" />
    <map:transform type="xslt" src="{my:something}" />
    <map:serialize type="html" />
  </map:match>

Now with the newer cocoon 2.2 project, we needed to reuse the InputModule. In order to get an Avalon component working in cocoon 2.2, you need:

  • src/main/java/com/myco/MyInputModule.java
  • src/main/resources/META-INF/cocoon/avalon/my-input-module.xconf

The latter is the old style avalon configuration file and looks like this:

  <components>
    <input-modules>
      <component-instance
        class="com.com.myco.MyInputModule"
        logger="core.modules.input"
        name="my"
        />
    </input-modules>
  </components>

But cocoon 2.2 is meant to be Spring based. So we should be able to write a Spring bean that does the same thing. Unfortunately, this doesn’t appear to be documented anywhere. But there’s always the source code for cocoon itself.

As an aside, I’m hugely grateful to Jukka Zitting for making available git mirrors of apache projects. This let me download all of cocoon’s source, including full history in a very short time. It’s a helluva useful resource, and I hope it gets proper support from the ASF.

So, I started looking through the Cocoon source code for Spring config files that might reference InputModules.

⇒ find . -name '*.xml' | grep META-INF/cocoon/spring | xargs egrep InputModule
./core/cocoon-servlet-service/cocoon-servlet-service-components/src/main/resources/META-INF/cocoon/spring/cocoon-servlet-linkRewritingReader.xml:        <property name="inputModule" ref="org.apache.cocoon.components.modules.input.InputModule/servlet"/>
./core/cocoon-servlet-service/cocoon-servlet-service-components/src/main/resources/META-INF/cocoon/spring/cocoon-servlet-service-complete-path-module.xml:    <bean name="org.apache.cocoon.components.modules.input.InputModule/servlet"
./core/cocoon-servlet-service/cocoon-servlet-service-components/src/main/resources/META-INF/cocoon/spring/cocoon-servlet-service-complete-path-module.xml:        <property name="blockPathModule" ref="org.apache.cocoon.components.modules.input.InputModule/block-path"/>
./core/cocoon-servlet-service/cocoon-servlet-service-components/src/main/resources/META-INF/cocoon/spring/cocoon-servlet-service-path-module.xml:    <bean name="org.apache.cocoon.components.modules.input.InputModule/block-path"
./core/cocoon-servlet-service/cocoon-servlet-service-components/src/main/resources/META-INF/cocoon/spring/cocoon-servlet-service-property-module.xml:	<bean name="org.apache.cocoon.components.modules.input.InputModule/block-property"

That seems to suggest that by creating a Spring bean whose name is the interface followed by a slash and then a name, you should be able to get at it in the sitemap. And it does indeed work like that. This is the bean name I ended up with.

  <bean name="org.apache.cocoon.components.modules.input.InputModule/my"
        class="com.myco.MyInputModule">
    <property name="message" value="aardvark" />
  </bean>

In order for it to not be an Avalon component, I also removed the base class I was using and just implemented the InputModule interface directly. And to my surprise, I could use the bean directly in the sitemap.

But we have the source, so we can see why it’s working. Did you notice the ROLE constant in InputModule? We can grep for that in the source. That quickly led me to PreparedVariableResolver.java, which contains:

  InputModule module;
  try {
    module = (InputModule) this.manager.lookup(InputModule.ROLE + '/' + moduleName);
  } catch (ServiceException e) {
    throw new PatternException("Cannot get module named '" + moduleName +
    "' in expression '" + this.originalExpr + "'", e);
  }

The manager field is an instance of ServiceManager, which just happens to be AvalonServiceManager. And lookup() is a standard Spring construct:

  try {
    return this.beanFactory.getBean(role);
  } catch (BeansException be) {
    throw new ServiceException("AvalonServiceManager",
      "Exception during lookup of component with '" + role + "'.", be);
  }

Further nosing around reveals that Generators are looked up in Spring in a similar manner inside AbstractProcessingPipeline:

  try {
    this.generator = (Generator) this.newManager.lookup(Generator.ROLE + '/' + role);
  } catch (ServiceException ce) {
    throw ProcessingException.throwLocated("Lookup of generator '" + role + "' failed", ce, getLocation(param));
  }

I’m guessing that transformers, serializers and readers follow a similar pattern.

So, that’s how to do Cocoon components in Spring. It’s a bit simpler than before, which can’t be a bad thing. I just wish it was documente, but that’s a big problem for Cocoon anyway…

2 Comments to Sitemap components in Cocoon 2.2

  1. Mansour says:

    I can not thnk you enough for this neat article. I agree with you about the cocoon documentation thing. I don’t know if they are looking to improve it or not.

  2. András Hatvani says:

    Hello Giraffe,

    thanks for sharing this!

    Cheers,
    Andras