Reputation: 60203
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 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
Why are two instances being used?
How to make all modules use the same instance of my service implementation?
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
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
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