getting started with osgi, i wonder what's the conceptual diffence between bundles and components. And when to use which of them. Any pointers are welcome.
EDIT:
Components and Bundles provide different interfaces and therefore they are probably not interchangeable
ComponentContext
BundleContext
OSGi is a set of specifications. Its core specification defines a component and service model for Java. A software component in OSGi is called bundle or plug-in, both terms are interchangeable. Services are Java implementations which OSGi allows to start and access.
In OSGi, a single component is called a bundle. Logically, a bundle is a piece of functionality that has an independent lifecycle – which means it can be started, stopped and removed independently. Technically, a bundle is just a jar file with a MANIFEST. MF file containing some OSGi-specific headers.
A component has a lifecycle (de/activate and modify), service dependency management (un/bind). A service is an extension of a component; via the service registry, it offers it's services to other bundles etc by publishing implemented interfaces and properties.
There is basically no difference. A JAR is a bundle and a bundle is a JAR, the formats are identical. However, a useful bundle requires OSGi metadata in its manifest so that an OSGi framework can manage the visibility of classes between bundles.
A component is:
In short:
A bundle can have only one activator (needing a BundleContext
), and can have as many active components as you want.
That means you may end up trying to fit in one activator several loosely-related concerns into a single class.
That is why it may be easier to manage those components by Declarative Services, through the SCR (the "Service Component Runtime" which is an "extender bundle" implementing the new and improved OSGi R4.2 DS - Declarative Service - specification).
This is especially true since OSGi 4.2 because it is now much easier to write DS components as POJOs: the activate
and deactivate
methods are no longer required to take a ComponentContext
parameter. See also Lazy Declarative Service.
Note:
It can help to replace those terms in the context of OSGi and look at "how we got there" (excellent blog post by Neil Bartlett)
Here are some relevant extracts, where the "modules" end up being the OSGi Bundles (managing Components which declare Services):
Our first requirement is to cleanly separate modules so that classes from one module do not have the uncontrolled ability to see and obscure classes from other modules.
In traditional Java the so-called “classpath” is an enormous list of classes, and if multiple classes happen to have the same fully-qualified name then the first will always be found and the second and all others will be ignored.The way to prevent uncontrolled visibility and obscuring of classes is to create a class loader for each module. A class loader is able to load only the classes it knows about directly, which in our system would be the contents of a single module.
If we stop here then modules will be completely isolated and unable to communicate with each other. To make the system practical we need to add back in the ability to see classes in other modules, but we do it in a careful and constrained way.
At this point we input another requirement: modules would like the ability to hide some of their implementation details.We would like to have a “module” access level, but the problem today is that the javac compiler has no idea where the module boundaries lie.
The solution we choose in our module system is to allow modules to “export” only portions of their contents. If some part of a module is non-exported then it simply cannot be seen by other modules.
When importing, we should import what we actually need to use, irrespective of where it comes from and ignoring all the things that happen to be packaged alongside it.
OSGi chooses packages.
The contents of a Java package are intended to be somewhat coherent, but it is not too onerous to list packages as imports and exports, and it doesn’t break anything to put some packages in one module and other packages in another module.
Code that is supposed to be internal to our module can be placed in one or more non-exported packages.
Now that we have a model for how modules isolate themselves and then reconnect, we can imagine building a framework that constructs concrete runtime instances of these modules. It would be responsible for installing modules and constructing class loaders that know about the contents of their respective modules.
Then it would look at the imports of newly installed modules and trying to find matching exports.
An unexpected benefit from this is we can dynamically install, update and uninstall modules. Installing a new module has no effect on those modules that are already resolved, though it may enable some previously unresolvable modules to be resolved. When uninstalling or updating, the framework knows exactly which modules are affected and it will change their state if necessary.
Our module system is looking good, but we cannot yet handle the changes that inevitably occur in modules over time. We need to support versions.
How do we do this? First, an exporter can simply state some useful information about the packages it is exporting: “this is version 1.0.0 of the API”. An importer can now import only the version that is compatible with what it expects and has been compiled/tested against, and refuse to accept
Our module system will need a way to package the contents of a module along with metadata describing the imports and exports into a deployable unit.
So the only question is, where should we put the metadata, i.e. the lists of imports and exports, versions and so on?
As it happens OSGi was designed before 2000, so it did choose either of these solutions. Instead it looked back at the JAR File Specification, where the answer is spelled out:
META-INF/MANIFEST.MF
is the standard location for arbitrary application-specific metadata.
The final piece of modularity puzzle is late binding of implementations to interfaces. I would argue that it is a crucial feature of modularity, even though some module systems ignore it entirely, or at least consider it out of scope.
We should look for a decentralised approach.
Rather than being told what to do by the God Class, let us suppose that each module can simply create objects and publish them somewhere that the other modules can find them. We call these published objects “services”, and the place where they are published the “service registry”.
The most important information about a service is the interface (or interfaces) that it implements, so we can use that as the primary registration key.
Now a module needing to find instances of a particular interface can simply query the registry and find out what services are available at that time. The registry itself is still a central component existing outside of any module, but it is not “God”… rather, it is like a shared whiteboard.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With