An OSGi BundleActivator that uses reflection to find JSR 330 Provider implementations and injects to provide and consume OSGi services.
Steinar Bang be56d6a997 Ignore metadata directory created when making top level directory of project an eclipse workspace | 11 miesięcy temu | |
---|---|---|
.utilities | 9 lat temu | |
jsr330activator.gogoshell | 3 lat temu | |
jsr330activator.implementation | 6 lat temu | |
jsr330activator.mocks | 6 lat temu | |
jsr330activator.testbundles | 6 lat temu | |
jsr330activator.tests | 3 lat temu | |
.gitignore | 11 miesięcy temu | |
.travis.yml | 7 lat temu | |
LICENSE | 9 lat temu | |
README.org | 6 lat temu | |
pom.xml | 4 lat temu |
file:https://travis-ci.org/sbang/jsr330activator.png file:https://coveralls.io/repos/sbang/jsr330activator/badge.svg file:https://sonarcloud.io/api/project_badges/measure?project=no.steria.osgi.jsr330activator%3Ajsr330activator&metric=alert_status#.svg file:https://maven-badges.herokuapp.com/maven-central/no.steria.osgi.jsr330activator/jsr330activator.implementation/badge.svg
file:https://sonarcloud.io/api/project_badges/measure?project=no.steria.osgi.jsr330activator%3Ajsr330activator&metric=ncloc#.svg file:https://sonarcloud.io/api/project_badges/measure?project=no.steria.osgi.jsr330activator%3Ajsr330activator&metric=bugs#.svg file:https://sonarcloud.io/api/project_badges/measure?project=no.steria.osgi.jsr330activator%3Ajsr330activator&metric=vulnerabilities#.svg file:https://sonarcloud.io/api/project_badges/measure?project=no.steria.osgi.jsr330activator%3Ajsr330activator&metric=code_smells#.svg file:https://sonarcloud.io/api/project_badges/measure?project=no.steria.osgi.jsr330activator%3Ajsr330activator&metric=coverage#.svg
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.
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.
And that's it. It's formed from what I did with bundle activators.
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.
Bundle-Activator
and Embed-Dependency
settings in the maven-bundle-plugin configuration:@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.testbundle2 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.
I 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.
There 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: #+BEGIN_SRC java private String getNamedAnnotationValue(StorageService storageService) { Named named = storageService.getClass().getAnnotation(Named.class); if (named != null) { return named.value(); }
return null; } #+END_SRC
If 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): #+BEGIN_SRC java private String getNamedAnnotationValue(StorageService storageService) { Annotation[] annotations = storageService.getClass().getAnnotations(); for (Annotation annotation : annotations) { Class[] interfaces = annotation.getClass().getInterfaces(); for (Class annotationInterface : interfaces) { if (Named.class.getCanonicalName().equals(annotationInterface.getCanonicalName())) { try { Method valueMethod = annotationInterface.getMethod("value", new Class[0]); String namedValue = (String) valueMethod.invoke(annotation, new Object[0]); return namedValue; } catch (Exception e) { } } } }
return null; } #+END_SRC
Since this is an OSGi project it seemed wrong not to make the Jsr330Activator a bundle.
There's the README you're currently reading, written in org format
The Jsr330Activator is under the Eclipse public license v. 1.0. See the LICENSE file for the full license text.
Both "No" and "Yes" are possible answers here.
"No" because this use a different mechanism to DS, and "yes" because they basically do the same thing.
The mechanism used by the JSR330Activator is the BundleActivator which is an old OSGi mechanism and available in all OSGi implementation.
Declarative Services is a built-in capability of OSGi 6.
I don't know...? For my own sake, both places I might use an OSGi bundle (apache karaf and eclipse) now both support DS.
When I wrote the JSR330Activator there were several component/dependency injection implementations for OSGi and no single implementation would work everywhere I would want to use it.
I wanted something that would work everywhere a custom written BundleActivator would work, but with the development friendliness of dependency injection.
But now there is DS everywhere I want to go. And it's better to use a standard mechanism than rolling your own.
On the other hand JSR330Activator has some capabilities DS doesn't have, like e.g. the capability to directly manage the life cycle of the objects implementing the services, rather than have OSGi manage the life cycle.
There may be some use cases where this is useful.
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:
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)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
exit 0
If the Provider<> implementations that are activated by the Jsr330Activator needs to load resources other than constant resources in the bundles themselves, they will need to know the BundleContext. One way this could be allowing @Inject of BundleContext, i.e.: #+BEGIN_SRC java class SomeServiceProvider implements Provider, SomeService { @Inject private BundleContext context;
SomeService get() { return this; } } #+END_SRC 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.
So here 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:
| | implementation | testbundle1 | testbundle2 | testbundle3 | testbundle4 | testbundle5 | testbundle6 | testbundle7 | | 1.0.1 | 14527 | 5886 | 19002 | 23556 | | | | | | collection injections | 15996 | 6101 | 20696 | 25331 | 23103 | 23193 | 22310 | 25247 | | named injections | 16623 | 6102 | 21321 | 25956 | 23729 | 23818 | 22935 | 26675 | | optional injections | 17244 | 6101 | 21942 | 26580 | 24351 | 24441 | 23556 | 28787 |
Collection 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).
Both the @Named and @Optional injections will go into the next release.
The @Optional injections implementation 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. But it is still an annoyance...
This provider is recognized: #+BEGIN_SRC java public class HelloServiceProvider2 implements Provider, HelloService {
public String getMessage() { return "Hello from HelloServiceProvider2"; }
public HelloService get() { return this; }
} #+END_SRC
This provider isn't recognized: #+BEGIN_SRC java public class HelloService2Provider2 implements HelloService2, Provider {
public String getMessage() { return "Hello from HelloService2Provider2"; }
public HelloService2 get() { return this; }
} #+END_SRC
The unit test Jsr330ActivatorTest.testFindProvidersMultipleInterfacesWithProviderNotFirst() reproduces this bug (the failing test is checked in as @Ignored to avoid failing the continous integration).