An OSGi BundleActivator that uses reflection to find JSR 330 Provider implementations and injects to provide and consume OSGi services.

Steinar Bang ee4ac1f418 Fix #1 Avoid NullpointerException when releasing an injected service. 7 years ago
jsr330activator.gogoshell 906fca5f44 [maven-release-plugin] prepare for next development iteration 9 years ago
jsr330activator.implementation ee4ac1f418 Fix #1 Avoid NullpointerException when releasing an injected service. 7 years ago
jsr330activator.mocks 906fca5f44 [maven-release-plugin] prepare for next development iteration 9 years ago
jsr330activator.testbundle1 906fca5f44 [maven-release-plugin] prepare for next development iteration 9 years ago
jsr330activator.testbundle2 906fca5f44 [maven-release-plugin] prepare for next development iteration 9 years ago
jsr330activator.testbundle3 906fca5f44 [maven-release-plugin] prepare for next development iteration 9 years ago
jsr330activator.tests 906fca5f44 [maven-release-plugin] prepare for next development iteration 9 years ago
.gitignore b4e4c53515 Ignore the Apache Felix gogo shell runner directory. 9 years ago
.travis.yml 5f29f7f03b Added coveralls code coverage reporting on travis. 9 years ago
LICENSE 3384520a17 Added an Eclipse v 1.0 license. 9 years ago
README.org ee4ac1f418 Fix #1 Avoid NullpointerException when releasing an injected service. 7 years ago
pom.xml 703a4cfd1b Avoid javadoc of Java 1.8 breaking maven build. 7 years ago

README.org

What's this?

This is an implementation of the OSGi BundleActivator interface, that will scan the bundle for implementations of the JSR 330 Provider<> interface, instantiate the providers, and use them to register the provided interface as an OSGi service.

    Its dependencies are:
  • The Java runtime
  • OSGi core
  • javax.inject

Why does it exist

BundleActivators exposing services and setting up service listeners for dependecies, are the "four wheel drives" of OSGi plugins. A bundle using a BundleActivator can be dropped in any OSGi container using any (or none) component model, e.g. eclipse, Spring dynamic modules, iPOJO, and make its services available there.

This project is an attempt at replacing the handwritten code with something that keeps the "four wheel drive" capability, but at the same time is much simpler to maintain. To do so, it should be simple and small, and to stay small it has to limit what it can support.

    So, in short, this is what you get:
  • Each type parameter to a Provider<> implementation will be exposed as a service
  • Each Provider<> implementation found will be instantiated once
  • Each Provider<> implementation will be scanned for @Inject annotations on fields and methods with a single argument, and service listeners will be set up for the types of the fields/types of the method argument
  • An implementation of the type parameter to each instantiated Provider<> will fetched from Provider<>.get() and registered as a service
  • Immediately after Provider<> instantiation if there are no injections
  • Once all Injections have been satisfied, if there are injections

And that's it. It's formed from what I did with bundle activators.

How to use it

If this is not enough for your requirements, you're probably better off with a real component framework.

The preferred way to use the Jsr330Activator, is to embed the Jsr330Activator together with javax.inject inside your bundle. This makes your bundle work without any dependencies outside of OSGi itself and whatever it needs to do its work. The jsr330activator.testbundle3 is an example of this.

    What's needed is:
  1. Use the maven-bundle-plugin (the simplest way is to create a project using the Felix osgi-bundle archetype)
  2. Add the required bundles as direct dependencies (if they arrive as transitive dependencies, they will not be embedded by the maven-bundle-plugin):
  3. #+BEGIN_SRC nxml javax.inject javax.inject 1 provided no.steria.osgi.jsr330activator jsr330activator.implementation 1.0.1 provided #+END_SRC
  4. Use the Bundle-Activator and Embed-Dependency settings in the maven-bundle-plugin configuration:
  5. #+BEGIN_SRC nxml org.apache.felix maven-bundle-plugin 2.3.7 true no.steria.osgi.jsr330activator.Jsr330Activator no.steria.osgi.jsr330activator.testbundle3 jsr330activator.implementation;inline=true,javax.inject;inline=true #+END_SRC
  6. Implement a provider for your interface. I have found a good pattern in letting the Provider also implement the service interface and return "this" from the get method, because it lets the Jsr330Activator completely manage the service implementation's life cycle:
  7. #+BEGIN_SRC java public class HelloService3aProvider implements Provider, HelloService3a { private HelloService helloService;

    @Inject public void setHelloService(HelloService helloService) { this.helloService = helloService; }

    // This is from the HelloService3a interface public String getMessage() { return helloService.getMessage(); }

    // This is from the Provider interface public HelloService3a get() { return this; } } #+END_SRC

    Both the Jsr330Activator and javax.bundle can be provided as OSGi dependencies instead of embedding them. The jsr330activator.testbundle1 project is an example of this. Or one of the dependencies can be embedded and one can be provided as an OSGi dependency. The jsr330activator.testbundle1 project is an example of this, it embeds the Jsr330Activator and gets javax.inject from the OSGi runtime.

    The config of the maven-bundle-plugin in jsr330activator.testbundle1/pom.xml doesn't contain an Embed-Dependency setting, and the Embed-Dependency setting in jsr330activator.testbundle2/pom.xml only mentions the artifactId of the dependency that is to be embedded.

    License

    The Jsr330Activator is under the Eclipse public license v. 1.0. See the LICENSE file for the full license text.

    Description of the project

    Version history

    Development stuff

      This is a Java project built by maven. The maven projects, are:
    • jsr330activator
    • The parent POM for the other projects
    • jsr330activator.mocks
    • A plain Java jar maven project
    • Mock implementations of OSGi interfaces, used in unit tests that needs logic (registration and unregistration of services)
    • jsr330activator.implementation
    • A maven-bundle-plugin project producing an OSGi bundle
    • Builds an OSGi bundle exporting a package containing the Jsr330Activator
    • Tested with JUnit unit tests, using Mockito mocks and classes from the jsr330activator.mocks project
    • jsr330activator.testbundle1
    • A maven-bundle-plugin project producing an OSGi bundle used in integration tests
    • Exports a package containing the interface HelloService
    • Implements Provider in a non-exported package
    • Uses the Jsr330Activator to find the Provider implementation and uses the implementation to register the service
    • jsr330activator.testbundle2
    • A maven-bundle-plugin project producing an OSGi bundle used in integration tests
    • Exports a package containing the interface HelloService2
    • Implements Provider in a non-exported package
    • Embeds the Jsr330Activator, and uses the embedded Jsr330Activator to find the Provider implementation and uses the implementation to register the service
    • jsr330activator.testbundle3
    • A maven-bundle-plugin project producing an OSGi bundle used in integration tests
    • Exports a package containing the interfaces HelloService3a, HelloService3b and HelloService3c
    • Implements Provider, Provider and Provider in a non-exported package
    • The providers have different injection requirements:
    • Provider depends on HelloService from jsr330activator.testbundle1
    • Provider depends on HelloService2 from jsr330activator.testbundle2
    • Provider depends on both HelloService from jsr330activator.testbundle1 and HelloService2 from jsr330activator.testbundle2
    • The providers in this bundle all also implement the interface they are providing and return "this" from the get() method
    • Embeds the Jsr330Activator, and uses the embedded Jsr330Activator to find the Provider implementation and uses the implementation to register the service
    • jsr330activator.tests
    • A maven project containing Pax Exam integration tests that starts up OSGi containers to test the activator on actual OSGi bundles
    • jsr330activator.gogoshell
    • A project that doesn't participate in the automated build and testing, but is used to start a "gogo shell" with jsr330activator.testbundle1, jsr330activator.testbundle2 and jsr330activator.testbundle3 to be able to examine whether the bundles start up and shut down properly and what services they expose etc.
    • 1.0.2 Bugfix release
    • Fixes jsr330activator issue #1 Unregistration fails with nullpointerexception in pax exam tests with org.osgi.core 6.0
    • 1.0.1 First successful release
    • 1.0.0 Failed deployment to OSSRH (aka. "maven central")
    • Some development-related links:
    • Source code on github
    • Continous Integration on Travis CI
    • Code coverage reports on Coveralls
    • Issue tracker
    • OSSRH issue tracking deployment to OSSRH (formerly "maven central")
    • Using the Apache Felix gogo shell for debugging

      file:https://travis-ci.org/sbang/jsr330activator.png file:https://coveralls.io/repos/sbang/jsr330activator/badge.svg file:https://maven-badges.herokuapp.com/maven-central/no.steria.osgi.jsr330activator/jsr330activator.implementation/badge.svg The jsr330activator.gogoshell module isn't used for anything directly in the build process. This module is used to start an OSGi shell, where the bundles and their behaviour can be examined.

      This is the place to go if the integration tests starts failing: error messages and exception stack traces from the gogo shell start and stop can be illuminating. Examining what the bundles actually provide and expect can also be illuminating.

      All bundles that should be loaded for the testing, should be listed as "provided" dependencies of type "jar", in the jsr330activator.gogoshell/provision/pom.xml file.

      To use the shell for debugging, do the following:

    • Open a command line window and start the shell with maven:
    • #+BEGIN_EXAMPLE cd jsr330activator.gogoshell mvn install pax:provision #+END_EXAMPLE
    • During startup, look specifically for error messages with stack traces, and if they involve some of the bundles listed as dependencies in the provision/pom.xml file, they should be studied carefully: look for missing bundle dependencies, and look for missing services (often indicating that the bundle activator hasn't been successfully started)
    • After startup give the command:
    • bundles
      

      This command lists all bundles. Check that all bundles show up as "Active". If they have a different state, something probably went wrong in the initialization phase

    • Examine what services the bundles expose (the final argument is the bundle name):
    • #+BEGIN_EXAMPLE inspect capability service no.steria.osgi.jsr330activator.testbundle3 inspect cap service no.steria.osgi.jsr330activator.testbundle2 inspect cap service no.steria.osgi.jsr330activator.testbundle1 #+END_EXAMPLE (note that "cap" is a legal appreviation of "capabilitiy". Note also that the shell accepts arrow up and arrow down to browse previous commands and that the shell allows command editing)
    • Shut down the shell
    • exit 0
      

      Future enhanchements

        There should be no error messages during an orderly shutdown. Look specifically for errors and stack traces from bundles listed in the dependencies in the =provision/pom.xml= file The idea is to keep the Jsr330Activator as simple as possible, so I won't be adding all of the enhancements I can think of.

      However there is one thing that I need for the project that prompted the existence of the Jsr330Activator and that is multiple injections of the same service, and start of the provided service without all of the instances present (several different storage backends in my case).

        I see two ways of doing it:
      1. Allow injections into a collection, as outlined here
      2. Create an @Optional annotation in the Jsr330Activator jar itself, and use it together with @Named in addition to @Inject on dependencies, and put @Named on the Provider implementations
      3. Alternative 2. would seem to be the most complete solution (because it lets the injection point determine which services are injected. It can also be used to require a particular implementation of a service and let the rest be optional), but alternative 1. is the simplest one to implement (it doesn't require an extra annotation and reflection code looking for two extra annotations).