2017-08-25 1 views
1

J'ai écrit un module qui définit un service:Deux instances de mon module de mise en œuvre de services Liferay

public interface TranslationService { 
    // a method. 
} 

... un module qui implémente le 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. 
} 

avec son activateur de service:

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); 
    } 

    [...] 
} 

... et un module qui utilise le service: 01

@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

Se connecter quand je commence (via Gogo Shell) les modules API et mise en œuvre:

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

Puis, quand je commence (via Gogo Shell), le module en utilisant:

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

Question

Pourquoi deux instances sont-elles utilisées?
Comment faire pour que tous les modules utilisent la même instance de mon implémentation de service?

Détails

stacktraces des deux fois le constructeur de mise en œuvre est appelée:

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) 

Puis:

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) 

donc son semble que je ne devrais pas instancier la mise en œuvre dans l'activateur de service ? C'est comme cela que cela semble être fait en this tutorial cependant.

+0

Cela semble impossible. Avez-vous déjà vu une ligne indiquant "Configurer my.TranslationServiceImpl 1451438505"? –

+0

Pouvez-vous vérifier avec constructor-logging? –

+0

@NeilBartlett: J'ai ajouté plus de détails, plus la journalisation du constructeur. J'ai aussi réalisé que mon activateur de service causait probablement le problème, mais je ne suis pas sûr de savoir comment le changer. Merci beaucoup! –

Répondre

3

Vous obtenez une deuxième instance du composant parce que vous le créez explicitement, sur la ligne 24 de ServiceActivator.java. Ceci est révélé par la première trace de pile que vous avez publiée. Il suffit de retirer cette ligne de code, et tout ira bien.

En fait, vous n'avez probablement pas besoin de ServiceActivator. Je suppose que c'est le BundleActivator pour votre bundle? Vous n'avez pas besoin d'écrire des activateurs lorsque vous utilisez DS, car tout ce que vous pouvez faire dans un activateur peut être fait dans un composant DS, et bien plus encore. En fait, ma recommandation générale est de ne jamais écrire d'activateurs de paquets.

+0

Oups J'ai raté le fait que vous avez posté le code pour ServiceActivator.java. Oui, vous n'avez certainement pas besoin de ça. Il suffit de le supprimer. –

2

Neil a trouvé la réponse, permettez-moi d'ajouter quelques détails:

Comme Neil a dit, un activateur de service n'est pas nécessaire, en dépit de ce que suggère le tutoriel, je lié à ma question. Il suffit de le supprimer.

De toute évidence, également supprimer la ligne Bundle-Activator: de bnd.bnd sinon vous obtiendrez une erreur Bundle-Activator not found on the bundle class path nor in imports lors du déploiement.

À la place de l'activateur de service, utilisez ce que Daniel dit de «DS». Cela signifie Services Déclaratifs, et est une technologie OSGi qui fonctionne avec des annotations.Pour l'utiliser, ajoutez simplement une instruction service = dans l'annotation @Component de votre classe d'implémentation, avec l'interface du service implémenté (API) en tant que valeur. Exemple:

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

Hey, j'ai lu le tutoriel que vous avez lié et il ne semble pas très bon. L'un des problèmes que nous avons dans OSGi est qu'il y a un million de tutoriels écrits par des gens qui, franchement, ont besoin d'étudier plus avant d'essayer d'enseigner. Mais en tant que débutant total, il est difficile de dire quels tutoriels sont bons et quels sont les indésirables. FWIW Je ne pense pas que le site "proliferay.com" ait quelque chose à voir avec Liferay, et viole probablement leur marque. –