0
votes

I am working on a OSGi application(with felix scr annotations) which exposes a service. The Service registers with external api's by passing String values.

listener.addSchemaChangeListener(new ChangeListener()
{
    @Override
    public void schemaChange(ChangeEvent changeEvent)
    {
        String schemaName = changeEvent.getSchemaName();

        if (null != myBuilder && schemaList.contains(schemaName))
        {
            initVariables();
        }
    }
}, "SCHEMA1");

Service uses the above piece of code to register the listeners for mulitple values "SCHEMA1", "SCHEMA1", "SCHEMA3" ... I am planning to reuse this service in various bundles. But i want to listen only for SCHEMA1 changes instead of all.

@Reference (name = "ServiceListener"", policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.MANDATORY_UNARY, bind = "bind", unbind = "unbind", referenceInterface = ServiceListener.class) private AtomicReference myServiceListener = new AtomicReference<>();

If i try to use it in another service with @Reference then there is no provision to pass values to service to listen only for particular schema changes so that the service can be resued across my bundle by only passing the list of schema to listen instead of all. Because activate method will be called once the service is binded properly in the usage class(component). Is there any provision in OSGi to acheive this functionality ?

2

2 Answers

3
votes

You have included very little description of how your application is actually working, which makes this question challenging to answer.

From the code that you have shared it looks as though you are following quite a bad pattern. The Listener pattern is a source of many synchronisation problems and memory leaks, and the whiteboard pattern should be preferred when you are in OSGi.

The whiteboard pattern is pretty simple. Rather than having each listener look up a service and register with it, you invert the model. The source of events (in this case schema changes) looks for listener services that are registered in the OSGi service registry. That way the listeners are simple to write and filter, and there is no messy and error-prone add/remove listener logic to code.

A better model would use service properties to select particular schemas and look something like this (using the standard OSGi annotations).

Listener 1 (listens to changes for SCHEMA1)

@Component(
    property="schemaName=SCHEMA1")
public class MyListener implements ChangeListener {
    // Your implementation in here
}

Listener 2 (listens to changes for SCHEMA1, SCHEMA2, and SCHEMA3)

@Component(
    property={"schemaName=SCHEMA1",
              "schemaName=SCHEMA2",
              "schemaName=SCHEMA3"})
public class MyListener implements ChangeListener {
    // Your implementation in here
}

Example Source of events for Schema1:

@Component
public class MyListener implements ChangeListener {

    @Reference(policy=DYNAMIC, target="(schemaName=SCHEMA1)")
    private final List<ChangeListener> listeners = new CopyOnWriteArrayList<>();

    private void onSchemaChange(ChangeEvent event) {
        listeners.forEach(l -> l.schemaChange(event);
    }

    // Rest of your implementation in here
}
0
votes

One way is to create one service for each schema. You can do this by supplying the schema name as a config value and using several configs. Each such service will then also hav this config parameter as a service property. So the clients then can filter for the schema property.

If you do not want to use these configs then you could create one service that offers a factory. Each client then would bind the factory and create an instance by supplying the schema name in the create method of the factory.