Hi All,

        Hope all is well!
        
        Last week I started a thread about easing the implementation
        of custom markers within Avalon. After a bit of R&D I've come up with
        a potential solution for review/discussion.
        
        I've attached the required code changes to this email in a tgz.
        The changes relate to the Fortress source base, and the attached
        archive should untar directly into a fresh Fortress's src dir from
        current CVS.
        
        (I didn't do a diff since there's a reasonable number of new files, and
        I didn't really want to duplicate the Fortress code into scratchpad
        (although I can do this if that would be easier for people), so I hope
        the form of an attachment is ok).
        
        The attached code has been tested with the ContainerProfile
        testcase using some simple components, so some parts may be missing.
        
        At this stage it would be great to get some feedback about it so I/we
        can improve/change/modify/enhance the idea to get to something
        production quality.
        
        To recap on the previous thread - the motivation was to make it easier
        to add a new custom marker to an avalon system without having to
        subclass CM's/CS's, and to be able to define where the new custom
        marker should be checked for during a component's lifetime.

        The basic idea is to introduce a new interface called ComponentMarker:

public interface ComponentMarker
{
        /**
         * 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(Component 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(Component 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(Component 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(Component component, Context context)
                throws Exception;
}

        Component marker defines various methods that can be called on a
        given component during 1 of the 4 phases during that component's
        lifetime. ie:
        
        o instantiation
        o access
        o release
        o decommissioning
        
        These methods are essentially callbacks.
        
        To implement a marker for a particular phase, a concrete class is
        created that implements the above interface, performing some
        operation on the given component in the appropriate method. For
        example, LogEnabled would implemented like this:
        
public class LogEnabledMarkerImpl extends AbstractMarker
{
        /**
         * <code>create</code> method, enables logging 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 create(Component component, Context context)
                throws Exception
        {
                final Configuration config = (Configuration) 
context.get(COMPONENT_CONFIGURATION);
                final String logger = config.getAttribute("logger", null);

                final LoggerManager logManager = (LoggerManager) 
context.get(LOGGER_MANAGER);
                final LogEnabled logEnabled = (LogEnabled) component;

                if (null == logger)
                        logEnabled.enableLogging(logManager.getDefaultLogger());
                else
                        
logEnabled.enableLogging(logManager.getLoggerForCategory(logger));
        }
}

        Since LogEnabled only applies to when the component is being
        instantiated, only the 'create' method needs to be implemented
        (interfaces like Startable require more).
        
        Implementing a custom marker is done in the same way, simply by
        creating a class that implements ComponentMarker, or extends from a
        helper class AbstractComponentMarker (which just provides empty
        implementations of all 4 methods).
        
        All of the available ComponentMarker classes are then listed in a
        configuration file, eg. markers.xml as follows:
        
<?xml version="1.0"?>

<!-- Component Management Configuration

     This configuration file defines the marker interfaces that every component in
     this container will be tested for during the appropriate phases of a components
     life.

     The life of a component is broken down into 4 phases:

     1. creation - when a component is instantiated by a ComponentFactory class.
     2. access   - when a component is accessed by a ComponentHandler class.
     3. release  - when a component is released by a ComponentHandler class.
     4. destroy  - when a component is decomissioned by a ComponentFactory class.

     A component will be created and destroyed only once during it's lifetime, but
     accessed and released potentially many times, depending on the particular type 
     of handler that is responsible for the component.

     For each phase, a configuration section is defined below, which lists the marker
     interfaces which should be tested for and executed in order, on each component
     being used in this system.

     A 'marker' block defines an interface to be tested for by the 'interface' 
attribute,
     and an 'action' class to be executed should the test succeed. 'action' classes
     must implement the ComponentMarker interface, which defines methods that will be
     called at each phase.
 -->
<component-management>

  <create>
    <marker interface="org.apache.avalon.framework.logger.LogEnabled"
            action="org.apache.excalibur.fortress.markers.impl.LogEnabledMarkerImpl"/>
    <marker interface="org.apache.avalon.framework.context.Contextualizable"
            
action="org.apache.excalibur.fortress.markers.impl.ContextualizableMarkerImpl"/>
    <marker interface="org.apache.avalon.framework.component.Composable"
            action="org.apache.excalibur.fortress.markers.impl.ComposableMarkerImpl"/>
    <marker interface="org.apache.avalon.framework.configuration.Configurable"
            
action="org.apache.excalibur.fortress.markers.impl.ConfigurableMarkerImpl"/>
    <marker interface="org.apache.avalon.framework.parameters.Parameterizable"
            
action="org.apache.excalibur.fortress.markers.impl.ParameterizableMarkerImpl"/>
    <marker interface="org.apache.avalon.framework.activity.Initializable"
            
action="org.apache.excalibur.fortress.markers.impl.InitializableMarkerImpl"/>
    <marker interface="org.apache.avalon.framework.activity.Startable"
            action="org.apache.excalibur.fortress.markers.impl.StartableMarkerImpl"/>
  </create>

  <access>
  </access>

  <release>
  </release>

  <destroy>
    <marker interface="org.apache.avalon.framework.activity.Disposable"
            action="org.apache.excalibur.fortress.markers.impl.DisposableMarkerImpl"/>
  </destroy>

</component-management> 
        
        This file is read by/configures a MarkerManager class, which the
        ComponentFactory and ComponentHandlers reference. When a component
        is requested, the appropriate markers are tested for, in order, on the
        particular component and executed if implemented.
        
        Adding a custom marker can be done by adding a new <marker../> tag
        to the above file, or by modifying the above configuration
        programatically at runtime (programatic modification is not yet done).
        
        In the attachment there's also a few changes to the
        ContextBuilder/Manager classes to specify the location of this
        file, and to have a MarkerManager object set on the component handlers.
        
        A few open issues so far:
        
        o Validation that the standard avalon interfaces are not tampered
        with. ie. reordered/removed/etc.
        
        o Performance penalty incurred due to increased use of
        dynamics/reflection. This new code *does* decrease performance
        compared to the previous 'instanceof LogEnabled, instanceof
        Contextualizable' way. There are probably ways to improve
        performance though that need to be investigated.
        
        o Use of context. Currently everything that is needed within a
        ComponentMarker instance needs to be retrieved from the
        supplied Context object.
        
        This means the context has to be populated with all required data
        before components are instantiated (or at least populated with objects
        that can provide that data), unless static methods, etc are used.
        This is though as far as I can tell, only an issue for custom markers.
        
        Ok, hope the explanation wasn't too terse. The code should
        illustrate everything though, feel free to ask any questions, etc.
        
        Would be great to hear what you all think.
        
        Cheers,
        
        Marcus
-- 
        .....
     ,,$$$$$$$$$,      Marcus Crafter
    ;$'      '$$$$:    Computer Systems Engineer
    $:         $$$$:   ManageSoft GmbH
     $       o_)$$$:   82-84 Mainzer Landstrasse
     ;$,    _/\ &&:'   60327 Frankfurt Germany
       '     /( &&&
           \_&&&&'
          &&&&.
    &&&&&&&:

Attachment: markers.tgz
Description: GNU Unix tar archive

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

Reply via email to