Hi All,

        Hope all is well.
        
        Over the past couple of weeks I've been working on providing
        support for adding custom lifecycle extensions to containers.
        
        The basic idea is to provide a standard way for extending the
        component lifecycle, without requiring excessive subclassing of
        handlers, or containers.

        First, to summarize what's previously been discussed:

<summary>

        A few weeks back I sent in a proposal for 'custom markers' which
        sparked several discussions about how to customize/extend the
        lifecycle of a component.
        
        Based on the discussions I examined the following implementation ideas
        (the following examples use Fortress as the base container).
        
        1. Extend lifecycle via a custom component handler/factory:
        
        eg. in the roles file:
        
        <role name="org.apache.avalon.excalibur.xml.Parser">
          <component shorthand="parser"
                class="org.apache.avalon.excalibur.xml.JaxpParser"
                handler="package.MyComponentHandler"/>
        </role>

        This is easy to implement, and doesn't require container
        modification. The component handler acts as a 'sub-container', and
        provides all extensions via the defined
        ComponentHandler/Factory interfaces (get,put,newInstance,etc).
        
        The main problem I find with this approach is that it mixes 2
        concerns -> extensions code, and component handler code. If you want
        to put the same extension code in a different handler, code has to be
        copied and/or a handler has to be subclassed.

        2. Extend lifecycle via a custom container:
        
        eg. (using Fortress's AbstractContainer as a base)
        
        public class MyContainer extends AbstractContainer
        {
                protected ComponentManager getComponentManager()
                {
                        return new MyComponentManager(
                                super.getComponentManager()
                        );
                }

                // My ComponentManager providing extensions
                private class MyComponentManager implements ComponentManager
                {
                        public MyComponentManager(ComponentManager cm)
                        {
                                m_parent = cm;
                        }
                        
                        public Component lookup(String role) throws ...
                        {
                                Component comp = m_parent.lookup(role);
                                
                                // Extensions code here, for example
                                if (comp instanceof SomeInterface)
                                {
                                        ((SomeInterface) comp.someMethod();
                                }
                                if (comp instanceof AnotherInterface)
                                {
                                        ((AnotherInterface) comp.otherMethod();
                                }
                        }
                        
                        public void release(Component component)
                        {
                                ...
                        }
                        
                        ...
                }
        }

        (TIMTOWTDI I'm sure (Perl-speak))

        This approach also doesn't require modification of the container
        foundations (but it is tied to the base container implementation
        used, if any). It also doesn't cover ComponentSelectors, which also
        need to be wrapped.
        
        The main problem I see with this solution is not so much
        at the technical level, but that it promotes a proliferation of
        container subclasses to add lifecycle functionality. The problem
        manifests when a concrete container is already chosen in an existing
        system, like Cocoon for example. Merging container hierarchies
        then becomes a difficult task.

        3. Extend lifecycle by container modification.

        This is where the container is modified to support lifecycle
        extensions via callbacks to an extension class.

        The idea is to be able to get a container and register lifecycle
        extensions with it, which will then be checked for and executed at
        runtime, during the various phases of a components lifetime.

        No container or handler subclassing is required, extensions are
        inserted at compile time or runtime when they are needed. The
        drawback, container modification is obviously necessary. Components
        that use this functionality are also tied to containers that only
        support lifecycle extensions.
        
</summary>

        After several discussions with people on and off the list I
        decided to continue examining the 3rd option.
        
        The basis for this decision came down to the requirement of not
        having to create excessive subclasses of either handlers or
        containers to extend the lifecycle.
        
        What I really wanted to provide was a solution that could be taken
        and used in a system, but one that also allowed that system to be
        extended without having to make architectural changes or change high 
        level classes.
        
        Unfortunately, options 1 and 2 require subclassing so after much
        thought I discounted them.

        I realize that several people on the list feel that container
        modification is not the way to go. I do recognize and have thought
        about this, and would really like to find a way to make both camps
        happy.
        
        It would be great to get feedback about the various approaches to
        come up with an ideal solution that's workable, portable, and usable
        across many containers and application domains. I'm more than
        happy to make changes to make things better. I hope this is the
        right-thing-to-do (TM).

        So, here's a run-down of the current solution:
        
        The aim is to make it possible to extend a component's lifecycle
        in the following areas if its lifetime:
        
        1. access               (when its accessed via lookup())
        2. release              (when its released via release())
        3. creation             (when the component is instantiated)
        4. destruction          (when the component is decomissioned)
        
        This allows one to create extensions that are executed on a
        component once when the component is actually instantiated or
        disposed, ready for GC - or potentially multiple times when a
        component is accessed and released.
        
        The following LifecycleExtension interface is defined:
        
/**
 * <code>LifecycleExtension</code> interface. This interface defines the methods that
 * a <code>LifecycleExtensionManager</code> can call on a particular concrete
 * <code>LifecycleExtensionMarker</code> class.
 *
 * @author <a href="mailto:[EMAIL PROTECTED]";>Marcus Crafter</a>
 * @version CVS $Revision: 1.7 $ $Date: 2002/05/13 12:17:39 $
 */
public interface LifecycleExtension
{
    /**
     * Create, called when the given component is being 
     * instantiated.
     *
     * @param component a <code>Component</code> instance
     * @param context a <code>Context</code> instance
     * @exception Exception if an error occurs
     */
    void create( Object component, Context context )
        throws Exception;

    /**
     * Destroy, called when the given component is being
     * decomissioned.
     *
     * @param component a <code>Component</code> instance
     * @param context a <code>Context</code> 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 <code>Component</code> instance
     * @param context a <code>Context</code> 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 <code>Component</code> instance
     * @param context a <code>Context</code> instance
     * @exception Exception if an error occurs
     */
    void release( Object component, Context context )
        throws Exception;
}

        where each method reflects an extension to a particular phase of a
        components lifecycle.
        
        Concrete extension classes then implement the methods they see fit,
        and are then registered with a LifecyceExtensionManager class,
        which the containers component manager calls upon at the appropriate
        times in a components life.
        
        More than one extension object can be registered with the extension
        manager, and more than one lifecycle extension can be performed by
        any particular extension object as well.
        
        The container context is also passed to each extension method to
        allow the developer to pass other objects around that may
        influence how the particular extension works.
        
        The LifecycleExtensionManager class looks like:
        
/**
 * <code>LifecycleExtensionManager</code> class. This class manages lists
 * of extensions objects that are executed on components during the various
 * stages of their lifecycles.
 *
 * <p>
 * It provides 4 methods for adding extension objects to the system,
 * and 4 methods for executing them on a particular component object. The
 * current context is also passed in to the extension objects to facilitate
 * the communication of any global values.
 * </p>
 *
 * @author <a href="mailto:[EMAIL PROTECTED]";>Marcus Crafter</a>
 * @version CVS $Revision: 1.7 $ $Date: 2002/05/13 12:17:39 $
 */
public class LifecycleExtensionManager
    extends AbstractLifecycleExtensionManager
{
    // extensions objects
    private final List m_accessExtensions = new ArrayList();
    ...
    
    /**
     * <code>executeAccessExtensions</code> method, executes all <i>access</i>
     * level extensions on the given component.
     *
     * @param component a <code>Component</code> instance
     * @param context a <code>Context</code> instance
     * @exception Exception if an error occurs
     */
    public void executeAccessExtensions( Object component, Context context )
        throws Exception
    {
        executeExtensions( m_accessExtensions.toArray(), component, context, ACCESS );
    }

    /**
     * Obtains the access level lifecycle extension this manager manages.
     *
     * @return a <code>List</code> of extensions
     */
    public List getAccessLifecycleExtensions()
    {
        return m_accessExtensions;
    }

    ...
}

        (I've just shown the 'access' methods, there's also methods for
        each of the other phases in a components lifetime).
        
        This class is then used FortressComponent/ServiceManager as follows:

/**
 * This is the Default ServiceManager for the Container.  It provides
 * a very simple abstraction, and makes it easy for the Container to manage
 * the references.
 *
 * @author <a href="mailto:[EMAIL PROTECTED]";>Berin Loritsch</a>
 * @version CVS $Revision: 1.3 $ $Date: 2002/07/09 10:40:26 $
 */
public class FortressServiceManager implements ServiceManager
{
    ...
    
    public Object lookup( String role )
        throws ServiceException
    {
        ...
        
        try
        {
            if( !handler.isInitialized() )
            {
                handler.initialize();
            }

            component = handler.get();

>>>         m_extManager.executeAccessExtensions( component, m_context );
        }
        
        ...

        return component;
    }

    public void release( Object component )
    {
        final ComponentHandler handler;

        try
        {
>>>         m_extManager.executeReleaseExtensions( component, m_context );
        }
        
        ...
    }
}

        and also similarly in the ComponentFactory class.

        Here's an example that implements a SecurityManageable extension:

        First the lifecycle extension interface:

/**
 * Simple custom lifecycle extension interface for supplying a component
 * with a security manager.
 *
 * @author <a href="mailto:[EMAIL PROTECTED]";>Marcus Crafter</a>
 * @version CVS $Revision: 1.7 $ $Date: 2002/05/13 12:17:39 $
 */
public interface SecurityManageable
{
    /**
     * Pass a SecurityManager object to the component
     *
     * @param manager a <code>SecurityManager</code> value
     */
    void secure( SecurityManager manager )
        throws SecurityException;
}

        Then the extensions class that is registered with the container.
        
/**
 * Some custom extensions for this container's components.
 *
 * @author <a href="mailto:[EMAIL PROTECTED]";>Marcus Crafter</a>
 * @version CVS $Revision: 1.7 $ $Date: 2002/05/13 12:17:39 $
 */
public class Extensions
    extends AbstractLifecycleExtension
{
    /**
     * Access, called when the given component is being
     * accessed (ie. via lookup() or select()).
     *
     * @param component a <code>Component</code> instance
     * @param context a <code>Context</code> 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 for testing, a real
            // system might want to pass in specialized/custom security managers
            ( ( SecurityManageable ) component ).secure( new SecurityManager() );
        }
    }
}

        And an example component that uses it:
        
/**
 * <code>TestComponentImpl</code>, demonstrating the use of a custom
 * lifecycle stage <code>SecurityManageable</code>. This code does
 * a simple access check for several files on the file system and logs
 * the results accordingly.
 *
 * @author <a href="mailto:[EMAIL PROTECTED]";>Marcus Crafter</a>
 * @version CVS $Revision: 1.7 $ $Date: 2002/05/13 12:17:39 $
 */
public class ExtendedComponentImpl
    extends AbstractLogEnabled
    implements ExtendedComponent, SecurityManageable
{
    /**
     * Pass a SecurityManager object to the component
     *
     * @param manager a <code>SecurityManager</code> value
     */
    public void secure( final SecurityManager manager )
        throws SecurityException
    {
        getLogger().debug( "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( "Object can read " + files[ i ] );
            }

            catch ( SecurityException e )
            {
                getLogger().info( "Object can not read " + files[ i ] );
            }
        }
    }
}

        Simple example, but I hope you get the idea.

        I'm in the process of checking the code into Fortress, and will also
        be checking in the above example into the
        jakarta-avalon-excalibur/fortress/examples directory.
        
        Once the code is checked in, I'm more than open to suggestions, and
        feedback, so please feel free to fire away with any questions
        (hopefully I or someone else can answer them all!) :)
        
        Finally a special thanks to Berin and Leo Simons for their help! :)
        
        Cheers,
        
        Marcus
        
-- 
        .....
     ,,$$$$$$$$$,      Marcus Crafter
    ;$'      '$$$$:    Computer Systems Engineer
    $:         $$$$:   ManageSoft GmbH
     $       o_)$$$:   82-84 Mainzer Landstrasse
     ;$,    _/\ &&:'   60327 Frankfurt Germany
       '     /( &&&
           \_&&&&'
          &&&&.
    &&&&&&&:

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to