Search code examples
javaosgiequinoxe4declarative-services

Bundle is being instantiated more than once using DS


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?


Solution

  • 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.