Osgi service activated in the JavaFX Application Thread? [message #1771730] |
Wed, 30 August 2017 00:32 |
Marcelo Ruiz Messages: 80 Registered: April 2017 |
Member |
|
|
Hi everyone,
I notice a strange (at least for me) behavior while playing with osgi services in my application.
I have a bundle defining a service:
public interface MyService {
public void doSomething();
}
and a different bundle with implementation:
@Component
public class MyServiceImpl {
@Activate
public void activate() {
System.err.println("activate() called in " + Thread.currentThread());
}
@Deactivate
public void deactivate() {
System.err.println("deactivate() called in " + Thread.currentThread());
}
public void doSomething() {
//doing something here
}
}
and I was curious and also added an activator in the same bundle:
public class Activator implements BundleActivator {
public void start(BundleContext bundleContext) {
System.err.println("start() called in " + Thread.currentThread());
}
public void stop(BundleContext bundleContext) {
System.err.println("stop() called in " + Thread.currentThread());
}
}
Then I have a different bundle with a controller that uses the service:
public class MyController {
@Inject
@Service
private MyService service;
@PostConstruct
public void postConstruct() {
service.doSomething();
}
}
When the controller gets instantiated I receive the following output:
start() called in Thread[JavaFX Application Thread,5,main]
activate() called in Thread[JavaFX Application Thread,5,main]
deactivate() called in Thread[Framework stop,5,main]
stop() called in Thread[Framework stop,5,main]
It got my attention that the service and the activator are called by 2 different threads during their lifecycle events.
Also, assuming this service will start and access a remote database, how can I configure things to avoid the database being accessed by the JavaFX Application Thread?
I understand that after the service are created, I could access them using ThreadSynchronize but that doesn't help the creation problem. The only way I thought of avoiding that is by using an ExecutorService in the service implementation class and forcing everything to run there. It seems overkill... Maybe there is a simpler way (via configuration)?
I would appreciate any thoughts!
Thanks!
[Updated on: Wed, 30 August 2017 17:13] Report message to a moderator
|
|
|
|
|
|
|
|
Re: Osgi service activated in the JavaFX Application Thread? [message #1771836 is a reply to message #1771808] |
Thu, 31 August 2017 08:24 |
Thomas Schindl Messages: 6651 Registered: July 2009 |
Senior Member |
|
|
The things you show are absolutely expected. Lazy bundle is activated when the first class is loaded and the activation is a synchronous process (hence you see the JavaFX-Thread in the activator). For services I think Dirk's statement is not 100% correct because the SCR-Thread is only responsible to manage the meta-data and resolving of references. OSGi-Services are created in lazy fashion so once more the caller thread is the thread the service is instantiated and created (not sure although if the spec enforces that).
Bottom-line: You should never do long running operation on @Activate (eg connecting to a database) or your BundleActivator#start because you block at least the frameworks initialization/booting and in the worst case your application code (eg. by blocking the JavaFX UI-Thread). Too proof all this I pushed a test repo: https://github.com/BestSolution-at/osgi-activation-debug
@Override
public Object start(IApplicationContext context) throws Exception {
CountDownLatch l = new CountDownLatch(3);
Thread t = new Thread("Activate A") {
@Override
public void run() {
loadService(ServiceA.class);
l.countDown();
}
};
t.start();
t = new Thread("Activate B") {
@Override
public void run() {
loadService(ServiceB.class);
l.countDown();
}
};
t.start();
t = new Thread("Activate C") {
@Override
public void run() {
try {
Class.forName("test.activation.lazyc.MyAPI");
l.countDown();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
l.await();
return IApplication.EXIT_OK;
}
who yields the following output:
LazCActivator start: Thread[Activate C,5,main]
LazyBActivator start: Thread[Activate B,5,main]
ServiceBImpl instance: Thread[Activate B,5,main]
ServiceBImpl activated: Thread[Activate B,5,main]
test.activation.lazyb.ServiceBImpl@929617f
LazyAActivator start: Thread[Activate A,5,main]
ServiceAImpl instance: Thread[Activate A,5,main]
ServiceAImpl activated: Thread[Activate A,5,main]
test.activation.lazya.ServiceAImpl@6cfb06fc
|
|
|
|
|
Powered by
FUDForum. Page generated in 0.03746 seconds