search cq related information on adobe, day, jackrabbit, sling and other sites
   We would like to hear from you. Suggestions to improve cqblueprints.com are very welcome. Please contact us.
Loading
Blueprints » Deploying 3rd Party Libraries

Deploying 3rd Party Libraries

Last modified by Craig S. Dickson on 2011/08/01 22:48

CQ is built on top of Apache Sling, and Apache Sling is built on top of an OSGi container (Apache Felix specifically).

OSGi containers behave slightly differently (in terms of how classes are loaded and made available on the classpath) than most Java developers are used to.

To be able to make classes available within the OSGi container, Jar files need to be packaged in a specific way, including adding extra meta-data to the standard MANIFEST.MF file. The problem this can create is that libraries created by other developers that have not been built with OSGi in mind are missing this extra information and so their Jar files cannot be deployed in CQ.

This Blueprint details how to easily and reliably expose non-OSGi enabled libraries in CQ. For more details about building and deploying OSGi bundles to CQ, read this Blueprint - Building and Deploying OSGi Bundles.

A Naive Solution

One solution to this problem would be to take the Jar file, explode it, add the extra information to the manifest, repackage the jar and deploy it to CQ. There are at least 3 major issues with this approach:

  1. If the Jar has been signed, then these modifications will make the signature invalid and any tools that check that kind of thing will generate warnings or even failures because of it. Checksums can also cause similar problems.
  2. When there is a new release of the 3rd party Jar, this manual "upgrade" process will have to be completed (correctly) again.
  3. In a team environment, making that modified Jar available to everyone can be problematic. Maybe it can be pushed into the team repository and override the non-modified version of the Jar - not many of the repository management tools will like that very much though (see point 1 above). Alternatively, maybe each team member could push the updated Jar into their local repositories, but Maven may report issues when they build next (see point 1 above again).

The bottom line is that manually modifying artifacts is a really bad idea and will only lead to trouble, often the kind of trouble that is maddeningly hard to debug.

The Recommended Solution

The recommended solution is to create your own OSGi bundle that wraps one or more of these non-OSGi Jars. This has several benefits:

  1. Apache Maven can be used to automated and perform the whole process, so it is reliable and repeatable.
  2. The original Jars are not modified.
  3. When a new version of the Jar(s) becomes available, the dependency in the POM just needs to be updated. Then simply make a new release of the bundle.
  4. Because the bundle is a proper first class Maven artifact, it can easily be shared with team developers with no issues related to signatures and checksums etc.

To get started, create a new Maven project. In the POM set the packaging type to bundle and include the Apache Felix bundle plugin.

<?xml version="1.0" encoding="UTF-8"?>

<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">

   <modelVersion>4.0.0</modelVersion>

   <parent>
       <groupId>com.headwire.cqblueprints</groupId>
       <artifactId>parent</artifactId>
       <version>5.3.2-SNAPSHOT</version>
   </parent>

   <artifactId>cqblueprints-examples-wrap3rdpartylib</artifactId>
   <packaging>bundle</packaging>

   <name>${project.groupId} - ${project.artifactId}</name>

   <description>
        Demonstrates how to easily deploy the classes contained in a pre-packaged vanilla jar file into an OSGi
        container, when the jar does not contain the necessary OSGi manifest headers.
   </description>

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

</project>

Next add dependencies to the 3rd party libraries that the bundle will make available in CQ. Make sure to use a property to declare the versions of the dependencies, as they will need to be specified again later in the POM.

   <properties>
       <commons-codec.version>1.3</commons-codec.version>
       <commons-validator.version>1.3.1</commons-validator.version>
   </properties>

   <dependencies>
       <dependency>
           <groupId>commons-codec</groupId>
           <artifactId>commons-codec</artifactId>
           <version>${commons-codec.version}</version>
       </dependency>
       <dependency>
           <groupId>commons-validator</groupId>
           <artifactId>commons-validator</artifactId>
           <version>${commons-validator.version}</version>
       </dependency>
   </dependencies>

Next configure the bundle plugin.

           <plugin>
               <groupId>org.apache.felix</groupId>
               <artifactId>maven-bundle-plugin</artifactId>
               <configuration>
                   <instructions>
                       <Bundle-Activator>com.headwire.cqblueprints.examples.wrap3rdpartylib.Activator</Bundle-Activator>
                       <Embed-Dependency>*;scope=compile|runtime</Embed-Dependency>
                       <Embed-Directory>OSGI-INF/lib</Embed-Directory>
                       <Private-Package>com.headwire.cqblueprints.examples.wrap3rdpartylib</Private-Package>
                       <_exportcontents>
                            org.apache.commons.codec.*;version=${commons-codec.version},
                            org.apache.commons.validator.*;version=${commons-validator.version}
                       </_exportcontents>
                   </instructions>
               </configuration>
           </plugin>

There are many options that can be specified to control how the plugin behaves. The one that is crucial for this task is the <_exportcontents> tag. This setting defines which packages from the embedded 3rd party jars should be exposed to the OSGi environment. The .* suffix means that all sub-packages are included. It is important to include the version information for each 3rd party jar - otherwise the packages will be marked with the version of the bundle you are creating, which is almost certainly incorrect and may cause issues for package resolution within the OSGi container.

For an explanation of the other settings, see Building and Deploying OSGi Bundles.

Once the dependencies are set and the plugin is configured properly, it is simply a matter of running the standard Maven package/install process to generate the OSGi bundle. Inside of the built bundle, the META-INF/MANIFEST.MF now has the necessary OSGi meta data included in it. It will look something like this:

Manifest-Version: 1.0
Export-Package: org.apache.commons.codec;version="1.3",org.apache.comm
 ons.codec.net;uses:="org.apache.commons.codec,org.apache.commons.code
 c.binary";version="1.3",org.apache.commons.codec.language;uses:="org.
 apache.commons.codec";version="1.3",org.apache.commons.codec.digest;u
 ses:="org.apache.commons.codec.binary";version="1.3",org.apache.commo
 ns.codec.binary;uses:="org.apache.commons.codec";version="1.3",org.ap
 ache.commons.validator.javascript;version="1.3.1",org.apache.commons.
 validator.resources;version="1.3.1",org.apache.commons.validator.util
 ;uses:="org.apache.commons.beanutils,org.apache.commons.logging,org.a
 pache.commons.collections,org.apache.commons.validator";version="1.3.
 1",org.apache.commons.validator.routines;version="1.3.1",org.apache.c
 ommons.validator;uses:="org.apache.commons.validator.util,org.apache.
 oro.text.perl,org.apache.commons.beanutils,org.apache.commons.collect
 ions,org.apache.commons.logging,org.apache.commons.digester,org.xml.s
 ax,org.apache.commons.digester.xmlrules";version="1.3.1"
Embed-Directory: OSGI-INF/lib
Bundle-ClassPath: .,OSGI-INF/lib/commons-validator-1.3.1.jar,OSGI-INF/
 lib/commons-codec-1.3.jar
Built-By: craig
Tool: Bnd-1.43.0
Bundle-Name: com.headwire.cqblueprints - cqblueprints-examples-wrap3rd
 partylib
Created-By: Apache Maven Bundle Plugin
Build-Jdk: 1.6.0_26
Bundle-Version: 5.3.2.SNAPSHOT
Bnd-LastModified: 1311886098106
Bundle-ManifestVersion: 2
Bundle-Activator: com.headwire.cqblueprints.examples.wrap3rdpartylib.A
 ctivator
Bundle-SymbolicName: com.headwire.cqblueprints.examples-wrap3rdpartyli
 b
Import-Package: com.squeakysand.osgi.framework,org.apache.commons.bean
 utils,org.apache.commons.collections,org.apache.commons.digester,org.
 apache.commons.digester.xmlrules,org.apache.commons.logging,org.apach
 e.oro.text.perl,org.xml.sax
Embed-Dependency: *;scope=compile|runtime

Notice the value of the Export-Package property. It contains all of packages from the wrapped Jars that are going to be made available to the OSGi container.

Also notice the value of the Import-Package property - it not only declares any dependencies that any code within the bundle may have (if any), it also declares the dependencies of the bundled 3rd party Jars. To avoid relying on the OSGi container to provide the dependencies for the bundled Jars, include all of the transitive dependencies of the wrapped Jars in the bundle - this way the bundle is fully self contained and can be deployed into any container without issue. This can easily be done using the bundle plugin by adding one extra configuration tag:

<Embed-Transitive>true</Embed-Transitive>

The bundle can now be deployed to a CQ instance. For details on how to automate this process, see Building and Deploying OSGi Bundles. The classes in the packages that are exposed will now be available to other bundles and code within the CQ instance.

Tags:
Created by Craig S. Dickson on 2011/07/28 20:58
cqblueprints provided by headwire.com, Inc