Thursday, 8 November 2012

How to use a Maven project to create a content package for CQ

-->

How to use a Maven project to create a content package for CQ (or more specifically Sling)


This articles describes how to get hold of an example Maven project for creating a content package; how to use it to install a content package; and, a little bit of explanation of the POM provided.

Here are some important summary items.


Downloading the sample Maven project


$ mvn archetype:generate

...
301: remote -> org.apache.sling:sling-bundle-archetype (-)
302: remote -> org.apache.sling:sling-initial-content-archetype (Maven archetype for initial content)
303: remote -> org.apache.sling:sling-jcrinstall-bundle-archetype (-)
304: remote -> org.apache.sling:sling-launchpad-standalone-archetype (-)
305: remote -> org.apache.sling:sling-launchpad-webapp-archetype (-)
306: remote -> org.apache.sling:sling-servlet-archetype (Maven archetype for Sling Servlets)
….

Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 225: 302
Downloading: http://repo1.maven.org/maven2/org/apache/sling/sling-initial-content-archetype/1.0.0/sling-initial-content-archetype-1.0.0.jar
Downloaded: http://repo1.maven.org/maven2/org/apache/sling/sling-initial-content-archetype/1.0.0/sling-initial-content-archetype-1.0.0.jar (10 KB at 104.4 KB/sec)
Downloading: http://repo1.maven.org/maven2/org/apache/sling/sling-initial-content-archetype/1.0.0/sling-initial-content-archetype-1.0.0.pom
Downloaded: http://repo1.maven.org/maven2/org/apache/sling/sling-initial-content-archetype/1.0.0/sling-initial-content-archetype-1.0.0.pom (4 KB at 7.4 KB/sec)
Define value for property 'groupId': : com.mkalugin.cq5
Define value for property 'artifactId': : test-wibble
Define value for property 'version':  1.0-SNAPSHOT: :
Define value for property 'package':  com.mkalugin.cq5: :
Confirm properties configuration:
groupId: com.mkalugin.cq5
artifactId: test-wibble
version: 1.0-SNAPSHOT
package: com.mkalugin.cq5
 Y: :
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: sling-initial-content-archetype:1.0.0
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.mkalugin.cq5
[INFO] Parameter: artifactId, Value: test-wibble
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: com.mkalugin.cq5
[INFO] Parameter: packageInPathFormat, Value: com/mkalugin/cq5
[INFO] Parameter: package, Value: com.mkalugin.cq5
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: groupId, Value: com.mkalugin.cq5
[INFO] Parameter: artifactId, Value: test-wibble
[INFO] project created from Archetype in dir: /cq/mkalugin-cq/projectX/proj-cq5-core/tmp/test-wibble
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 19.944s
[INFO] Finished at: Tue Oct 30 14:56:39 GMT 2012
[INFO] Final Memory: 7M/81M
[INFO] ------------------------------------------------------------------------
$ ls
test-wibble/

$ cd test-wibble
$ mvn -PautoInstallBundle install
[INFO] Scanning for projects...
[INFO]                                                                        
[INFO] ------------------------------------------------------------------------
[INFO] Building test-wibble 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] Installing Bundle com.mkalugin.cq5.test-wibble(/cq/mkalugin-cq/projectX/proj-cq5-core/tmp/test-wibble/target/test-wibble-1.0-SNAPSHOT.jar) to http://localhost:4502/system/console via POST
[INFO] Bundle installed
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

Anatomy of the POM

The above section showed how to get the example maven project for a CQ (more specifically a Sling) content package.
Now follows an analysis of the produced POM.

Standard first 3 lines :-

<?xml version="1.0" encoding="ISO-8859-1"?>
<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

Now, our project specific information (NB, this appears in Felix bundles).

    <groupId>com.mkalugin.cq5</groupId>
    <artifactId>test-wibble</artifactId>

NB, Packaging is specified as "bunlde", this will be an OSGi bundle (not a CRX package).

    <packaging>bundle</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>test-wibble</name>
    <description>com.mkalugin.cq5 - test-wibble</description>

Now comes the maven-bundle-plugin definition - this does the work of creating an OSGi Sling bundle with instructions of how to install (or uninstall if you with) the content nodes.

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <version>2.3.7</version>

Now comes the details of the content nodes and what we want to do with them.

                <configuration>
                    <instructions>
                        <Sling-Nodetypes>
                            SLING-INF/nodetypes/nodetypes.cnd
                        </Sling-Nodetypes>
                        <Sling-Initial-Content>
                            SLING-INF/scripts;overwrite:=true;uninstall:=true;path:=/apps/my/node,
                            SLING-INF/content;overwrite:=true;uninstall:=true;path:=/content
                        </Sling-Initial-Content>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>

An autoInstallBundle profile is given by default showing how the produced bundle can be loaded & installed in to Felix.
This is documented at : http://sling.apache.org/site/content-loading-jcrcontentloader.html

    <profiles>
        <profile>
            <id>autoInstallBundle</id>
            <build>
                <plugins>
                    <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>
                                <configuration>
                                    <slingUrl>http://localhost:4502/system/console</slingUrl>
                                    <user>admin</user>
                                    <password>admin</password>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
</project>

Further reading : http://sling.apache.org/site/content-loading-jcrcontentloader.html

The end.

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/