[Note: The Service Binder was the original project to attempt to automate service dependency management for the OSGi platform and was the inspiration for Declarative Services introduced in OSGi R4. The Service Binder is no longer under active development, but this example is kept in the tutorial for historical purposes. New projects should consider using one of the other dependency injection technologies (e.g., Declarative Services, Dependency Manager, or iPOJO).]
The purpose of this example is to re-implement the spell checker service in Example 6, but to do so using the Service Binder; to complete this, we must download the Service Binder bundle. The Service Binder bundle is needed to compile the example and at run time to execute the example.
The spell checker service of Example 6 was complex because it needed to aggregate all available dictionary services and monitor their dynamic availability. In addition, the spell checker service's own availability was dependent upon the availability of dictionary services; in other words, the spell checker service had a dynamic, one-to-many dependency on dictionary services. As it turns out, service dependencies are not managed at all by the OSGi framework and end up being the responsibility of the application developer. The Service Binder tries to eliminate complex and error-prone service dependency handling by automating it. To do this, the Service Binder replaces the BundleActivator code with a generic bundle activator that parses an XML file that describes the instances we want to create and their service dependencies. Instead of writing a lot of complex code, we simply write a declarative XML file. For an example, consider the following code for the new service's bundle activator in a file called Activator.java:
All custom functionality has been removed from the bundle activator, it is only necessary to subclass the generic activator exported by the Service Binder. The generic activator performs its task by parsing an XML meta-data file that describes what instances should be created and what their service dependencies are; for our example, we create a file called metadata.xml that contains the instance and service dependency meta-data:
The above meta-data tells the generic activator to create one instance of tutorial.example8.SpellCheckerImpl, which we will define next. The meta-data also tells the generic activator that the instance has an aggregate service dependency (in this case, one-to-many) on dictionary services and that the services should be tracked dynamically. It also specifies the bind and unbind methods that should be called on the instance when dictionary services appear and disappear. It is important to understand that the generic activator is constantly trying to maintain the instances defined in the meta-data file. At any given point in time, a specific instance may be valid (if all service dependencies are satisfied) or invalid (if any service dependencies are unsatisfied), but at all times the generic activator is trying to get the declared instances into a valid state. The code for our new spell checker service is very similar to the implementation in Example 6, but it is no longer implemented as an inner class of the activator. We define the new spell checker service in a file called SpellCheckerImpl.java as follows:
Notice how much simpler this service implementation is when compared to the same service implemented in Example 6. There are no references to OSGi interfaces in our application code and all tricky and complex code dealing with monitoring of services is handled for us. We must still create a manifest.mf file that contains the meta-data for the bundle; the manifest file is as follows:
We specify which class is used to activate the bundle via the Bundle-Activator attribute and also specify that the bundle imports the spell checker, dictionary, and Service Binder packages. (Note: Make sure your manifest file ends in a trailing carriage return or else the last line will be ignored.)
To compile the source code, we must include the felix.jar file (found in Felix' lib directory), the servicebinder.jar file, the example2.jar file, and the example6.jar file in the class path. We compile the source file using a command like:
This command compiles all source files and outputs the generated classes into a subdirectory of the c:\classes directory; this subdirectory is tutorial\example8, named after the package we specified in the source file. For the above command to work, the c:\classes directory must exist.
Before we can create our bundle JAR file, we must copy the bundle's service dependency meta-data file, called metadata.xml above, into the example class' package. Assuming that we used the above command to compile the bundle, then we should copy the metadata.xml file into c:\classes\tutorial\example8. Now we can create the JAR file for our bundle using the following command:
This command creates a JAR file using the manifest file we created and includes all of the classes and resources in the tutorial\example8 directory inside of the c:\classes directory. Once the JAR file is created, we are ready to install and start the bundle.
To run Felix, we follow the instructions described in usage.html. When we start Felix, it asks for a profile name, we will put all of our bundles in a profile named tutorial. After running Felix, we should stop all tutorial bundles except for the service bundles. Use the lb command to make sure that only the bundles from Example 2 and Example 2b are active; use the start and stop commands as appropriate to start and stop the various tutorial bundles, respectively. (Note: Felix uses some bundles to provide its command shell, so do not stop these bundles.) We must also install the servicebinder.jar bundle that we downloaded at the beginning of this example. Assuming that we saved the bundle in our tutorial directory, we install the bundle using the following command:
We do not need to start the Service Binder bundle, because it is only a library. Now we can install and start our spell checker service bundle. Assuming that we created our bundle in the directory c:\tutorial, we can install and start it in Felix' shell using the following command:
The above command installs and starts the bundle in a single step; it is also possible to install and start the bundle in two steps by using the Felix install and start shell commands. To stop the bundle, use the Felix stop shell command. Use the Felix shell lb command to get the bundle identifier number for the spell checker service bundle to stop and restart it at will using the stop and start commands, respectively. Using the services command, we can see which services are currently available in the OSGi framework, including our dictionary and spell checker services. We can experiment with our spell checker service's dynamic availability by stopping the dictionary service bundles; when both dictionary services are stopped, the services command will reveal that our bundle is no longer offering its spell checker service. Likewise, when the dictionary services comeback, so will our spell checker service. This bundle will work with the spell checker client bundle that we created in Example 7, so feel free to experiment. To exit Felix, use the shutdown command.
[Note: The spell checker client bundle in Example 7 could also be re-implemented using the Service Binder approach outlined in this example. The spell checker client has a one-to-one, dynamic service dependency on the spell checker service. Further, an entire application of instances could be described in a single metadata.xml in a single bundle or across a collection of bundles and the Service Binder will automatically manage the service dependencies among them.]