Modularize Cocoon apps (Using blocks)
In the basic tutorial "Your first Cocoon application using Maven 2" you created your first block. Cocoon 2.2 introduced the concept of blocks because it should help you to split your application into smaller parts. This increases the maintainability and reusability by orders of magnitude.
In this tutorial you will
- create a second block
- connect one block with another one
- use a pipeline fragement available in one block from within another block
Create a second block
For this purpose move into the getting-started-app directory and use the Maven 2 archetype again:mvn archetype:create -DarchetypeGroupId=org.apache.cocoon -DarchetypeArtifactId=cocoon-22-archetype-block -DarchetypeVersion=1.0.0-RC2 -DgroupId=com.mycompany -DartifactId=myBlock2The result is a second block and you should find following directory structure:
getting-started-app
+-myBlock1
| +-pom.xml
| +-src
| +-[...]
+-myBlock2
+-pom.xml
+-src
+-[...]
Then runmvn install eclipse:eclipse
in order to make it ready for be imported in Eclipse and to put it into the local Maven repository. Then you can import the project into Eclipse.
Connect two blocks
Let's assume that you want to use a pipeline defined in block2 from within block1. You have to establish the connection between the two blocks. Edit getting-started-app/myBlock1/src/main/resources/META-INF/cocoon/spring/servlet-service.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:servlet="http://cocoon.apache.org/schema/servlet"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://cocoon.apache.org/schema/servlet http://cocoon.apache.org/schema/servlet/cocoon-servlet-1.0.xsd">
<bean id="com.mycompany.myBlock1.service" class="org.apache.cocoon.sitemap.SitemapServlet">
<servlet:context mount-path="/myBlock1" context-path="blockcontext:/myBlock1/">
<servlet:connections>
<entry key="myBlock2" value-ref="com.mycompany.myBlock2.service"/>
</servlet:connections>
</servlet:context>
</bean>
</beans>
Additionally add the new block as dependency to the block's Maven project descriptor (getting-started-app/myBlock1/pom.xml):
<?xml version="1.0" encoding="UTF-8"?>
<project>
[...]
<dependencies>
[...]
<dependency>
<groupId>com.mycompany</groupId>
<artifactId>myBlock2</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
[...]
</project>
If you use the RCL goal of the Cocoon Maven plugin, you will also want to add the location of the new block to the configuration file of block1. This has the advantage that you can work on block2 and the changes take effect immediatly. Open getting-started-app/myBlock1/rcl.properties and add
com.mycompany.myBlock1.service%classes-dir=./target/classes com.mycompany.myBlock2.service%classes-dir=../myBlock2/target/classes %exclude-lib=com.mycompany:myBlock2
Restart the servlet container by invoking
mvn jetty:run
Now it's time to test connection. For that purpose add a pipeline to the sitemap of block1 (getting-started-app/myBlock1/src/main/resources/COB-INF/sitemap.xmap):
<?xml version="1.0" encoding="UTF-8"?>
<map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
<map:pipelines>
[...]
<map:pipeline>
<map:match pattern="callingBlock2">
<map:generate src="servlet:myBlock2:/spring-bean" type="file"/>
<map:serialize type="xml"/>
</map:match>
</map:pipeline>
[...]
</map:pipelines>
</map:sitemap>
The file generator of this pipeline uses a special purpose protocol, the
servlet protocol, to access a pipeline defined in the other block. If you enter
http://localhost:8888/myBlock1/callingBlock2 shows the output of the
spring-bean pipeline of myBlock2 but routed through myBlock1.
Though this is a "hello word" style example you might already imagine
the power of this protocol, e.g. you can move all styling resources and
pipelines to a particular block.
Use a pipeline fragment
The previous example showed how you can call a pipeline from another block. But here is even more you can do! A sitemap can also provide pipeline fragements that can be used by other blocks:
<?xml version="1.0" encoding="UTF-8"?>
<map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
<map:pipelines>
<map:pipeline>
<map:match pattern="callingTransformationService">
<map:generate src="demo/welcome.xml"/>
<map:transform type="servletService">
<map:parameter name="service"
value="servlet:myBlock2:/myXsltTransformation-service"/>
</map:transform>
<map:serialize type="xml"/>
</map:match>
</map:pipeline>
</map:pipelines>
</map:sitemap>When the requests arrives at callingTransformationService pipeline, the generator produces SAX events of demo/welcome.xml. There is nothing special here. The interesting part comes with the following transformer of type servletService. It calls a transformation service which is provided by myBlock2.
Add this service to the sitemap of myBlock2 (getting-started-app/myBlock2/src/main/resources/COB-INF/sitemap.xmap). It consists of one XSLT transformation step:
<?xml version="1.0" encoding="UTF-8"?>
<map:sitemap xmlns:map="http://apache.org/cocoon/sitemap/1.0">
<map:pipelines>
<map:pipeline>
<map:match pattern="myXsltTransformation-service">
<map:generate src="service-consumer:"/>
<map:transform src="myXsltTransformation.xslt"/>
<map:serialize type="xml"/>
</map:match>
</map:pipeline>
</map:pipelines>
</map:sitemap>The generator uses the service-consumer protocol which initializes the service. Then the pipeline continues with an XSLT transformation myXsltTranformation.xslt, which has to be put into the same directory as the myBlock2 sitemap:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <html> <head> <title>Output of the XSLT transformation service.</title> </head> <body> Output of the XSLT transformation service. </body> </html> </xsl:template> </xsl:stylesheet>
Point your browser at http://localhost:8888/myBlock1/callingTransformationService and see the output of the pipeline.


