Hello Philipp,
So, let's see if I understand you correctly:
- you have N "Device" services (each having a "device.id" service propery).
- then you have N corresponding DeviceParameter instances, each one also
having a "device.id" service property, matching a corresponding Device.
- and you want to consume each pair of Device / DeviceParameter (having
both the same value for the "device.id" service property).
So, one possible solution (if I my understanding is correct) could consist
in implementing your Consumer as a DependencyManager "Adapter" component,
and this adapter would then define dynamically from it's init() method an
extra dependency on the DeviceParameter, by reusing the id from the
original Device service.
Let's see what it could look like using a sample code. First here are the
sample for the Device/DeviceParameter related classes:
public interface Device {
int getDeviceId();
}
public class DeviceImpl implements Device {
final int id;
DeviceImpl(int id) {
this.id = id;
}
@Override
public int getDeviceId() {
return id;
}
}
public interface DeviceParameter {
int getDeviceId();
}
public class DeviceParameterImpl implements DeviceParameter {
final int id;
DeviceParameterImpl(int id) {
this.id = id;
}
@Override
public int getDeviceId() {
return id;
}
}
Now, let's define an Activator which creates two pair of
Device/DeviceParameter:
public class Activator extends DependencyActivatorBase {
@Override
public void init(BundleContext context, DependencyManager dm) throws
Exception {
createDeviceAndParameter(dm, 1);
createDeviceAndParameter(dm, 2);
}
private void createDeviceAndParameter(DependencyManager dm, int id) {
Hashtable<String, Object> props = new Hashtable<>();
props.put("device.id", id);
dm.add(createComponent()
.setImplementation(new
DeviceImpl(id)).setInterface(Device.class.getName(), props));
props = new Hashtable<>();
props.put("device.id", id);
dm.add(createComponent()
.setImplementation(new
DeviceParameterImpl(id)).setInterface(DeviceParameter.class.getName(),
props));
}
}
Now, here is the DeviceConsumer: First, let's add a declaration in the
Activator.init method, which defines the DeviceConsumer as an "Adapter":
dm.add(createAdapterService(Device.class, null)
.setImplementation(DeviceConsumer.class));
This says that we'll instantiate a DeviceConsumer component (which is here
not providing a service) when a Device services comes up.
And now, we define the DeviceConsumer like this:
(Notice the init method, where we declare an extra dependency, dynamically
created using the device id of the initially injected Device object)
public class DeviceConsumer {
volatile Device device; // injected before init()
volatile DeviceParameter deviceParameter; // extra dependency defined
from init(), but injected before start()
void init(Component c) {
// Dynamically add a dependency on the corresponding
DeviceParameter service.
DependencyManager dm = c.getDependencyManager();
c.add(dm.createServiceDependency()
.setService(DeviceParameter.class, "(device.id=" +
device.getDeviceId() + ")")
.setRequired(true));
}
void start() {
// At this point, we have been injected with
theDevice/DeviceParameter pair.
System.out.println("Created a DeviceConsumer for device id " +
device.getDeviceId() + ", device parameter id "
+ deviceParameter.getDeviceId());
}
}
So, in the init() method, we are using the id of the injected Device in
order to create an extra dependency on the corresponding DeviceParameter.
And, when the init() method returns, Dependency Manager will recalculate
the dependencies, and when the corresponding DeviceParameter comes in, then
it will be auto-injected in the "deviceParameter" field and then, your
start () method will be called, where you can manipulate the pair of
Device/DeviceParameter.
One important remark: the above code works with the upcomming DM 4.0.0
(currently committed in the Felix sandbox, from [1]), but if you are using
the currently released DM (3.2.0), then in your init() method, you will
have to call an extra "setInstanceBound(true)" method:
void init(Component c) {
DependencyManager dm = c.getDependencyManager();
c.add(dm.createServiceDependency()
.setService(DeviceParameter.class, "(device.id=" +
device.getDeviceId() + ")")
.setRequired(true)
.setInstanceBound(true));
}
This "setInstanceBound" method has been removed in the DM 4.0.0 (currently
committed in [1]), but in the DM 3.2.0, you still have to call this method
when you declare extra dependencies from any component's init methods.
Does this help ? Does this corresponds to what you would need ?
Since this example is interesting (hope it corresponds to your needs), I
will probably commit it in the sample code from the DM 4.0.0, in [2].
[1]
http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/
[2]
http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager-prototype/org.apache.felix.dependencymanager.samples/
cheers;
/Pierre
On Mon, Sep 22, 2014 at 4:15 PM, Bulu <[email protected]> wrote:
> Hello all
>
> I'm trying Felix Dependency Manager and have come across the following
> case.
>
> For each registered service of type A, I want to create a component, only
> when the corresponding service B is also present. The corresponding service
> is determined by a property of A.
>
> Example:
> I have a services of type "Device" which each have a unique property "
> device.id".
> objectClass=com.example.Device
> device.id=device_0001
>
> I have another service DeviceParameter, which uses the same id to
> indicate, it is corresponding to the above device
> objectClass=com.example.DeviceParameter
> device.id=device_0001
>
> Now I want to start a consuming component, which uses both. That is for
> each Device service which appears, create the consumer as soon as the
> corresponding DeviceParameter also appears.
>
> // component, not registered as a service
> class Consumer {
> // injected by DM. Both are required for Consumer to be built
> private volatile Device d;
> private volatile DeviceParameter p;
> ...
> }
>
> How can this be done in DM?
>
> For the record, my attempt so far:
> mgr.add(createComponent()
> .setImplementation(Consumer.class)
> .add(createServiceDependency()
> .setRequired(true)
> .setService(Device.class.getName()))
>
> .add(createServiceDependency()
> .setRequired(true)
> .setService(DeviceParameter.class.getName(), "(
> device.id=" + ... +")")));
>
> How to fill the filter "..."?
>
> Thanks Philipp
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [email protected]
> For additional commands, e-mail: [email protected]
>
>