Integration Testing OSGi Bundles in Apache Karaf/ServiceMix with Pax Exam 3

This short post demonstrates a bare-bones configuration of Pax Exam 3 for testing an OSGi bundle deployed in ServiceMix 4.  Over the last couple years, Pax Exam and Apache Karaf introduced some fantastic features enabling the provisioning of a clean Apache Karaf distribution,  such as Apache Karaf itself, Apache ServiceMix, or JBoss Fuse, for integration testing.  In the past, I have manually configured 100+ facets of Pax Exam 1 to mimic an out-of-the-box Apache ServiceMix as well as written JMX based control code to provision an OSGi application into a stand-alone instance of Apache ServiceMix.  While both of these approaches enabled successful unit and integration testing, the features introduced since Pax Exam 1 make such approaches unnecessary.  These new features enable the provisioning of a Karaf distribution  with minimal configuration of Pax Exam.

While Apache Karaf and Pax exam both provide some example code around the features needed to provision a Karaf based distribution for testing, the code has recently shuffled between the two projects and it takes a bit of digging to get it all working.  The points illustrated below provide some tips in conjunction with the working sample project available in GitHub.  Building on the code here, and optionally incorporating the Pax Exam Maven Plug-in, one can create an elaborate set of integration test profiles for an OSGi based application or bundle.

The following snippet from the example POM file demonstrates the important aspects of the Maven project configuration.  Note that contrary to some examples in the Karaf documentation, in Pax Exam 3 you do not need to include the Karaf distribution as a Maven dependency in your integration test project.  Not requiring the distribution as a dependency reduces the chances of inadvertent classpath contamination by the distribution’s transitive dependencies.

    ...
    <dependency>
      <groupId>org.ops4j.pax.exam</groupId>
      <artifactId>pax-exam-container-karaf</artifactId>
      <version>3.2.0</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.ops4j.pax.exam</groupId>
      <artifactId>pax-exam-junit4</artifactId>
      <version>3.2.0</version>
      <scope>test</scope>
    </dependency>
    ...
  • Lines 2-6 – These lines incorporate the Karaf container support from Pax Exam.  This code was previously provided by a Karaf JAR but recently moved to the Pax Exam project.  You must include a container implementation in order for Pax Exam to work.
  • Lines 7-11 – These lines incorporate the Pax Exam support for JUnit based tests. It is possible to use test frameworks other than JUnit if desired.

The following snippet from the integration test example demonstrates the important aspects of the test case.

package valeri.blog.examples.pax_exam_servicemix.impl;

import static org.junit.Assert.*;
import static org.ops4j.pax.exam.CoreOptions.*;
import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.*;

...

import javax.inject.Inject;

...

import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.Configuration;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.PaxExam;
import org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.ConfigurationAdmin;

import valeri.blog.examples.pax_exam_servicemix.Service;

...
@RunWith(PaxExam.class)
public class DefaultServiceIT {

    @Inject
    private BundleContext bundleContext;

    @Inject
    private ConfigurationAdmin configurationAdmin;

    @Configuration
    public Option[] config() {
        return new Option[] {
                // Provision and launch a container based on a distribution of Karaf (Apache ServiceMix).
                karafDistributionConfiguration()
                    .frameworkUrl(
                             maven()
                                     .groupId("org.apache.servicemix")
                                     .artifactId("apache-servicemix")
                                     .type("zip")
                                     .version("4.5.2"))
                    .karafVersion("2.2.11")
                    .name("Apache ServiceMix")
                    .unpackDirectory(new File("target/pax"))
                    .useDeployFolder(false),
                // It is really nice if the container sticks around after the test so you can check the contents
                // of the data directory when things go wrong.
                keepRuntimeFolder(),
                // Don't bother with local console output as it just ends up cluttering the logs
                configureConsole().ignoreLocalConsole(),
                // Force the log level to INFO so we have more details during the test.  It defaults to WARN.
                logLevel(LogLevel.INFO),
                // Provision the example feature exercised by this test
                features(
                        "mvn:valeri.blog.examples.pax-exam-servicemix/pax-exam-servicemix-features/1.0.0-SNAPSHOT/xml/features",
                        "pax-exam-servicemix"),
                // Remember that the test executes in another process.  If you want to debug it, you need
                // to tell Pax Exam to launch that process with debugging enabled.  Launching the test class itself with
                // debugging enabled (for example in Eclipse) will not get you the desired results.
                //debugConfiguration("5000", true),
                };
    }

    @Test
    public void test() throws Exception {

        ...
    }
}

  • Line 29 and Line 32 – These lines use the @Inject annotation to trigger the injection of OSGi services into the test.  You will note that we do not inject the service implementation being tested by the integration test.  The service under test is not injected as the test triggers the creation and destruction of the service.  Pax Exam does not proxy the injected services as Blueprint or Spring DM would.  Therefore, restarting the service results in the test holding a stale reference to the service.  As such, the service is retrieved from the OSGi service registry immediately before each use in the test.  If your test doesn’t register and unregister the service under test, you may use the @Inject annotation to retrieve a reference.
  • Lines 39-49 – These lines configure Pax Exam 3 to download a distribution of Apache ServiceMix from the Maven repository and extract the distribution to the desired location.
  • Lines 50-56 – These lines provide additional configuration that makes working with the tests a little easier.  Disabling the local console reduces Maven log clutter, retaining the extracted distribution allows for easier debugging when tests fail, and increasing the logging level to the INFO level provides a little more detail about what is happening in the container during the test.
This entry was posted in Uncategorized and tagged , , , , . Bookmark the permalink.

4 Responses to Integration Testing OSGi Bundles in Apache Karaf/ServiceMix with Pax Exam 3

  1. Marco Maccio says:

    Thanks for give an good example I needed to add a few dependency due to a ClassNotFoundException. I’ll come back later with my pom, but in general I made it work with Apache Karaf.

  2. likh0 says:

    Hello and thank you for your great tutorial. I still have trouble running it. The main service class ist not found. Any ideas ?? The log:

    Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 21.913 sec <<< FAILURE! – in valeri.blog.examples.pax_exam_servicemix.impl.DefaultServiceIT
    test(valeri.blog.examples.pax_exam_servicemix.impl.DefaultServiceIT) Time elapsed: 21.595 sec <<< ERROR!
    java.lang.ClassNotFoundException: valeri.blog.examples.pax_exam_servicemix.Service not found by PAXEXAM-PROBE-5ff85e2e-ff68-41b6-8ed0-1a450c8c55e0 [201]

  3. likh0 says:

    I replaced your blueprint based bundle with a felix scr bundle. The integration test is working now. Yay!
    Here a snippet from my bundle module:

    your.package.name

    org.apache.felix
    maven-scr-plugin
    1.15.0

    generate-scr-descriptions

    scr

    org.apache.maven.plugins
    maven-compiler-plugin
    ${maven.compiler.plugin}

    1.6
    1.6

    org.apache.felix
    org.apache.felix.scr.annotations
    1.9.6
    provided

  4. Diego says:

    The only fix I needed for ClassNotFound: change valeri.blog.examples.pax_exam_servicemix.impl.DefaultServiceIT.config() to read

    features(
    “mvn:valeri.blog.examples.pax-exam-servicemix/pax-exam-servicemix-features/1.0.1-SNAPSHOT/xml/features”,
    “pax-exam-servicemix”),

    i.e. bump the referred version to 1.0.1-SNASPSHOT.

Leave a reply to Diego Cancel reply