package org.codehaus.plexus;
/*
* Copyright 2001-2006 Codehaus 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.
*/
import static org.codehaus.plexus.PlexusConstants.PLEXUS_DEFAULT_HINT;
import static org.codehaus.plexus.component.CastUtils.cast;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
import org.codehaus.plexus.component.discovery.ComponentDiscoverer;
import org.codehaus.plexus.component.discovery.ComponentDiscovererManager;
import org.codehaus.plexus.component.discovery.ComponentDiscoveryEvent;
import org.codehaus.plexus.component.discovery.ComponentDiscoveryListener;
import org.codehaus.plexus.component.factory.ComponentFactoryManager;
import org.codehaus.plexus.component.repository.ComponentDescriptor;
import org.codehaus.plexus.component.repository.ComponentDescriptorListener;
import org.codehaus.plexus.component.repository.ComponentSetDescriptor;
import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.component.repository.exception.ComponentRepositoryException;
import org.codehaus.plexus.component.repository.io.PlexusTools;
import org.codehaus.plexus.configuration.PlexusConfiguration;
import org.codehaus.plexus.configuration.PlexusConfigurationException;
import org.codehaus.plexus.configuration.PlexusConfigurationMerger;
import org.codehaus.plexus.configuration.source.ConfigurationSource;
import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
import org.codehaus.plexus.container.initialization.ContainerInitializationContext;
import org.codehaus.plexus.container.initialization.ContainerInitializationPhase;
import org.codehaus.plexus.context.Context;
import org.codehaus.plexus.context.ContextException;
import org.codehaus.plexus.context.ContextMapAdapter;
import org.codehaus.plexus.context.DefaultContext;
import org.codehaus.plexus.logging.AbstractLogEnabled;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.logging.LoggerManager;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.InterpolationFilterReader;
import org.codehaus.plexus.util.ReaderFactory;
/**
* Default implementation of PlexusContainer and MutablePlexusContainer.
* @author Jason van Zyl
* @author Kenney Westerhof
*/
public class DefaultPlexusContainer
extends AbstractLogEnabled
implements MutablePlexusContainer
{
protected static final String DEFAULT_CONTAINER_NAME = "default";
protected static final String DEFAULT_REALM_NAME = "plexus.core";
/**
* Arbitrary data associated with the container. Data in the container has highest precedence when configuring
* a component to create.
*/
protected Context containerContext;
protected PlexusConfiguration configuration;
// todo: don't use a reader
protected Reader configurationReader;
protected ClassWorld classWorld;
protected ClassRealm containerRealm;
// ----------------------------------------------------------------------------
// Core components
// ----------------------------------------------------------------------------
private ComponentRegistry componentRegistry;
/**
* Simple index (registry) of ComponentDiscovers and ComponentDiscoveryListener.
*/
protected ComponentDiscovererManager componentDiscovererManager;
/**
* Trivial class to look-up ComponentFactory instances in this container.
*/
protected ComponentFactoryManager componentFactoryManager;
/**
* Generic logger interface.
*/
protected LoggerManager loggerManager;
/**
* Converts a ComponentDescriptor into PlexusConfiguration.
*/
protected ConfigurationSource configurationSource;
// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------
// TODO: Is there a more threadpool-friendly way to do this?
private ThreadLocal<ClassRealm> lookupRealm = new ThreadLocal<ClassRealm>();
public void addComponent( Object component, String role )
throws ComponentRepositoryException
{
ClassRealm classRealm = null;
// find a realm for this instance
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if ( classLoader instanceof ClassRealm )
{
classRealm = (ClassRealm) classLoader;
}
if (classRealm == null)
{
classRealm = getContainerRealm();
}
addComponent( component, getRoleClass( role ), PLEXUS_DEFAULT_HINT, classRealm );
}
public <T> void addComponent( T component, Class<?> type, String roleHint, ClassRealm classRealm )
throws ComponentRepositoryException
{
componentRegistry.addComponent( component, type, roleHint, classRealm );
}
public ClassRealm setLookupRealm( ClassRealm realm )
{
ClassRealm oldRealm = lookupRealm.get();
lookupRealm.set( realm );
return oldRealm;
}
public ClassRealm getLookupRealm()
{
return lookupRealm.get();
}
// ----------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------
public DefaultPlexusContainer()
throws PlexusContainerException
{
construct( new DefaultContainerConfiguration() );
}
public DefaultPlexusContainer( ContainerConfiguration c )
throws PlexusContainerException
{
construct( c );
}
public ClassRealm createChildRealm( String id )
{
try
{
return containerRealm.createChildRealm( id );
}
catch ( DuplicateRealmException e )
{
try
{
return classWorld.getRealm( id );
}
catch ( NoSuchRealmException e1 )
{
return null;
}
}
}
private void construct( ContainerConfiguration c )
throws PlexusContainerException
{
configurationSource = c.getConfigurationSource();
// ----------------------------------------------------------------------------
// ClassWorld
// ----------------------------------------------------------------------------
classWorld = c.getClassWorld();
// Make sure we have a valid ClassWorld
if ( classWorld == null )
{
classWorld = new ClassWorld( DEFAULT_REALM_NAME, Thread.currentThread().getContextClassLoader() );
}
containerRealm = c.getRealm();
if ( containerRealm == null )
{
try
{
containerRealm = classWorld.getRealm( DEFAULT_REALM_NAME );
}
catch ( NoSuchRealmException e )
{
containerRealm = (ClassRealm) classWorld.getRealms().iterator().next();
if ( containerRealm == null )
{
System.err.println( "No container realm! Expect errors." );
new Throwable().printStackTrace();
}
}
}
setLookupRealm( containerRealm );
// ----------------------------------------------------------------------------
// Context
// ----------------------------------------------------------------------------
if ( c.getContext() != null )
{
containerContext = new DefaultContext( c.getContext() );
}
else
{
containerContext = new DefaultContext();
}
// ----------------------------------------------------------------------------
// Configuration
// ----------------------------------------------------------------------------
InputStream in = null;
if ( c.getContainerConfiguration() != null )
{
in = toStream( c.getContainerConfiguration() );
}
try
{
if ( c.getContainerConfigurationURL() != null )
{
in = c.getContainerConfigurationURL().openStream();
}
}
catch ( IOException e )
{
throw new PlexusContainerException( "Error reading configuration URL", e );
}
try
{
configurationReader = in == null ? null : ReaderFactory.newXmlReader( in );
}
catch ( IOException e )
{
throw new PlexusContainerException( "Error reading configuration file", e );
}
try
{
initialize( c );
start();
}
finally
{
IOUtil.close( configurationReader );
}
for( Class clazz : c.getComponentDiscoverers() )
{
try
{
ComponentDiscoverer cd = (ComponentDiscoverer) lookup( clazz );
componentDiscovererManager.addComponentDiscoverer( cd );
}
catch ( ComponentLookupException e )
{
}
}
for( Class clazz : c.getComponentDiscoveryListeners() )
{
try
{
ComponentDiscoveryListener cdl = (ComponentDiscoveryListener) lookup( clazz );
componentDiscovererManager.registerComponentDiscoveryListener( cdl );
}
catch ( ComponentLookupException e )
{
}
}
}
// ----------------------------------------------------------------------------
// Lookup
// ----------------------------------------------------------------------------
private Class<?> getInterfaceClass( String role, String hint )
{
if ( hint == null ) hint = PLEXUS_DEFAULT_HINT;
try
{
ClassRealm realm = getLookupRealm();
if ( realm != null )
{
return realm.loadClass( role );
}
}
catch ( Throwable e )
{
}
try
{
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if ( loader != null )
{
return loader.loadClass( role );
}
}
catch ( Throwable e )
{
}
try
{
ComponentDescriptor<?> cd = componentRegistry.getComponentDescriptor( Object.class, hint );
if ( cd != null )
{
ClassLoader loader = cd.getImplementationClass().getClassLoader();
if ( loader != null )
{
return loader.loadClass( role );
}
}
}
catch ( Throwable ignored )
{
}
return Object.class;
}
private Class<?> getRoleClass( String role )
{
return getInterfaceClass( role, null );
}
private Class<?> getRoleClass( String role, String hint )
{
return getInterfaceClass( role, hint );
}
public Object lookup( String role ) throws ComponentLookupException
{
return componentRegistry.lookup( getRoleClass( role ), PLEXUS_DEFAULT_HINT );
}
public Object lookup( String role, String roleHint ) throws ComponentLookupException
{
return componentRegistry.lookup( getRoleClass( role, roleHint ), roleHint );
}
public <T> T lookup( Class<T> type ) throws ComponentLookupException
{
return componentRegistry.lookup( type, PLEXUS_DEFAULT_HINT );
}
public <T> T lookup( Class<T> type, String roleHint ) throws ComponentLookupException
{
return componentRegistry.lookup( type, roleHint );
}
@Deprecated
public <T> T lookup( Class<T> type, String role, String roleHint ) throws ComponentLookupException
{
return componentRegistry.lookup( type, roleHint );
}
public <T> T lookup( ComponentDescriptor<T> descriptor ) throws ComponentLookupException
{
return componentRegistry.lookup( descriptor );
}
public List<Object> lookupList( String role ) throws ComponentLookupException
{
return cast(componentRegistry.lookupList( getRoleClass( role ), null));
}
public List<Object> lookupList( String role, List<String> roleHints ) throws ComponentLookupException
{
return cast(componentRegistry.lookupList( getRoleClass( role ), roleHints ));
}
public <T> List<T> lookupList( Class<T> type ) throws ComponentLookupException
{
return componentRegistry.lookupList( type, null );
}
public <T> List<T> lookupList( Class<T> type, List<String> roleHints ) throws ComponentLookupException
{
return componentRegistry.lookupList( type, roleHints );
}
public Map<String, Object> lookupMap( String role ) throws ComponentLookupException
{
return cast(componentRegistry.lookupMap( getRoleClass( role ), null ));
}
public Map<String, Object> lookupMap( String role, List<String> roleHints ) throws ComponentLookupException
{
return cast(componentRegistry.lookupMap( getRoleClass( role ), roleHints ));
}
public <T> Map<String, T> lookupMap( Class<T> type ) throws ComponentLookupException
{
return componentRegistry.lookupMap( type, null );
}
public <T> Map<String, T> lookupMap( Class<T> type, List<String> roleHints ) throws ComponentLookupException
{
return componentRegistry.lookupMap( type, roleHints );
}
// ----------------------------------------------------------------------
// Component Descriptor Lookup
// ----------------------------------------------------------------------
public boolean hasComponent( String role )
{
return componentRegistry.getComponentDescriptor( getRoleClass( role ), PLEXUS_DEFAULT_HINT ) != null;
}
public boolean hasComponent( String role, String roleHint )
{
return componentRegistry.getComponentDescriptor( getRoleClass( role ), roleHint ) != null;
}
public boolean hasComponent( Class<?> type )
{
return componentRegistry.getComponentDescriptor( type, PLEXUS_DEFAULT_HINT ) != null;
}
public boolean hasComponent( Class<?> type, String roleHint )
{
return componentRegistry.getComponentDescriptor( type, roleHint ) != null;
}
public ComponentDescriptor<?> getComponentDescriptor( String role )
{
return componentRegistry.getComponentDescriptor( getRoleClass( role ), PLEXUS_DEFAULT_HINT );
}
public ComponentDescriptor<?> getComponentDescriptor( String role, String roleHint )
{
return componentRegistry.getComponentDescriptor( getRoleClass( role ), roleHint );
}
public <T> ComponentDescriptor<T> getComponentDescriptor( Class<T> type )
{
return componentRegistry.getComponentDescriptor( type, PLEXUS_DEFAULT_HINT );
}
public <T> ComponentDescriptor<T> getComponentDescriptor( Class<T> type, String roleHint )
{
return componentRegistry.getComponentDescriptor( type, roleHint );
}
public Map<String, ComponentDescriptor<?>> getComponentDescriptorMap( String role )
{
return cast(componentRegistry.getComponentDescriptorMap( getRoleClass( role ) ));
}
@Deprecated
public <T> Map<String, ComponentDescriptor<T>> getComponentDescriptorMap( Class<T> type, String role )
{
return componentRegistry.getComponentDescriptorMap( type );
}
public List<ComponentDescriptor<?>> getComponentDescriptorList( String role )
{
return cast(componentRegistry.getComponentDescriptorList( getRoleClass( role ) ));
}
public <T> List<ComponentDescriptor<T>> getComponentDescriptorList( Class<T> type )
{
return componentRegistry.getComponentDescriptorList( type );
}
public void addComponentDescriptor( ComponentDescriptor<?> componentDescriptor ) throws ComponentRepositoryException
{
if ( componentDescriptor.getRealm() == null )
{
componentDescriptor.setRealm( this.containerRealm );
// throw new ComponentImplementationNotFoundException( "ComponentDescriptor is missing realmId" );
}
componentRegistry.addComponentDescriptor( componentDescriptor );
}
// ----------------------------------------------------------------------
// Component Release
// ----------------------------------------------------------------------
public void release( Object component )
throws ComponentLifecycleException
{
componentRegistry.release( component );
}
public void releaseAll( Map<String, ?> components )
throws ComponentLifecycleException
{
for ( Object component : components.values() )
{
release( component );
}
}
public void releaseAll( List<?> components )
throws ComponentLifecycleException
{
for ( Object component : components )
{
release( component );
}
}
// ----------------------------------------------------------------------
// Lifecycle Management
// ----------------------------------------------------------------------
protected void initialize( ContainerConfiguration containerConfiguration )
throws PlexusContainerException
{
try
{
initializeConfiguration( containerConfiguration );
initializePhases( containerConfiguration );
containerContext.put( PlexusConstants.PLEXUS_KEY, this );
discoverComponents( getContainerRealm() );
PlexusConfiguration[] loadOnStartComponents = getConfiguration().getChild( "load-on-start" ).getChildren( "component" );
getLogger().debug( "Found " + loadOnStartComponents.length + " components to load on start" );
ClassLoader prevCl = Thread.currentThread().getContextClassLoader();
try
{
for ( PlexusConfiguration loadOnStartComponent : loadOnStartComponents )
{
String role = loadOnStartComponent.getChild( "role" ).getValue( null );
String roleHint = loadOnStartComponent.getChild( "role-hint" ).getValue( null );
if ( role == null )
{
throw new PlexusContainerException( "Missing 'role' element from load-on-start." );
}
if ( roleHint == null )
{
roleHint = PlexusConstants.PLEXUS_DEFAULT_HINT;
}
if ( roleHint.equals( "*" ) )
{
getLogger().info( "Loading on start all components with [role]: " + "[" + role + "]" );
lookupList( role );
}
else
{
getLogger().info( "Loading on start [role,roleHint]: " + "[" + role + "," + roleHint + "]" );
lookup( role, roleHint );
}
}
}
catch ( ComponentLookupException e )
{
throw new PlexusContainerException( "Error looking up load-on-start component.", e );
}
finally
{
Thread.currentThread().setContextClassLoader( prevCl );
}
}
catch ( ContextException e )
{
throw new PlexusContainerException( "Error processing configuration", e );
}
catch ( PlexusConfigurationException e )
{
throw new PlexusContainerException( "Error configuring components", e );
}
catch ( IOException e )
{
throw new PlexusContainerException( "Error reading configuration file", e );
}
catch ( ComponentRepositoryException e )
{
throw new PlexusContainerException( "Error discoverying components.", e );
}
}
protected void initializePhases( ContainerConfiguration containerConfiguration )
throws PlexusContainerException
{
ContainerInitializationPhase[] initPhases = containerConfiguration.getInitializationPhases();
ContainerInitializationContext initializationContext = new ContainerInitializationContext( this, classWorld, containerRealm, configuration, containerConfiguration );
for ( ContainerInitializationPhase phase : initPhases )
{
try
{
phase.execute( initializationContext );
}
catch ( Exception e )
{
throw new PlexusContainerException( "Error initializaing container in " + phase.getClass().getName()
+ ".", e );
}
}
}
protected void start()
throws PlexusContainerException
{
// XXX this is called after initializeConfiguration - is this correct?
configuration = null;
}
public void dispose()
{
try
{
componentRegistry.dispose();
boolean needToDisposeRealm = false;
try
{
containerRealm.setParentRealm( null );
if ( needToDisposeRealm )
{
classWorld.disposeRealm( containerRealm.getId() );
}
}
catch ( NoSuchRealmException e )
{
getLogger().debug( "Failed to dispose realm." );
}
}
finally
{
lookupRealm.set( null );
}
}
public void addContextValue( Object key, Object value )
{
containerContext.put( key, value );
}
// ----------------------------------------------------------------------
// Misc Configuration
// ----------------------------------------------------------------------
public ClassWorld getClassWorld()
{
return classWorld;
}
public void setClassWorld( ClassWorld classWorld )
{
this.classWorld = classWorld;
}
public ClassRealm getContainerRealm()
{
return containerRealm;
}
public void setContainerRealm( ClassRealm containerRealm )
{
this.containerRealm = containerRealm;
}
// ----------------------------------------------------------------------
// Context
// ----------------------------------------------------------------------
public Context getContext()
{
return containerContext;
}
// ----------------------------------------------------------------------
// ComponentListener
// ----------------------------------------------------------------------
public void addComponentDescriptorListener( ComponentDescriptorListener<?> listener )
{
componentRegistry.addComponentDescriptorListener( listener );
}
public void removeComponentDescriptorListener( ComponentDescriptorListener<?> listener )
{
componentRegistry.removeComponentDescriptorListener( listener );
}
// ----------------------------------------------------------------------
// Configuration
// ----------------------------------------------------------------------
// This is the default PlexusXmlDiscovery
protected void initializeConfiguration( ContainerConfiguration c )
throws PlexusConfigurationException, ContextException, IOException
{
// We need an empty plexus configuration for merging. This is a function of removing the
// plexus-boostrap.xml file.
configuration = new XmlPlexusConfiguration( "plexus" );
if ( configurationReader != null )
{
// User userConfiguration
PlexusConfiguration userConfiguration = PlexusTools.buildConfiguration( "<User Specified Configuration Reader>", getInterpolationConfigurationReader( configurationReader ) );
// Merger of bootstrapConfiguration and user userConfiguration
configuration = PlexusConfigurationMerger.merge( userConfiguration, configuration );
}
}
protected Reader getInterpolationConfigurationReader( Reader reader )
{
return new InterpolationFilterReader( reader, new ContextMapAdapter( containerContext ) );
}
public Logger getLogger()
{
return super.getLogger();
}
// ----------------------------------------------------------------------
// Discovery
// ----------------------------------------------------------------------
public void registerComponentDiscoveryListener( ComponentDiscoveryListener listener )
{
componentDiscovererManager.registerComponentDiscoveryListener( listener );
}
public void removeComponentDiscoveryListener( ComponentDiscoveryListener listener )
{
componentDiscovererManager.removeComponentDiscoveryListener( listener );
}
// ----------------------------------------------------------------------------
// Mutable Container Interface
// ----------------------------------------------------------------------------
public ComponentRegistry getComponentRegistry()
{
return componentRegistry;
}
public void setComponentRegistry( ComponentRegistry componentRegistry )
{
this.componentRegistry = componentRegistry;
}
public ComponentDiscovererManager getComponentDiscovererManager()
{
return componentDiscovererManager;
}
public void setComponentDiscovererManager( ComponentDiscovererManager componentDiscovererManager )
{
this.componentDiscovererManager = componentDiscovererManager;
}
public ComponentFactoryManager getComponentFactoryManager()
{
return componentFactoryManager;
}
public void setComponentFactoryManager( ComponentFactoryManager componentFactoryManager )
{
this.componentFactoryManager = componentFactoryManager;
}
// Configuration
public PlexusConfiguration getConfiguration()
{
return configuration;
}
public void setConfiguration( PlexusConfiguration configuration )
{
this.configuration = configuration;
}
// ----------------------------------------------------------------------------
// Component Realms
// ----------------------------------------------------------------------------
public ClassRealm getComponentRealm( String realmId )
{
ClassRealm realm = null;
try
{
realm = classWorld.getRealm( realmId );
}
catch ( NoSuchRealmException e )
{
// This should never happen: when a component is discovered, it is discovered from a realm and
// it is at that point the realm id is assigned to the component descriptor.
}
if ( realm == null )
{
// The core components need the container realm.
realm = containerRealm;
}
return realm;
}
public void removeComponentRealm( ClassRealm realm )
throws PlexusContainerException
{
if ( getContainerRealm().getId().equals( realm.getId() ) )
{
throw new IllegalArgumentException( "Cannot remove container realm: " + realm.getId()
+ "\n(trying to remove container realm as if it were a component realm)." );
}
componentRegistry.removeComponentRealm( realm );
ClassRealm lookupRealm = getLookupRealm();
if ( ( lookupRealm != null ) && lookupRealm.getId().equals( realm.getId() ) )
{
setLookupRealm( getContainerRealm() );
}
}
private InputStream toStream( String resource )
throws PlexusContainerException
{
if ( resource == null )
{
return null;
}
String relativeResource = resource;
if ( resource.startsWith( "/" ) )
{
relativeResource = resource.substring( 1 );
}
InputStream is = getClass().getClassLoader().getResourceAsStream( relativeResource );
if ( is == null )
{
try
{
return new FileInputStream( resource );
}
catch ( FileNotFoundException e )
{
return null;
}
}
return is;
}
/**
* Utility method to get a default lookup realm for a component.
*/
public ClassRealm getLookupRealm( Object component )
{
if ( component.getClass().getClassLoader() instanceof ClassRealm )
{
return ( (ClassRealm) component.getClass().getClassLoader() );
}
else
{
return getLookupRealm();
}
}
public void setConfigurationSource( ConfigurationSource configurationSource )
{
this.configurationSource = configurationSource;
}
public ConfigurationSource getConfigurationSource()
{
return configurationSource;
}
public LoggerManager getLoggerManager()
{
// TODO Auto-generated method stub
return loggerManager;
}
public void setLoggerManager( LoggerManager loggerManager )
{
this.loggerManager = loggerManager;
}
// Discovery
public List<ComponentDescriptor<?>> discoverComponents( ClassRealm realm )
throws PlexusConfigurationException, ComponentRepositoryException
{
List<ComponentSetDescriptor> componentSetDescriptors = new ArrayList<ComponentSetDescriptor>();
List<ComponentDescriptor<?>> discoveredComponentDescriptors = new ArrayList<ComponentDescriptor<?>>();
for ( ComponentDiscoverer componentDiscoverer : getComponentDiscovererManager().getComponentDiscoverers() )
{
for ( ComponentSetDescriptor componentSetDescriptor : componentDiscoverer.findComponents( getContext(), realm ) )
{
// Here we should collect all the urls
// do the interpolation against the context
// register all the components
// allow interception and replacement of the components
componentSetDescriptors.add(componentSetDescriptor);
for( ComponentDescriptor<?> componentDescriptor : componentSetDescriptor.getComponents() )
{
addComponentDescriptor( componentDescriptor );
discoveredComponentDescriptors.add( componentDescriptor );
}
// Fire the event
ComponentDiscoveryEvent event = new ComponentDiscoveryEvent( componentSetDescriptor );
componentDiscovererManager.fireComponentDiscoveryEvent( event );
}
}
return discoveredComponentDescriptors;
}
}