params.json 25 KB

1
  1. {"name":"Jsr330activator","tagline":"An OSGi BundleActivator that uses reflection to find JSR 330 Provider implementations and injects to provide and consume OSGi services.","body":"<div id=\"table-of-contents\">\r\n<h2>Table of Contents</h2>\r\n<div id=\"text-table-of-contents\">\r\n<ul>\r\n<li><a href=\"#sec-1\">1. What's this?</a></li>\r\n<li><a href=\"#sec-2\">2. Why does it exist</a></li>\r\n<li><a href=\"#sec-3\">3. How to use it</a>\r\n<ul>\r\n<li><a href=\"#sec-3-1\">3.1. To embed or not to embed</a></li>\r\n<li><a href=\"#sec-3-2\">3.2. Why is the Jsr330Activator a bundle if it's meant to be embedded</a></li>\r\n<li><a href=\"#sec-3-3\">3.3. Javadocs</a></li>\r\n</ul>\r\n</li>\r\n<li><a href=\"#sec-4\">4. License</a></li>\r\n<li><a href=\"#sec-5\">5. Description of the project</a></li>\r\n<li><a href=\"#sec-6\">6. Version history</a></li>\r\n<li><a href=\"#sec-7\">7. Development stuff</a>\r\n<ul>\r\n<li><a href=\"#sec-7-1\">7.1. Using the Apache Felix gogo shell for debugging</a></li>\r\n</ul>\r\n</li>\r\n<li><a href=\"#sec-8\">8. Future enhanchements</a></li>\r\n</ul>\r\n</div>\r\n</div>\r\n\r\n# What's this?<a id=\"sec-1\"></a>\r\n\r\nThis is an implementation of the [OSGi BundleActivator interface](https://osgi.org/javadoc/r4v43/core/org/osgi/framework/BundleActivator.html), that will scan the bundle for implementations of the [JSR 330 Provider<> interface](http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/Provider.html), instantiate the providers, and use them to register the provided interface as an OSGi service.\r\n\r\nIts dependencies are:\r\n\r\n- The Java runtime\r\n- OSGi core\r\n- javax.inject\r\n\r\n# Why does it exist<a id=\"sec-2\"></a>\r\n\r\n[BundleActivators](https://osgi.org/javadoc/r4v43/core/org/osgi/framework/BundleActivator.html) exposing services and [setting up service listeners for dependecies](http://www.knopflerfish.org/osgi_service_tutorial.html#white), 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.\r\n\r\nThis 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.\r\n\r\nSo, in short, this is what you get:\r\n\r\n- Each type parameter to a [Provider<>](http://docs.oracle.com/javaee/6/api/javax/inject/Provider.html) implementation will be exposed as a service\r\n- Each Provider<> implementation found will be instantiated once\r\n- Each Provider<> implementation will be scanned for [@Inject](http://docs.oracle.com/javaee/6/api/javax/inject/Inject.html) 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\r\n- An implementation of the type parameter to each instantiated Provider<> will fetched from [Provider<>.get()](http://docs.oracle.com/javaee/6/api/javax/inject/Provider.html#get()) and registered as a service\r\n - Immediately after Provider<> instantiation if there are no injections\r\n - Once all Injections have been satisfied, if there are injections\r\n\r\nAnd that's it. It's formed from what I did with bundle activators.\r\n\r\nIf this is not enough for your requirements, you're probably better off with a real component framework.\r\n\r\n# How to use it<a id=\"sec-3\"></a>\r\n\r\nThe 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. \r\n\r\nWhat's needed is:\r\n\r\n1. Use the maven-bundle-plugin (the simplest way is to create a project using the Felix osgi-bundle archetype)\r\n2. Add the required bundles as direct dependencies (if they arrive as transitive dependencies, they will not be embedded by the maven-bundle-plugin):\r\n \r\n <project>\r\n <dependencies>\r\n <dependency>\r\n <groupId>javax.inject</groupId>\r\n <artifactId>javax.inject</artifactId>\r\n <version>1</version>\r\n </dependency>\r\n <dependency>\r\n <groupId>no.steria.osgi.jsr330activator</groupId>\r\n <artifactId>jsr330activator.implementation</artifactId>\r\n <version>1.0.1</version>\r\n <scope>test</scope>\r\n </dependency>\r\n </dependencies>\r\n </project>\r\n \r\n Note the \"test\" dependency of the Jsr330Activator itself. Using \"compile\" or \"provided\" here would create a maven dependency to the Jsr330Activator, that would make a maven-provisioned OSGi runtime (e.g. gogo shell) pull in the Jsr330Activator bundle into the OSGi runtime. This does no harm, but it isn't necessary when the Jsr330Activator has been embedded, and therefore shouldn't be there. \r\n \r\n The javax.inject jar isn't a bundle and therefore is skipped by gogo shell and friends\r\n3. Use the `Bundle-Activator` and `Embed-Dependency` settings in the maven-bundle-plugin configuration:\r\n \r\n <project>\r\n <build>\r\n <plugins>\r\n <plugin>\r\n <groupId>org.apache.felix</groupId>\r\n <artifactId>maven-bundle-plugin</artifactId>\r\n <version>2.3.7</version>\r\n <extensions>true</extensions>\r\n <configuration>\r\n <instructions>\r\n <Bundle-Activator>no.steria.osgi.jsr330activator.Jsr330Activator</Bundle-Activator>\r\n <Export-Package>no.steria.osgi.jsr330activator.testbundle3</Export-Package>\r\n <Embed-Dependency>jsr330activator.implementation;inline=true,javax.inject;inline=true</Embed-Dependency>\r\n </instructions>\r\n </configuration>\r\n </plugin>\r\n </plugins>\r\n </build>\r\n </project>\r\n4. 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:\r\n \r\n public class HelloService3aProvider implements Provider<HelloService3a>, HelloService3a {\r\n private HelloService helloService;\r\n \r\n @Inject\r\n public void setHelloService(HelloService helloService) {\r\n this.helloService = helloService;\r\n }\r\n \r\n // This is from the HelloService3a interface\r\n public String getMessage() {\r\n return helloService.getMessage();\r\n }\r\n \r\n // This is from the Provider<HelloService3a> interface\r\n public HelloService3a get() {\r\n return this;\r\n }\r\n }\r\n\r\nBoth 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.\r\n\r\nThe 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.\r\n\r\n## To embed or not to embed<a id=\"sec-3-1\"></a>\r\n\r\nI haven't been able to think of a use case for not embedding the Jsr330Activator itself. The Jsr330Activator's reason for existence, is to create self-contained service-providinng and service-listening bundles, that don't require anything from the run-time other than basic OSGi support.\r\n\r\nThere is a use-case for not embedding javax.inject, and that is if one wish to access the @Named annotation of an injected service. If the javax.inject package is provided through OSGi, then one can access the name this way:\r\n\r\n private String getNamedAnnotationValue(StorageService storageService) {\r\n Named named = storageService.getClass().getAnnotation(Named.class);\r\n if (named != null) {\r\n return named.value();\r\n }\r\n \r\n return null;\r\n }\r\n\r\nIf javax.inject has been embedded in either the service-providing bundles or the service receiving bundles, or both, the @Named annotation has to be accessed using reflection on a dynamic proxy (that itself uses reflection):\r\n\r\n private String getNamedAnnotationValue(StorageService storageService) {\r\n Annotation[] annotations = storageService.getClass().getAnnotations();\r\n for (Annotation annotation : annotations) {\r\n Class<?>[] interfaces = annotation.getClass().getInterfaces();\r\n for (Class<?> annotationInterface : interfaces) {\r\n if (Named.class.getCanonicalName().equals(annotationInterface.getCanonicalName())) {\r\n try {\r\n Method valueMethod = annotationInterface.getMethod(\"value\", new Class<?>[0]);\r\n String namedValue = (String) valueMethod.invoke(annotation, new Object[0]);\r\n return namedValue;\r\n } catch (Exception e) { }\r\n }\r\n }\r\n }\r\n \r\n return null;\r\n }\r\n\r\n## Why is the Jsr330Activator a bundle if it's meant to be embedded<a id=\"sec-3-2\"></a>\r\n\r\nSince this is an OSGi project it seemed wrong *not* to make the Jsr330Activator a bundle.\r\n\r\n## Javadocs<a id=\"sec-3-3\"></a>\r\n\r\n- [Released version javadocs](http://www.javadoc.io/doc/no.steria.osgi.jsr330activator/jsr330activator.implementation)\r\n\r\n# License<a id=\"sec-4\"></a>\r\n\r\nThe Jsr330Activator is under the Eclipse public license v. 1.0. See the LICENSE file for the full license text.\r\n\r\n# Description of the project<a id=\"sec-5\"></a>\r\n\r\nThis is a Java project built by maven. The maven projects, are:\r\n\r\n- jsr330activator\r\n - The parent POM for the other projects\r\n- jsr330activator.mocks\r\n - A plain Java jar maven project\r\n - Mock implementations of OSGi interfaces, used in unit tests that needs logic (registration and unregistration of services)\r\n- jsr330activator.implementation\r\n - A maven-bundle-plugin project producing an OSGi bundle\r\n - Builds an OSGi bundle exporting a package containing the Jsr330Activator\r\n - Tested with JUnit unit tests, using Mockito mocks and classes from the jsr330activator.mocks project\r\n- jsr330activator.testbundle1\r\n - A maven-bundle-plugin project producing an OSGi bundle used in integration tests\r\n - Exports a package containing the interface HelloService\r\n - Implements Provider<HelloService> in a non-exported package\r\n - Uses the Jsr330Activator to find the Provider<HelloService> implementation and uses the implementation to register the service\r\n- jsr330activator.testbundle2\r\n - A maven-bundle-plugin project producing an OSGi bundle used in integration tests\r\n - Exports a package containing the interface HelloService2\r\n - Implements Provider<HelloService2> in a non-exported package\r\n - Embeds the Jsr330Activator, and uses the embedded Jsr330Activator to find the Provider<HelloService> implementation and uses the implementation to register the service\r\n- jsr330activator.testbundle3\r\n - A maven-bundle-plugin project producing an OSGi bundle used in integration tests\r\n - Exports a package containing the interfaces HelloService3a, HelloService3b and HelloService3c\r\n - Implements Provider<HelloService3a>, Provider<HelloService3b> and Provider<HelloService3c> in a non-exported package\r\n - The providers have different injection requirements:\r\n - Provider<HelloService3a> depends on HelloService from jsr330activator.testbundle1\r\n - Provider<HelloService3b> depends on HelloService2 from jsr330activator.testbundle2\r\n - Provider<HelloService3c> depends on both HelloService from jsr330activator.testbundle1 and HelloService2 from jsr330activator.testbundle2\r\n - The providers in this bundle all also implement the interface they are providing and return \"this\" from the get() method\r\n - Embeds the Jsr330Activator, and uses the embedded Jsr330Activator to find the Provider<HelloService> implementation and uses the implementation to register the service\r\n- jsr330activator.testbundle8\r\n - A maven-bundle-plugin project producing an OSGi bundle used in integration tests\r\n - Exports a package containing the interface StorageService\r\n - The bundle has no activator\r\n- jsr330activator.testbundle4\r\n - A maven-bundle-plugin project producing an OSGi bundle used in integration tests\r\n - The bundle exports no packages\r\n - The bundle has a Provider<StorageService> that implements a mock file storage\r\n- jsr330activator.testbundle5\r\n - A maven-bundle-plugin project producing an OSGi bundle used in integration tests\r\n - The bundle exports no packages\r\n - The bundle has a Provider<StorageService> that implements a mock database storage\r\n- jsr330activator.testbundle6\r\n - A maven-bundle-plugin project producing an OSGi bundle used in integration tests\r\n - The bundle exports no packages\r\n - The bundle has a Provider<StorageService> that implements a dummy storage service (save does nothing, load always returns null)\r\n- jsr330activator.testbundle7\r\n - A maven-bundle-plugin project producing an OSGi bundle used in integration tests\r\n - The bundle exports a package containing the services CollectionInjectionCatcher and NamedServiceInjectionCatcher that are injected into an integration test\r\n - The bundle has a Provider for CollectionInjectionCatcher that has a Collection<StorageService> field annotated by @Inject and will be activated by at least one instance of StorageService\r\n - The bundle has a provider for NamedServiceInjectionCatcher that at the point of writing has no injections\r\n- jsr330activator.tests\r\n - A maven project containing Pax Exam integration tests that starts up OSGi containers to test the activator on actual OSGi bundles\r\n- jsr330activator.gogoshell\r\n - 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.\r\n\r\n# Version history<a id=\"sec-6\"></a>\r\n\r\n- 1.0.1 First successful release\r\n- 1.0.0 Failed deployment to OSSRH (aka. \"maven central\")\r\n\r\n# Development stuff<a id=\"sec-7\"></a>\r\n\r\nSome development-related links:\r\n\r\n- [Source code on github](https://github.com/sbang/jsr330activator)\r\n- [Continous Integration on Travis CI](https://travis-ci.org/sbang/jsr330activator/)\r\n- [Code coverage reports on Coveralls](https://coveralls.io/r/sbang/jsr330activator)\r\n- [javadoc from latest travis build](http://sbang.github.io/jsr330activator/javadoc/)\r\n- [Issue tracker](https://github.com/sbang/jsr330activator/issues)\r\n- [OSSRH issue tracking deployment to OSSRH (formerly \"maven central\")](https://issues.sonatype.org/browse/OSSRH-15092)\r\n\r\n[![img](https://travis-ci.org/sbang/jsr330activator.png)](https://travis-ci.org/sbang/jsr330activator) [![img](https://coveralls.io/repos/sbang/jsr330activator/badge.svg)](https://coveralls.io/r/sbang/jsr330activator) [![img](https://maven-badges.herokuapp.com/maven-central/no.steria.osgi.jsr330activator/jsr330activator.implementation/badge.svg)](https://maven-badges.herokuapp.com/maven-central/no.steria.osgi.jsr330activator/jsr330activator.implementation)\r\n\r\n## Using the Apache Felix gogo shell for debugging<a id=\"sec-7-1\"></a>\r\n\r\nThe 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.\r\n\r\nThis 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.\r\n\r\nAll 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.\r\n\r\nTo use the shell for debugging, do the following:\r\n\r\n1. Open a command line window and start the shell with maven:\r\n \r\n cd jsr330activator.gogoshell\r\n mvn install pax:provision\r\n \r\n This will also start the \"Felix Webconsole\" on <http://localhost:8080/system/console> (username/password: admin/admin) where the bundles can be thoroughly explored\r\n2. 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)\r\n3. After startup give the command:\r\n \r\n bundles\r\n \r\n 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\r\n4. Examine what services the bundles expose (the final argument is the bundle name):\r\n \r\n inspect capability service no.steria.osgi.jsr330activator.testbundle3\r\n inspect cap service no.steria.osgi.jsr330activator.testbundle2\r\n inspect cap service no.steria.osgi.jsr330activator.testbundle1\r\n \r\n (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)\r\n5. Shut down the shell\r\n \r\n exit 0\r\n \r\n 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\r\n\r\n# Future enhanchements<a id=\"sec-8\"></a>\r\n\r\nThe idea is to keep the Jsr330Activator as simple as possible, so I won't be adding all of the enhancements I can think of.\r\n\r\nHowever there is one thing that I need for [the project that prompted the existence of the Jsr330Activator](https://github.com/steinarb/modelstore) 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).\r\n\r\nI see two ways of doing it:\r\n\r\n1. Allow injections into a collection, as outlined [here](http://stackoverflow.com/a/25327839)\r\n2. 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<T> implementations\r\n\r\nAlternative 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).\r\n\r\n*Note*: Alternative 1 i.e. injection into a collection is now on master.\r\n*Note2*: I have discovered a need for @Optional in [my project using the Jsr330Activator](https://github.com/steinarb/modelstore): I need logging and the logservice @Inject should probably be optional, so I will probably end up pulling all of the branches below in on master and making a release. I will make tags on the branch points so that it will be possible to make slimmer versions (won't be made by me, though&#x2026;).\r\n\r\nThe behaviour is like this:\r\n\r\n- Add a List<Service> field to the provider class\r\n- All instances of \"Service\" that arrives are added to the collection (see CollectionInjectionCatcherProvider.java in jsr330activator.testbundle7 for an example)\r\n- All instances of \"Service\" that are retracted are removed from the collection\r\n- For the purposes of service activation, the field is seen as injected as long as it has at least one item\r\n - This means that an optional injection could be faked by inserting a dummy service into a collection injection\r\n\r\nThere is now an implementation of @Named injections in the branch add-named-inject-support.\r\nThere is also an implementation of @Optonal on top of @Named in the branch add-optional-injection-support.\r\n\r\nI haven't decided whether to pull these into master or not.\r\n\r\nThis is because the primary use case of the Jsr330Activator is to embed it, and it's therefore important to keep it as small as possible. And one of the ways of keeping it small is to not pull in features that aren't strictly necessary.\r\n\r\nHere is a little table to show what the extra costs for the new features are, wrt. to increasing the size of the jar (testbundle1 which embeds nothing is shown for comparison). These are sizes in bytes of the jar files, testbundle3 is the one to track through all of the changes, the implementation also gives a good indication of the code growth:\r\n\r\n<table border=\"2\" cellspacing=\"0\" cellpadding=\"6\" rules=\"groups\" frame=\"hsides\">\r\n\r\n\r\n<colgroup>\r\n<col class=\"left\" />\r\n\r\n<col class=\"right\" />\r\n\r\n<col class=\"right\" />\r\n\r\n<col class=\"right\" />\r\n\r\n<col class=\"right\" />\r\n\r\n<col class=\"right\" />\r\n\r\n<col class=\"right\" />\r\n\r\n<col class=\"right\" />\r\n\r\n<col class=\"right\" />\r\n</colgroup>\r\n<tbody>\r\n<tr>\r\n<td class=\"left\">&#xa0;</td>\r\n<td class=\"right\">implementation</td>\r\n<td class=\"right\">testbundle1</td>\r\n<td class=\"right\">testbundle2</td>\r\n<td class=\"right\">testbundle3</td>\r\n<td class=\"right\">testbundle4</td>\r\n<td class=\"right\">testbundle5</td>\r\n<td class=\"right\">testbundle6</td>\r\n<td class=\"right\">testbundle7</td>\r\n</tr>\r\n\r\n\r\n<tr>\r\n<td class=\"left\">1.0.1</td>\r\n<td class=\"right\">14527</td>\r\n<td class=\"right\">5886</td>\r\n<td class=\"right\">19002</td>\r\n<td class=\"right\">23556</td>\r\n<td class=\"right\">&#xa0;</td>\r\n<td class=\"right\">&#xa0;</td>\r\n<td class=\"right\">&#xa0;</td>\r\n<td class=\"right\">&#xa0;</td>\r\n</tr>\r\n\r\n\r\n<tr>\r\n<td class=\"left\">collection injections</td>\r\n<td class=\"right\">15996</td>\r\n<td class=\"right\">6101</td>\r\n<td class=\"right\">20696</td>\r\n<td class=\"right\">25331</td>\r\n<td class=\"right\">23103</td>\r\n<td class=\"right\">23193</td>\r\n<td class=\"right\">22310</td>\r\n<td class=\"right\">25247</td>\r\n</tr>\r\n\r\n\r\n<tr>\r\n<td class=\"left\">named injections</td>\r\n<td class=\"right\">16623</td>\r\n<td class=\"right\">6102</td>\r\n<td class=\"right\">21321</td>\r\n<td class=\"right\">25956</td>\r\n<td class=\"right\">23729</td>\r\n<td class=\"right\">23818</td>\r\n<td class=\"right\">22935</td>\r\n<td class=\"right\">26675</td>\r\n</tr>\r\n\r\n\r\n<tr>\r\n<td class=\"left\">optional injections</td>\r\n<td class=\"right\">17244</td>\r\n<td class=\"right\">6101</td>\r\n<td class=\"right\">21942</td>\r\n<td class=\"right\">26580</td>\r\n<td class=\"right\">24351</td>\r\n<td class=\"right\">24441</td>\r\n<td class=\"right\">23556</td>\r\n<td class=\"right\">28787</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n\r\nCollection injections add around 1800 bytes to the size of the jar (1775 bytes on testbundle3, and 1249 bytes on the implementation). @Named injections add around 625 bytes on top of this (implementation 627 bytes, testbundle3 625 bytes), and @Optional adds around 625 bytes on top of this again (implementation 621 bytes, testbundle3 624 bytes). Total for @Named and @Optional is an additional 1250 bytes (implementation 1248 bytes, testbundle3 1249 bytes).\r\n\r\nCollection injections will go into the next release, because I need them myself.\r\n\r\nWhether @Named and @Optional will go in is more in question, especially since @Optional introduced a felix runtime dependency on the Jsr330Activator. This is more than an annoyance than an actual problem, beause if the launcher is felix it doesn't really matter if the Jsr330Activator is pulled in or not, except for a small extra runtime cost.","google":"","note":"Don't delete this file! It's used internally to help with page regeneration."}