Tracking multiple service in Felix DM4
2015-03-18
3 minutes read

Apache Felix DependencyManager v4 is out with lots of new features and improvements, which are documented quite well and I’ll not repeat all. Instead, I’d like to highlight two new features that are really convenient in lots of situations where you want to track multiple components of the same type.

Suppose we have a service named Color with the following contract:

public interface Color {
    /** @return red value from [0..255]. */
    int r();
    /** @return green value from [0..255]. */
    int g();
    /** @return blue value from [0..255]. */
    int b();
}

If you have multiple instances of this Color service you’d like to keep track of, you probably want to keep them in a Collection in your consuming code to have direct access to them. In the DM3 and earlier, you manually had to define the service add/removal callbacks and write the necessary boilerplate code to update your collection of Colors. So, your bundle activator looked something like:

// !!! DM3 and earlier
public class Activator implements BundleActivator {
    public void init( BundleContext context, DependencyManager dm ) {
        dm.add( createComponent()
            .setImplementation( Consumer.class )
            .add( createServiceDependency()
                .setService( Color.class )
                .setCallbacks( "added", "removed" ) )
        );
        // ...
    }
}

And your Consumer implementation had something like:

// !!! DM3 and earlier
public class Consumer {
    private final Collection<Color> m_colors = new CopyOnWriteArrayList<>();
    // ...
    public void added( Color color ) {
        m_colors.add( color );
    }
    
    public void removed( Color color ) {
        m_color.remove( color );
    }
    // ...
    // called by Felix DM upon start of this component
    public void start() {
        System.out.printf( "There are %d colors present...%n", m_colors.size() );
    }
}

As you see, lots of boilerplate. The good news is that in Apache Felix DependencyManager 4, this is no longer needed as they added support to inject multi-cardinality fields directly. This means that our example Activator can be simplified to:

// !!! DM4 and later
public class Activator implements BundleActivator {
    public void init( BundleContext context, DependencyManager dm ) {
        dm.add( createComponent()
            .setImplementation( Consumer.class )
            .add( createServiceDependency()
                .setService( Color.class ) )
        );
        // ...
    }
}

As you can see, we no longer need to supply those callbacks, which means that our Consumer implementation becomes simpler as well:

// !!! DM4 and later
public class Consumer {
    private volatile Collection<Color> m_colors;
    // ...
    // called by Felix DM upon start of this component
    public void start() {
        System.out.printf( "There are %d colors present...%n", m_colors.size() );
    }
}

Note that we no longer are required to instantiate the m_colors field, as DM4 will do this for us, and we no longer need to implement the added and removed methods.

Besides injecting tracked services in a Collection (sub)type, you can also let DM4 inject your tracked services in a Map. The keys of the map represent the tracked services, and the value a Dictionary represent their service properties. This is especially handy if you need to access the service properties often. Suppose we’ve registered all of our Color services with a service property called luminance that represents the perceived luminance of that color. Our Consumer would look like:

// !!! DM4 and later
public class Consumer {
    private volatile Map<Color, Dictionary<String, ?>> m_colors;
    // ...
    // called by Felix DM upon start of this component
    public void start() {
        System.out.printf( "There are %d color entries present...%n", m_colors.size() );
    }
}

If you want to know more about the features of Apache Felix DM4, check out its (also improved) documentation.


Back to posts