What are lifecycle extensions ?Lifecycle extensions are additional stages a component can traverse through during it's lifetime. Lifecycle extensions allow a Container to provide extra functionality to Components in addition to the standard stages defined by Avalon Framework. Avalon Framework defines a set of standard interfaces often termed as Lifecycle metainfo which tells the ComponentManager how a particular Component should be treated during it's life. This metainfo allows the developer to separate the various concerns involved when writing a Component, often termed SoC and IoC (Separation of Concerns and Inversion of Control) and is one of primary advantages of using Avalon. Sometimes it's useful to extend this development paradigm from the framework level into the application domain, to create customized lifecycle extensions that are called upon in addition to the standard set defined by the Avalon Framework. Such custom lifecycle stages can further enable domain specific logic across many, perhaps even unrelated components, can reduce code duplication, and allows the developer to reuse the same development and thinking paradigm as the standard lifecycle stages. For example, you might want to pass a specialized SecurityManager to some of your components before they are initialized, or have their internal state persistently cached during system shutdown and restored at during startup. You might want to pass user dependent decryption keys to your component, or give components the opportunity to recycle themselves before being disposed or returned to a pooled component handler. The possibilities and number of extensions are only limited by the requirements of your particular application domain. This document describes how to add new lifecycle extensions using Fortress . This document assumes a knowledge of what an Avalon lifecycle is, and a basic understanding of the standard lifecycle interfaces Avalon Framework defines. References in this document to Component and ComponentManager can also be freely interpreted as Service and ServiceManager by the reader. Support for lifecycle extensions in the other Avalon containers is technically possible but has not yet been discussed. Please check with the Avalon developer mailing list if you use one of these containers and would like to use lifecycle extensions. How do I extend a Component's lifecycle ?Extending a Component's lifecycle is straightforward. An overview of the process follows:
When can a Component's lifecycle be extended ?The life of any component can be broken down to the following phases:
A Component will go through it's Creation and Destruction phase only once. Since
Lifecycle extensions can be added to any of the above defined phases. This allows you to choose when your particular extension will be executed. For example, thread or user dependent extensions would be added at the access and release levels (ie. when the component is retrieved and returned to the ComponentManager) as they depend on runtime data not available until they are actually used. More static, or global extensions would be added at the creation or destruction level, since they do not depend on any external data that change during runtime, nor are they particular to any one context of use. Which interfaces and classes do I need to use ?Support for lifecycle extensions in Fortress is done using the following classes/interfaces. The Component Extension InterfaceThis interface specifies the business particular extension components will be tested for. It defines the new interface that components will implement to receive additional functionality. There is no particular base interface the developer needs to extend, and the interface can be kept separate from the Container itself. The LifecycleExtension Interface
Component extensions are invoked via a Lifecycle extension object. Lifecycle extension
objects are managed via a
All Lifecycle extension objects must implement the
The 4 methods (
The container
Each method may throw an exception to indicate an error, which will be logged, but will not terminate other extensions from being executed on that Component. /** * LifecycleExtension interface. This interface defines the methods that * a LifecycleExtensionManager can call on a particular concrete * LifecycleExtensionMarker class. */ public interface LifecycleExtension { /** * Create, called when the given component is being * instantiated. * * @param component a Component instance * @param context a Context instance * @exception Exception if an error occurs */ void create( Object component, Context context ) throws Exception; /** * Destroy, called when the given component is being * decommissioned. * * @param component a Component instance * @param context a Context instance * @exception Exception if an error occurs */ void destroy( Object component, Context context ) throws Exception; /** * Access, called when the given component is being * accessed (ie. via lookup() or select()). * * @param component a Component instance * @param context a Context instance * @exception Exception if an error occurs */ void access( Object component, Context context ) throws Exception; /** * Release, called when the given component is being * released (ie. by a CM or CS). * * @param component a Component instance * @param context a Context instance * @exception Exception if an error occurs */ void release( Object component, Context context ) throws Exception; }
Many extensions will not require implementation of every method defined in the
above interface, for that reason, there's a
The LifecycleExtensionManager class
The
The LifecycleExtensionManager class API is too big to list here, instead please look at the following link . It essentially defines 4 methods for executing extension objects at the various phases of a component's lifecycle, and several methods for registering extension objects with the manager.
The
By default, all Fortress based containers will be initialized with a default
To add a new lifecycle extension object to the manager simply call the method
FortressComponentManager/FortressComponentSelectorFortress' inbuilt Component Manager/Selector/Factory code will automatically call upon the LifecycleExtensionManager class at each phase in a Component's life at the following predefined times:
access
and
release
extensions. This is
because the code performing this logic is located in the ComponentManager/Selector classes
(independent from all handlers).
An ExampleLet's look at a simple example. The following is also available as a working sample in Fortress' examples directory.
Our example implements a Lifecycle extension for passing a
Define the component extension interfaceFirst we define the new Component extension interface. /** * Simple custom lifecycle extension interface for supplying a component * with a security manager. */ public interface SecurityManageable { /** * Pass a SecurityManager object to the component * * @param manager a SecurityManager value */ void secure( SecurityManager manager ) throws SecurityException; } Create the lifecycle extensions class
Next we define the actual extension implementation which invokes the
/** * Some custom extensions for this container's components. */ public class Extensions extends AbstractLifecycleExtension { /** * Access, called when the given component is being * accessed (ie. via lookup() or select()). * * @param component a Component instance * @param context a Context instance * @exception Exception if an error occurs */ public void access( Object component, Context context ) throws Exception { if ( component instanceof SecurityManageable ) { // pass in a simple security manager, a real system might want to pass // in specialized/custom security managers ( ( SecurityManageable ) component ).secure( new SecurityManager() ); } } } An extension class may run components through any given number of extensions, and are not limited to just one. Register the lifecycle extensions class
We then inform our container about the extension. This could be done in several different
ways, for simplicity we'll extend
(an alternative might be to initialize a LifecycleExtensionManager before creating the
container and pass it in via the
/** * Simple container that includes custom lifecycle extensions. */ public final class ExtendedContainer extends AbstractContainer { public void initialize() throws Exception { super.initialize(); m_extManager.addExtension( new Extensions() ); } } Use the new component interface
To use the new SecurityManageable lifecycle extension, we simply implement
SecurityManageable just as we do with any other Avalon lifecycle interfaces
(assuming a predefined Component interface
/** * ExtendedComponentImpl, demonstrating the use of a custom * lifecycle stage SecurityManageable. This code does * a simple access check for several files on the file system and logs * the results accordingly. */ public class ExtendedComponentImpl extends AbstractLogEnabled implements ExtendedComponent, SecurityManageable { /** * Pass a SecurityManager object to the component * * @param manager a SecurityManager value */ public void secure( final SecurityManager manager ) throws SecurityException { getLogger().info( "Received SecurityManager instance: " + manager ); final String[] files = { "/tmp", "/vmlinuz", "/usr/lib/libc.a" }; for ( int i = 0; i < files.length; ++i ) { try { manager.checkRead( files[ i ] ); getLogger().info( "Thread can read " + files[ i ] ); } catch ( SecurityException e ) { getLogger().info( "Thread can not read " + files[ i ] ); } } } } Need more information?If you have any particular questions, comments, etc, please send an email to the Avalon developer mailing list . |