Neil Bartlett: OSGi: It’s Time to Ban Bundle Activators
Well… maybe not ban them exactly. Yes, the title of this post is deliberately provocative and overstates my argument, for which I apologise. Nevertheless it’s true that, in many cases, activators would be better written as Declarative Services (DS) components.
Compare the following two code blocks. First an activator:
-
import org.osgi.framework.BundleActivator;
-
import org.osgi.framework.BundleContext;
-
-
public class MyActivator implements BundleActivator {
-
public void start(BundleContext context) {
-
}
-
public void stop(BundleContext context) {
-
}
-
}
Next a DS component:
-
public class MyActiveComponent {
-
public void activate(Map<String,Object> config) {
-
}
-
public void deactivate() {
-
}
-
}
Notice the difference? The DS one doesn’t depend on OSGi APIs at all, so it’s easier to test and it’s reusable in non-OSGi containers. It can receive configuration data easily via a Map instance, although we can omit that parameter if we don’t need it. Also whereas a bundle can have only one activator, it can have as many active components as we like, which saves having to multiplex several loosely-related concerns into a single class.
Now, you may have heard that DS requires you to write an XML file that is pretty ugly. Never mind, let’s just get our build tool to generate it from our source code. Bnd can do that, here’s an example… let’s say we also want to change the names of the lifecycle methods because the default names (activate and deactivate) are too boring:
-
public class MyActiveComponent {
-
@Activate
-
public void getBusyLiving(Map<String,Object> config) {
-
}
-
@Deactivate
-
public void getBusyDying() {
-
}
-
}
Something else we often need to do from an activator is invoke a service. This is quite hard because we don’t know if the service will be available when our bundle starts. The solution to this when writing activators is to implement a ServiceListener or ServiceTracker so we can be called back later when the service we want becomes available. Unfortunately it all ends up in very complex programmatic listener code. With DS we can do this declaratively by saying we need at least one instance of the service, so please don’t activate us until such a service instance is available. Here’s the code for that:
-
public class MyActiveComponent {
-
EventAdmin events;
-
@Activate
-
public void getBusyLiving(Map<String,Object> config) {
-
}
-
@Deactivate
-
public void getBusyDying() {
-
}
-
public void setEventAdmin(EventAdmin ea) {
-
this.events = ea;
-
}
-
}
Couldn’t get much easier, could it? We can even add an optional attribute to that @Reference annotation to say that we would like to use the service if it is available, but we’ll go ahead and continue working without it if not.
Finally a common task that activators do is publish a service. Although that’s not particularly difficult to do programmatically, in DS it’s even simpler:
-
public class MyExecutorService implements Executor {
-
}
-
}
Now this is where DS gets really cool. When the bundle containing the above component is activated, DS will register the service for us in the service registry but will not actually instantiate the class. Only when a consumer bundle comes along and really uses the service does DS create the implementation object. Thus we can create large numbers of components as services essentially for free, and only the ones that are actually used will be loaded. In contrast, when we use activators we generally must eagerly create an implementation backing a service before we know whether any consumers even want to use the service… so we may end up creating many service objects that are never used, and this wastes memory and CPU time.
So most of the time we can get by without bundle activators. When do we still need to use them? Generally if we want to do something like writing an extender bundle — which manipulates the state and behaviour of other bundles — we need access to the OSGi APIs as provided by a bundle activator. We cannot write extenders as POJOs, but we can use them to enable POJO-based programming in other bundles. In other words, extenders are “glue” code, not business logic. For the majority of your application, stick to DS components and POJOs.


delicious
digg
dzone
delicious
digg
dzone