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 Color
s.
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.