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