Tuesday 30 October 2012

A detailed analysis of a Maven POM for an Adobe CQ5 OSGi Bundle

This article describes everything about the Maven POM which is used to build an OSGi Bundle for CQ5.

Important summary items

  • The “packaging” directive must be set to “bundle”.
  • Look at the bundles listed in Felix and make sure that your dependency list specifies the correct version numbers. 
  • Version numbers are becoming mandatory, so always specify them.
  • The encoding for source & target files can be set in a global <encoding> property & will apply throughout your build.
  • Use profiles to define different installation locations (not shown here).

POM.xml

The POM first line, complete with name-spaces :-
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

Tell Maven which model version of the POM this is:

     <modelVersion>4.0.0</modelVersion>

Define project specific attributes:   
     <groupId>com.day.cq5.lily</groupId>
     <artifactId>lily-cq5-core</artifactId>
     <version>0.0.2-SNAPSHOT</version>
     <name>CQ5 Lily Core</name>
     <description>Contains the blah blah.</description>

The packaging definition is very important!!  This tells maven about the output format that it must produce (in order to produce an OSGi bundle, we need the maven-bundle-plugin): 
     <packaging>bundle</packaging>




The properties section defines some variable which we use further on within the POM.
     <properties>
          <encoding>utf-8</encoding>
          <devserver>http://localhost:4502/system/console/install</devserver>
          <devuser>admin</devuser>
          <devpassword>admin</devpassword>
     </properties>

Next, we tell Maven to use our local CQ5 instance as a repository of dependencies (an Archiva repository).  You need to install the CQ Archiva servlet (see dev.day.com).
     <repositories>
        <repository>
            <id>localinstance</id>
            <name>CQ 5.x localinstance</name>
            <url>http://localhost:4502/maven/repository</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
     </repositories>

     <pluginRepositories>
        <pluginRepository>
            <id>localinstance</id>
            <name>CQ 5.x localinstance</name>
            <url>http://localhost:4502/maven/repository</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
     </pluginRepositories>
The above is optional – if you have access to a full repository, then maybe use that instead.  This could be defined in settings.xml but is kept here so that it is visible to this project.

We now take a look at the build definition for the OSGi bundle.
This is driven by the defined plugin definitions which have goals that default to the default Maven life cycle phases.

The compiler plugin controls the javac compilation.  Note that java 1.5 is used by default therefore, here we have specified the use of Java 1.6.
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>

The bundle plugin controls the packaging of the output in to an OSGi bundle.  The key element here is the Export-Package element which defines the package that we want to expose in OSGi.  There are lots of additional items that can be specified here to avoid clashes with other classes loaded by the class-loader.
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <version>2.3.7</version>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                    <Export-Package>
                        com.day.cq5.lily.util.*;
                        version=${project.version}
                    </Export-Package>
                    </instructions>
                </configuration>
            </plugin>

The sling plugin is used to install the bundle in to the local CQ instance.  Note, it would probably be better to define this configuration within a profile definition so that the bundle could be installed on other CQ5 instances (such as SIT or UAT environments):
            <plugin>
                <groupId>org.apache.sling</groupId>
                <artifactId>maven-sling-plugin</artifactId>
                <version>2.1.0</version>
                     <executions>
                          <execution>
                               <id>install-bundle</id>
                               <goals>
                                    <goal>install</goal>
                               </goals>
                          </execution>
                     </executions>
                <configuration>
                          <slingUrl>${devserver}</slingUrl>
                          <user>${devuser}</user>
                          <password>${devpassword}</password>
                     </configuration>
            </plugin>
        </plugins>
    </build>

The following section defines the Java bundles that our code is dependent upon.  Note that there are some commented out dependencies which are not required in the demonstration package but, which you will probably need at some time or other.
    <dependencies>

        <dependency>
            <groupId>com.day.cq.wcm</groupId>
            <artifactId>cq-wcm-api</artifactId>
            <version>5.5.0</version>
        </dependency>

        <dependency>
            <groupId>com.day.cq</groupId>
            <artifactId>cq-commons</artifactId>
            <version>5.5.0</version>
        </dependency>

         <dependency>
            <groupId>org.apache.sling</groupId>
            <artifactId>org.apache.sling.api</artifactId>
            <version>2.2.4</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
             <version>3.0.1</version>
        </dependency>

        <!--        
        <dependency>
            <groupId>org.apache.sling</groupId>
            <artifactId>org.apache.sling.scripting.jst</artifactId>
            <version>2.0.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.felix</groupId>
            <artifactId>org.apache.felix.scr.annotations</artifactId>
            <version>1.7.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.felix</groupId>
            <artifactId>org.apache.felix.scr</artifactId>
            <version>1.2.0</version>
        </dependency>
          -->

    </dependencies>
Note that CQ5 ships with commons-lang3!

This following section of profiles defines some Quality Assurrance processes which can be optionally used within the project:
    <profiles>
        <profile>
            <id>runChecks</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-checkstyle-plugin</artifactId>
                        <version>2.9.1</version>
                        <executions>
                            <execution>
                                <phase>compile</phase>
                                <goals>
                                    <goal>check</goal>
                                </goals>
                            </execution>
                        </executions>
                        <configuration>
                            <configLocation>${project.basedir}/config_rulesets/lily_checkstyle.xml</configLocation>
                            <violationSeverity>error</violationSeverity>
                            <logViolationsToConsole>true</logViolationsToConsole>
                        </configuration>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-pmd-plugin</artifactId>
                        <version>2.7.1</version>
                        <executions>
                            <execution>
                                <phase>compile</phase>
                                <goals>
                                    <goal>check</goal>
                                </goals>
                            </execution>
                        </executions>
                        <configuration>
                            <sourceEncoding>utf-8</sourceEncoding>
                            <failurePriority>2</failurePriority>
                            <rulesets>
                                <ruleset>${project.basedir}/config_rulesets/lily_PMD.xml</ruleset>
                            </rulesets>
                            <targetJdk>1.6</targetJdk>
                            <outputDirectory>target/pmd-reports</outputDirectory>
                            <minimumPriority>2</minimumPriority>
                            <verbose>true</verbose>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>

And now, we close off the POM.xml :-
</project>
 
The end.

Appendix - some useful pointers

Maven Bundle Plugin - Configuration
http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html

Maven Bundle Plugin - Goals
http://svn.apache.org/repos/asf/felix/releases/maven-bundle-plugin-2.3.7/doc/site/index.html

OSGi Sling Service Example
http://blogs.adobe.com/kmossman/2012/04/osgi-sling-service-example.html

Maven Sling Plugin - definition
http://mvnrepository.com/artifact/org.apache.sling/maven-sling-plugin/2.1.0

Maven Sling Plugin - project home
http://sling.apache.org/site/sling.html

General Maven Pointers
http://maven.apache.org/guides/getting-started/index.html

http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html

http://maven.apache.org/ref/3.0.4/maven-settings/settings.html

http://maven.apache.org/guides/mini/guide-configuring-plugins.html

http://docs.codehaus.org/display/MAVENUSER/Compiler+Plugin

http://search.maven.org/

http://mvnrepository.com/