Nicolas Raoul
Nicolas Raoul

Reputation: 60203

Two instances of my Liferay service implementation module

I wrote a module that defines a service:

public interface TranslationService {
    // a method.
}

... a module that implements the service:

@Component(
    immediate = true,
    configurationPid = "my.TranslationConfiguration"
)
public class TranslationServiceImpl implements TranslationService {

    log.info("Constructor " + getClass().getName()
             + " " + System.identityHashCode(this));

    @Activate
    @Modified
    protected void activate(Map<String, Object> properties) {
        log.info("Configuring " + translationService.getClass().getName()
                 + " " + System.identityHashCode(this));
        configuration = ConfigurableUtil.createConfigurable(
            TranslationConfiguration.class, properties);
}

    // an implementation of the method.
}

with its service activator:

public class ServiceActivator implements BundleActivator {

    private ServiceRegistration registration;

    @Override
    public void start(BundleContext context) throws Exception {
        registration = context.registerService(TranslationService.class.getName(), new TranslationServiceImpl(), null);
    }

    [...]
}

... and a module that uses the service:

@Component(
    immediate = true,
    configurationPid = [...]
    service = Portlet.class
)
public class TranslationPortlet extends MVCPortlet {

    @Reference(unbind = "-")
    protected void setTranslationService(TranslationService translationService) {
        log.info("Using " + translationService.getClass().getName()
               + " " + System.identityHashCode(translationService));
        this.translationService = translationService;
    }

    private TranslationService translationService;
}

Log

Log when I start (via Gogo Shell) the API and implementation modules:

Constructor my.TranslationServiceImpl 606817095
Service registered.
STARTED my.impl_1.0.0 [538]
Constructor my.TranslationServiceImpl 362465287
Configuring my.TranslationServiceImpl 362465287

Then when I start (via Gogo Shell) the using module:

STARTED my.app_1.0.0 [558]
Using my.TranslationServiceImpl 606817095

Question

Why are two instances being used?
How to make all modules use the same instance of my service implementation?

Details

Stacktraces of the two times the implementation constructor is called:

my.TranslationServiceImpl.<init>(TranslationServiceImpl.java:56)
my.ServiceActivator.start(ServiceActivator.java:24)
org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:774)
org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1)
java.security.AccessController.doPrivileged(Native Method)
org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:767)
org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:724)
org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:951)
org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:328)
org.eclipse.osgi.container.Module.doStart(Module.java:566)
org.eclipse.osgi.container.Module.start(Module.java:434)
org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:402)
org.apache.felix.gogo.command.Basic.start(Basic.java:729)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:498)
org.apache.felix.gogo.runtime.Reflective.invoke(Reflective.java:137)

Then:

my.TranslationServiceImpl.<init>(TranslationServiceImpl.java:56)
sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
java.lang.reflect.Constructor.newInstance(Constructor.java:423)
java.lang.Class.newInstance(Class.java:442)
org.apache.felix.scr.impl.manager.SingleComponentManager.createImplementationObject(SingleComponentManager.java:236)
org.apache.felix.scr.impl.manager.SingleComponentManager.createComponent(SingleComponentManager.java:108)
org.apache.felix.scr.impl.manager.SingleComponentManager.getService(SingleComponentManager.java:906)
org.apache.felix.scr.impl.manager.SingleComponentManager.getServiceInternal(SingleComponentManager.java:879)
org.apache.felix.scr.impl.manager.AbstractComponentManager.activateInternal(AbstractComponentManager.java:748)
org.apache.felix.scr.impl.manager.AbstractComponentManager.enableInternal(AbstractComponentManager.java:674)
org.apache.felix.scr.impl.manager.AbstractComponentManager.enable(AbstractComponentManager.java:429)
org.apache.felix.scr.impl.manager.ConfigurableComponentHolder.enableComponents(ConfigurableComponentHolder.java:657)
org.apache.felix.scr.impl.BundleComponentActivator.initialEnable(BundleComponentActivator.java:341)
org.apache.felix.scr.impl.Activator.loadComponents(Activator.java:403)
org.apache.felix.scr.impl.Activator.access$200(Activator.java:54)
org.apache.felix.scr.impl.Activator$ScrExtension.start(Activator.java:278)
org.apache.felix.utils.extender.AbstractExtender.createExtension(AbstractExtender.java:259)
org.apache.felix.utils.extender.AbstractExtender.modifiedBundle(AbstractExtender.java:232)
org.osgi.util.tracker.BundleTracker$Tracked.customizerModified(BundleTracker.java:482)
org.osgi.util.tracker.BundleTracker$Tracked.customizerModified(BundleTracker.java:1)
org.osgi.util.tracker.AbstractTracked.track(AbstractTracked.java:232)
org.osgi.util.tracker.BundleTracker$Tracked.bundleChanged(BundleTracker.java:444)
org.eclipse.osgi.internal.framework.BundleContextImpl.dispatchEvent(BundleContextImpl.java:905)
org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230)
org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:148)
org.eclipse.osgi.internal.framework.EquinoxEventPublisher.publishBundleEventPrivileged(EquinoxEventPublisher.java:165)
org.eclipse.osgi.internal.framework.EquinoxEventPublisher.publishBundleEvent(EquinoxEventPublisher.java:75)
org.eclipse.osgi.internal.framework.EquinoxEventPublisher.publishBundleEvent(EquinoxEventPublisher.java:67)
org.eclipse.osgi.internal.framework.EquinoxContainerAdaptor.publishModuleEvent(EquinoxContainerAdaptor.java:102)
org.eclipse.osgi.container.Module.publishEvent(Module.java:461)
org.eclipse.osgi.container.Module.start(Module.java:452)
org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:402)
org.apache.felix.gogo.command.Basic.start(Basic.java:729)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:498)
org.apache.felix.gogo.runtime.Reflective.invoke(Reflective.java:137)

So its seems like I should not instantiate the implementation in the service activator? That's how it seems to be done in this tutorial though.

Upvotes: 1

Views: 454

Answers (2)

Nicolas Raoul
Nicolas Raoul

Reputation: 60203

Neil has found the answer, let me just add a few details:

As Neil said, a service activator is not needed, despite what suggests the tutorial I linked to in my question. Just delete it.

Obviously, also remove the Bundle-Activator: line from bnd.bnd otherwise you will get a Bundle-Activator not found on the bundle class path nor in imports error when deploying.

In place of the service activator, use that "DS" thing Neil talks about. It means Declarative Services, and is an OSGi technology that works with annotations. To use it, just add a service = instruction in the @Component annotation of your implementation class, with the implemented service (API)'s interface as a value. Example:

@Component(
    service = TranslationService.class,
    immediate = true,
    configurationPid = "my.TranslationConfiguration"
)
public class TranslationServiceImpl implements TranslationService {

Upvotes: 2

Neil Bartlett
Neil Bartlett

Reputation: 23948

You are getting a second instance of the component because you are explicitly creating it, on line 24 of ServiceActivator.java. This is revealed by the first stack trace that you posted.

Simply remove that line of code, and everything will be fine.

In fact you probably don't need ServiceActivator at all. I assume that it is the BundleActivator for your bundle? You do not need to write activators when using DS, because anything you can do in an activator can be done in a DS component, and much more besides. In fact my general recommendation is to never write bundle activators.

Upvotes: 3

Related Questions