0

I'm creating an application that has hips of bundles. Some of them are interfaces and some are implementation of those interfaces. I'm using declarative services (DS) to provide and use services, which means that each bundle has a component.xml describing the service(s) I'm providing/referencing.

Currently I have a class called ClockWidget that implements 3 interfaces (as you can see in the attached diagram). For each service implemented, I have a specific class that is referencing this service. For example, the Timeout class has bind methods that receives anyone who implements the TimeoutListener service. enter image description here

The problem is that the constructor of the ClockWidget class is being called three times. Obviously I associated that with the number of services that it's being used by other bundles.

The question is: what's a good approach/practice in order to deal with bundles that implements more than one service? I mean, I don't want this bundle to be duplicated in the application. I'd like to use the same instance in the three classes that are referencing this guy. I tried to enable the singleton property in the manifest, but nothing has changed.

ClockWidget.class:

public class ClockWidget implements Widget, TimeoutListener, DummyInterface {

    public ClockWidget() {
        System.out.println("ClockWidget constructor.");
    }

    @Override
    public String getWidgetName() {
        return "Clock widget";
    }

    @Override
    public void onTimeout() {
        System.out.println("Timeout!");
    }

    @Override
    public void dummyMethod() {
        // Does nothing.
    }
}

Its component definition ClockWidget.xml

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="test.e4.ClockWidget">
   <implementation class="test.e4.history.ClockWidget"/>
   <service>
      <provide interface="test.e4.widget.Widget"/>
      <provide interface="test.e4.timeoutlistener.TimeoutListener"/>
      <provide interface="test.e4.dummyinterface.DummyInterface"/>
   </service>
</scr:component>

A class that uses a service provided by ClockWidget. In this case, the Timeout class:

public class Timeout {

    private ArrayList<TimeoutListener> listeners;

    public Timeout() {
        listeners = new ArrayList<>();
        startTimeoutTimer();
    }

    public void startTimeoutTimer() {
        long timeoutInMs = 60 * 1000;
        Timer timeoutTimer = new Timer();
        timeoutTimer.schedule(new TimerTask() {

            @Override
            public void run() {
                timeout();
            }
        }, timeoutInMs);
    }

    // Bind method from DS
    public void addListener(TimeoutListener listener) {
        listeners.add(listener);
    }

    // Unbind method from DS
    public void removeListener(TimeoutListener listener) {
        listeners.remove(listener);
    }

    public void timeout() {
        for (TimeoutListener listener : listeners) {
            listener.onTimeout();
        }
    }
}

Timeout component description:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="test.e4.Timeout">
   <implementation class="test.e4.Timeout"/>
   <reference bind="addListener" cardinality="0..n" interface="test.e4.timeoutlistener.TimeoutListener" name="TimeoutListener" policy="dynamic" unbind="removeListener"/>
</scr:component>

Any suggestion?

Fappaz
  • 3,033
  • 3
  • 28
  • 39
  • Can you also post your code? How does the ClockWidget look like? Are you using the DS annotations or the xml config? – Christian Schneider May 28 '14 at 06:32
  • I updated the question with codes. My `ClockWidget` is a simple class that implements 3 interfaces. I'm using XML to declare a service. – Fappaz May 29 '14 at 21:09
  • Do the three instances overlap in time, or do they seem to be created one after the other? I'm speculating, but the component might be getting lazily created when each consumer binds it -- because it is not declared as `immediate` -- and then destroyed after that consumer unbinds. You could experiment by setting `immediate` to true and reporting the results. – Neil Bartlett May 29 '14 at 22:10

2 Answers2

2

Is ClockWidget one DS component registered under the 3 services names? Or 3 DS components each registered under one service name. If the latter, then yes, ClockWidget will get instantiated once for each service since that is all DS knows. If the former, then ClockWidget should only be instantiated once since it is a single service made available under 3 service names.

BJ Hargrave
  • 9,324
  • 1
  • 19
  • 27
  • That's what I thought at first. However, the 3 services are being declared in the same XML. I updated the question with the component declarations. If I run the application, I see the ClockWidget constructor being called three times. – Fappaz May 29 '14 at 21:11
0

Sorry guys, I wasn't completely honest with you. Of course my system is much more complex. It's like my ClockWidget class has another method that is being called in the constructor. I didn't know that this could cause the issue. Here is how the full ClockWidget.class would look like:

public class ClockWidget extends Widget implements TimeoutListener, DummyInterface {

    private ArrayList<ActivityListeners> activityListeners;

    public ClockWidget() {
        super();
        System.out.println("ClockWidget constructor.");
        lookupActivityListeners();
    }

    @Override
    public String getWidgetName() {
        return "Clock widget";
    }

    @Override
    public void onTimeout() {
        System.out.println("Timeout!");
    }

    @Override
    public void dummyMethod() {
        // Does nothing.
    }

    private void lookupActivityListeners() {

        activityListeners = new ArrayList<>();

        try {
            BundleContext context = FrameworkUtil.getBundle(this.getClass()).getBundleContext(); 
            Collection<ServiceReference<ActivityListener>> serviceReferences = context.getServiceReferences(ActivityListener.class, null);

            for (ServiceReference<ActivityListener> serviceReference : serviceReferences) {
                ActivityListener activityListener = (ActivityListener) context.getService(serviceReference);
                if (activityListener != null) {
                    activityListeners.add(activityListener);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

As you can see, the method lookupActivityListeners searches for all ActivityListeners installed in OSGi and adds them in an array list. I've commented the line ActivityListener activityListener = (ActivityListener) context.getService(serviceReference); and the constructor was called once.

So I had to change the lookupActivityListeners(); calling to another place instead of the constructor. Now everything is fine. However, I still don't know why that would be the source of the problem.

Thanks for the answers anyway.

Fappaz
  • 3,033
  • 3
  • 28
  • 39
  • That's a really really strange way to get hold of service instances... especially as you're already in a DS component and can just use injection... – Neil Bartlett May 30 '14 at 20:11
  • Hmm interesting... how could I do DI with a list? – Fappaz Jun 02 '14 at 06:34
  • 1
    Make a reference with cardinality of 0..n or 1..n. Also make sure to use the dynamic rather than static reference policy. Now your bind method (e.g. `addFoo`) will be called for each instance of the service, and your unbind method (e.g. `removeFoo`) will be called when any of those services goes away. – Neil Bartlett Jun 02 '14 at 11:53
  • Oh, I am already doing that in the other classes (see the Timeout sample in the first post). Well, I will change to this approach in my real application as it seems that is a better way to handle service instances. Thanks. – Fappaz Jun 02 '14 at 21:17