avalon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Anton Tagunov <atagu...@mail.cnt.ru>
Subject [Patch] #10
Date Mon, 26 May 2003 12:19:22 GMT
Hello, Berin!

This is patch that provides one possible solution
of the problems related to the LifecycleExtensionManager.

1)

It adds makeReadOnly() and copyFrom( LifecycleExtensionManager )
to LifeCycleExtensionManager and copies the LifecycleExtensionManager
inside AbstractContainer instead of modifying the passed-in one.

This is the part of the patch that I like.

2)

Note that the patch should be applied on top of [Patch] #9)

3)

The patch also passes the newly created LifecycleExtensionManager
to child components. This is the part of the patch that
I do not like that much.
It is not elegant.
But the best I could think of.

And besides we _need_ to provide a way to create new containers
derived from AbstractContainer and these containers may wish
to modify the context passed to children. So probably we need
to adopt the machinery proposed in this patch anyway.

The machinery is quite flexible: if the component wants the
new context _not_ to be child of its own, it can do it.

If it _wants_ it to be it also can do it.

As for the LifecycleExtensionManager the question remains open
for me wether we should pass the newly created ExtensionManager
to our child components. _IF_ we should, this patch does this.

My best regards, Anton
:-)

diff -rU12 src.0/org/apache/avalon/fortress/impl/AbstractContainer.java src/org/apache/avalon/fortress/impl/AbstractContainer.java
--- src.0/org/apache/avalon/fortress/impl/AbstractContainer.java        2003-05-26 13:06:02.000000000
+0400
+++ src/org/apache/avalon/fortress/impl/AbstractContainer.java  2003-05-26 16:11:58.000000000
+0400
@@ -128,24 +128,31 @@
     /**
      * Contains entries mapping roles to hint maps, where the hint map contains
      * mappings from hints to ComponentHandlers.
      */
     protected Map m_mapper = new StaticBucketMap();
     /** Contains an entry for each ComponentHandler */
     protected List m_components = new ArrayList( 10 );
 
     protected List m_shutDownOrder;
 
     private ProxyManager m_proxyManager = new ProxyManager(true);
 
+    /** track whether we have created a new extensions manager or have used one
+     *  supplied in our context. */
+    private   boolean m_extManagerNew = false;
+    /** the context manager passed to our children. Override the
+     *  getChildContext() method to change its value. */
+    private   Context m_childContext;
+
     /**
      * Pull the manager items from the context so we can use them to set up
      * the system.
      *
      * @avalon.context type="ClassLoader" optional="true"
      * @avalon.context type="LoggerManager"
      * @avalon.context type="PoolManager"
      * @avalon.context type="InstrumentManager"
      * @avalon.context type="MetaInfoManager"
      * @avalon.context type="LifecycleExtensionManager" optional="true"
      * @avalon.context type="Queue" optional="true"
      *
@@ -189,67 +196,80 @@
     /**
      * Add an InstrumentableCreator to ExtensionsManager
      * creating our private ExtensionsManager if one has not been provided.
      * Note that our child components will never see this private ExtensionsManager
      * as it won't be put into the context passed to their contextualize().
      */
     protected void setupExtensionManager( final Context context )
     {
         final Logger eLogger = m_loggerManager.getLoggerForCategory( "system.extensions"
);
 
         try
         {
-            m_extManager =
+            final LifecycleExtensionManager extManager =
                 (LifecycleExtensionManager) context.get( LifecycleExtensionManager.ROLE );
 
             if ( eLogger.isDebugEnabled() ) 
             { 
                 eLogger.debug( LifecycleExtensionManager.ROLE + "found" ); 
             }
 
             /* Add all the standard extensions if they have not been already been added */
             boolean isInstrumentEnabled = false;
 
-            final Iterator it = m_extManager.creatorExtensionsIterator();
+            final Iterator it = extManager.creatorExtensionsIterator();
             while ( it.hasNext() )
             {
                 if ( it.next() instanceof InstrumentableCreator )
                 {
                     isInstrumentEnabled = true;
                 }
             }
         
-            if ( !isInstrumentEnabled )
+            if ( isInstrumentEnabled )
+            {
+                // ok, use this LifecycleExtensionManager instance
+                m_extManager = extManager;
+            }
+            else
             {
                 if ( eLogger.isDebugEnabled() )
                 {
-                    eLogger.debug( "No " + InstrumentableCreator.class.getName() + 
-                        " yet, adding a new one" );
+                    final String message =  "No " + InstrumentableCreator.class.getName()
+ 
+                        " yet, creating a copy of "+LifecycleExtensionManager.ROLE +
+                        ", adding a new " + InstrumentableCreator.class.getName() +
+                        " to it";
+                    eLogger.debug( message );
                 }
+                m_extManager = extManager.writableCopy();
                 m_extManager.addCreatorExtension( new InstrumentableCreator( m_instrumentManager
) );
+                m_extManager.makeReadOnly();
+                m_extManagerNew = true;
             }
         }
         catch( ContextException ce )
         {
             if ( eLogger.isDebugEnabled() )
             {
                 final String message =
                     "No " + LifecycleExtensionManager.ROLE + " is given, " +
                     "installing a default lifecycle extension manager with " +
                     "1 extension";
                 eLogger.debug( message );
             }
             m_extManager = new LifecycleExtensionManager();
             m_extManager.enableLogging( eLogger );
             m_extManager.addCreatorExtension( new InstrumentableCreator( m_instrumentManager
) );
+            m_extManager.makeReadOnly();
+            m_extManagerNew = true;
         }
     }
 
     /**
      * Root ServiceManager.  The Container may choose to have it's
      * ServiceManager delegate to the root manager, or it may choose to be
      * entirely self contained.
      *
      * @param serviceManager the service manager to apply to the impl
      * @throws ServiceException is a servicing related error occurs
      *
      */
@@ -415,28 +435,31 @@
     /**
      * Create an objectFactory for specified Object configuration.
      *
      * @param classname the classname of object
      * @param configuration the objests configuration
      * @return the ObjectFactory
      * @throws ClassNotFoundException if the specified class does not exist
      */
     protected ObjectFactory createObjectFactory( final String classname,
                                                  final Configuration configuration )
         throws Exception
     {
+        // invoke this for when the first component is being added
+        if ( m_childContext == null ) m_childContext = getChildContext();
+
         final Class clazz = m_classLoader.loadClass( classname );
         final ComponentFactory componentFactory =
             new ComponentFactory( clazz, configuration,
-                m_serviceManager, m_context,
+                m_serviceManager, m_childContext,
                 m_loggerManager, m_extManager );
         return m_proxyManager.getWrappedObjectFactory( componentFactory );
     }
 
     /**
      * This is the method that the ContainerComponentManager and Selector use
      * to gain access to the ComponentHandlers and ComponentSelectors.  The
      * actual access of the ComponentHandler is delegated to the Container.
      *
      * @param  role  The role we intend to access a Component for.
      * @param  hint  The hint that we use as a qualifier
      *         (note: if null, the default implementation is returned).
@@ -719,13 +742,36 @@
      * Exposes to subclasses the service manager which this impl
      * uses to manage its child components.
      * The returned ServiceManager <i>is</i> aware of the services passed
      * in to <i>this</i> impl, and services that were passed in through
      * service() are hence available to subclasses.
      *
      * @return the service manager that contains the child components.
      */
     protected ServiceManager getServiceManager()
     {
         return m_serviceManager;
     }
+
+    /**
+     *  Create a context for our child components. Derived containers may
+     *  override this method to their preference. Invoked once when the
+     *  first component is added.
+     *  The type of returned object will be dynamically tested against
+     *  DefaultContext. In case of a match makeReadOnly will be called.
+     *  Otherwise the context will be wrapped into a read-only DefaultContext.
+     */
+    protected Context getChildContext()
+    {
+        if ( m_extManagerNew )
+        {
+            DefaultContext context = new DefaultContext( m_context );
+            /* we have created a new extensions manager */
+            context.put( LifecycleExtensionManager.ROLE, m_extManager );
+            return context;
+        }
+        else
+        {
+            return m_context;
+        }
+    }
 }
diff -rU12 src.0/org/apache/avalon/fortress/util/LifecycleExtensionManager.java src/org/apache/avalon/fortress/util/LifecycleExtensionManager.java
--- src.0/org/apache/avalon/fortress/util/LifecycleExtensionManager.java        2003-04-22
16:37:10.000000000 +0400
+++ src/org/apache/avalon/fortress/util/LifecycleExtensionManager.java  2003-05-26 16:08:32.000000000
+0400
@@ -80,24 +80,42 @@
  * @author <a href="mailto:crafterm@apache.org">Marcus Crafter</a>
  * @version CVS $Revision: 1.8 $ $Date: 2003/04/22 12:37:09 $
  */
 public final class LifecycleExtensionManager
     extends AbstractLogEnabled
 {
     public static final String ROLE = LifecycleExtensionManager.class.getName();
 
     // extensions objects
     private final CachedArrayList m_accessorExtensions = new CachedArrayList();
     private final CachedArrayList m_creatorExtensions = new CachedArrayList();
 
+    private boolean m_readOnly = false;
+
+    public void makeReadOnly()
+    {
+        m_readOnly = true;
+    }
+
+    /**
+     * Create a copy; it will be writable even if the original was not.
+     */
+    public LifecycleExtensionManager writableCopy()
+    {
+        final LifecycleExtensionManager copy = new LifecycleExtensionManager();
+        copy.m_accessorExtensions.copyFrom( m_accessorExtensions );
+        copy.m_creatorExtensions.copyFrom( m_creatorExtensions );
+        return copy;
+    }
+    
     /**
      * <code>executeAccessExtensions</code> method, executes all access
      * 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( final Object component, final Context context )
         throws Exception
     {
         executeExtensions( m_accessorExtensions.toArray(), component, context, ACCESS );
@@ -159,78 +177,84 @@
     //    execute operation)
     // 4. The book 'Refactoring' says we shouldn't do it :-)
     //
     // I'm open to suggestions though if there's any better ideas ?
 
     /**
      * Adds an accessor extension to the manager
      *
      * @param extension a <code>Accessor</code> instance
      */
     public void addAccessorExtension( final Accessor extension )
     {
+        checkWriteable();
         m_accessorExtensions.add( extension );
     }
 
     /**
      * Adds a creator extension to the manager
      *
      * @param extension a <code>Creator</code> instance
      */
     public void addCreatorExtension( final Creator extension )
     {
+        checkWriteable();
         m_creatorExtensions.add( extension );
     }
 
     /**
      * Inserts an accessor extension at a given index in the manager
      *
      * @param position an <code>int</code> index value
      * @param extension a <code>Accessor</code> instance
      */
     public void insertAccessorExtension( final int position, final Accessor extension )
     {
+        checkWriteable();
         m_accessorExtensions.insert( position, extension );
     }
 
     /**
      * Inserts a creator extension at a given index in the manager
      *
      * @param position an <code>int</code> index value
      * @param extension a <code>Creator</code> instance
      */
     public void insertCreatorExtension( final int position, final Creator extension )
     {
+        checkWriteable();
         m_creatorExtensions.insert( position, extension );
     }
 
     /**
      * Removes a particular accessor extension from the manager
      *
      * @param position an <code>int</code> index value
      * @return a <code>Accessor</code> instance
      */
     public Accessor removeAccessorExtension( final int position )
     {
+        checkWriteable();
         return (Accessor) m_accessorExtensions.remove( position );
     }
 
     /**
      * Removes a particular creator extension from the manager
      *
      * @param position an <code>int</code> index value
      * @return a <code>Creator</code> instance
      */
     public Creator removeCreatorExtension( final int position )
     {
+        checkWriteable();
         return (Creator) m_creatorExtensions.remove( position );
     }
 
     /**
      * Obtain an iterator.
      *
      * @return an <code>Iterator</code> instance
      */
     public Iterator accessorExtensionsIterator()
     {
         return m_accessorExtensions.iterator();
     }
@@ -283,32 +307,34 @@
      * @return a <code>Creator</code> instance
      */
     public Creator getCreatorExtension( final int index )
     {
         return (Creator) m_creatorExtensions.get( index );
     }
 
     /**
      * Clears all accessor extensions registered with this manager
      */
     public void clearAccessorExtensions()
     {
+        checkWriteable();
         m_accessorExtensions.clear();
     }
 
     /**
      * Clears all creator extensions registered with this manager
      */
     public void clearCreatorExtensions()
     {
+        checkWriteable();
         m_creatorExtensions.clear();
     }
 
     // Lifecycle method constants, these are passed to executeExtensions()
     protected static final int ACCESS = 0;
     protected static final int RELEASE = 1;
     protected static final int CREATE = 2;
     protected static final int DESTROY = 3;
 
     /**
      * <code>executeExtensions</code> method, executes a given array of
      * lifecycle interfaces on a given component.
@@ -358,24 +384,40 @@
 
             default:
                 if ( getLogger().isErrorEnabled() )
                 {
                     final String message =
                         "Incorrect extension phase specified: " + type;
                     getLogger().error( message );
                 }
         }
     }
 
     /**
+     * Utility method to check if LifecycleExtensionsManager
+     * is writeable and if not throw exception.
+     *
+     * @throws IllegalStateException if context is read only
+     */
+    protected final void checkWriteable()
+        throws IllegalStateException
+    {
+        if( m_readOnly )
+        {
+            final String message =
+                "LifecycleExtensionsManager is read only and can not be modified";
+            throw new IllegalStateException( message );
+        }
+    }
+    /**
      * <code>CachedArrayList</code> class.
      *
      * <p>
      * This class wraps a synchronized ArrayList to provide an optimized
      * <code>toArray()</code> method that returns an internally cached array,
      * rather than a new array generated per <code>toArray()</code>
      * invocation.
      * </p>
      *
      * <p>
      * Use of the class by the Manager results in <code>toArray()</code>
      * being invoked far more often than any other method. Caching the value
@@ -395,24 +437,34 @@
     private final class CachedArrayList
     {
         // Empty array constant
         private final Object[] EMPTY_ARRAY = new Object[0];
 
         // Actual list for storing elements
         private final List m_proxy = Collections.synchronizedList( new ArrayList() );
 
         // Proxy cache, saves unnecessary conversions from List to Array
         private Object[] m_cache = EMPTY_ARRAY;
 
         /**
+         *  Become a copy of another CachedArrayList.
+         */
+        public void copyFrom( final CachedArrayList original )
+        {
+            m_proxy.clear();
+            m_proxy.addAll( original.m_proxy );
+            m_cache = original.m_cache; // it won't mutate anyway :-)
+        }
+
+        /**
          * Add an object to the list
          *
          * @param object an <code>Object</code> value
          */
         public void add( final Object object )
         {
             m_proxy.add( object );
             m_cache = m_proxy.toArray();
         }
 
         /**
          * Insert an object into a particular position in the list
@@ -431,31 +483,32 @@
          *
          * @param position an <code>int</code> value
          * @return a <code>Object</code> value
          */
         public Object remove( final int position )
         {
             final Object object = m_proxy.remove( position );
             m_cache = m_proxy.toArray();
             return object;
         }
 
         /**
-         * Obtain an iterator
+         * Obtain an iterator. This iterator is read-only.
          *
          * @return an <code>Iterator</code> value
          */
         public Iterator iterator()
         {
-            return m_proxy.iterator();
+            final Iterator base = m_proxy.iterator();
+            return new UnmodifiableIterator( base );
         }
 
         /**
          * Obtain the size of the list
          *
          * @return an <code>int</code> value
          */
         public int size()
         {
             return m_proxy.size();
         }
 
@@ -493,13 +546,35 @@
         /**
          * Obtain the list as an array. Subsequents calls to this method
          * will return the same array object, until a write operation is
          * performed on the list.
          *
          * @return an <code>Object[]</code> value
          */
         public Object[] toArray()
         {
             return m_cache;
         }
     }
+
+    private static class UnmodifiableIterator implements Iterator
+    {
+        private final Iterator m_base;
+        UnmodifiableIterator( final Iterator base )
+        {
+            if ( base == null ) throw new NullPointerException( "base can not be null" );
+            m_base = base;
+        }
+        public boolean hasNext()
+        {
+            return m_base.hasNext();
+        }
+        public Object next()
+        {
+            return m_base.next();
+        }
+        public void remove()
+        {
+            throw new IllegalStateException( "Unmodifiable iterator" );
+        }
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@avalon.apache.org
For additional commands, e-mail: dev-help@avalon.apache.org


Mime
View raw message