Frequently Asked Questions
- Don’t I tie my bundles to the OSGi API if I use Domino? Isn’t that what component models like Blueprint want to prevent?
- Why the name "Domino"?
- I’ve seen that
DominoActivator
is made of several traits each of which implements a part of the DSL. Can’t I just mix in one of these traits instead of extendingDominoActivator
if I don’t need the complete DSL but only a little part? - Is it possible to use unresolved type parameters when querying or providing services?
- I’m seeing this eror message: The Bundle Activator does not implement BundleActivator.
Don’t I tie my bundles to the OSGi API if I use Domino? Isn’t that what component models like Blueprint want to prevent?
You tie your bundle to the OSGi API, that’s right. In particular the bundle activator. And as stated here, this is something component models try to avoid.
But tying your code to the OSGi API is not necessarily a bad thing. Whether it’s good or bad depends on the kind of code. You see, if you tie your core logic to OSGi, you significantly lower its potential for reuse - which is in most cases indeed bad. But if you just couple your activation logic to OSGi, that’s a completely different thing. It makes sense because you can leverage all OSGi features and you don’t have to learn another abstraction layer.
That’s why I recommend following approach: If your core logic is non-trivial and shall be widely reusable, implement it without any dependencies to the OSGi API. And then, write a small bundle activator which tailors your core logic to OSGi by instantiating and activating it as you desire (for example using Domino).
Sometimes it even makes sense to put the bundle activator into a separate bundle. Then you can have multiple bundle activators which activate your logic in different ways.
The deployment of an OSGi bundle often triggers a series of cascading events: A service gets available, another bundle has been waiting for it and makes an own service available which in turn activates another service and so on … This strongly reminded me of the Domino effect. The analogy is not perfect though because the Domino Effect cannot easily be made undone whereas in OSGi, the chain reaction will automatically be "reverted" as soon as the bundle is plugged out.
I’ve seen that DominoActivator
is made of several traits each of which implements a part of the DSL. Can’t I just mix in one of these traits instead of extending DominoActivator
if I don’t need the complete DSL but only a little part?
Well, indeed you can, but I strongly discourage it because of the way traits are implemented in Scala!
If you do it, chances are quite high your bundle doesn’t work with future versions of Domino — even if it’s only a minor version update.
Why?
Let’s assume, a Domino update adds a new private method to the ServiceWatching
trait.
This is commonly considered a minor change since it doesn’t break backward compatibility …
assumed you extend from the class DominoActivator
.
But if you have mixed in the ServiceWatching
trait, your bundle won’t work anymore because that new private method cannot be found in your activator.
If your bundle gets incompatible with a minor Domino update, you have two options: Either restrict the version range for the Domino package import or recompile and rerelease your bundle. The first option has the downside that you cannot benefit from Domino bugfix releases. The second option involves much work.
In both cases your bundle gets significantly less upward compatible. And that attracts negative attention especially in OSGi applications where people are used to hot swapping bundle versions.
If you really want to use only a part of the DSL, use the approach outlined in the User Guide, section "Extending Domino".
Extend from EmptyBundleActivator
(an abstract class), create a SimpleServiceWatching
object in your activator as a val
and import its members.
That’s it.
It’s almost as convenient as mixing in the trait but doesn’t lower the compatibility of your bundle.
It is possible but there are serious limitations. Have a look at the following contrived example in bundle A:
def provideAsList[S: ClassTag: TypeTag](service: S) {
List(service).providesService[List[S]]
}
First, it is important to define the context bounds ClassTag
and TypeTag
for S
.
The ClassTag
bound ensures that a client bundle B using your method actually uses the class referenced by S
(so BND generates the correct Package-Import
directive).
The TypeTag
bound makes sure that the complete type information for S
is preserved.
At runtime, this works only if the actual class substituted for the unresolved type parameter S
is visible to bundle A at runtime.
Otherwise you get a scala.reflect.internal.MissingRequirementError
telling you that it cannot find the class.
However, in most practical use cases, this class is not visible to bundle A
but instead to bundle B because bundle B depends on bundle A and not the other way around.
A quick workaround would be to add the DynamicImport-Package:
directive to bundle *A, but that’s really bad practice.
In future, Scala reflection hopefully doesn’t reevaluate the type information for S
but uses the one which is implicitly given.
Then it wouldn’t be necessary anymore to access the actual class substituted for S
.
This might be related to some issue in the bnd tool (bndtools/bnd#1005). Using a newer version (>= 3.2) should fix it.