Author: cziegeler
Date: Tue Oct 19 12:10:38 2004
New Revision: 55087

Added:
   cocoon/whiteboard/ecmplus/src/java/
   cocoon/whiteboard/ecmplus/src/java/org/
   cocoon/whiteboard/ecmplus/src/java/org/apache/
   cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/
   cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/
   cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/
   
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/AbstractComponentHandler.java
   
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/CocoonServiceManager.java
   
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/CocoonServiceSelector.java
   
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/ComponentFactory.java
   
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/ComponentsSelector.java
   
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/PoolableComponentHandler.java
   
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/RoleManager.java
   
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/SingleThreadedComponentHandler.java
   
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/ThreadSafeComponentHandler.java
Log:
First version of ECM++

Added: 
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/AbstractComponentHandler.java
==============================================================================
--- (empty file)
+++ 
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/AbstractComponentHandler.java
     Tue Oct 19 12:10:38 2004
@@ -0,0 +1,242 @@
+/* 
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed  under the  Apache License,  Version 2.0  (the "License");
+ * you may not use  this file  except in  compliance with the License.
+ * You may obtain a copy of the License at 
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed  under the  License is distributed on an "AS IS" BASIS,
+ * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
+ * implied.
+ * 
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.container;
+
+import org.apache.avalon.excalibur.logger.LoggerManager;
+import org.apache.avalon.excalibur.pool.Poolable;
+import org.apache.avalon.framework.component.Composable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.thread.SingleThreaded;
+import org.apache.avalon.framework.thread.ThreadSafe;
+
+/**
+ * This class acts like a Factory to instantiate the correct version
+ * of the component handler that you need.
+ *
+ * @version CVS $Revision: 1.6 $ $Date: 2004/02/28 11:47:14 $
+ */
+public abstract class AbstractComponentHandler {
+    
+    private final Object referenceSemaphore = new Object();
+    private int references = 0;
+
+    protected final Logger logger;
+    
+    /** This factory is used to created new objects */
+    protected final ComponentFactory factory;
+    
+    /** State management boolean stating whether the Handler is disposed or 
not */
+    protected boolean disposed = false;
+
+    /** State management boolean stating whether the Handler is initialized or 
not */
+    protected boolean initialized = false;
+
+    /**
+     * Looks up and returns a component handler for a given component class.
+     *
+     * @param role           The role name of the component. This must be
+     *                       a fully-qualified classname
+     * @param componentClass Class of the component for which the handle is
+     *                       being requested.
+     * @param configuration The configuration for this component.
+     * @param serviceManager The service manager which will be managing the 
service.
+     * @param context The current context object.
+     * @param logger  The current logger
+     * @param loggerManager The current LoggerManager.
+     *
+     * @throws Exception If there were any problems obtaining a 
ComponentHandler
+     */
+    public static AbstractComponentHandler getComponentHandler( final String 
role,
+                                                        final Class 
componentClass,
+                                                        final Configuration 
configuration,
+                                                        final ServiceManager 
serviceManager,
+                                                        final Context context,
+                                                        final Logger logger,
+                                                        final LoggerManager 
loggerManager,
+                                                        final RoleManager 
roleManager)
+    throws Exception {
+        int numInterfaces = 0;
+
+        if( SingleThreaded.class.isAssignableFrom( componentClass ) ) {
+            numInterfaces++;
+        }
+
+        if( ThreadSafe.class.isAssignableFrom( componentClass ) ) {
+            numInterfaces++;
+        }
+
+        if( Poolable.class.isAssignableFrom( componentClass ) ) {
+            numInterfaces++;
+        }
+
+        if( numInterfaces > 1 ) {
+            throw new Exception( "[CONFLICT] More than one lifecycle interface 
in "
+                                 + componentClass.getName() + "  May implement 
no more than one of "
+                                 + "SingleThreaded, ThreadSafe, or Poolable" );
+        }
+
+        // Early check for Composable
+        if ( Composable.class.isAssignableFrom( componentClass ) ) {
+            throw new Exception("Interface Composable is not supported 
anymore. Please change class "
+                                + componentClass.getName() + " to use 
Serviceable instead.");
+        }
+        
+        // Create the factory to use to create the instances of the Component.
+        ComponentFactory factory = new ComponentFactory( role,
+                                         componentClass,
+                                         configuration,
+                                         serviceManager,
+                                         context,
+                                         logger,
+                                         loggerManager,
+                                         roleManager);
+
+        AbstractComponentHandler handler;
+        
+        if( Poolable.class.isAssignableFrom( componentClass ) )  {
+            handler = new PoolableComponentHandler( logger, factory, 
configuration );
+        } else if( ThreadSafe.class.isAssignableFrom( componentClass ) ) {
+            handler = new ThreadSafeComponentHandler( logger, factory );
+        } else {
+            // This is a SingleThreaded component
+            handler = new SingleThreadedComponentHandler( logger, factory );
+        }
+
+        return handler;
+    }
+
+    /**
+     * Creates a new ComponentHandler.
+     */
+    public AbstractComponentHandler(Logger logger, ComponentFactory factory) {
+        this.logger = logger;
+        this.factory = factory;
+    }
+
+    /**
+     * Get an instance of the type of component handled by this handler.
+     * <p>
+     * Subclasses should not extend this method but rather the doGet method 
below otherwise
+     *  reference counts will not be supported.
+     * <p>
+     *
+     * @return an instance
+     * @exception Exception if an error occurs
+     */
+    public final Object get() throws Exception {
+        if( !this.initialized ) {
+            throw new IllegalStateException(
+                "You cannot get a component from an uninitialized handler." );
+        }
+        if( this.disposed ) {
+            throw new IllegalStateException( "You cannot get a component from 
a disposed handler." );
+        }
+
+        final Object component = this.doGet();
+
+        synchronized( this.referenceSemaphore ) {
+            this.references++;
+        }
+
+        return component;
+    }
+
+    /**
+     * Put back an instance of the type of component handled by this handler.
+     * <p>
+     * Subclasses should not extend this method but rather the doPut method 
below otherwise
+     *  reference counts will not be supported.
+     * <p>
+     *
+     * @param component a service
+     * @exception Exception if an error occurs
+     */
+    public final void put( Object component ) 
+    throws Exception {
+        if( !this.initialized ) {
+            throw new IllegalStateException(
+                "You cannot put a component to an uninitialized handler." );
+        }
+        //  The reference count must be decremented before any calls to doPut.
+        //  If there is another thread blocking, then this thread could stay 
deep inside
+        //  doPut for an undetermined amount of time until the thread 
scheduler gives it
+        //  some cycles again.  (It happened).  All ComponentHandler state 
must therefor
+        //  reflect the thread having left this method before the call to 
doPut to avoid
+        //  warning messages from the dispose() cycle if that takes place 
before this
+        //  thread has a chance to continue.
+        synchronized( this.referenceSemaphore ) {
+            this.references--;
+        }
+
+        try {
+            this.doPut( component );
+        } catch( Throwable t ) {
+            this.logger.error("Exception during putting back a component.", t);
+        }
+    }
+
+    /**
+     * Concrete implementation of getting a component.
+     *
+     * @return a service
+     * @exception Exception if an error occurs
+     */
+    protected abstract Object doGet() throws Exception;
+
+    /**
+     * Concrete implementation of putting back a component.
+     *
+     * @param component a <code>Component</code> value
+     * @exception Exception if an error occurs
+     */
+    protected abstract void doPut( Object component ) throws Exception;
+
+    /**
+     * Returns <code>true</code> if this component handler can safely be
+     * disposed (i.e. none of the components it is handling are still
+     * being used).
+     *
+     * @return <code>true</code> if this component handler can safely be
+     *         disposed; <code>false</code> otherwise
+     */
+    public final boolean canBeDisposed() {
+        return ( this.references == 0 );
+    }
+    
+    /**
+     * Dispose of the component handler and any associated Pools and Factories.
+     */
+    public void dispose() {
+        this.disposed = true;
+    }
+    
+    /**
+     * Initialize this handler
+     */
+    public void initialize() throws Exception {
+        if( this.initialized ) {
+            return;
+        }
+        this.initialized = true;
+        if( this.logger.isDebugEnabled() ) {
+            this.logger.debug( "ThreadSafeComponentHandler initialized for: " 
+ this.factory.getCreatedClass().getName() );
+        }
+    }
+}

Added: 
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/CocoonServiceManager.java
==============================================================================
--- (empty file)
+++ 
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/CocoonServiceManager.java
 Tue Oct 19 12:10:38 2004
@@ -0,0 +1,594 @@
+/* 
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed  under the  Apache License,  Version 2.0  (the "License");
+ * you may not use  this file  except in  compliance with the License.
+ * You may obtain a copy of the License at 
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed  under the  License is distributed on an "AS IS" BASIS,
+ * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
+ * implied.
+ * 
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.container;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.avalon.excalibur.logger.LoggerManager;
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.activity.Initializable;
+import org.apache.avalon.framework.configuration.Configurable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.configuration.DefaultConfiguration;
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.components.SitemapConfigurable;
+import org.apache.cocoon.components.SitemapConfigurationHolder;
+
+/**
+ * Default component manager for Cocoon's components.
+ *
+ * @version CVS $Revision: 1.6 $ $Date: 2004/02/28 11:47:14 $
+ */
+public class CocoonServiceManager
+    extends AbstractLogEnabled
+    implements ServiceManager,
+    Configurable,
+    Contextualizable,
+    Initializable,
+    Disposable {
+    
+    /** The parent ComponentLocator */
+    private final ServiceManager parentManager;
+
+    /** The classloader used for this system. */
+    private final ClassLoader loader;
+
+    /** The application context for components */
+    private Context context;
+
+    /** Static component mapping handlers. */
+    private final Map componentMapping = Collections.synchronizedMap(new 
HashMap());
+
+    /** Used to map roles to ComponentHandlers. */
+    private final Map componentHandlers = Collections.synchronizedMap(new 
HashMap());
+
+    /** added component handlers before initialization to maintain
+     *  the order of initialization
+     */
+    private final List newComponentHandlers = new ArrayList();
+
+    /** RoleInfos. */
+    private RoleManager roleManager;
+
+    /** LogKitManager. */
+    private LoggerManager loggerManager;
+
+    /** Is the Manager disposed or not? */
+    private boolean disposed;
+
+    /** Is the Manager initialized? */
+    private boolean initialized;
+
+    /** The [EMAIL PROTECTED] SitemapConfigurationHolder}s */
+    private Map sitemapConfigurationHolders = new HashMap(15);
+
+    /** Temporary list of parent-aware components.  Will be null for most of
+     * our lifecycle. */
+    private ArrayList parentAwareComponents = new ArrayList();
+
+    /** Create the ComponentLocator with a Classloader and parent 
ComponentLocator */
+    public CocoonServiceManager( final ServiceManager parent, 
+                                 final ClassLoader loader ) {
+        if( null == loader ) {
+            this.loader = Thread.currentThread().getContextClassLoader();
+        } else {
+            this.loader = loader;
+        }
+
+        this.parentManager = parent;
+    }
+
+    /* (non-Javadoc)
+     * @see 
org.apache.avalon.framework.service.ServiceManager#lookup(java.lang.String)
+     */
+    public Object lookup( final String role )
+    throws ServiceException {
+        if( !this.initialized ) {
+            if( this.getLogger().isWarnEnabled() ) {
+                this.getLogger().warn(
+                    "Looking up component on an uninitialized 
CocoonServiceManager [" + role + "]" );
+            }
+        }
+
+        if( this.disposed ) {
+            throw new IllegalStateException(
+                "You cannot lookup components on a disposed 
CocoonServiceManager" );
+        }
+
+        if( role == null ) {
+            final String message =
+                "CocoonServiceManager attempted to retrieve service with null 
role.";
+
+            if( this.getLogger().isErrorEnabled() ) {
+                this.getLogger().error( message );
+            }
+            throw new ServiceException( role, message );
+        }
+
+        AbstractComponentHandler handler = 
(AbstractComponentHandler)this.componentHandlers.get( role );
+
+        // Retrieve the instance of the requested component
+        if ( handler == null )
+        {
+            if( this.parentManager != null ) {
+                try {
+                    return this.parentManager.lookup( role );
+                } catch( Exception e ) {
+                    if( this.getLogger().isWarnEnabled() ) {
+                        final String message =
+                            "ComponentLocator exception from parent CM during 
lookup.";
+                        this.getLogger().warn( message, e );
+                    }
+                    // ignore.  If the exception is thrown, we try to
+                    // create the component next
+                }
+            }
+
+            if( this.roleManager != null ) {
+                final String className = 
this.roleManager.getDefaultClassNameForRole( role );
+
+                if( className != null ) {
+                    if( this.getLogger().isDebugEnabled() ) {
+                        this.getLogger().debug( "Could not find 
ComponentHandler, attempting to create "
+                            + "one for role [" + role + "]" );
+                    }
+
+                    try {
+                        final Class componentClass = this.loader.loadClass( 
className );
+
+                        final Configuration configuration = new 
DefaultConfiguration( "", "-" );
+
+                        handler = 
AbstractComponentHandler.getComponentHandler( role,
+                                                       componentClass,
+                                                       configuration,
+                                                       this,
+                                                       context,
+                                                       this.getLogger(),
+                                                       this.loggerManager,
+                                                       this.roleManager);
+
+                        handler.initialize();
+                    } catch (ServiceException se) {
+                        throw se;
+                    } catch( final Exception e ) {
+                        final String message = "Could not find component";
+                        if( this.getLogger().isDebugEnabled() ) {
+                            this.getLogger().debug( message + " for role: " + 
role, e );
+                        }
+                        throw new ServiceException( role, message, e );
+                    }
+
+                    this.componentHandlers.put( role, handler );
+                }
+            } else {
+                this.getLogger().debug( "Component requested without a 
RoleManager set.\n"
+                    + "That means setRoleManager() was not called during 
initialization." );
+            }
+        }
+
+        if( handler == null ) {
+            final String message = "Could not find component";
+            if( this.getLogger().isDebugEnabled() )
+            {
+                this.getLogger().debug( message + " for role: " + role );
+            }
+            throw new ServiceException( role, message );
+        }
+
+        Object component = null;
+
+        try {
+            component = handler.get();
+        } catch( final IllegalStateException ise ) {
+            try {
+                handler.initialize();
+                component = handler.get();
+            } catch( final ServiceException ce ) {
+                // Rethrow instead of wrapping a ComponentException with 
another one
+                throw ce;
+            } catch( final Exception e ) {
+                final String message = "Could not access the Component";
+                if( this.getLogger().isDebugEnabled() ) {
+                    this.getLogger().debug( message + " for role [" + role + 
"]", e );
+                }
+
+                throw new ServiceException( role, message, e );
+            }
+        } catch ( ServiceException se) {
+            throw se;
+        } catch( final Exception e ) {
+            final String message = "Could not access the Component";
+            if( this.getLogger().isDebugEnabled() ) {
+                this.getLogger().debug( message + " for role [" + role + "]", 
e );
+            }
+
+            throw new ServiceException( role, message, e );
+        }
+
+        // Add a mapping between the component and its handler.
+        //  In the case of a ThreadSafeComponentHandler, the same component 
will be mapped
+        //  multiple times but because each put will overwrite the last, this 
is not a
+        //  problem.  Checking to see if the put has already been done would 
be slower.
+        this.componentMapping.put( component, handler );
+
+        if ( null != component && component instanceof SitemapConfigurable) {
+
+            // FIXME: how can we prevent that this is called over and over 
again?
+            SitemapConfigurationHolder holder;
+
+            holder = 
(SitemapConfigurationHolder)this.sitemapConfigurationHolders.get( role );
+            if ( null == holder ) {
+                // create new holder
+                holder = new DefaultSitemapConfigurationHolder( role );
+                this.sitemapConfigurationHolders.put( role, holder );
+            }
+
+            try {
+                ((SitemapConfigurable)component).configure(holder);
+            } catch (ConfigurationException ce) {
+                throw new ServiceException(role, "Exception during setup of 
SitemapConfigurable.", ce);
+            }
+        }
+        return component;
+    }
+
+    /* (non-Javadoc)
+     * @see 
org.apache.avalon.framework.service.ServiceManager#hasService(java.lang.String)
+     */
+    public boolean hasService( final String role ) {
+        if( !this.initialized ) return false;
+        if( this.disposed ) return false;
+
+        boolean exists = this.componentHandlers.containsKey( role );
+
+        if( !exists && null != this.parentManager ) {
+            exists = this.parentManager.hasService( role );
+        }
+
+        return exists;
+    }
+
+    /* (non-Javadoc)
+     * @see 
org.apache.avalon.framework.service.ServiceManager#release(java.lang.Object)
+     */
+    public void release( final Object component ) {
+        if( null == component ) {
+            return;
+        }
+
+        // The componentMapping StaticBucketMap itself is threadsafe, and 
because the same component
+        //  will never be released by more than one thread, this method does 
not need any
+        //  synchronization around the access to the map.
+
+        final AbstractComponentHandler handler =
+            (AbstractComponentHandler)this.componentMapping.get( component );
+
+        if ( handler != null ) {
+            // ThreadSafe components will always be using a 
ThreadSafeComponentHandler,
+            //  they will only have a single entry in the m_componentMapping 
map which
+            //  should not be removed until the ComponentLocator is disposed.  
All
+            //  other components have an entry for each instance which should 
be
+            //  removed.
+            if( !( handler instanceof ThreadSafeComponentHandler ) ) {
+                // Remove the component before calling put.  This is critical 
to avoid the
+                //  problem where another thread calls put on the same 
component before
+                //  remove can be called.
+                this.componentMapping.remove( component );
+            }
+
+            try {
+                handler.put( component );
+            } catch( Exception e ) {
+                if( this.getLogger().isDebugEnabled() ) {
+                    this.getLogger().debug( "Error trying to release 
component.", e );
+                }
+            }
+        }
+        else if( this.parentManager != null ) {
+            this.parentManager.release( component );
+        } else {
+            this.getLogger().warn( "Attempted to release a " + 
component.getClass().getName() +
+                              " but its handler could not be located." );
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see 
org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
+     */
+    public void configure( final Configuration configuration )
+    throws ConfigurationException {
+        if( null == roleManager ) {
+            final RoleManager roleInfo = new RoleManager();
+            roleInfo.enableLogging( getLogger() );
+            roleInfo.configure( configuration );
+            this.roleManager = roleInfo;
+            this.getLogger().debug( "No RoleManager given, deriving one from 
configuration" );
+        }
+
+        // Set components
+
+        final Configuration[] configurations = configuration.getChildren();
+
+        for( int i = 0; i < configurations.length; i++ ) {
+            String type = configurations[ i ].getName();
+
+            if( !type.equals( "role" ) ) {
+                String role = configurations[ i ].getAttribute( "role", "" );
+                String className = configurations[ i ].getAttribute( "class", 
"" );
+
+                if( role.equals( "" ) ) {
+                    role = roleManager.getRoleForName( type );
+                }
+
+                if( null != role && !role.equals( "" ) ) {
+                    if( className.equals( "" ) ) {
+                        className = roleManager.getDefaultClassNameForRole( 
role );
+                    }
+
+                    
+                    if ( 
"org.apache.cocoon.components.ExtendedComponentSelector".equals(className)) {
+                        className = CocoonServiceSelector.class.getName();
+                    } else if 
("org.apache.cocoon.components.treeprocessor.sitemap.ComponentsSelector".equals(className)
 ) {
+                        className = ComponentsSelector.class.getName();
+                    }
+                    try {
+                        if( this.getLogger().isDebugEnabled() ) {
+                            this.getLogger().debug( "Adding component (" + 
role + " = "
+                                + className + ")" );
+                        }
+
+                        final Class clazz = this.loader.loadClass( className );
+                        addComponent( role, clazz, configurations[ i ] );
+                    } catch( final ClassNotFoundException cnfe ) {
+                        final String message = "Could not get class ";
+
+                        if( this.getLogger().isErrorEnabled() ) {
+                            this.getLogger().error( message + className + " 
for role " + role
+                                + " on configuration element " + 
configurations[ i ].getName(),
+                                cnfe );
+                        }
+
+                        throw new ConfigurationException( message, cnfe );
+                    } catch( final ServiceException ce ) {
+                        final String message = "Bad component ";
+
+                        if( this.getLogger().isErrorEnabled() ) {
+                            this.getLogger().error( message + className + " 
for role " + role
+                                + " on configuration element " + 
configurations[ i ].getName(),
+                                ce );
+                        }
+
+                        throw new ConfigurationException( message, ce );
+                    } catch( final Exception e ) {
+                        if( this.getLogger().isErrorEnabled() ) {
+                            this.getLogger().error( "Unexpected exception for 
hint: " + role, e );
+                        }
+                        throw new ConfigurationException( "Unexpected 
exception", e );
+                    }
+                }
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see 
org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
+     */
+    public void contextualize( final Context context ) {
+        this.context = context;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.avalon.framework.activity.Initializable#initialize()
+     */
+    public void initialize()
+    throws Exception {
+        synchronized( this ) {
+            this.initialized = true;
+
+            for( int i = 0; i < this.newComponentHandlers.size(); i++ )
+            {
+                final AbstractComponentHandler handler =
+                    (AbstractComponentHandler)this.newComponentHandlers.get( i 
);
+                try {
+                    handler.initialize();
+                } catch( Exception e ) {
+                    if( this.getLogger().isErrorEnabled() )
+                    {
+                        this.getLogger().error( "Caught an exception trying to 
initialize "
+                                           + "the component handler.", e );
+                    }
+
+                    // Rethrow the exception
+                    throw e;
+                }
+            }
+
+            List keys = new ArrayList( this.componentHandlers.keySet() );
+
+            for( int i = 0; i < keys.size(); i++ ) {
+                final Object key = keys.get( i );
+                final AbstractComponentHandler handler =
+                    (AbstractComponentHandler)this.componentHandlers.get( key 
);
+
+                if( !this.newComponentHandlers.contains( handler ) ) {
+                    try {
+                        handler.initialize();
+
+                    } catch( Exception e ) {
+                        if( this.getLogger().isErrorEnabled() ) {
+                            this.getLogger().error( "Caught an exception 
trying to initialize "
+                                               + "the component handler.", e );
+
+                        }
+                        // Rethrow the exception
+                        throw e;
+                    }
+                }
+            }
+            this.newComponentHandlers.clear();
+
+            if (parentAwareComponents == null) {
+                throw new ServiceException(null, "CocoonServiceManager already 
initialized");
+            }
+            // Set parents for parentAware components
+            Iterator iter = parentAwareComponents.iterator();
+            while (iter.hasNext()) {
+                String role = (String)iter.next();
+                getLogger().debug(".. "+role);
+                if ( parentManager != null && parentManager.hasService( role ) 
) {
+                    // lookup new component
+                    Object component = null;
+                    try {
+                        component = this.lookup( role );
+                        ((CocoonServiceSelector)component).setParentLocator( 
this.parentManager, role );
+                    } catch (ServiceException ignore) {
+                        // we don't set the parent then
+                    } finally {
+                        this.release( component );
+                    }
+                }
+            }
+            parentAwareComponents = null;  // null to save memory, and catch 
logic bugs.
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.avalon.framework.activity.Disposable#dispose()
+     */
+    public void dispose() {
+        synchronized( this ) {
+            boolean forceDisposal = false;
+
+            final List disposed = new ArrayList();
+
+            while( componentHandlers.size() > 0 ) {
+                for( Iterator iterator = componentHandlers.keySet().iterator();
+                     iterator.hasNext(); ) {
+                    final Object role = iterator.next();
+
+                    final AbstractComponentHandler handler =
+                        (AbstractComponentHandler)componentHandlers.get( role 
);
+
+                    if( forceDisposal || handler.canBeDisposed() ) {
+                        if( forceDisposal && getLogger().isWarnEnabled() ) {
+                            this.getLogger().warn
+                                ( "disposing of handler for unreleased 
component."
+                                  + " role [" + role + "]" );
+                        }
+
+                        handler.dispose();
+                        disposed.add( role );
+                    }
+                }
+
+                if( disposed.size() > 0 ) {
+                    final Iterator i = disposed.iterator();
+                    while ( i.hasNext() ) {
+                        this.componentHandlers.remove( i.next() );
+                    }
+                    disposed.clear();
+                } else {   
+                    // no more disposable handlers!
+                    forceDisposal = true;
+                }
+            }
+
+            this.disposed = true;
+        }
+    }
+
+    /*---------------------------------------------------------------
+     * RoleManageable Methods
+     *-------------------------------------------------------------*/
+    /**
+     * Configure the RoleManager
+     */
+    public void setRoleManager( final RoleManager roles )
+    {
+        if( null == roleManager )
+        {
+            roleManager = roles;
+        }
+    }
+
+
+    /**
+     * Configure the LoggerManager.
+     */
+    public void setLoggerManager( final LoggerManager loggerManager ) {
+        this.loggerManager = loggerManager;
+    }
+
+    /**
+     * Add a new component to the manager.
+     *
+     * @param role the role name for the new component.
+     * @param component the class of this component.
+     * @param configuration the configuration for this component.
+     */
+    public void addComponent( final String role,
+                              final Class component,
+                              final Configuration configuration )
+    throws ServiceException {
+        if( this.initialized ) {
+            throw new ServiceException( role,
+                "Cannot add components to an initialized 
CocoonServiceManager." );
+        }
+
+        try {
+            if( this.getLogger().isDebugEnabled() ) {
+                this.getLogger().debug( "Attempting to get handler for role [" 
+ role + "]" );
+            }
+
+            final AbstractComponentHandler handler = 
AbstractComponentHandler.getComponentHandler( role,
+                                                                  component,
+                                                                  
configuration,
+                                                                  this,
+                                                                  context,
+                                                                  
this.getLogger(),
+                                                                  
this.loggerManager,
+                                                                  
this.roleManager);
+
+            if( this.getLogger().isDebugEnabled() ) {
+                this.getLogger().debug( "Handler type = " + 
handler.getClass().getName() );
+            }
+
+            this.componentHandlers.put( role, handler );
+            this.newComponentHandlers.add( handler );
+        } catch ( final ServiceException se ) {
+            throw se;
+        } catch( final Exception e ) {
+            throw new ServiceException( role, "Could not set up component 
handler.", e );
+        }
+        // Note that at this point, we're not initialized and cannot do
+        // lookups, so defer parental introductions to initialize().
+        if ( CocoonServiceSelector.class.isAssignableFrom( component ) ) {
+            this.parentAwareComponents.add(role);
+        }
+    }
+
+}

Added: 
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/CocoonServiceSelector.java
==============================================================================
--- (empty file)
+++ 
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/CocoonServiceSelector.java
        Tue Oct 19 12:10:38 2004
@@ -0,0 +1,692 @@
+/* 
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed  under the  Apache License,  Version 2.0  (the "License");
+ * you may not use  this file  except in  compliance with the License.
+ * You may obtain a copy of the License at 
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed  under the  License is distributed on an "AS IS" BASIS,
+ * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
+ * implied.
+ * 
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.container;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.avalon.excalibur.logger.LoggerManager;
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.activity.Initializable;
+import org.apache.avalon.framework.configuration.Configurable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.configuration.DefaultConfiguration;
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.ServiceSelector;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.avalon.framework.thread.ThreadSafe;
+
+/**
+ * Default component selector for Cocoon's components.
+ *
+ * @version CVS $Revision: 1.6 $ $Date: 2004/02/28 11:47:14 $
+ */
+public class CocoonServiceSelector
+    extends AbstractLogEnabled
+    implements Contextualizable,
+    ServiceSelector,
+    Serviceable,
+    Configurable,
+    Initializable,
+    ThreadSafe,
+    Disposable {
+    
+    /** The classloader used for this system. */
+    protected final ClassLoader loader;
+
+    /** The role name for this instance
+     */
+    private String rolename;
+
+    /** The application context for components
+     */
+    protected Context context;
+
+    /** The application context for components
+     */
+    private ServiceManager serviceManager;
+
+    /** Static configuraiton object.
+     */
+    private Configuration configuration;
+
+    /** Static component handlers.
+     */
+    private Map componentHandlers = Collections.synchronizedMap(new HashMap());
+
+    /** Dynamic component handlers mapping.
+     */
+    private Map componentMapping = Collections.synchronizedMap(new HashMap());
+
+    /** Flag for if this is disposed or not.
+     */
+    private boolean disposed;
+
+    /** Flag for if this is initialized or not.
+     */
+    private boolean initialized;
+
+    /** The RoleManager to get hint shortcuts
+     */
+    protected RoleManager roleManager;
+
+    private LoggerManager loggerManager;
+
+    /** The parent selector, if any */
+    protected CocoonServiceSelector parentSelector;
+
+    /** The parent locator, if any */
+    protected ServiceManager parentLocator;
+
+    /** The role of this selector. Set in <code>configure()</code>. */
+    protected String roleName;
+
+    /** The default hint */
+    protected String defaultHint;
+
+    /** This selector's location (used for debugging purposes) */
+    private String location;
+
+    
+    /** Create the ComponentSelector */
+    public CocoonServiceSelector() {
+        this.loader = Thread.currentThread().getContextClassLoader();
+    }
+
+    /* (non-Javadoc)
+     * @see 
org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
+     */
+    public void contextualize( final Context context ) {
+        this.context = context;
+    }
+
+    /* (non-Javadoc)
+     * @see 
org.apache.avalon.framework.service.ServiceSelector#select(java.lang.Object)
+     */
+    public Object select( Object hint )
+    throws ServiceException {
+        if( !this.initialized ) {
+            if( this.getLogger().isWarnEnabled() ) {
+                this.getLogger().warn( "Looking up component on an 
uninitialized ComponentLocator "
+                    + "with hint [" + hint + "]" );
+            }
+        }
+
+        if( this.disposed ) {
+            throw new IllegalStateException(
+                "You cannot select a Component from a disposed 
ComponentSelector" );
+        }
+
+        if (hint == null) {
+            hint = this.defaultHint;
+        }
+
+        AbstractComponentHandler handler = 
(AbstractComponentHandler)this.componentHandlers.get( hint );
+
+        // Retrieve the instance of the requested component
+        if( null == handler ) {
+            // Doesn't exist here : try in parent selector
+           if ( this.parentSelector != null ) {
+                return this.parentSelector.select(hint);                
+            }
+            final String message = getName()
+                + ": ComponentSelector could not find the component for hint 
[" + hint + "]";
+            if( this.getLogger().isDebugEnabled() ) {
+                this.getLogger().debug( message );
+            }
+            throw new ServiceException( hint.toString(), message );
+        }
+
+        Object component = null;
+
+        try {
+            component = handler.get();
+        } catch( final ServiceException ce ) {
+            //rethrow
+            throw ce;
+        } catch( final Exception e ) {
+            final String message = getName()
+                + ": ComponentSelector could not access the Component for hint 
[" + hint + "]";
+
+            if( this.getLogger().isDebugEnabled() ) {
+                this.getLogger().debug( message, e );
+            }
+            throw new ServiceException( hint.toString(), message, e );
+        }
+
+        if( null == component ) {
+            // Doesn't exist here : try in parent selector
+            if ( this.parentSelector != null ) {
+                component = this.parentSelector.select(hint);
+            } else {
+                final String message = getName()
+                    + ": ComponentSelector could not find the component for 
hint [" + hint + "]";
+                if( this.getLogger().isDebugEnabled() ) {
+                    this.getLogger().debug( message );
+                }
+                throw new ServiceException( hint.toString(), message );
+            }
+        }
+
+        this.componentMapping.put( component, handler );
+        return component;
+
+    }
+
+    /* (non-Javadoc)
+     * @see 
org.apache.avalon.framework.service.ServiceSelector#isSelectable(java.lang.Object)
+     */
+    public boolean isSelectable( Object hint ) {
+        if( !this.initialized ) return false;
+        if( this.disposed ) return false;
+
+        if (hint == null) {
+            hint = this.defaultHint;
+        }
+
+        boolean exists = false;
+
+        try {
+            AbstractComponentHandler handler = 
(AbstractComponentHandler)this.componentHandlers.get( hint );
+            exists = (handler != null);
+        } catch( Throwable t ) {
+            // We can safely ignore all exceptions
+        }
+
+        if ( !exists && this.parentSelector != null ) {
+            exists = this.parentSelector.isSelectable( hint );
+        }
+        return exists;
+    }
+
+    /* (non-Javadoc)
+     * @see 
org.apache.avalon.framework.service.ServiceSelector#release(java.lang.Object)
+     */
+    public void release( final Object component ) {
+        if( null == component ) {
+            return;
+        }
+
+        // Was it selected on the parent ?
+        if ( this.parentSelector != null &&
+             this.parentSelector.canRelease(component) ) {
+            this.parentSelector.release(component);
+
+        } else {
+            final AbstractComponentHandler handler =
+                (AbstractComponentHandler)this.componentMapping.get( component 
);
+    
+            if( null == handler ) {
+                this.getLogger().warn( "Attempted to release a " + 
component.getClass().getName()
+                    + " but its handler could not be located." );
+                return;
+            }
+    
+            // ThreadSafe components will always be using a 
ThreadSafeComponentHandler,
+            //  they will only have a single entry in the m_componentMapping 
map which
+            //  should not be removed until the ComponentLocator is disposed.  
All
+            //  other components have an entry for each instance which should 
be
+            //  removed.
+            if( !( handler instanceof ThreadSafeComponentHandler ) ) {
+                // Remove the component before calling put.  This is critical 
to avoid the
+                //  problem where another thread calls put on the same 
component before
+                //  remove can be called.
+                this.componentMapping.remove( component );
+            }
+    
+            try {
+                handler.put( component );
+            } catch( Exception e ) {
+                if( this.getLogger().isDebugEnabled() ) {
+                    this.getLogger().debug( "Error trying to release 
component", e );
+                }
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see 
org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
+     */
+    public void service( final ServiceManager componentManager )
+    throws ServiceException {
+        this.serviceManager = componentManager;
+    }
+
+    /* (non-Javadoc)
+     * @see 
org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
+     */
+    public void configure( final Configuration config )
+    throws ConfigurationException {
+        // Store location
+        this.location = config.getLocation();
+
+        this.roleName = getRoleName(config);
+
+        // Pass a copy of the top-level object to superclass so that
+        // our name is properly initialized
+        // FIXME : could be avoided if parent m_role was protected or had 
protected accessors
+        DefaultConfiguration temp = new DefaultConfiguration(config.getName(), 
this.location);
+        if (config.getAttribute("role", null) != null) {
+            temp.setAttribute("role", this.roleName);
+        }
+
+        this.configuration = temp;
+
+        if( this.getLogger().isDebugEnabled() ) {
+            this.getLogger().debug( "ComponentSelector setting up with root 
element: " +
+                    this.configuration.getName() );
+        }
+
+        final String name = configuration.getName();
+        if( name.equals( "component" ) ) {
+            this.rolename = this.configuration.getAttribute( "role" );
+        } else {
+            this.rolename = this.roleManager.getRoleForName( name );
+        }
+
+        Configuration[] instances = this.configuration.getChildren();
+
+        for( int i = 0; i < instances.length; i++ ) {
+            final Object hint = instances[ i ].getAttribute( "name" ).trim();
+            String className;
+
+            if( "component-instance".equals( instances[ i ].getName() ) ) {
+                className = (String)instances[ i ].getAttribute( "class" 
).trim();
+            } else {
+                className = this.roleManager.getDefaultClassNameForHint( 
this.rolename,
+                                                                instances[ i 
].getName() );
+            }
+
+            if ( 
"org.apache.cocoon.components.ExtendedComponentSelector".equals(className)) {
+                className = CocoonServiceSelector.class.getName();
+            } else if 
("org.apache.cocoon.components.treeprocessor.sitemap.ComponentsSelector".equals(className)
 ) {
+                className = ComponentsSelector.class.getName();
+            }
+            
+            try {
+                final Class clazz = this.loader.loadClass( className );
+                this.addComponent( hint, clazz, instances[ i ] );
+            } catch( final ClassNotFoundException cnfe ) {
+                final String message =
+                    "The component instance for hint [" + hint
+                    + "] has an invalid class name (" + className + ").";
+                if( this.getLogger().isErrorEnabled() ) {
+                    this.getLogger().error( message, cnfe );
+                }
+
+                throw new ConfigurationException( message, cnfe );
+            } catch( final Exception e ) {
+                if( this.getLogger().isErrorEnabled() ) {
+                    this.getLogger().error( "Unexpected exception for hint [" 
+ hint + "]", e );
+                }
+                throw new ConfigurationException( "Unexpected exception", e );
+            }
+        }
+        // Get default hint
+        this.defaultHint = 
config.getAttribute(this.getDefaultHintAttributeName(), null);
+
+        // Add components
+        String compInstanceName = getComponentInstanceName();
+
+        instances = config.getChildren();
+
+        for (int i = 0; i < instances.length; i++) {
+
+            Configuration instance = instances[i];
+
+            Object hint = instance.getAttribute("name").trim();
+
+            String classAttr = instance.getAttribute(getClassAttributeName(), 
null);
+            String className;
+
+            if (compInstanceName == null) {
+                // component-instance implicitly defined by the presence of 
the 'class' attribute
+                if (classAttr == null) {
+                    className = 
this.roleManager.getDefaultClassNameForHint(roleName, instance.getName());
+                } else {
+                    className = classAttr.trim();
+                }
+
+            } else {
+                // component-instances names explicitly defined
+                if (compInstanceName.equals(instance.getName())) {
+                    className = (classAttr == null) ? null : classAttr.trim();
+                } else {
+                    className = 
this.roleManager.getDefaultClassNameForHint(roleName, instance.getName());
+                }
+            }
+
+            if (className == null) {
+                String message = "Unable to determine class name for component 
named '" + hint +
+                    "' at " + instance.getLocation();
+
+                getLogger().error(message);
+                throw new ConfigurationException(message);
+            }
+
+            if ( 
"org.apache.cocoon.components.ExtendedComponentSelector".equals(className)) {
+                className = CocoonServiceSelector.class.getName();
+            } else if 
("org.apache.cocoon.components.treeprocessor.sitemap.ComponentsSelector".equals(className)
 ) {
+                className = ComponentsSelector.class.getName();
+            }
+
+            try {
+                Class clazz = this.loader.loadClass(className);
+                addComponent(hint, clazz, instance);
+
+            } catch(Exception e) {
+
+                String message = "Could not load class " + className + " for 
component named '" +
+                    hint + "' at " + instance.getLocation();
+
+                getLogger().error(message, e);
+                throw new ConfigurationException(message, e);
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.avalon.framework.activity.Initializable#initialize()
+     */
+    public void initialize() {
+        synchronized( this ) {
+            this.initialized = true;
+
+            List keys = new ArrayList( this.componentHandlers.keySet() );
+
+            for( int i = 0; i < keys.size(); i++ ) {
+                final Object key = keys.get( i );
+                final AbstractComponentHandler handler =
+                    (AbstractComponentHandler)this.componentHandlers.get( key 
);
+
+                try {
+                    handler.initialize();
+                } catch( Exception e ) {
+                    if( this.getLogger().isDebugEnabled() ) {
+                        this.getLogger().debug( "Caught an exception trying to 
initialize "
+                            + "of the component handler.", e );
+                    }
+                }
+
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.avalon.framework.activity.Disposable#dispose()
+     */
+    public void dispose() {
+        synchronized( this ) {
+            Iterator keys = this.componentHandlers.keySet().iterator();
+            List keyList = new ArrayList();
+
+            while( keys.hasNext() ) {
+                Object key = keys.next();
+                AbstractComponentHandler handler =
+                    (AbstractComponentHandler)this.componentHandlers.get( key 
);
+
+                handler.dispose();
+
+                keyList.add( key );
+            }
+
+            keys = keyList.iterator();
+
+            while( keys.hasNext() ) {
+                this.componentHandlers.remove( keys.next() );
+            }
+
+            keyList.clear();
+
+            if ( this.parentLocator != null ) {
+                this.parentLocator.release( this.parentSelector );
+                this.parentLocator = null;
+                this.parentSelector = null;
+            }
+
+            this.disposed = true;
+        }
+    }
+
+    public void setRoleManager( final RoleManager roles ) {
+        this.roleManager = roles;
+    }
+
+    /**
+     * Configure the LoggerManager.
+     */
+    public void setLoggerManager( final LoggerManager manager ) {
+        this.loggerManager = manager;
+    }
+
+    /**
+     * Obtain a new ComponentHandler for the specified component.  This method
+     *  allows classes which extend the ExcaliburComponentSelector to use their
+     *  own ComponentHandlers.
+     *
+     * @param componentClass Class of the component for which the handle is
+     *                       being requested.
+     * @param configuration The configuration for this component.
+     * @param componentManager The ComponentLocator which will be managing
+     *                         the Component.
+     * @param context The current context object.
+     * @param roleManager The current RoleManager.
+     * @param logkitManager The current LogKitManager.
+     *
+     * @throws Exception If there were any problems obtaining a 
ComponentHandler
+     */
+    protected AbstractComponentHandler getComponentHandler( final String role,
+                                                    final Class componentClass,
+                                                    final Configuration 
configuration,
+                                                    final ServiceManager 
componentManager,
+                                                    final Context context,
+                                                    final LoggerManager 
logkitManager )
+    throws Exception {
+        return AbstractComponentHandler.getComponentHandler( role,
+                                                     componentClass,
+                                                     configuration,
+                                                     this.serviceManager,
+                                                     context,
+                                                     this.getLogger(),
+                                                     this.loggerManager,
+                                                     this.roleManager);
+    }
+
+    /**
+     * Makes the ComponentHandlers available to subclasses.
+     *
+     * @return A reference to the componentHandler Map.
+     */
+    protected Map getComponentHandlers() {
+        return this.componentHandlers;
+    }
+
+    /** Add a new component to the manager.
+     * @param hint the hint name for the new component.
+     * @param component the class of this component.
+     * @param configuration the configuration for this component.
+     */
+    public void addComponent( final Object hint,
+                              final Class component,
+                              final Configuration configuration )
+    throws ServiceException {
+        if( this.initialized ) {
+            throw new ServiceException( hint.toString(),
+                "Cannot add components to an initialized ComponentSelector", 
null );
+        }
+
+        try {
+            final AbstractComponentHandler handler = getComponentHandler( 
this.rolename,
+                                                                  component,
+                                                                  
configuration,
+                                                                  
this.serviceManager,
+                                                                  this.context,
+                                                                  
this.loggerManager );
+
+            handler.initialize();
+            this.componentHandlers.put( hint, handler );
+
+            if( this.getLogger().isDebugEnabled() ) {
+                this.getLogger().debug(
+                    "Adding " + component.getName() + " for hint [" + 
hint.toString() + "]" );
+            }
+        } catch( final Exception e ) {
+            final String message =
+                "Could not set up Component for hint [ " + hint + "]";
+            if( this.getLogger().isErrorEnabled() ) {
+                this.getLogger().error( message, e );
+            }
+
+            throw new ServiceException( hint.toString(), message, e );
+        }
+    }
+
+    /**
+     * Return this selector's configuration name or a default name if no such
+     * configuration was provided. This accounts for the case when a static
+     * component instance has been added through
+     * <code>addComponentInstance</code> with no associated configuration
+     */
+    private String getName() {
+        if( null != this.configuration &&
+            !this.configuration.getName().equals( "" ) ) {
+            return this.configuration.getName();
+        }
+
+        return "Unnamed service selector";
+    }
+
+    /**
+     * Get the name for component-instance elements (i.e. components not 
defined
+     * by their role shortcut. If <code>null</code>, any element having a 
'class'
+     * attribute will be considered as a component instance.
+     * <p>
+     * The default here is to return <code>null</code>, and subclasses can 
redefine
+     * this method to return particular values.
+     *
+     * @return <code>null</code>, but can be changed by subclasses
+     */
+    protected String getComponentInstanceName() {
+        return null;
+    }
+
+    /**
+     * Get the name of the attribute giving the class name of a component.
+     * The default here is "class", but this can be overriden in subclasses.
+     *
+     * @return "<code>class</code>", but can be changed by subclasses
+     */
+    protected String getClassAttributeName() {
+        return "class";
+    }
+
+    /**
+     * Get the name of the attribute giving the default hint to use if
+     * none is given. The default here is "default", but this can be
+     * overriden in subclasses. If this method returns <code>null</code>,
+     * no default hint can be specified.
+     *
+     * @return "<code>default</code>", but can be changed by subclasses
+     */
+    protected String getDefaultHintAttributeName() {
+        return "default";
+    }
+    
+    /**
+     * Get the role name for this selector. This is called by 
<code>configure()</code>
+     * to set the value of <code>this.roleName</code>.
+     *
+     * @return the role name, or <code>null<code> if it couldn't be determined.
+     */
+    protected String getRoleName(Configuration config) {
+        // Get the role for this selector
+        String roleName = config.getAttribute("role", null);
+        if (roleName == null && this.roleManager != null) {
+            roleName = this.roleManager.getRoleForName(config.getName());
+        }
+
+        return roleName;
+    }
+
+    /**
+     * Get the default hint, if any for this selector.
+     */
+    public String getDefaultHint() {
+        return this.defaultHint;
+    }
+
+    /**
+     * Does this selector declare a given hint? Check is performed on the 
components declared for this
+     * selector only, and <strong>not</strong> those potentially inherited 
from the parent selector.
+     * 
+     * @param hint the hint to check for
+     * @return <code>true</code> if this selector has the specified hint
+     */
+    protected boolean hasDeclaredComponent(Object hint) {
+        if (hint == null) {
+            hint = this.defaultHint;
+        }
+
+        return this.isSelectable(hint);
+    }
+
+    /**
+     * Set the ComponentLocatorImpl that allows access to a possible
+     * parent of this selector
+     * @param locator
+     * @throws ComponentException
+     */
+    public void setParentLocator(ServiceManager locator, String role)
+    throws ServiceException {
+        if (this.parentSelector != null) {
+            throw new ServiceException(null, "Parent selector is already set");
+        }
+        this.parentLocator = locator;
+        
+        // Get the parent, unwrapping it as far as needed
+        Object parent = locator.lookup(role);
+        
+        if (parent instanceof CocoonServiceSelector) {
+            this.parentSelector = (CocoonServiceSelector)parent;
+        } else {
+            throw new IllegalArgumentException("Parent selector is not an 
extended component selector (" + parent + ")");
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see 
org.apache.avalon.excalibur.component.ExcaliburComponentSelector#canRelease(org.apache.avalon.framework.component.Component)
+     */
+    protected boolean canRelease(Object component) {
+        if ( this.parentSelector != null &&
+             this.parentSelector.canRelease(component) ) {
+            return true;
+        }
+        return this.componentMapping.containsKey( component );
+    }
+
+    
+
+}

Added: 
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/ComponentFactory.java
==============================================================================
--- (empty file)
+++ 
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/ComponentFactory.java
     Tue Oct 19 12:10:38 2004
@@ -0,0 +1,169 @@
+/* 
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed  under the  Apache License,  Version 2.0  (the "License");
+ * you may not use  this file  except in  compliance with the License.
+ * You may obtain a copy of the License at 
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed  under the  License is distributed on an "AS IS" BASIS,
+ * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
+ * implied.
+ * 
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.container;
+
+import org.apache.avalon.excalibur.logger.LoggerManager;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.container.ContainerUtil;
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.avalon.framework.logger.LogEnabled;
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.avalon.framework.parameters.Parameterizable;
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+
+/**
+ * Factory for Avalon based components.
+ *
+ * @version CVS $Revision: 1.6 $ $Date: 2004/02/28 11:47:14 $
+ */
+public class ComponentFactory {
+    
+    /** The class which this <code>ComponentFactory</code>
+     * should create.
+     */
+    private final Class componentClass;
+
+    /** The Context for the component
+     */
+    private final Context context;
+
+    /** The service manager for this component
+     */
+    private final ServiceManager serviceManager;
+    
+    /** The configuration for this component.
+     */
+    private final Configuration configuration;
+
+    /** The parameters for this component
+     */
+    private Parameters parameters;
+    
+    private final Logger logger;
+    
+    private final String role;
+    
+    private final LoggerManager loggerManager;
+
+    private final RoleManager roleManager;
+    
+    /**
+     * Construct a new component factory for the specified component.
+     *
+     * @param componentClass the class to instantiate (must have a default 
constructor).
+     * @param configuration the <code>Configuration</code> object to pass to 
new instances.
+     * @param componentManager the component manager to pass to 
<code>Composable</code>s.
+     * @param context the <code>Context</code> to pass to 
<code>Contexutalizable</code>s.
+     *
+     */
+    public ComponentFactory( final String role,
+                             final Class componentClass,
+                             final Configuration configuration,
+                             final ServiceManager serviceManager,
+                             final Context context,
+                             final Logger logger,
+                             final LoggerManager loggerManager,
+                             final RoleManager roleManager) {
+        this.role = role;
+        this.componentClass = componentClass;
+        this.configuration = configuration;
+        this.serviceManager = serviceManager;
+        this.context = context;
+        this.logger = logger;
+        this.loggerManager = loggerManager;
+        this.roleManager = roleManager;
+    }
+
+    /**
+     * Create a new instance
+     */
+    public Object newInstance()
+    throws Exception {
+        final Object component = this.componentClass.newInstance();
+
+        if( this.logger.isDebugEnabled() ) {
+            this.logger.debug( "ComponentFactory creating new instance of " +
+                    this.componentClass.getName() + "." );
+        }
+
+        if ( component instanceof LogEnabled ) {
+            if( null == this.configuration ) {
+                ContainerUtil.enableLogging( component, this.logger );
+            } else {
+                final String logger = this.configuration.getAttribute( 
"logger", null );
+                if( null == logger ) {
+                    this.logger.debug( "no logger attribute available, using 
standard logger" );
+                    ContainerUtil.enableLogging( component, this.logger );
+                } else {
+                    this.logger.debug( "logger attribute is " + this.logger );
+                    ContainerUtil.enableLogging( component, 
this.loggerManager.getLoggerForCategory( logger ) );
+                }
+            }
+        }
+
+
+        if( component instanceof Contextualizable ) {
+            ContainerUtil.contextualize( component, this.context );
+        }
+
+        if( component instanceof Serviceable ) {
+            ContainerUtil.service( component, this.serviceManager );
+        }
+
+        if ( component instanceof CocoonServiceSelector ) {
+            
((CocoonServiceSelector)component).setLoggerManager(this.loggerManager);
+            
((CocoonServiceSelector)component).setRoleManager(this.roleManager);
+        }
+        
+        ContainerUtil.configure( component, this.configuration );
+
+        if( component instanceof Parameterizable ) {
+            if ( this.parameters == null ) {
+                this.parameters = Parameters.fromConfiguration( 
this.configuration );
+            }
+            ContainerUtil.parameterize( component, this.parameters );
+        }
+
+        ContainerUtil.initialize( component );
+
+        ContainerUtil.start( component );
+
+        return component;
+    }
+
+    public Class getCreatedClass() {
+        return this.componentClass;
+    }
+
+    /**
+     * Destroy an instance
+     */
+    public void decommission( final Object component )
+    throws Exception {
+        if( this.logger.isDebugEnabled() ) {
+            this.logger.debug( "ComponentFactory decommissioning instance of " 
+
+                    this.componentClass.getName() + "." );
+        }
+
+        ContainerUtil.stop( component );
+        ContainerUtil.dispose( component );
+    }
+
+}

Added: 
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/ComponentsSelector.java
==============================================================================
--- (empty file)
+++ 
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/ComponentsSelector.java
   Tue Oct 19 12:10:38 2004
@@ -0,0 +1,257 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.container;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.avalon.framework.CascadingRuntimeException;
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.configuration.DefaultConfiguration;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.cocoon.acting.Action;
+import org.apache.cocoon.components.pipeline.ProcessingPipeline;
+import org.apache.cocoon.generation.Generator;
+import org.apache.cocoon.generation.GeneratorFactory;
+import org.apache.cocoon.matching.Matcher;
+import org.apache.cocoon.reading.Reader;
+import org.apache.cocoon.selection.Selector;
+import org.apache.cocoon.serialization.Serializer;
+import org.apache.cocoon.serialization.SerializerFactory;
+import org.apache.cocoon.transformation.Transformer;
+import org.apache.cocoon.transformation.TransformerFactory;
+
+/**
+ * Component selector for sitemap components.
+ *
+ * @version CVS $Id: ComponentsSelector.java 47303 2004-09-27 16:21:30Z 
vgritsenko $
+ */
+public class ComponentsSelector extends CocoonServiceSelector {
+
+    public static final int UNKNOWN     = -1;
+    public static final int GENERATOR   = 0;
+    public static final int TRANSFORMER = 1;
+    public static final int SERIALIZER  = 2;
+    public static final int READER      = 3;
+    public static final int MATCHER     = 4;
+    public static final int SELECTOR    = 5;
+    public static final int ACTION      = 6;
+    public static final int PIPELINE    = 7;
+
+    public static final String[] SELECTOR_ROLES = {
+        Generator.ROLE   + "Selector",
+        Transformer.ROLE + "Selector",
+        Serializer.ROLE  + "Selector",
+        Reader.ROLE      + "Selector",
+        Matcher.ROLE     + "Selector",
+        Selector.ROLE    + "Selector",
+        Action.ROLE      + "Selector",
+        ProcessingPipeline.ROLE + "Selector"
+    };
+
+    public static final String[] COMPONENT_NAMES = {
+        "generator",
+        "transformer",
+        "serializer",
+        "reader",
+        "matcher",
+        "selector",
+        "action",
+        "pipe"
+    };
+
+    /** The role as an integer */
+    private int roleId;
+
+    /** The set of known hints, used to add standard components (see 
ensureExists) */
+    private Set knownHints = new HashSet();
+
+
+    /**
+     * Return the component instance name according to the selector role
+     * (e.g. "action" for "org.apache.cocoon.acting.Action").
+     */
+    protected String getComponentInstanceName() {
+        return (this.roleId == UNKNOWN) ? null : COMPONENT_NAMES[this.roleId];
+    }
+
+    /**
+     * Get the attribute for class names. This is "src" for known roles, and
+     * "class" (the default) for other roles.
+     */
+    protected String getClassAttributeName() {
+        return (this.roleId == UNKNOWN) ? "class" : "src";
+    }
+
+    public void configure(Configuration config) throws ConfigurationException {
+        // Who are we ?
+        String role = getRoleName(config);
+        this.roleId = UNKNOWN; // unknown
+        for (int i = 0; i < SELECTOR_ROLES.length; i++) {
+            if (SELECTOR_ROLES[i].equals(role)) {
+                this.roleId = i;
+                break;
+            }
+        }
+
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug("Setting up sitemap component selector for " +
+                              role + " (role id = " + this.roleId + ")");
+        }
+
+        super.configure(config);
+    }
+
+    /**
+     * Add a component in this selector.
+     */
+    public void addComponent(Object hint, Class clazz, Configuration config) 
throws ServiceException {
+        super.addComponent(hint, clazz, config);
+
+        // Add to known hints. This is needed as we cannot call isSelectable() 
if initialize()
+        // has not been called, and we cannot add components once it has been 
called...
+        this.knownHints.add(hint);
+    }
+
+    /**
+     * Ensure system-defined components exist (e.g. &lt;aggregator&gt;) and 
initialize
+     * the selector.
+     */
+    public void initialize() /* throws Exception */ {
+        // FIXME : need to catch exceptions since ECS doesn't propagate the 
throws clause of Initializable
+        try {
+            DefaultConfiguration config = null;
+
+            // Ensure all system-defined hints exist.
+            // NOTE : checking this here means they can be user-defined in the 
sitemap
+            switch(this.roleId) {
+                case GENERATOR :
+                    config = new 
DefaultConfiguration(COMPONENT_NAMES[GENERATOR], "autogenerated");
+                    config.setAttribute("name", "<notifier>");
+                    ensureExists("<notifier>",
+                                 
org.apache.cocoon.sitemap.NotifyingGenerator.class, config);
+
+                    config = new 
DefaultConfiguration(COMPONENT_NAMES[GENERATOR], "autogenerated");
+                    config.setAttribute("name", "<aggregator>");
+                    ensureExists("<aggregator>",
+                                 
org.apache.cocoon.sitemap.ContentAggregator.class, config);
+                break;
+
+                case TRANSFORMER :
+                    config = new 
DefaultConfiguration(COMPONENT_NAMES[TRANSFORMER], "autogenerated");
+                    config.setAttribute("name", "<translator>");
+                    ensureExists("<translator>",
+                                 
org.apache.cocoon.sitemap.LinkTranslator.class, config);
+
+                    config = new 
DefaultConfiguration(COMPONENT_NAMES[TRANSFORMER], "autogenerated");
+                    config.setAttribute("name", "<gatherer>");
+                    ensureExists("<gatherer>",
+                                 org.apache.cocoon.sitemap.LinkGatherer.class, 
config);
+                break;
+            }
+
+            super.initialize();
+
+            // Don't keep known hints (they're no more needed)
+            this.knownHints = null;
+        } catch (Exception e) {
+            throw new CascadingRuntimeException("Cannot setup default 
components", e);
+        }
+    }
+
+    /**
+     * Ensure a component exists or add it otherwhise. We cannot simply call 
hasComponent()
+     * since it requires to be initialized, and we want to add components, and 
this must
+     * be done before initialization.
+     */
+    private void ensureExists(Object hint, Class clazz, Configuration config) 
throws ServiceException {
+        if (!this.knownHints.contains(hint)) {
+            if (this.parentSelector == null || 
!this.parentSelector.isSelectable(hint)) {
+                addComponent(hint, clazz, config);
+            }
+        }
+    }
+
+    /**
+     * Override parent to implement support for [EMAIL PROTECTED] 
GeneratorFactory},
+     * [EMAIL PROTECTED] TransformerFactory}, and [EMAIL PROTECTED] 
SerializerFactory}.
+     */
+    public Object select(Object hint) throws ServiceException {
+        final Object component = super.select(hint);
+
+        switch (this.roleId) {
+            case GENERATOR:
+                if (component instanceof GeneratorFactory) {
+                    return ((GeneratorFactory)component).getInstance();
+                }
+                break;
+            case TRANSFORMER:
+                if (component instanceof TransformerFactory) {
+                    return ((TransformerFactory)component).getInstance();
+                }
+                break;
+            case SERIALIZER:
+                if (component instanceof SerializerFactory) {
+                    return ((SerializerFactory)component).getInstance();
+                }
+                break;
+        }
+
+        return component;
+    }
+
+    /**
+     * Override parent to implement support for [EMAIL PROTECTED] 
GeneratorFactory},
+     * [EMAIL PROTECTED] TransformerFactory}, and [EMAIL PROTECTED] 
SerializerFactory}.
+     */
+    public void release(Object component) {
+
+        // If component is an Instance returned by Factory, get the Factory.
+        switch (this.roleId) {
+            case GENERATOR:
+                if (component instanceof GeneratorFactory.Instance) {
+                    // Dispose component, if needed
+                    if (component instanceof Disposable) {
+                        ((Disposable)component).dispose();
+                    }
+                    component = 
((GeneratorFactory.Instance)component).getFactory();
+                }
+                break;
+            case TRANSFORMER:
+                if (component instanceof TransformerFactory.Instance) {
+                    // Dispose component, if needed
+                    if (component instanceof Disposable) {
+                        ((Disposable)component).dispose();
+                    }
+                    component = 
((TransformerFactory.Instance)component).getFactory();
+                }
+                break;
+            case SERIALIZER:
+                if (component instanceof SerializerFactory.Instance) {
+                    // Dispose component, if needed
+                    if (component instanceof Disposable) {
+                        ((Disposable)component).dispose();
+                    }
+                    component = 
((SerializerFactory.Instance)component).getFactory();
+                }
+                break;
+        }
+
+        super.release(component);
+    }
+}

Added: 
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/PoolableComponentHandler.java
==============================================================================
--- (empty file)
+++ 
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/PoolableComponentHandler.java
     Tue Oct 19 12:10:38 2004
@@ -0,0 +1,245 @@
+/* 
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed  under the  Apache License,  Version 2.0  (the "License");
+ * you may not use  this file  except in  compliance with the License.
+ * You may obtain a copy of the License at 
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed  under the  License is distributed on an "AS IS" BASIS,
+ * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
+ * implied.
+ * 
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.container;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import org.apache.avalon.excalibur.pool.Poolable;
+import org.apache.avalon.excalibur.pool.Recyclable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.logger.Logger;
+
+/**
+ * The PoolableComponentHandler to make sure that poolable components are 
initialized
+ * destroyed and pooled correctly.
+ * <p>
+ * Components which implement Poolable may be configured to be pooled using 
the following
+ *  example configuration.  This example assumes that the user component class 
MyComp
+ *  implements Poolable.
+ * <p>
+ * Configuration Example:
+ * <pre>
+ *   &lt;my-comp pool-max="8"/&gt;
+ * </pre>
+ * <p>
+ * Roles Example:
+ * <pre>
+ *   &lt;role name="com.mypkg.MyComponent"
+ *         shorthand="my-comp"
+ *         default-class="com.mypkg.DefaultMyComponent"/&gt;
+ * </pre>
+ * <p>
+ * Configuration Attributes:
+ * <ul>
+ * <li>The <code>pool-max</code> attribute is used to set the maximum number 
of components which
+ *  will be pooled. (Defaults to "8") If additional instances are required, 
they're created,
+ *  but not pooled.</li>
+ * </ul>
+ *
+ * @version CVS $Revision: 1.5 $ $Date: 2004/03/30 14:15:23 $
+ */
+public class PoolableComponentHandler
+extends AbstractComponentHandler {
+    
+    /** The default max size of the pool */
+    public static final int DEFAULT_MAX_POOL_SIZE = 8;
+
+    /**
+     * Object used to synchronize access to the get and put methods
+     */
+    protected final Object semaphore = new Object();
+
+    /**
+     * The maximum size of the pool.
+     */
+    private final int max;
+
+    /**
+     * List of the Poolable instances which are available for use.
+     */
+    private LinkedList ready;
+
+    /**
+     * Store the size of the ready list to optimize operations which require 
this value.
+     */
+    private int readySize;
+
+    /**
+     * Total number of Poolable instances in the pool
+     */
+    private int size;
+    
+    /**
+     * Create a PoolableComponentHandler which manages a pool of Components
+     *  created by the specified factory object.
+     *
+     * @param factory The factory object which is responsible for creating the 
components
+     *                managed by the ComponentHandler.
+     * @param config The configuration to use to configure the pool.
+     */
+    public PoolableComponentHandler( final Logger logger,
+                                     final ComponentFactory factory,
+                                     final Configuration config )
+    throws Exception {
+        super(logger, factory);
+
+        final int poolMax = config.getAttributeAsInteger( "pool-max", 
DEFAULT_MAX_POOL_SIZE );
+        this.max = ( poolMax <= 0 ? Integer.MAX_VALUE : poolMax );
+
+        // Create the pool lists.
+        this.ready = new LinkedList();
+    }
+
+    /**
+     * Dispose of the ComponentHandler and any associated Pools and Factories.
+     */
+    public void dispose() {
+        super.dispose();
+
+        // Any Poolables in the m_ready list need to be disposed of
+        synchronized( this.semaphore ) {
+            // Remove objects in the ready list.
+            for( Iterator iter = this.ready.iterator(); iter.hasNext(); ) {
+                Poolable poolable = (Poolable)iter.next();
+                iter.remove();
+                this.readySize--;
+                permanentlyRemovePoolable( poolable );
+            }
+
+            if( ( this.size > 0 ) && this.logger.isDebugEnabled() ) {
+                this.logger.debug( "There were " + this.size
+                                   + " outstanding objects when the pool was 
disposed." );
+            }
+        }
+    }
+    
+    /**
+     * Permanently removes a poolable from the pool's active list and
+     *  destroys it so that it will not ever be reused.
+     * <p>
+     * This method is only called by threads that have m_semaphore locked.
+     */
+    protected void permanentlyRemovePoolable( Object poolable ) {
+        this.size--;
+        this.removePoolable( poolable );
+    }
+
+    /**
+     * Called when an object is being removed permanently from the pool.
+     * This is the method to override when you need to enforce destructional
+     * policies.
+     * <p>
+     * This method is only called by threads that have m_semaphore locked.
+     *
+     * @param poolable Poolable to be completely removed from the pool.
+     */
+    protected void removePoolable( Object poolable ) {
+        try {
+            this.factory.decommission( poolable );
+        } catch( Exception e ) {
+            if( this.logger.isDebugEnabled() ) {
+                this.logger.debug( "Error decommissioning object", e );
+            }
+        }
+    }
+
+    /**
+     * Gets a Poolable from the pool.  If there is room in the pool, a new 
Poolable will be
+     *  created.  Depending on the parameters to the constructor, the method 
may block or throw
+     *  an exception if a Poolable is not available on the pool.
+     *
+     * @return Always returns a Poolable.  Contract requires that put must 
always be called with
+     *  the Poolable returned.
+     * @throws Exception An exception may be thrown as described above or if 
there is an exception
+     *  thrown by the ObjectFactory's newInstance() method.
+     */
+    protected Object doGet() throws Exception {
+        Object poolable;
+        synchronized( this.semaphore ) {
+            // Look for a Poolable at the end of the m_ready list
+            if( this.readySize > 0 ){
+                // A poolable is ready and waiting in the pool
+                poolable = (Poolable)this.ready.removeLast();
+                this.readySize--;
+            } else {
+                // Create a new poolable.  May throw an exception if the 
poolable can not be
+                //  instantiated.
+                poolable = this.factory.newInstance();
+                this.size++;
+
+                if( this.logger.isDebugEnabled() ) {
+                    this.logger.debug( "Created a new " + 
poolable.getClass().getName()
+                                       + " from the object factory." );
+                }
+            }
+        }
+
+        if( this.logger.isDebugEnabled() ) {
+            this.logger.debug( "Got a " + poolable.getClass().getName() + " 
from the pool." );
+        }
+
+        return poolable;
+    }
+
+    /**
+     * Returns a poolable to the pool 
+     *
+     * @param poolable Poolable to return to the pool.
+     */
+    protected void doPut( final Object poolable ) {
+        // Handle Recyclable objects
+        if( poolable instanceof Recyclable ) {
+            ( (Recyclable)poolable ).recycle();
+        }
+
+        synchronized( this.semaphore ) {
+            if( this.size <= this.max )
+            {
+                if( this.disposed ) {
+                    // The pool has already been disposed.
+                    if( this.logger.isDebugEnabled() ) {
+                        this.logger.debug( "Put called for a " + 
poolable.getClass().getName()
+                                           + " after the pool was disposed." );
+                    }
+
+                    this.permanentlyRemovePoolable( poolable );
+                } else {
+                    // There is room in the pool to keep this poolable.
+                    if( this.logger.isDebugEnabled() ) {
+                        this.logger.debug( "Put a " + 
poolable.getClass().getName()
+                                           + " back into the pool." );
+                    }
+
+                    this.ready.addLast( poolable );
+                    this.readySize++;
+
+                }
+            } else {
+                // More Poolables were created than can be held in the pool, 
so remove.
+                if( this.logger.isDebugEnabled() ) {
+                    this.logger.debug( "No room to put a " + 
poolable.getClass().getName()
+                                       + " back into the pool, so remove it." 
);
+                }
+
+                this.permanentlyRemovePoolable( poolable );
+            }
+        }
+    }
+
+    
+}

Added: 
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/RoleManager.java
==============================================================================
--- (empty file)
+++ 
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/RoleManager.java
  Tue Oct 19 12:10:38 2004
@@ -0,0 +1,215 @@
+/* 
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed  under the  Apache License,  Version 2.0  (the "License");
+ * you may not use  this file  except in  compliance with the License.
+ * You may obtain a copy of the License at 
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed  under the  License is distributed on an "AS IS" BASIS,
+ * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
+ * implied.
+ * 
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.container;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.avalon.framework.configuration.Configurable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+
+/**
+ * Default RoleManager implementation.  It populates the RoleManager
+ * from a configuration file.
+ *
+ * @version CVS $Revision: 1.4 $ $Date: 2004/02/28 11:47:14 $
+ */
+public class RoleManager
+extends AbstractLogEnabled
+implements Configurable {
+    
+    /** Map for shorthand to role mapping */
+    private Map shorthands;
+
+    /** Map for role to default classname mapping */
+    private Map classNames;
+
+    /** Map for role->hint to classname mapping */
+    private Map hintClassNames;
+
+    /** Parent <code>RoleManager</code> for nested resolution */
+    private final RoleManager parent;
+
+    /**
+     * Default constructor--this RoleManager has no parent.
+     */
+    public RoleManager() {
+        this.parent = null;
+    }
+
+    /**
+     * Alternate constructor--this RoleManager has the specified
+     * parent.
+     *
+     * @param parent  The parent <code>RoleManager</code>.
+     */
+    public RoleManager( RoleManager parent ) {
+        this.parent = parent;
+    }
+
+    /**
+     * Retrieves the real role name from a shorthand name.  Usually
+     * the shorthand name refers to a configuration element name.  If
+     * this RoleManager does not have the match, and there is a parent
+     * RoleManager, the parent will be asked to resolve the role.
+     *
+     * @param shorthandName  The shortname that is an alias for the role.
+     * @return the official role name.
+     */
+    public final String getRoleForName( final String shorthandName ) {
+        final String role = (String)this.shorthands.get( shorthandName );
+
+        if( null == role && null != this.parent )
+        {
+            return this.parent.getRoleForName( shorthandName );
+        }
+
+        if( this.getLogger().isDebugEnabled() ) {
+            this.getLogger().debug( "looking up shorthand " + shorthandName +
+                               ", returning " + role );
+        }
+
+        return role;
+    }
+
+    /**
+     * Retrieves the default class name for the specified role.  This
+     * is only called when the configuration does not specify the
+     * class explicitly.  If this RoleManager does not have the match,
+     * and there is a parent RoleManager, the parent will be asked
+     * to resolve the class name.
+     *
+     * @param role  The role that has a default implementation.
+     * @return the Fully Qualified Class Name (FQCN) for the role.
+     */
+    public final String getDefaultClassNameForRole( final String role ) {
+        final String className = (String)this.classNames.get( role );
+
+        if( null == className && null != this.parent )
+        {
+            return this.parent.getDefaultClassNameForRole( role );
+        }
+
+        return className;
+    }
+
+    /**
+     * Retrieves a default class name for a role/hint combination.
+     * This is only called when a role is mapped to a
+     * DefaultComponentSelector, and the configuration elements use
+     * shorthand names for the type of component.  If this RoleManager
+     * does not have the match, and there is a parent RoleManager, the
+     * parent will be asked to resolve the class name.
+     *
+     * @param role  The role that this shorthand refers to.
+     * @param shorthand  The shorthand name for the type of Component
+     * @return the FQCN for the role/hint combination.
+     */
+    public final String getDefaultClassNameForHint( final String role,
+                                                    final String shorthand ) {
+        if( getLogger().isDebugEnabled() ) {
+            getLogger().debug( "looking up hintmap for role " + role );
+        }
+
+        final Map hintMap = (Map)this.hintClassNames.get( role );
+
+        if( null == hintMap ) {
+            if( null != this.parent ) {
+                return this.parent.getDefaultClassNameForHint( role, shorthand 
);
+            } else {
+                return "";
+            }
+        }
+
+        if( getLogger().isDebugEnabled() ) {
+            getLogger().debug( "looking up classname for hint " + shorthand );
+        }
+
+        final String s = ( String ) hintMap.get( shorthand );
+
+        if( s == null && null != this.parent ) {
+            return this.parent.getDefaultClassNameForHint( role, shorthand );
+        } else {
+            return s;
+        }
+    }
+
+    /**
+     * Reads a configuration object and creates the role, shorthand,
+     * and class name mapping.
+     *
+     * @param configuration  The configuration object.
+     * @throws ConfigurationException if the configuration is malformed
+     */
+    public final void configure( final Configuration configuration )
+    throws ConfigurationException {
+        final Map shorts = new HashMap();
+        final Map classes = new HashMap();
+        final Map hintclasses = new HashMap();
+
+        final Configuration[] roles = configuration.getChildren( "role" );
+
+        for( int i = 0; i < roles.length; i++ ) {
+            final String name = roles[ i ].getAttribute( "name" );
+            final String shorthand = roles[ i ].getAttribute( "shorthand" );
+            final String defaultClassName =
+                roles[ i ].getAttribute( "default-class", null );
+
+            shorts.put( shorthand, name );
+
+            if( null != defaultClassName ) {
+                classes.put( name, defaultClassName );
+            }
+
+            final Configuration[] hints = roles[ i ].getChildren( "hint" );
+            if( hints.length > 0 ) {
+                HashMap hintMap = new HashMap();
+
+                for( int j = 0; j < hints.length; j++ ) {
+                    final String shortHand = hints[ j ].getAttribute( 
"shorthand" ).trim();
+                    String className = hints[ j ].getAttribute( "class" 
).trim();
+
+                    if ( 
"org.apache.cocoon.components.ExtendedComponentSelector".equals(className)) {
+                        className = CocoonServiceSelector.class.getName();
+                    } else if 
("org.apache.cocoon.components.treeprocessor.sitemap.ComponentsSelector".equals(className)
 ) {
+                        className = ComponentsSelector.class.getName();
+                    }
+                    hintMap.put( shortHand, className );
+                    if( getLogger().isDebugEnabled() ) {
+                        getLogger().debug( "Adding hint type " + shortHand +
+                                           " associated with role " + name +
+                                           " and class " + className );
+                    }
+                }
+
+                hintclasses.put( name, Collections.unmodifiableMap( hintMap ) 
);
+            }
+
+            if( getLogger().isDebugEnabled() ) {
+                getLogger().debug( "added Role " + name + " with shorthand " +
+                                   shorthand + " for " + defaultClassName );
+            }
+        }
+
+        this.shorthands = Collections.unmodifiableMap( shorts );
+        this.classNames = Collections.unmodifiableMap( classes );
+        this.hintClassNames = Collections.unmodifiableMap( hintclasses );
+    }
+}

Added: 
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/SingleThreadedComponentHandler.java
==============================================================================
--- (empty file)
+++ 
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/SingleThreadedComponentHandler.java
       Tue Oct 19 12:10:38 2004
@@ -0,0 +1,75 @@
+/* 
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed  under the  Apache License,  Version 2.0  (the "License");
+ * you may not use  this file  except in  compliance with the License.
+ * You may obtain a copy of the License at 
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed  under the  License is distributed on an "AS IS" BASIS,
+ * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
+ * implied.
+ * 
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.container;
+
+import org.apache.avalon.framework.logger.Logger;
+
+/**
+ * The DefaultComponentHandler to make sure components are initialized
+ * and destroyed correctly.
+ *
+ * @version CVS $Revision: 1.4 $ $Date: 2004/02/28 11:47:14 $
+ */
+public class SingleThreadedComponentHandler
+extends AbstractComponentHandler {
+
+    /**
+     * Create a DefaultComponentHandler which manages a pool of Components
+     *  created by the specified factory object.
+     *
+     * @param factory The factory object which is responsible for creating the 
components
+     *                managed by the ComponentHandler.
+     * @param config The configuration to use to configure the pool.
+     *
+     * @throws Exception If there are any problems creating the handler.
+     */
+    public SingleThreadedComponentHandler( final Logger logger,
+                                    final ComponentFactory factory ) {
+        super(logger, factory);
+    }
+
+    /**
+     * Get a reference of the desired Component
+     *
+     * @return A component instance.
+     *
+     * @throws Exception If there are any problems encountered acquiring a
+     *                   component instance.
+     */
+    protected Object doGet()
+    throws Exception {
+        return this.factory.newInstance();
+    }
+
+    /**
+     * Return a reference of the desired Component
+     *
+     * @param component Component to be be put/released back to the handler.
+     */
+    protected void doPut( final Object component ) {
+        try {
+            this.factory.decommission( component );
+        } catch( final Exception e ) {
+            if( this.logger.isWarnEnabled() )
+            {
+                this.logger.warn( "Error decommissioning component: "
+                    + this.factory.getCreatedClass().getName(), e );
+            }
+        }
+    }
+
+}

Added: 
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/ThreadSafeComponentHandler.java
==============================================================================
--- (empty file)
+++ 
cocoon/whiteboard/ecmplus/src/java/org/apache/cocoon/components/container/ThreadSafeComponentHandler.java
   Tue Oct 19 12:10:38 2004
@@ -0,0 +1,85 @@
+/* 
+ * Copyright 2002-2004 The Apache Software Foundation
+ * Licensed  under the  Apache License,  Version 2.0  (the "License");
+ * you may not use  this file  except in  compliance with the License.
+ * You may obtain a copy of the License at 
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed  under the  License is distributed on an "AS IS" BASIS,
+ * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
+ * implied.
+ * 
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.components.container;
+
+import org.apache.avalon.framework.logger.Logger;
+
+/**
+ * The ThreadSafeComponentHandler to make sure components are initialized
+ * and destroyed correctly.
+ *
+ * @version CVS $Revision: 1.4 $ $Date: 2004/02/28 11:47:14 $
+ */
+public class ThreadSafeComponentHandler
+extends AbstractComponentHandler {
+    
+    private Object instance;
+
+    /**
+     * Create a ThreadSafeComponentHandler which manages a single instance
+     * of an object return by the component factory.
+     * @param logger The logger to use
+     * @param factory The factory object which is responsible for creating the 
components
+     *                managed by the handler.
+     */
+    public ThreadSafeComponentHandler( final Logger logger,
+                                       final ComponentFactory factory ) {
+        super(logger, factory);
+    }
+    
+    public void initialize() 
+    throws Exception {
+        if( this.initialized ) {
+            return;
+        }
+        if( this.instance == null ) {
+            this.instance = this.factory.newInstance();
+        }
+        super.initialize();
+    }
+
+    /**
+     * Get a reference of the desired Component
+     */
+    protected Object doGet()
+    throws Exception {
+        return this.instance;
+    }
+
+    /**
+     * Return a reference of the desired Component
+     */
+    protected void doPut( final Object component ) {
+        // nothing to do
+    }
+
+    /**
+     * Dispose of the ComponentHandler and any associated Pools and Factories.
+     */
+    public void dispose() {
+        try {
+            this.factory.decommission( this.instance );
+            this.instance = null;
+        } catch( final Exception e ) {
+            if( this.logger.isWarnEnabled() ) {
+                this.logger.warn( "Error decommissioning component: " +
+                                  this.factory.getCreatedClass().getName(), e 
);
+            }
+        }
+        super.dispose();
+    }
+}

Reply via email to