/*
* Copyright 2003-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.avalon.fortress.impl.handler;
import org.apache.avalon.excalibur.logger.LoggerManager;
import org.apache.avalon.fortress.util.LifecycleExtensionManager;
import org.apache.avalon.framework.CascadingException;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.component.WrapperComponentManager;
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.DefaultContext;
import org.apache.avalon.framework.logger.LogKit2AvalonLoggerAdapter;
import org.apache.avalon.framework.logger.Loggable;
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.excalibur.instrument.AbstractLogEnabledInstrumentable;
import org.apache.excalibur.instrument.CounterInstrument;
import org.apache.excalibur.mpool.ObjectFactory;
/**
* Factory for Avalon components.
*
* @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
* @version CVS $Revision: 1.28 $ $Date: 2004/02/28 15:16:25 $
* @since 4.0
*/
public final class ComponentFactory
extends AbstractLogEnabledInstrumentable
implements ObjectFactory
{
private final CounterInstrument m_newInstance;
private final CounterInstrument m_dispose;
/** The class which this <code>ComponentFactory</code>
* should create.
*/
private final Class m_componentClass;
/** The Context for the component
*/
private final Context m_context;
/** The component manager for this component.
*/
private final ServiceManager m_serviceManager;
/** The configuration for this component.
*/
private final Configuration m_configuration;
/** The LogKitManager for child ComponentSelectors
*/
private final LoggerManager m_loggerManager;
/** Lifecycle extensions manager
*/
private final LifecycleExtensionManager m_extManager;
/** The component's logger
*/
private final Logger m_componentLogger;
/**
* 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 serviceManager the service manager to pass to <code>Serviceable</code>s.
* @param context the <code>Context</code> to pass to <code>Contexutalizable</code>s.
* @param loggerManager the loggerManager manager instance.
*/
public ComponentFactory( final Class componentClass,
final Configuration configuration,
final ServiceManager serviceManager,
final Context context,
final LoggerManager loggerManager,
final LifecycleExtensionManager extManager )
{
m_componentClass = componentClass;
m_configuration = configuration;
m_serviceManager = serviceManager;
m_context = new DefaultContext( context );
final String name = configuration.getAttribute( "id", componentClass.getName() );
( (DefaultContext) m_context ).put( "component.name", name );
( (DefaultContext) m_context ).put( "component.logger", configuration.getAttribute( "logger", name ) );
// Take each configuration attribute, and make a context entry of form "component.<attribName>"
String[] attribNames = configuration.getAttributeNames();
for ( int index = 0; index < attribNames.length; index++ )
{
String oneName = attribNames[index];
( (DefaultContext) m_context ).put( "component." + oneName, configuration.getAttribute( oneName, "" ) );
}
( (DefaultContext) m_context ).put( "component.configuration", configuration );
( (DefaultContext) m_context ).makeReadOnly();
m_loggerManager = loggerManager;
m_extManager = extManager;
enableLogging( m_loggerManager.getLoggerForCategory( "system.factory" ) );
m_componentLogger = aquireLogger();
m_newInstance = new CounterInstrument( "creates" );
m_dispose = new CounterInstrument( "destroys" );
setInstrumentableName( "factory" );
addInstrument( m_newInstance );
addInstrument( m_dispose );
}
/**
* Returns a new instance of a component and optionally applies a logging channel,
* instrumentation, context, a component or service manager, configuration, parameters,
* lifecycle extensions, initialization, and execution phases based on the interfaces
* implemented by the component class.
*
* @return the new instance
*/
public Object newInstance()
throws Exception
{
final Object component;
try
{
component = m_componentClass.newInstance();
if ( getLogger().isDebugEnabled() )
{
final String message =
"ComponentFactory creating new instance of " +
m_componentClass.getName() + ".";
getLogger().debug( message );
}
ContainerUtil.enableLogging( component, m_componentLogger );
if ( component instanceof Loggable )
{
final org.apache.log.Logger logkitLogger =
LogKit2AvalonLoggerAdapter.createLogger( m_componentLogger );
( (Loggable) component ).setLogger( logkitLogger );
}
ContainerUtil.contextualize( component, m_context );
if ( component instanceof Composable )
{
ContainerUtil.compose( component, new WrapperComponentManager( m_serviceManager ) );
}
ContainerUtil.service( component, m_serviceManager );
ContainerUtil.configure( component, m_configuration );
if ( component instanceof Parameterizable )
{
ContainerUtil.parameterize( component, Parameters.fromConfiguration( m_configuration ) );
}
m_extManager.executeCreationExtensions( component, m_context );
ContainerUtil.initialize( component );
ContainerUtil.start( component );
if ( m_newInstance.isActive() )
{
m_newInstance.increment();
}
}
catch (LinkageError le)
{
throw new CascadingException("Could not load component", le);
}
return component;
}
private Logger aquireLogger()
{
Logger logger;
try
{
final String name = (String) m_context.get( "component.logger" );
if ( getLogger().isDebugEnabled() )
{
final String message = "logger name is " + name;
getLogger().debug( message );
}
logger = m_loggerManager.getLoggerForCategory( name );
}
catch ( ContextException ce )
{
if ( getLogger().isDebugEnabled() )
{
final String message = "no logger name available, using standard name";
getLogger().debug( message );
}
logger = m_loggerManager.getDefaultLogger();
}
return logger;
}
/**
* Returns the component class.
* @return the class
*/
public final Class getCreatedClass()
{
return m_componentClass;
}
/**
* Disposal of the supplied component instance.
* @param component the component to dispose of
* @exception Exception if a disposal error occurs
*/
public final void dispose( final Object component )
throws Exception
{
if ( getLogger().isDebugEnabled() )
{
final String message = "ComponentFactory decommissioning instance of " +
getCreatedClass().getName() + ".";
getLogger().debug( message );
}
if ( getCreatedClass().equals( component.getClass() ) )
{
ContainerUtil.shutdown( component );
m_extManager.executeDestructionExtensions( component, m_context );
if ( m_dispose.isActive() )
{
m_dispose.increment();
}
}
else
{
final String message = "The object given to be disposed does " +
"not come from this ObjectFactory";
throw new IllegalArgumentException( message );
}
}
}