This continues the "correct service import behavior" thread. I want to discuss the flip side of the coin - how to properly export.

Currently there are two rules which I have not disputed (but maybe I should?):

- A bundle should mushroom into a runtime object structure when activated and shrink back to a bare activator when deactiveted.

- An exported service should either work as expected or fail fast. It is not permitted to behave randomly even during bundle shutdown. For example do not return unexpected values and do not keep quiet from void method calls as if everything is still working.

What rattles my brain now is how to properly implement the bundle activation and deactivation. Currently my understanding is this:

BundleActivator.start():
1) Import: Create ServiceTrackers and such. Store them in non-final fields.
2) Instantiate: instantiate the root objects of the bundle internal structure and store them in non-final fields.
3) Activate: call start()/open() and methods on the bundle roots if required.
4) Export: if needed register some of the bundle roots with the framework.

As you can see this sequence is carefully arranged so that objects are exposed to other bundles only after being fully constructed and activated. From the point of view of the service importers the bundle activation is an atomic step.

BundleActivator.stop():
1) Unexport: unregisted everything exported.
2) Deactivate: close sockets, stop threads.
3) Release: null all non-final fields that were assigned in the startup phase.

Here the explicit unexport in the beginning is performed in hope to create a neat barrier after which I can tear apart my services without having to keep my work-or-fail obligations. If there are importers that retain references to my service object after the explicit unregistration this is a bug in the importer for, which I'm not responsible. You can see how this atomic shutdown mentality has led to the synchronization and locking scheme I described in the "how to import a service" thread.

I realize now that my shutdown scheme is wrong. Unlike bundle startup where we can have a clear division between "setup" and "export", bundle shutdown is unpleasantly fuzzy. Event after the unregistration event dispatch there can be references to the service object stored on the stacks of calling threads. This obliges me to keep my work correctly or crash cleanly contract forever! Even after I null the references to my "bundle roots" some thread might still call my stale service object.

Seems that the "Deactivate" step must transform the bundle internal content into a fail-clean-and-fast ball of objects. That ball is than detached from the activator and can remain behind until all calling threads flush out their references to it. Only than will the ball and potentially it's class loader be garbage collected.

What I am worried about is should I make this teardown transformation atomic (ouch!)? Should I add extra deactivation boilerplate? For example I might place a boolean flag that marks the service as invalid and check that flag in a small synchronized block at the start of every method. Than add a privately visible close() method to mark the service as invalid. But this still feels like the utopian "safe" mentality :P

I'm hoping such boilerplate is needed almost never. Instead all I have to do is code my bundle according to good error handling practices. E.g. wrap try/finally/close around code that reads files. I am hoping that I need thread-safe close() methods and other invalidation code only for objects that store in fields or in composed objects things like sockets and files.

Cheers,
Todor

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@felix.apache.org
For additional commands, e-mail: users-h...@felix.apache.org

Reply via email to