leosimons 2003/01/05 14:16:41 Modified: fortress/src/java/org/apache/excalibur/fortress/container AbstractContainer.java Log: more doccing. Some questions about design decisions sprinkeled through the code Revision Changes Path 1.23 +610 -557 jakarta-avalon-excalibur/fortress/src/java/org/apache/excalibur/fortress/container/AbstractContainer.java Index: AbstractContainer.java =================================================================== RCS file: /home/cvs/jakarta-avalon-excalibur/fortress/src/java/org/apache/excalibur/fortress/container/AbstractContainer.java,v retrieving revision 1.22 retrieving revision 1.23 diff -u -r1.22 -r1.23 --- AbstractContainer.java 5 Jan 2003 17:03:03 -0000 1.22 +++ AbstractContainer.java 5 Jan 2003 22:16:41 -0000 1.23 @@ -1,558 +1,611 @@ -/* - - ============================================================================ - The Apache Software License, Version 1.1 - ============================================================================ - - Copyright (C) @year@ The Apache Software Foundation. All rights reserved. - - Redistribution and use in source and binary forms, with or without modifica- - tion, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - 3. The end-user documentation included with the redistribution, if any, must - include the following acknowledgment: "This product includes software - developed by the Apache Software Foundation (http://www.apache.org/)." - Alternately, this acknowledgment may appear in the software itself, if - and wherever such third-party acknowledgments normally appear. - - 4. The names "Jakarta", "Avalon", "Excalibur" and "Apache Software Foundation" - must not be used to endorse or promote products derived from this software - without prior written permission. For written permission, please contact - [EMAIL PROTECTED] - - 5. Products derived from this software may not be called "Apache", nor may - "Apache" appear in their name, without prior written permission of the - Apache Software Foundation. - - THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, - INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- - DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - This software consists of voluntary contributions made by many individuals - on behalf of the Apache Software Foundation. For more information on the - Apache Software Foundation, please see <http://www.apache.org/>. - -*/ -package org.apache.excalibur.fortress.container; - -import java.util.ArrayList; -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.Configuration; -import org.apache.avalon.framework.container.ContainerUtil; -import org.apache.avalon.framework.context.Context; -import org.apache.avalon.framework.context.ContextException; -import org.apache.avalon.framework.context.Contextualizable; -import org.apache.avalon.framework.logger.AbstractLogEnabled; -import org.apache.avalon.framework.service.DefaultServiceManager; -import org.apache.avalon.framework.service.ServiceException; -import org.apache.avalon.framework.service.ServiceManager; -import org.apache.avalon.framework.service.Serviceable; -import org.apache.commons.collections.BoundedFifoBuffer; -import org.apache.commons.collections.StaticBucketMap; -import org.apache.excalibur.event.Queue; -import org.apache.excalibur.fortress.Container; -import org.apache.excalibur.fortress.container.commands.PrepareHandlerCommand; -import org.apache.excalibur.fortress.handler.ComponentFactory; -import org.apache.excalibur.fortress.handler.ComponentHandler; -import org.apache.excalibur.fortress.handler.LEAwareComponentHandler; -import org.apache.excalibur.fortress.handler.ProxyObjectFactory; -import org.apache.excalibur.fortress.lifecycle.LifecycleExtensionManager; -import org.apache.excalibur.fortress.lookup.FortressServiceManager; -import org.apache.excalibur.fortress.lookup.FortressServiceSelector; -import org.apache.excalibur.fortress.role.ExcaliburRoleManager; -import org.apache.excalibur.fortress.role.RoleEntry; -import org.apache.excalibur.fortress.role.RoleManager; -import org.apache.excalibur.instrument.InstrumentManager; -import org.apache.excalibur.instrument.Instrumentable; -import org.apache.excalibur.mpool.ObjectFactory; -import org.apache.excalibur.mpool.PoolManager; - -/** - * This abstract implementation provides basic functionality for building - * an implementation of the [EMAIL PROTECTED] Container} interface. - * It exposes a protected getServiceManager() method so that the - * Container's Manager can expose that to the instantiating class. - * - * @author <a href="mailto:avalon-dev@jakarta.apache.org">The Avalon Team</a> - * @version CVS $Revision$ $Date$ - */ -public abstract class AbstractContainer - extends AbstractLogEnabled - implements Contextualizable, Serviceable, Initializable, Disposable, Container -{ - /** contains the container's context passed in through contextualize() */ - protected Context m_context; - /** contains the ServiceManager the container will use, based on the one passed in through service() */ - protected ServiceManager m_serviceManager; - /** contains the container's LoggerManager, which is extracted from m_serviceManager */ - protected LoggerManager m_loggerManager; - /** contains the container's PoolManager, which is extracted from m_serviceManager */ - protected PoolManager m_poolManager; - /** contains the container's Queue, which is extracted from m_serviceManager */ - protected Queue m_commandQueue; - /** contains the container's root ClassLoader, which is extracted from m_serviceManager */ - protected ClassLoader m_classLoader; - /** contains the container's RoleManager, which is extracted from m_serviceManager */ - protected RoleManager m_roleManager; - /** contains the container's InstrumentManager, which is extracted from m_serviceManager */ - protected InstrumentManager m_instrumentManager; - /** contains the container's LifecycleExtensionManager, which is extracted from m_serviceManager */ - protected LifecycleExtensionManager m_extManager; - /** - * Contains entries mapping roles to hint maps, where the hint map contains - * mappings from hints to ComponentHandlers. - */ - protected Map m_mapper = new StaticBucketMap(); - /** Contains an entry for each ComponentHandler */ - protected List m_components = new ArrayList( 10 ); - - /** - * Pull the manager items from the context so we can use them to set up - * the system. - * - * @avalon.context type="ClassLoader" optional="true" - */ - public void contextualize( final Context context ) - throws ContextException - { - m_context = context; - try - { - m_classLoader = (ClassLoader)context.get( ClassLoader.class.getName() ); - } - catch( ContextException ce ) - { - m_classLoader = Thread.currentThread().getContextClassLoader(); - } - } - - /** - * Root ServiceManager. The Container may choose to have it's - * ServiceManager delegate to the root manager, or it may choose to be - * entirely self contained. - * - * @avalon.dependency type="LoggerManager" - * @avalon.dependency type="PoolManager" - * @avalon.dependency type="InstrumentManager" - * @avalon.dependency type="LifecycleExtensionManager" optional="true" - * @avalon.dependency type="RoleManager" optional="true" - * @avalon.dependency type="Queue" optional="true" - */ - public void service( final ServiceManager serviceManager ) - throws ServiceException - { - // get non-optional services - - m_loggerManager = (LoggerManager)serviceManager.lookup( LoggerManager.ROLE ); - m_poolManager = (PoolManager)serviceManager.lookup( PoolManager.ROLE ); - m_instrumentManager = (InstrumentManager)serviceManager.lookup( InstrumentManager.ROLE ); - - // get optional services, or a default if the service isn't provided - - if( serviceManager.hasService( LifecycleExtensionManager.ROLE ) ) - { - m_extManager = - (LifecycleExtensionManager)serviceManager.lookup( LifecycleExtensionManager.ROLE ); - } - else - { - m_extManager = new LifecycleExtensionManager(); - m_extManager.enableLogging( getLogger() ); - - if( getLogger().isDebugEnabled() ) - { - final String message = - "No Container.LIFECYCLE_EXTENSION_MANAGER is given, " + - "installing default lifecycle extension manager with " + - "0 extensions"; - getLogger().debug( message ); - } - } - - if( serviceManager.hasService( Queue.ROLE ) ) - { - m_commandQueue = (Queue)serviceManager.lookup( Queue.ROLE ); - } - else - { - final String message = - "No Container.COMMAND_QUEUE is given, all " + - "management will be performed synchronously"; - getLogger().warn( message ); - } - - if( serviceManager.hasService( RoleManager.ROLE ) ) - { - m_roleManager = (RoleManager)serviceManager.lookup( RoleManager.ROLE ); - } - else - { - try - { - m_roleManager = createDefaultRoleManager(); - } - catch( final Exception e ) - { - final String message = "Unable to create default role manager"; - throw new ServiceException( message, e ); - } - } - - // set up our ServiceManager - m_serviceManager = new FortressServiceManager( this, serviceManager ); - } - - /** - * Create a default RoleManager that can be used to addRole system. - * - * @return the default role manager - * @throws Exception if unable to create role manager - */ - private ExcaliburRoleManager createDefaultRoleManager() - throws Exception - { - final ExcaliburRoleManager roleManager = - new ExcaliburRoleManager( null, m_classLoader ); - ContainerUtil.enableLogging( roleManager, getLogger().getChildLogger( "roles" ) ); - ContainerUtil.initialize( roleManager ); - return roleManager; - } - - /** - * Add a Component into the container. This sets the component up for management - * by the container by creating an appropriate [EMAIL PROTECTED] ComponentHandler}. - */ - protected void addComponent( final ComponentHandlerMetaData metaData ) - { - // figure out Role - final String classname = metaData.getClassname(); - final RoleEntry roleEntry = m_roleManager.getRoleForClassname( classname ); - if( null == roleEntry ) - { - final String message = "No role defined for " + classname; - throw new IllegalArgumentException( message ); - } - - // create a handler for the combo of Role+MetaData - final ComponentHandler handler = - getComponentHandler( roleEntry, metaData ); - - final String role = roleEntry.getRole(); - - // put the role into our role mapper. If the role doesn't exist - // yet, just stuff it in as "default". If it does, we create a - // ServiceSelector and put that in as "selector". - if( null != role && null != classname && null != handler ) - { - Map hintMap = (StaticBucketMap)m_mapper.get( role ); - - if( null == hintMap ) - { - hintMap = new StaticBucketMap(); - } - - hintMap.put( metaData.getName(), handler ); - - if( hintMap.containsKey( "default" ) ) - { - if( !hintMap.containsKey( "selector" ) ) - { - hintMap.put( "selector", - new FortressServiceSelector( this, role ) ); - } - } - else - { - hintMap.put( "default", handler ); - } - - // FIXME(MC) is this always needed ? should only need to be done if - // hintMap is not yet in m_mapper ? or is it here due to threading issues ? - m_mapper.put( role, hintMap ); - } - } - - - /** - * Get a ComponentHandler with the standard - * <code>HANDLER_CONSTRUCTOR</code> for the component class passed in. - */ - private ComponentHandler getComponentHandler( final RoleEntry roleEntry, - final ComponentHandlerMetaData metaData ) - { - ComponentHandler handler = null; - final String classname = roleEntry.getComponentClass().getName(); - try - { - final Configuration configuration = metaData.getConfiguration(); - final ObjectFactory factory = - createObjectFactory( classname, configuration ); - - final ComponentHandler targetHandler = - (ComponentHandler)roleEntry.getHandlerClass().newInstance(); - - ContainerUtil.contextualize( targetHandler, m_context ); - - final DefaultServiceManager serviceManager = - new DefaultServiceManager( getServiceManager() ); - serviceManager.put( ObjectFactory.ROLE, factory ); - serviceManager.makeReadOnly(); - - ContainerUtil.service( targetHandler, serviceManager ); - ContainerUtil.configure( targetHandler, configuration ); - ContainerUtil.initialize( targetHandler ); - - if( targetHandler instanceof Instrumentable ) - { - final Instrumentable instrumentable = (Instrumentable)targetHandler; - final String name = instrumentable.getInstrumentableName(); - m_instrumentManager.registerInstrumentable( instrumentable, name ); - } - - handler = - new LEAwareComponentHandler( targetHandler, m_extManager, m_context ); - } - catch( final Exception e ) - { - if( getLogger().isDebugEnabled() ) - { - final String message = - "Could not create the handler for the '" + - classname + "' component."; - getLogger().debug( message, e ); - } - return null; - } - - if( getLogger().isDebugEnabled() ) - { - final String message = - "Component " + classname + - " uses handler " + roleEntry.getHandlerClass().getName(); - getLogger().debug( message ); - } - - final ComponentHandlerEntry entry = - new ComponentHandlerEntry( handler, metaData ); - m_components.add( entry ); - - return handler; - } - - /** - * Create an objectFactory for specified Object configuration. - * - * @param classname the classname of object - * @param configuration the objests configuration - * @return the ObjectFactory - * @throws Exception if unable to create object factory - */ - protected ObjectFactory createObjectFactory( final String classname, - final Configuration configuration ) - throws Exception - { - final Class clazz = m_classLoader.loadClass( classname ); - final ComponentFactory componentFactory = - new ComponentFactory( clazz, configuration, - m_serviceManager, m_context, - m_loggerManager, m_extManager, - m_instrumentManager ); - return new ProxyObjectFactory( componentFactory ); - } - - /** - * This is the method that the ContainerComponentManager and Selector use - * to gain access to the ComponentHandlers and ComponentSelectors. The - * actual access of the ComponentHandler is delegated to the Container. - * - * @param role The role we intend to access a Component for. - * @param hint The hint that we use as a qualifier - * (note: if null, the default implementation is returned). - * - * @return Object a reference to the ComponentHandler or - * ComponentSelector for the role/hint combo. - */ - public Object get( final String role, final Object hint ) - throws ServiceException - { - final Map hintMap = (StaticBucketMap)m_mapper.get( role ); - Object value; - - if( null == hintMap ) - { - final String key = role + "/" + hint; - final String message = "Component does not exist"; - throw new ServiceException( key, message ); - } - - if( null == hint ) - { - value = hintMap.get( "selector" ); - - if( null == value ) - { - value = hintMap.get( "default" ); - } - - return value; - } - - value = hintMap.get( hint ); - - if( null == value ) - { - final String key = role + "/" + hint; - final String message = "Component does not exist"; - throw new ServiceException( key, message ); - } - - return value; - } - - /** - * This is the method that the ContainerComponentManager and Selector use - * to gain access to the ComponentHandlers and ComponentSelectors. The - * actual access of the ComponentHandler is delegated to the Container. - * - * @param role The role we intend to access a Component for. - * @param hint The hint that we use as a qualifier - * (note: if null, the default implementation is returned). - * - * @return true if a reference to the role exists. - */ - public boolean has( final String role, final Object hint ) - { - final Map hintMap = (StaticBucketMap)m_mapper.get( role ); - boolean hasComponent = false; - - if( null != hintMap ) - { - hasComponent = true; - } - - if( hasComponent ) - { - if( null == hint ) - { - hasComponent = hintMap.containsKey( "selector" ); - - if( !hasComponent ) - { - hasComponent = hintMap.containsKey( "default" ); - } - } - else - { - hasComponent = hintMap.containsKey( hint ); - } - } - - return hasComponent; - } - - /** - * Initializes all components so that the system is ready to be used. - */ - public void initialize() - throws Exception - { - final Iterator i = m_components.iterator(); - final BoundedFifoBuffer buffer = new BoundedFifoBuffer( m_components.size() ); - - while( i.hasNext() ) - { - try - { - final ComponentHandlerEntry entry = (ComponentHandlerEntry)i.next(); - final ComponentHandler handler = entry.getHandler(); - if( !entry.getMetaData().isLazyActivation() ) - { - if( null != m_commandQueue ) - { - final PrepareHandlerCommand element = - new PrepareHandlerCommand( handler, getLogger() ); - m_commandQueue.enqueue( element ); - } - else - { - handler.prepareHandler(); - } - } - else - { - if( getLogger().isDebugEnabled() ) - { - final String message = "ComponentHandler (" + handler + - ") has specified request time initialization policy, " + - "initialization deferred till first use"; - getLogger().debug( message ); - } - } - } - catch( final Exception e ) - { - if( getLogger().isWarnEnabled() ) - { - final String message = "Could not initialize component"; - getLogger().warn( message, e ); - } - buffer.add( e ); - } - } - - if( buffer.size() > 0 ) - { - final StringBuffer message = new StringBuffer(); - while( !buffer.isEmpty() ) - { - message.append( ( (Exception)buffer.remove() ).getMessage() ); - } - - throw new Exception( message.toString() ); - } - } - - /** - * Disposes of all components and frees resources that they consume. - */ - public void dispose() - { - final Iterator i = m_components.iterator(); - while( i.hasNext() ) - { - final ComponentHandlerEntry entry = (ComponentHandlerEntry)i.next(); - final ComponentHandler handler = entry.getHandler(); - ContainerUtil.dispose( handler ); - } - } - - /** - * Exposes to subclasses the service manager which this container - * uses to manage its child components. - * - * @return the child component manager - */ - protected ServiceManager getServiceManager() - { - return m_serviceManager; - } +/* + + ============================================================================ + The Apache Software License, Version 1.1 + ============================================================================ + + Copyright (C) @year@ The Apache Software Foundation. All rights reserved. + + Redistribution and use in source and binary forms, with or without modifica- + tion, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. The end-user documentation included with the redistribution, if any, must + include the following acknowledgment: "This product includes software + developed by the Apache Software Foundation (http://www.apache.org/)." + Alternately, this acknowledgment may appear in the software itself, if + and wherever such third-party acknowledgments normally appear. + + 4. The names "Jakarta", "Avalon", "Excalibur" and "Apache Software Foundation" + must not be used to endorse or promote products derived from this software + without prior written permission. For written permission, please contact + [EMAIL PROTECTED] + + 5. Products derived from this software may not be called "Apache", nor may + "Apache" appear in their name, without prior written permission of the + Apache Software Foundation. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- + DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + This software consists of voluntary contributions made by many individuals + on behalf of the Apache Software Foundation. For more information on the + Apache Software Foundation, please see <http://www.apache.org/>. + +*/ +package org.apache.excalibur.fortress.container; + +import java.util.ArrayList; +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.Configuration; +import org.apache.avalon.framework.container.ContainerUtil; +import org.apache.avalon.framework.context.Context; +import org.apache.avalon.framework.context.ContextException; +import org.apache.avalon.framework.context.Contextualizable; +import org.apache.avalon.framework.logger.AbstractLogEnabled; +import org.apache.avalon.framework.service.DefaultServiceManager; +import org.apache.avalon.framework.service.ServiceException; +import org.apache.avalon.framework.service.ServiceManager; +import org.apache.avalon.framework.service.Serviceable; +import org.apache.commons.collections.BoundedFifoBuffer; +import org.apache.commons.collections.StaticBucketMap; +import org.apache.excalibur.event.Queue; +import org.apache.excalibur.fortress.Container; +import org.apache.excalibur.fortress.container.commands.PrepareHandlerCommand; +import org.apache.excalibur.fortress.handler.ComponentFactory; +import org.apache.excalibur.fortress.handler.ComponentHandler; +import org.apache.excalibur.fortress.handler.LEAwareComponentHandler; +import org.apache.excalibur.fortress.handler.ProxyObjectFactory; +import org.apache.excalibur.fortress.lifecycle.LifecycleExtensionManager; +import org.apache.excalibur.fortress.lookup.FortressServiceManager; +import org.apache.excalibur.fortress.lookup.FortressServiceSelector; +import org.apache.excalibur.fortress.role.ExcaliburRoleManager; +import org.apache.excalibur.fortress.role.RoleEntry; +import org.apache.excalibur.fortress.role.RoleManager; +import org.apache.excalibur.instrument.InstrumentManager; +import org.apache.excalibur.instrument.Instrumentable; +import org.apache.excalibur.mpool.ObjectFactory; +import org.apache.excalibur.mpool.PoolManager; + +/** + * This abstract implementation provides basic functionality for building + * an implementation of the [EMAIL PROTECTED] Container} interface. + * It exposes a protected getServiceManager() method so that the + * Container's Manager can expose that to the instantiating class. + * + * @author <a href="mailto:avalon-dev@jakarta.apache.org">The Avalon Team</a> + * @version CVS $Revision$ $Date$ + */ +public abstract class AbstractContainer + extends AbstractLogEnabled + implements Contextualizable, Serviceable, Initializable, Disposable, Container +{ + /** contains the container's context passed in through contextualize() */ + protected Context m_context; + /** contains the ServiceManager the container will use, based on the one passed in through service() */ + protected ServiceManager m_serviceManager; + /** contains the container's LoggerManager, which is extracted from m_serviceManager */ + protected LoggerManager m_loggerManager; + /** contains the container's PoolManager, which is extracted from m_serviceManager */ + protected PoolManager m_poolManager; + /** contains the container's Queue, which is extracted from m_serviceManager */ + protected Queue m_commandQueue; + /** contains the container's root ClassLoader, which is extracted from m_serviceManager */ + protected ClassLoader m_classLoader; + /** contains the container's RoleManager, which is extracted from m_serviceManager */ + protected RoleManager m_roleManager; + /** contains the container's InstrumentManager, which is extracted from m_serviceManager */ + protected InstrumentManager m_instrumentManager; + /** contains the container's LifecycleExtensionManager, which is extracted from m_serviceManager */ + protected LifecycleExtensionManager m_extManager; + /** + * Contains entries mapping roles to hint maps, where the hint map contains + * mappings from hints to ComponentHandlers. + */ + protected Map m_mapper = new StaticBucketMap(); + /** Contains an entry for each ComponentHandler */ + protected List m_components = new ArrayList( 10 ); + + /** + * Pull the manager items from the context so we can use them to set up + * the system. + * + * @avalon.context type="ClassLoader" optional="true" + */ + public void contextualize( final Context context ) + throws ContextException + { + m_context = context; + try + { + m_classLoader = (ClassLoader)context.get( ClassLoader.class.getName() ); + } + catch( ContextException ce ) + { + m_classLoader = Thread.currentThread().getContextClassLoader(); + } + } + + /** + * Root ServiceManager. The Container may choose to have it's + * ServiceManager delegate to the root manager, or it may choose to be + * entirely self contained. + * + * @avalon.dependency type="LoggerManager" + * @avalon.dependency type="PoolManager" + * @avalon.dependency type="InstrumentManager" + * @avalon.dependency type="LifecycleExtensionManager" optional="true" + * @avalon.dependency type="RoleManager" optional="true" + * @avalon.dependency type="Queue" optional="true" + */ + public void service( final ServiceManager serviceManager ) + throws ServiceException + { + // get non-optional services + + m_loggerManager = (LoggerManager)serviceManager.lookup( LoggerManager.ROLE ); + m_poolManager = (PoolManager)serviceManager.lookup( PoolManager.ROLE ); + m_instrumentManager = (InstrumentManager)serviceManager.lookup( InstrumentManager.ROLE ); + + // get optional services, or a default if the service isn't provided + + if( serviceManager.hasService( LifecycleExtensionManager.ROLE ) ) + { + m_extManager = + (LifecycleExtensionManager)serviceManager.lookup( LifecycleExtensionManager.ROLE ); + } + else + { + m_extManager = new LifecycleExtensionManager(); + m_extManager.enableLogging( getLogger() ); + + if( getLogger().isDebugEnabled() ) + { + final String message = + "No Container.LIFECYCLE_EXTENSION_MANAGER is given, " + + "installing default lifecycle extension manager with " + + "0 extensions"; + getLogger().debug( message ); + } + } + + if( serviceManager.hasService( Queue.ROLE ) ) + { + m_commandQueue = (Queue)serviceManager.lookup( Queue.ROLE ); + } + else + { + final String message = + "No Container.COMMAND_QUEUE is given, all " + + "management will be performed synchronously"; + getLogger().warn( message ); + } + + if( serviceManager.hasService( RoleManager.ROLE ) ) + { + m_roleManager = (RoleManager)serviceManager.lookup( RoleManager.ROLE ); + } + else + { + try + { + m_roleManager = createDefaultRoleManager(); + } + catch( final Exception e ) + { + final String message = "Unable to create default role manager"; + throw new ServiceException( RoleManager.ROLE, message, e ); + } + } + + // set up our ServiceManager + m_serviceManager = new FortressServiceManager( this, serviceManager ); + } + + /** + * Create a default RoleManager that can be used to addRole system. + * + * @return the default role manager + * @throws Exception if unable to create role manager + */ + private ExcaliburRoleManager createDefaultRoleManager() + throws Exception + { + final ExcaliburRoleManager roleManager = + new ExcaliburRoleManager( null, m_classLoader ); + ContainerUtil.enableLogging( roleManager, getLogger().getChildLogger( "roles" ) ); + ContainerUtil.initialize( roleManager ); + return roleManager; + } + + // TODO: shouldn't this rethrow its exception? + /** + * Add a Component into the container. This sets the component up for management + * by the container by creating an appropriate [EMAIL PROTECTED] ComponentHandler}. + * + * @param metaData the information needed to construct a ComponentHandler for the component + */ + protected void addComponent( final ComponentHandlerMetaData metaData ) + { + // figure out Role + final String classname = metaData.getClassname(); + final RoleEntry roleEntry = m_roleManager.getRoleForClassname( classname ); + if( null == roleEntry ) + { + final String message = "No role defined for " + classname; + throw new IllegalArgumentException( message ); + } + + // create a handler for the combo of Role+MetaData + final ComponentHandler handler = + getComponentHandler( roleEntry, metaData ); + + final String role = roleEntry.getRole(); + + // put the role into our role mapper. If the role doesn't exist + // yet, just stuff it in as "default". If it does, we create a + // ServiceSelector and put that in as "selector". + if( null != role && null != classname && null != handler ) + { + Map hintMap = (StaticBucketMap)m_mapper.get( role ); + + if( null == hintMap ) + { + hintMap = new StaticBucketMap(); + } + + hintMap.put( metaData.getName(), handler ); + + if( hintMap.containsKey( "default" ) ) + { + if( !hintMap.containsKey( "selector" ) ) + { + hintMap.put( "selector", + new FortressServiceSelector( this, role ) ); + } + } + else + { + hintMap.put( "default", handler ); + } + + // FIXME(MC) is this always needed ? should only need to be done if + // hintMap is not yet in m_mapper ? or is it here due to threading issues ? + m_mapper.put( role, hintMap ); + } + } + + + // TODO: what is a HANDLER_CONSTRUCTOR? + // TODO: shouldn't this rethrow its exception? + /** + * Get a ComponentHandler with the standard + * <code>HANDLER_CONSTRUCTOR</code> for the component class passed in. + * + * @param roleEntry the description of the Role this handler will be for + * @param metaData the information needed to construct a ComponentHandler for the component + */ + private ComponentHandler getComponentHandler( final RoleEntry roleEntry, + final ComponentHandlerMetaData metaData ) + { + // get info from params + ComponentHandler handler = null; + final String classname = roleEntry.getComponentClass().getName(); + try + { + final Configuration configuration = metaData.getConfiguration(); + final ObjectFactory factory = + createObjectFactory( classname, configuration ); + + // create the appropriate handler instance + final ComponentHandler targetHandler = + (ComponentHandler)roleEntry.getHandlerClass().newInstance(); + + // do the handler lifecycle + + ContainerUtil.contextualize( targetHandler, m_context ); + + final DefaultServiceManager serviceManager = + new DefaultServiceManager( getServiceManager() ); + serviceManager.put( ObjectFactory.ROLE, factory ); + serviceManager.makeReadOnly(); + + ContainerUtil.service( targetHandler, serviceManager ); + ContainerUtil.configure( targetHandler, configuration ); + ContainerUtil.initialize( targetHandler ); + + if( targetHandler instanceof Instrumentable ) + { + final Instrumentable instrumentable = (Instrumentable)targetHandler; + final String name = instrumentable.getInstrumentableName(); + m_instrumentManager.registerInstrumentable( instrumentable, name ); + } + + // TODO: should there be other lifecycle stages supported for + // ComponentHandler here as well? + + handler = + new LEAwareComponentHandler( targetHandler, m_extManager, m_context ); + } + catch( final Exception e ) + { + // if anything went wrong, the component cannot be worked with + // and it cannot be added into the container, so don't provide + // a handler + if( getLogger().isDebugEnabled() ) + { + final String message = + "Could not create the handler for the '" + + classname + "' component."; + getLogger().debug( message, e ); + } + return null; + } + + if( getLogger().isDebugEnabled() ) + { + final String message = + "Component " + classname + + " uses handler " + roleEntry.getHandlerClass().getName(); + getLogger().debug( message ); + } + + // we're still here, so everything went smooth. Register the handler + // and return it + final ComponentHandlerEntry entry = + new ComponentHandlerEntry( handler, metaData ); + m_components.add( entry ); + + return handler; + } + + /** + * Create an objectFactory for specified Object configuration. + * + * @param classname the classname of object + * @param configuration the objests configuration + * @return the ObjectFactory + * @throws Exception if unable to create object factory + */ + protected ObjectFactory createObjectFactory( final String classname, + final Configuration configuration ) + throws Exception + { + final Class clazz = m_classLoader.loadClass( classname ); + final ComponentFactory componentFactory = + new ComponentFactory( clazz, configuration, + m_serviceManager, m_context, + m_loggerManager, m_extManager, + m_instrumentManager ); + return new ProxyObjectFactory( componentFactory ); + } + + /** + * This is the method that the ContainerComponentManager and Selector use + * to gain access to the ComponentHandlers and ComponentSelectors. The + * actual access of the ComponentHandler is delegated to the Container. + * + * @param role The role we intend to access a Component for. + * @param hint The hint that we use as a qualifier + * (note: if null, the default implementation is returned). + * + * @return Object a reference to the ComponentHandler or + * ComponentSelector for the role/hint combo. + */ + public Object get( final String role, final Object hint ) + throws ServiceException + { + final Map hintMap = (StaticBucketMap)m_mapper.get( role ); + Object value; + + if( null == hintMap ) + { + // hints are tacked on using a '/'. + // TODO: is it smart to hardcode it this way? + final String key = role + "/" + hint; + final String message = "Component does not exist"; + throw new ServiceException( key, message ); + } + + if( null == hint ) + { + // no hint -> try selector + value = hintMap.get( "selector" ); + + if( null == value ) + { + // no selector -> use default + value = hintMap.get( "default" ); + } + + return value; + } + + // got a hint -> use it + value = hintMap.get( hint ); + + if( null == value ) + { + // hints are tacked on using a '/'. + // TODO: is it smart to hardcode it this way? + final String key = role + "/" + hint; + final String message = "Component does not exist"; + throw new ServiceException( key, message ); + } + + return value; + } + + /** + * This is the method that the ContainerComponentManager and Selector use + * to gain access to the ComponentHandlers and ComponentSelectors. The + * actual access of the ComponentHandler is delegated to the Container. + * + * @param role The role we intend to access a Component for. + * @param hint The hint that we use as a qualifier + * (note: if null, the default implementation is returned). + * + * @return true if a reference to the role exists. + */ + public boolean has( final String role, final Object hint ) + { + final Map hintMap = (StaticBucketMap)m_mapper.get( role ); + boolean hasComponent = false; + + if( null != hintMap ) + { + hasComponent = true; + } + + if( hasComponent ) + { + if( null == hint ) + { + // no hint -> try selector + hasComponent = hintMap.containsKey( "selector" ); + + if( !hasComponent ) + { + // no hint -> try default + hasComponent = hintMap.containsKey( "default" ); + } + } + else + { + // hint -> find it + hasComponent = hintMap.containsKey( hint ); + } + } + + return hasComponent; + } + + // TODO: we probably want to throw a more specific exception here with a list of the + // components we couldn't initialize. That'd allow more graceful recovery, even if + // we're technically violating the initialize() contract. Or maybe we want more + // advanced semantics where optional components not being started doesn't raise an + // exception? + /** + * Initializes the container and all the components it hosts so that they are ready to be used. + * Unless components ask for lazy activation, this is where they are activated. + * + * @throws Exception if one or more components could not be initialized. The system <i>is</i> + * running properly so if the component is not vital to operation, + * it should be possible to recover gracefully + */ + public void initialize() + throws Exception + { + // go over all components + final Iterator i = m_components.iterator(); + final BoundedFifoBuffer buffer = new BoundedFifoBuffer( m_components.size() ); + + while( i.hasNext() ) + { + try + { + final ComponentHandlerEntry entry = (ComponentHandlerEntry)i.next(); + final ComponentHandler handler = entry.getHandler(); + // if the component is not lazy, prepare it now, + // otherwise, don't do anything yet + if( !entry.getMetaData().isLazyActivation() ) + { + // if we're doing queueing, enqueue, otherwise tell + // the handler to prepare itself and its components. + if( null != m_commandQueue ) + { + final PrepareHandlerCommand element = + new PrepareHandlerCommand( handler, getLogger() ); + m_commandQueue.enqueue( element ); + } + else + { + handler.prepareHandler(); + } + } + else + { + if( getLogger().isDebugEnabled() ) + { + final String message = "ComponentHandler (" + handler + + ") has specified request time initialization policy, " + + "initialization deferred till first use"; + getLogger().debug( message ); + } + } + } + catch( final Exception e ) + { + if( getLogger().isWarnEnabled() ) + { + final String message = "Could not initialize component"; + getLogger().warn( message, e ); + } + buffer.add( e ); + } + } + + // if we were unable to activate one or more components, + // throw an exception + if( buffer.size() > 0 ) + { + final StringBuffer message = new StringBuffer(); + while( !buffer.isEmpty() ) + { + message.append( ( (Exception)buffer.remove() ).getMessage() ); + } + + throw new Exception( message.toString() ); + } + } + + /** + * Disposes of all components and frees resources that they consume. + */ + public void dispose() + { + final Iterator i = m_components.iterator(); + while( i.hasNext() ) + { + final ComponentHandlerEntry entry = (ComponentHandlerEntry)i.next(); + final ComponentHandler handler = entry.getHandler(); + + // TODO: stop() here? + + ContainerUtil.dispose( handler ); + } + } + + /** + * Exposes to subclasses the service manager which this container + * uses to manage its child components. + * The returned ServiceManager <i>is</i> aware of the services passed + * in to <i>this</i> container, and services that were passed in through + * service() are hence available to subclasses. + * + * @return the component manager that contains the child components. + */ + protected ServiceManager getServiceManager() + { + return m_serviceManager; + } }
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>