Using a ManagedServiceFactory 2010/12/19
Posted by Angelo van der Sijpt in Uncategorized.Tags: bndtools, java, managedservicefactory, osgi
add a comment
One of the most elusive, yet very useful parts of the OSGi specification is the ManagedServiceFactory (part of the ConfigurationAdmin specification, chapter 104 in the 4.2 OSGi compendium specification). A long time ago, I gave an example on the Apache Felix users mailing list, but it’s hard to find. So, time for a nicer looking example.
In the four steps below, we will take a simple program, and make a ManagedServiceFactory out of it. I find it useful to follow the progression, but if you’re only interested in the ManagedServiceFactory part, skip to step 3. I assume you’re familiar with basics of OSGi, and have seen a ManagedService up close before.
Running the examples
I have put up an example project, which shows all the code covered below. When you import the project, you will get a warning about a missing ‘test’ folder. Just create a new ‘test’ source folder, this is due to Zip not being able to package empty directories.
The examples use Neil Bartlett’s excellent Bndtools, and next to the environment that ships with, we use
- the Apache Felix Dependency Manager. At time of writing, this is gearing up towards a 3.0 release, but we use a SNAPSHOT version here; I’ve put one up for download. Once downloaded, you can drag it into the Bndtools repository.
- Apache Felix FileInstall, which you can download here.
The case
Step one, code in package net.luminis.websitewatcher.withmain.
Suppose we want to have some service that checks the availability of our favorite website. It’s pretty easy to devise a method that does this. Given that we have a method for this (full code in the example), we can call this repeatedly from a thread.
new Thread("Watcher for " + site.toExternalForm()) {
public void run() {
while (true) {
if (isReachable(site)) {
System.out.println(site.toExternalForm() + " is reachable.");
}
else {
System.out.println(site.toExternalForm() + " is NOT reachable.");
}
try {
Thread.sleep(5000);
}
catch (InterruptedException e) {
return;
}
}
}
}.start();
We can call this from a main method,
public static void main(String[] args) throws MalformedURLException {
new WebsiteWatcher(new URL("http://www.google.com"));
}
Run this, and you will see whether or not Google is available from your machine, every five seconds.
Wrap it in a bundle
Step two, code in package net.luminis.websitewatcher.simplebundle and simplebundle.bnd.
We can take the code we had before, and wrap that in a bundle. We add an activator, which uses the Apache Felix Dependency Manager to ease or registration, and create a component for our watcher,
manager.add(createComponent()
.setImplementation(new WebsiteWatcher(new URL("http://google.com"))));
In our WebsiteWatcher, we move the thread-code to an inner class, and add some plumbing code,
public WebsiteWatcher(final URL site) {
m_watcher = new WatcherThread(site);
}
public void start() {
m_watcher.start();
}
public void stop() {
m_watcher.interrupt();
}
The start() and stop() methods are called by the Dependency Manager when our component is, well, started or stopped.
You can see this working for yourself by running the bnd.bnd file in the project.
A ManagedService
Step three, code in package net.luminis.websitewatcher.managedservice and managedservice.bnd.
Moving it up one notch, we create a ManagedService out of our component. To do so, we make the WebsiteWatcher implement ManagedService, and add some contants we will need later.
public class WebsiteWatcher implements ManagedService {
public static final String PID = "net.luminis.websitewatcher.managedservice";
public static final String URL = "url";
A ManagedService must implement an updated(...) method,
@Override
public void updated(@SuppressWarnings("rawtypes") Dictionary properties) throws ConfigurationException {
if (properties != null) {
if (properties.get(URL) == null) {
throw new ConfigurationException(URL, "url cannot be null");
}
try {
m_site = new URL((String) properties.get(URL));
}
catch (MalformedURLException e) {
throw new ConfigurationException(URL, properties.get(URL) + " is not a valid URL", e);
}
}
}
This takes some explanation. Line 3 is a nullcheck on the configuration we receive: null is a valid configuration if our configuration is being removed. However, we will let the Dependency Manager take care of wether or not our configuration is available, so we can ignore the null-case. Line 4 checks the availability of our configuration property, and on line 8 we create a new URL to watch from the URL property in the configuration. If any of these checks go wrong, we create a ConfigurationException telling the world what happened (lines 5, 11).
Finally, we need to instruct the Dependency Manager of our newfound managed-service-ness.
manager.add(createComponent()
.setImplementation(WebsiteWatcher.class)
.add(createConfigurationDependency()
.setPid(WebsiteWatcher.PID)));
What happens when we do this?
The Configuration Admin spec tells us that a ManagedService should be registered as a service, with the PID as a service property. When a configuration becomes available, the Configuration Admin will call that ManagedService with the configuration properties. The Dependency Manager nicely wraps this as a ‘configuration dependency’, i.e., our component can only start if it has a configuration.
In the example project, we use the Apache Felix FileInstall bundle to get configurations into the Configuration Admin. It watches the load directory for files with a name like <pid>.cfg. You can play around with the configuration in net.luminis.websitewatcher.managedservice.cfg, and see what happens!
A ManagedServiceFactory
Piece de resistance, code in package net.luminis.websitewatcher.managedservicefactory and managedservicefactory.bnd.
Having a configurable watcher for a single website is nice, but we really want to be able to watch more sites, without having to create PIDs for each of those, or instantiating multiple watchers ‘by hand’. This is where the ManagedServiceFactory comes in.
The task of a ManagedServiceFactory is to create instances of whatever it manages, based on the configuration it gets.
We first create a ManagedServiceFactory implementation,
public class WebsiteWatcherFactory implements ManagedServiceFactory {
public static final String PID = "net.luminis.websitewatcher.managedservicefactory";
private volatile DependencyManager m_dependencyManager;
private final Map<String, Component> m_components = new HashMap<String, Component>();
@Override
public String getName() {
return "website watcher factory";
}
@Override
public void updated(String pid, @SuppressWarnings("rawtypes") Dictionary properties) throws ConfigurationException {
if (m_components.containsKey(pid)) {
return;
}
Component component = m_dependencyManager.createComponent()
.setImplementation(WebsiteWatcher.class)
.add(m_dependencyManager.createConfigurationDependency().setPid(pid));
m_components.put(pid, component);
m_dependencyManager.add(component);
}
@Override
public void deleted(String pid) {
m_dependencyManager.remove(m_components.remove(pid));
}
}
Right, what happens here?
- For each FactoryConfiguration, the Config Admin will call the updated() method on line 14. In here, we
- create a new Dependency Manager Component for our watcher (line 19),
- make it depend on a configuration for its own PID (line 21), like we did before,
- register its existence (line 23), and
- add the component to the Dependency Manager (line 24)
- If a configuration is deleted, the deleted() method on line 28 will be called; we thus remove the related component from the Dependency Manager.
Note the private volatile DependencyManager field: the Dependency Manager will inject an configured instance in each component that has a field of this type.
Now we have a factory, we need to register it in our activator.
Properties props = new Properties(); props.put(Constants.SERVICE_PID, WebsiteWatcherFactory.PID); manager.add(createComponent() .setInterface(ManagedServiceFactory.class.getName(), props) .setImplementation(WebsiteWatcherFactory.class));
And with that, we have a fully functional factory for creating numerous watchers of all you favorite websites. Apache Felix FileInstall will also handle factory configurations if you use filenames like <factoryPid>-<instance>.cfg, like net.luminis.websitewatcher.managedservicefactory-google.cfg.
When creating configurations, you might get a warning like
*ERROR* Configuration for net.luminis.websitewatcher.managedservicefactory.560fe0c0-a691-475e-854a-b4caab68f6d4 has already been used for service [org.osgi.service.cm.ManagedServiceFactory, id=39, bundle=10] and will now also be given to [org.osgi.service.cm.ManagedService, id=41, bundle=10]
which you can safely ignore. This has to do with the fact that the configuration is initially linked to our factory, but is used to configure the generated service later on.
By the way, you may have noticed that we wrote a new WebsiteWatcher for each step of the process, except for the last one: we just reused the one from step 3.
The OSGi ecosystem: a ‘service’ is not a ‘service’ 2010/05/08
Posted by Angelo van der Sijpt in Uncategorized.Tags: osgi, services
2 comments
Update 2010-05-21: Peter Kriens has made a similar point about μservices some time ago.
In a recent post, Kirk Knoernschild coined the notion of OSGi as an enabler for an ecosystem for software modules. While I do subscribe to this view in the long run, there is still some road to cover.
Current state
In the current marketplace, OSGi is still mainly an inside-business (some notable exceptions aside), providing a foundation to build applications, and if we’re lucky, allowing some reuse of homebrew components.
Within the context of OSGi itself, reuse is actually rampant. For instance, hardly a project goes by in which I don’t use an Event admin or Configuration admin, or one of the dependency management tools like the Apache Felix Dependency Manager.
Still, these either have a very broad range of usability, or are ‘internal affairs’. What we in this ecosystem are reusable business services, and I don’t mean general purpose libraries repackaged as a bundle. (Yes, of course we need those, but we should be able to go beyond that.)
Decoupling modules and services
OSGi has made the genius decision to decouple the notion of modules from the use of services as an architectural building block. I see this enabling three steps in the near future,
- Services as first-class citizens In my time working with OSGi, I notice I’m moving away from the regular notion of a service as ‘something that can do stuff for me’ to ‘an architectural building block’. I believe the notion of services (loosely summed up as ‘using interfaces to communicate, on steriods’) goes far beyond this: it allows us to rely on the framework for wiring up the application, and reduces complexity of the design; all of this is subject of another post (I promise).
- Real service-orientation eases modularization Once we move away from ‘services in the large’ to services as a regular building block, we can start to use those in a more stringent way to insulate concerns: even when the landscape is dynamic, we can carve up our application along the boundaries of services, giving us modularization for free.
- Service-centric modularization Now we have centered our modularization around services, we can take the next step: in stead of thinking about modules, we can now start thinking again about large services, by which I mean a block of functionality defined by a set of OSGi-level services. At this point, the actual deployment model and modularization mechanism is of no importance anymore.
So, what will we be selling in this ecosystem? I believe it not to be modules, but rather ‘services in the large’ built up of ‘services in the small’, which happen to be deployed using modules.



