10.2. Jitsi and the OSGi Framework
People have written entire books about OSGi, so we’re not going to go over everything the framework stands for. Instead we will only explain what it gives us and the way we use it in Jitsi.
Above everything else, OSGi is about modules. Features in OSGi applications are separated into bundles. An OSGi bundle is little more than a regular JAR file like the ones used to distribute Java libraries and applications. Jitsi is a collection of such bundles. There is one responsible for connecting to Windows Live Messenger, another one that does XMPP, yet another one that handles the GUI, and so on. All these bundles run together in an environment provided, in our case, by Apache Felix, an open source OSGi implementation.
All these modules need to work together. The GUI bundle needs to send messages via the protocol bundles, which in turn need to store them via the bundles handling message history. This is what OSGi services are for: they represent the part of a bundle that is visible to everyone else. An OSGi service is most often a group of Java interfaces that allow use of a specific functionality like logging, sending messages over the network, or retrieving the list of recent calls. The classes that actually implement the functionality are known as a service implementation. Most of them carry the name of the service interface they implement, with an “Impl” suffix at the end (e.g.,
ConfigurationServiceImpl). The OSGi framework allows developers to hide service implementations and make sure that they are never visible outside the bundle they are in. This way, other bundles can only use them through the service interfaces.
Most bundles also have activators. Activators are simple interfaces that define a
start and a
stop method. Every time Felix loads or removes a bundle in Jitsi, it calls these methods so that the bundle can prepare to run or shut down. When calling these methods Felix passes them a parameter called BundleContext. The BundleContext gives bundles a way to connect to the OSGi environment. This way they can discover whatever OSGi service they need to use, or register one themselves (Figure 10.1).
Figure 10.1: OSGi Bundle Activation
So let’s see how this actually works. Imagine a service that persistently stores and retrieves properties. In Jitsi this is what we call the ConfigurationService and it looks like this:
A very simple implementation of the ConfigurationService looks like this:
Notice how the service is defined in the
net.java.sip.communicator.service package, while the implementation is in
net.java.sip.communicator.impl. All services and implementations in Jitsi are separated under these two packages. OSGi allows bundles to only make some packages visible outside their own JAR, so the separation makes it easier for bundles to only export their service packages and keep their implementations hidden.
The last thing we need to do so that people can start using our implementation is to register it in the
BundleContext and indicate that it provides an implementation of the
ConfigurationService. Here’s how this happens:
ConfigurationServiceImpl class is registered in the
BundleContext, other bundles can start using it. Here’s an example showing how some random bundle can use our configuration service:
Once again, notice the package. In
net.java.sip.communicator.plugin we keep bundles that use services defined by others but that neither export nor implement any themselves. Configuration forms are a good example of such plugins: They are additions to the Jitsi user interface that allow users to configure certain aspects of the application. When users change preferences, configuration forms interact with the
ConfigurationService or directly with the bundles responsible for a feature. However, none of the other bundles ever need to interact with them in any way (Figure 10.2).
Figure 10.2: Service Structure