An interface implemented by objects which need to do some kind of
post-construction initialization before they can be used (like the creation
of worker threads).
The Active contract
For any component which implements this interface, the
Active.initialize() method must be the first method called or field
accessed after an instance is constructed, and it must be called only
once It is usually safest to call initialize() immediately
after construction to make sure of that. Note that it is okay to
create an instance of a component which implements active and never do
anything with it, not even calling initialize() or
Active.dispose() .
For any component which implements this interface, the
dispose() method must be called exactly once. There is
one exception to this rule: if initialize() is never called,
dispose() should never be called, either. After it has been
called, no more methods should be called on the instance, nor should
any fields be accessed. There is an exception to this rule as well: the
garbage collector may call a finalize() method if it exists,
which in turn may access fields and call other methods.
State definitions
With passive components, we just have safe (the component can be
safely used) and unsafe (the component cannot be safely used)
state. With active components, we usually introduce some more
terminology to describe component state:
An object whose constructor has returned has been created. While
the initialize() method is running the component is
initializing. Once the initialize() method has returned,
the object is initialized and active. It will remain active
until the dispose() method is called. While the
dispose() method is running, the component is disposing
or shutting down. Once the dispose() method has
returned, the component has shut down and it has been
disposed.
The contract in readable English...
After you create an instance of an Active component, call
its initialize() method. Then use the component as you would
use any other. When you are finished using it, call the
dispose() method.
some tips: - call
initialize() immediately
after you construct an instance. - To make sure an instance is only
ever
initialize() d and dispose() d once, keep the
responsibility for calling these methods with the same code that is
responsible for creating the instance. - Remove all references to an
instance immediately after you have called
dispose() (to
make sure you don't accidentally call a method on it again later, and so
that the component may be reclaimed for garbage collection).
Some notes on lifecycle management
The Active interface is the only lifecycle interface
to make it into the Jicarilla Framework. You should try to avoid its use as
much as possible, as components that depend on having
initialize() and dispose() require more work by
the client programmer.
One good reason to use the Active interface is when you need
to use threads, since those should not be created inside a constructor (see
Well
Behaved Citizens for more information).
Also, while the contract surrounding Active should be
crystal-clear to everyone and it should always.
For example of the Active interface in use, take a look at
any of the jicarilla components. You will note its use is minimal: most
components defer their thread management to helper components like thread
pools, freeing them from the need to be Active .
The inversion of active is passive. Try to make most of
your code passive, controlled by isolated bits of active code.
Enforcing the Active contract
There's many different things you can do to make sure that client code
doesn't fail, or at least fails gracefully, even if it doesn't honor the
Active contract correctly:
- extend from the
org.jicarilla.lang.AbstractActiveAbstractActive interface. It implements basic 'wrapper' methods around
initialize() and dispose() that shield the client class
from clients that call these methods in an inappropriate order, or more than
once. - implement lazy (re)initialization. To do this, you
make all your fields private, then add checks at the top of every public
method (and every protected method if you're really playing it safe) that
will
initialize() a component if it hasn't been initialized
yet, and maybe even reset, then re-initialize a component if it has been
disposed of already. The AbstractActive class implements such a
policy inside the
org.jicarilla.lang.AbstractActive.lazyInitializationlazyInitialization() method. - implement state checking. To
do this, add checks at the top of every public method that will throw an
exception if the component is not in an active state. The
AbstractActive class implements such a policy inside
org.jicarilla.lang.AbstractActive.checkActivecheckActive() method. - Use a container. If you run your
components inside an IoC container that is aware of the
Active
interface, such as Jicarilla-Container, the container can take care of
calling the initialize() and dispose() methods for
you automatically. It can even shield your components from malicious clients
by implementing a proxy (or a security policy) that will prevent them from
calling the initialize() or dispose() methods at
all!
Before you go overboard with these measures, consider the alternative:
trust. If you trust users of your components to be able to always remember
and follow the Active contract, don't bother with all the
checks (they clutter up the code and add overhead).
Active components and thread safety
Active components need all the usual synchronization and checking to be
safe in a multithreaded enviroment. In particular, be extra careful when
using lazy initialization or state checking, since you open up the
possibility of two concurrently called methods deciding to do lazy
initialization at the same time. Note that the relevant methods for these
two policies inside AbstractActive are all synchronized to
prevent this kind of problem.
author: Leo Simons version: $Id: Active.java,v 1.1 2004/03/23 13:37:56 lsimons Exp $ |