Using gradle to build an osgi bundle supporting SCR Annotations

Introduction

I’ve been working on projects using Apache Sling and Day (Adobe) CQ5 where the back-end Java code is packaged and deployed as OSGi bundles.

The IDE provided for CQ5 development is CRXDE and it provides a very easy mechanism for developing and building OSGi bundles.  (More on CRXDE here)  The wizard creates a bundle with a default BND descriptor file which can be edited to specify the package exports and imports according to the functionality of the bundle.  It also creates a default Activator class but I haven’t had to use it too much as we use SCR Annotations with our Java classes to register resources such as Sling Servlets or Services. (More on SCR Annotations here)  The Java code resides in the JCR repository (CRX in the case of CQ) and CRXDE builds this on the server and deploys the bundle to Apache Felix via JCR Install (a mechanism built on the File Install service).  This process is very transparent and is convenient for developers who want to get up and running very quickly without the headaches of setting up local build environments, checking out code from source control, writing and tweaking build scripts until finally something is deployed and working on the server.

However, for settings where multiple developers are working on the same code and the build and deploy scripts are approximating/mirroring those for continuous integration and deployment to test and production environments, it is useful to familiarise oneself with building and deploying offline.  It gives the developer functionality to make use of their IDE of choice to take advantage of various plugins for code completion, checkstyles, etc.  I prefer Eclipse among the IDEs I’ve worked with.

One of the ways to build the OSGi bundles is using Maven and make use of plugins such as the maven-bundle-plugin and maven-scr-plugin.  Most projects I’ve worked on had maven projects and we used maven to manage our builds.  An example of the maven setup to build and deploy OSGi bundles can be found in the Apache Sling project source code.

I came across Gradle (an alternative build tool based on Groovy) a while ago and I thought I’d try and teach myself by building something that I know how to do it in maven–building an OSGi bundle with SCR support.

Prerequisites

Gradle (1.0+)

– Maven repository with dependencies required for the bundle


Sample build.gradle file:

apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'osgi'

repositories {
   mavenCentral()
   mavenRepo urls: ['http://localhost:4502/maven/repository']
}

configurations {
    provided
    compile.transitive = true
}

dependencies {
  provided (
    [group: 'org.osgi', name: 'org.osgi.core', version: '4.1.0'],
    [group: 'org.osgi', name: 'org.osgi.compendium', version: '4.1.0'],
    [group: 'org.apache.sling', name: 'org.apache.sling.rewriter', version: '1.0.0'],
    [group: 'org.apache.sling', name: 'org.apache.sling.jcr.api', version: '2.1.0'],
    [group: 'com.day.cq', name: 'cq-commons', version: '5.4.6'],
    [group: 'com.day.cq', name: 'cq-security', version: '5.4.8'],
    [group: 'com.day.cq', name: 'cq-security-api', version: '5.4.4'],
    [group: 'com.day.cq.wcm', name: 'cq-wcm-foundation', version: '5.4.2'],
    [group: 'org.slf4j', name: 'slf4j-api', version: '1.4.3'],
    [group: 'org.slf4j', name: 'slf4j-simple', version: '1.5.2'],
    [group: 'org.apache.sling', name: 'org.apache.sling.api', version: '2.2.0'],
    [group: 'javax.servlet', name: 'servlet-api', version: '2.4'],
    [group: 'org.apache.felix', name: 'org.apache.felix.scr.ant', version: '1.1.0' ],
    [group: 'javax.jcr', name: 'jcr', version: '2.0' ],
    [group: 'org.apache.felix', name: 'org.apache.felix.webconsole', version: '3.1.8' ],
    [group: 'org.apache.felix', name: 'org.apache.felix.scr.annotations', version: '1.5.0' ]

  )

}

sourceCompatibility = 1.6
group = 'com.day.myapp'
version = '0.1-SNAPSHOT'
ant.properties.src = 'src/main/java'
ant.properties.classes = 'build/classes/main'

task genscr(dependsOn: compileJava) << {
   println ant.properties.classes
   ant.taskdef(resource: 'scrtask.properties', classpath: configurations.provided.asPath )
   ant.scr(srcdir: ant.properties.src, destdir: ant.properties.classes, classpath: configurations.provided.asPath)
}
jar.dependsOn(genscr)
jar {
    manifest {
        version = '0.1.0.SNAPSHOT'
        symbolicName = 'com.day.myapp.core'
        instruction 'Import-Package', '*'
        instruction 'Export-Package', 'com.day.myapp.*'
        instruction 'Bundle-Activator', 'com.day.myapp.Activator'
        instruction 'Service-Component', 'OSGI-INF/serviceComponents.xml'
	instruction 'Test', 'test'

    }
}

uploadArchives {
	repositories {
		flatDir(dirs: file('repos'))
	}
}

sourceSets.main.compileClasspath += configurations.provided

Let’s go through the build file.

apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'osgi'

These three lines tell gradle that we are building a Java project and will need some maven and OSGi related functions when building the project.

repositories {
mavenCentral()
mavenRepo urls: ['http://localhost:4502/maven/repository']
}

These add maven repositories to look for dependencies during compile and build time. The second maven repository is a custom one in our CQ5 instance through the ArchivaServlet which exposes all OSGi bundles installed in CQ5 as a maven repository.

configurations {
provided
compile.transitive = true
}

This I think is a named list of dependency scopes like maven (test, compile, provided, etc).

dependencies {
provided (
[group: 'org.osgi', name: 'org.osgi.core', version: '4.1.0'],
[group: 'org.osgi', name: 'org.osgi.compendium', version: '4.1.0'],
[group: 'org.apache.sling', name: 'org.apache.sling.rewriter', version: '1.0.0'],
[group: 'org.apache.sling', name: 'org.apache.sling.jcr.api', version: '2.1.0'],
[group: 'com.day.cq', name: 'cq-commons', version: '5.4.6'],
[group: 'com.day.cq', name: 'cq-security', version: '5.4.8'],
[group: 'com.day.cq', name: 'cq-security-api', version: '5.4.4'],
[group: 'com.day.cq.wcm', name: 'cq-wcm-foundation', version: '5.4.2'],
[group: 'org.slf4j', name: 'slf4j-api', version: '1.4.3'],
[group: 'org.slf4j', name: 'slf4j-simple', version: '1.5.2'],
[group: 'org.apache.sling', name: 'org.apache.sling.api', version: '2.2.0'],
[group: 'javax.servlet', name: 'servlet-api', version: '2.4'],
[group: 'javax.jcr', name: 'jcr', version: '2.0' ],
[group: 'org.apache.felix', name: 'org.apache.felix.scr.ant', version: '1.1.0' ],
[group: 'org.apache.felix', name: 'org.apache.felix.webconsole', version: '3.1.8' ],
[group: 'org.apache.felix', name: 'org.apache.felix.scr.annotations', version: '1.5.0' ]

)

}

This is a list of dependencies I needed to compile and build the osgi bundle. The last 3 dependencies were used to generate the SCR descriptor for the OSGi classes with SCR Annotations in them.

task genscr(dependsOn: compileJava) << {
println ant.properties.classes
ant.taskdef(resource: 'scrtask.properties', classpath: configurations.provided.asPath )
ant.scr(srcdir: ant.properties.src, destdir: ant.properties.classes, classpath: configurations.provided.asPath)
}

This is a groovy version of an ant target which calls the ant SCR task as there isn’t yet support for SCR in Gradle’s osgi plugin. The ant task looks in the build/classes folder for the generated classes and then generates a serviceComponents.xml file which describes all the OSGi components and services to be registered in the bundle.

jar.dependsOn(genscr)

This tells gradle that the jar task is dependent on the genscr task where the SCR descriptor is created.

jar {
manifest {
version = '0.1.0.SNAPSHOT'
symbolicName = 'com.day.myapp.core'
instruction 'Import-Package', '*'
instruction 'Export-Package', 'com.day.myapp.*'
instruction 'Bundle-Activator', 'com.day.myapp.Activator'
instruction 'Service-Component', 'OSGI-INF/serviceComponents.xml'
instruction 'Test', 'test'

}
}

This tells the jar task to create the MANIFEST.MF file for the OSGi bundle. The osgi plugin internally uses Peter Krient’s BND tool to generate the final MANIFEST.MF file. THe patterns used here in each of the “instruction” lines are the same that we’d use in a .bnd file.

uploadArchives {
repositories {
flatDir(dirs: file('repos'))
}
}

This task just deploys the file to a given repository, which in this case is a folder on the local file system. I haven’t actually used any mechanism to deploy the bundle to the OSGi container but that part is open to whatever mechanism the project uses. We use maven-sling-plugin to use HTTP POST to deploy the bundle to CRX which in turn uses JCR Install to install and start the bundle. This could just as well use File Install.

To build, have the source code under src/main/java like the typical maven project, and keep the build.gradle file in the root folder.  Then run

> gradle jar

or

>grade uploadArchives

– Sarwar Bhuiyan

Advertisements
Using gradle to build an osgi bundle supporting SCR Annotations

5 thoughts on “Using gradle to build an osgi bundle supporting SCR Annotations

  1. They usually do not use complicated logics and other syntaxes which their high end counterparts use and are therefore easily understood by common man quicker.
    And maximum number of school pass students is engaging in the bpo sector.
    When it comes to navigating tax penalties, the first thing to do is to determine what type of penalty you’re dealing with.

    1. Sarwar Bhuiyan says:

      By the way this was just some experimentation and not something I’ve updated or used in production. There are much better ways of creating osgi bundles in maven.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s