package org.codehaus.plexus;
import static com.google.common.base.ReferenceType.WEAK;
import static com.google.common.collect.Iterables.concat;
import com.google.common.collect.ListMultimap;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newConcurrentHashMap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.ReferenceMap;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import static org.codehaus.plexus.component.CastUtils.cast;
import org.codehaus.plexus.component.ComponentIndex;
import static org.codehaus.plexus.component.ComponentStack.pushComponentStack;
import static org.codehaus.plexus.component.ComponentStack.popComponentStack;
import org.codehaus.plexus.component.manager.ComponentManager;
import org.codehaus.plexus.component.manager.ComponentManagerFactory;
import org.codehaus.plexus.component.manager.StaticComponentManager;
import org.codehaus.plexus.component.repository.ComponentDescriptor;
import org.codehaus.plexus.component.repository.ComponentDescriptorListener;
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.lifecycle.LifecycleHandler;
import org.codehaus.plexus.lifecycle.LifecycleHandlerManager;
import org.codehaus.plexus.lifecycle.UndefinedLifecycleHandlerException;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.logging.NullLogger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
public class DefaultComponentRegistry implements ComponentRegistry
{
private static final String DEFAULT_INSTANTIATION_STRATEGY = "singleton";
private final MutablePlexusContainer container;
private final LifecycleHandlerManager lifecycleHandlerManager;
private final Logger logger;
private final AtomicBoolean disposed = new AtomicBoolean( false );
private final ConcurrentMap<String, ComponentManagerFactory> componentManagerFactories = newConcurrentHashMap();
private final ComponentIndex<ComponentManager<?>> index = new ComponentIndex<ComponentManager<?>>();
private final Map<ComponentDescriptor<?>, ComponentManager<?>> componentManagersByComponentDescriptor =
new ReferenceMap<ComponentDescriptor<?>, ComponentManager<?>>( WEAK, WEAK);
private final Map<Object, ComponentManager<?>> componentManagersByComponent =
new ReferenceMap<Object, ComponentManager<?>>( WEAK, WEAK);
private final ListMultimap<Pair<Class<?>, String>, ComponentDescriptorListener<?>> listeners = Multimaps.newArrayListMultimap();
public DefaultComponentRegistry( MutablePlexusContainer container, LifecycleHandlerManager lifecycleHandlerManager )
{
this.container = container;
this.lifecycleHandlerManager = lifecycleHandlerManager;
Logger containerLogger = container.getLogger();
if ( containerLogger != null )
{
logger = containerLogger;
}
else
{
logger = new NullLogger();
}
}
public void dispose()
{
if (disposed.getAndSet( true )) {
// already disposed
return;
}
List<ComponentManager<?>> managers;
synchronized ( index )
{
managers = new ArrayList<ComponentManager<?>>( index.clear() );
componentManagersByComponentDescriptor.clear();
componentManagersByComponent.clear();
}
// reverse sort the managers by startId
Collections.sort( managers, new Comparator<ComponentManager<?>>() {
public int compare( ComponentManager<?> left, ComponentManager<?> right )
{
if (left.getStartId() < right.getStartId() )
{
return 1;
}
else if (left.getStartId() == right.getStartId() )
{
return 0;
}
else
{
return -1;
}
}
});
// Call dispose callback outside of synchronized lock to avoid deadlocks
for ( ComponentManager<?> componentManager : managers )
{
try
{
componentManager.dispose();
}
catch ( Exception e )
{
// todo dain use a monitor instead of a logger
logger.error( "Error while disposing component manager. Continuing with the rest", e );
}
fireComponentDescriptorRemoved( componentManager.getComponentDescriptor() );
}
}
//
// Component Manager Factories
//
public void registerComponentManagerFactory( ComponentManagerFactory componentManagerFactory )
{
if ( disposed.get() )
{
throw new IllegalStateException("ComponentRegistry has been disposed");
}
componentManagerFactories.put( componentManagerFactory.getId(), componentManagerFactory );
}
//
// Component Descriptors
//
public <T> void addComponentDescriptor( ComponentDescriptor<T> componentDescriptor ) throws ComponentRepositoryException
{
if ( disposed.get() )
{
throw new ComponentRepositoryException("ComponentRegistry has been disposed", componentDescriptor);
}
// verify the descriptor matches the role hint and type
verifyComponentDescriptor( componentDescriptor );
// Get the ComponentManagerFactory
String instantiationStrategy = componentDescriptor.getInstantiationStrategy();
if ( instantiationStrategy == null )
{
instantiationStrategy = DEFAULT_INSTANTIATION_STRATEGY;
}
ComponentManagerFactory componentManagerFactory = componentManagerFactories.get( instantiationStrategy );
if ( componentManagerFactory == null )
{
throw new ComponentRepositoryException( "Unsupported instantiation strategy: " + instantiationStrategy,
componentDescriptor );
}
// Get the LifecycleHandler
LifecycleHandler lifecycleHandler;
try
{
lifecycleHandler = lifecycleHandlerManager.getLifecycleHandler( componentDescriptor.getLifecycleHandler() );
}
catch ( UndefinedLifecycleHandlerException e )
{
throw new ComponentRepositoryException( "Undefined lifecycle handler: " + componentDescriptor.getLifecycleHandler(),
componentDescriptor );
}
// Create the ComponentManager
ComponentManager<T> componentManager = componentManagerFactory.createComponentManager( container,
lifecycleHandler,
componentDescriptor );
// Add componentManager to indexe
synchronized ( index )
{
index.add( componentDescriptor.getRealm(),
componentDescriptor.getRoleClass(),
componentDescriptor.getRoleHint(),
componentManager);
componentManagersByComponentDescriptor.put(componentDescriptor, componentManager);
}
fireComponentDescriptorAdded( componentDescriptor );
}
public <T> ComponentDescriptor<T> getComponentDescriptor( Class<T> type, String roleHint )
{
ComponentManager<T> componentManager = (ComponentManager<T>) index.get( type, roleHint );
if ( componentManager == null )
{
return null;
}
return componentManager.getComponentDescriptor();
}
public <T> Map<String, ComponentDescriptor<T>> getComponentDescriptorMap( Class<T> type )
{
Map<String, ComponentManager<T>> componentManagers = cast( index.getAllAsMap( type ) );
Map<String, ComponentDescriptor<T>> descriptors = new LinkedHashMap<String, ComponentDescriptor<T>>(componentManagers.size());
for ( Entry<String, ComponentManager<T>> entry : componentManagers.entrySet() )
{
descriptors.put(entry.getKey(), entry.getValue().getComponentDescriptor());
}
return Collections.unmodifiableMap( descriptors );
}
public <T> List<ComponentDescriptor<T>> getComponentDescriptorList( Class<T> type )
{
List<ComponentManager<T>> componentManagers = cast( index.getAll( type ) );
List<ComponentDescriptor<T>> descriptors = new ArrayList<ComponentDescriptor<T>>(componentManagers.size());
for ( ComponentManager<T> componentManager : componentManagers )
{
descriptors.add( componentManager.getComponentDescriptor() );
}
return Collections.unmodifiableList( descriptors );
}
//
// Component Instances
//
public <T> void addComponent( T instance, Class<?> type, String roleHint, ClassRealm realm ) throws ComponentRepositoryException
{
if ( disposed.get() )
{
throw new ComponentRepositoryException("ComponentRegistry has been disposed", type, roleHint, realm);
}
StaticComponentManager<T> componentManager = new StaticComponentManager<T>( container, instance, type, roleHint, realm );
// verify descriptor is consistent
ComponentDescriptor<T> descriptor = componentManager.getComponentDescriptor();
verifyComponentDescriptor( descriptor );
synchronized ( index )
{
index.add( descriptor.getRealm(), descriptor.getRoleClass(), descriptor.getRoleHint(), componentManager);
componentManagersByComponentDescriptor.put( descriptor, componentManager);
}
fireComponentDescriptorAdded( descriptor );
}
public <T> T lookup( Class<T> type, String roleHint ) throws ComponentLookupException
{
// verify arguments
if ( type == null )
{
throw new NullPointerException( "type is null" );
}
if ( roleHint == null )
{
roleHint = PlexusConstants.PLEXUS_DEFAULT_HINT;
}
return getComponent( type, roleHint );
}
public <T> T lookup( ComponentDescriptor<T> componentDescriptor ) throws ComponentLookupException
{
ComponentManager<T> componentManager = (ComponentManager<T>) componentManagersByComponentDescriptor.get( componentDescriptor );
if ( componentManager == null )
{
throw new ComponentLookupException( "Component descriptor is not registered with PlexusContainer", componentDescriptor );
}
return getComponent( componentManager );
}
public <T> Map<String, T> lookupMap( Class<T> type, List<String> roleHints )
throws ComponentLookupException
{
// verify arguments
if ( type == null )
{
throw new NullPointerException( "type is null" );
}
// if no hints provided, get all valid hints for this role
Map<String, T> components = new LinkedHashMap<String, T>();
if ( roleHints == null )
{
Map<String, ComponentManager<T>> componentManagers = cast( index.getAllAsMap( type ) );
for ( Entry<String, ComponentManager<T>> entry : componentManagers.entrySet() )
{
String roleHint = entry.getKey();
ComponentManager<T> componentManager = entry.getValue();
// todo dain catch the exception... it isn't the callers problem when one component in a collection fails
T component = getComponent( componentManager );
components.put( roleHint, component);
}
}
else
{
for ( String roleHint : roleHints )
{
// todo dain catch the exception... it isn't the callers problem when one component in a collection fails
T component = getComponent( type, roleHint );
components.put( roleHint, component );
}
}
return components;
}
public <T> List<T> lookupList( Class<T> type, List<String> roleHints ) throws ComponentLookupException
{
// verify arguments
if ( type == null )
{
throw new NullPointerException( "type is null" );
}
// if no hints provided, get all valid hints for this role
List<T> components = new ArrayList<T>();
if ( roleHints == null )
{
List<ComponentManager<T>> componentManagers = cast( index.getAll( type ) );
for ( ComponentManager<T> componentManager : componentManagers )
{
// todo dain catch the exception... it isn't the callers problem when one component in a collection fails
T component = getComponent( componentManager );
components.add( component);
}
}
else
{
for ( String roleHint : roleHints )
{
// todo dain catch the exception... it isn't the callers problem when one component in a collection fails
T component = getComponent( type, roleHint );
components.add( component );
}
}
return components;
}
public <T> void addComponentDescriptorListener( ComponentDescriptorListener<T> listener )
{
Class<T> type = listener.getType();
List<String> roleHints = listener.getRoleHints();
synchronized ( this )
{
if (roleHints == null )
{
listeners.put(new Pair<Class<?>, String>(type, null), listener);
}
else
{
for ( String roleHint : roleHints )
{
listeners.put(new Pair<Class<?>, String>(type, roleHint), listener);
}
}
}
// if no hints provided, get all valid hints for this role
if ( roleHints == null )
{
List<ComponentManager<T>> componentManagers = cast( index.getAll( type ) );
for ( ComponentManager<T> componentManager : componentManagers )
{
listener.componentDescriptorAdded( componentManager.getComponentDescriptor() );
}
}
else
{
for ( String roleHint : roleHints )
{
ComponentManager<T> componentManager = (ComponentManager<T>) index.get( type, roleHint );
if (componentManager != null) {
listener.componentDescriptorAdded( componentManager.getComponentDescriptor() );
}
}
}
}
public synchronized <T> void removeComponentDescriptorListener( ComponentDescriptorListener<T> listener )
{
Class<?> type = listener.getType();
List<String> roleHints = listener.getRoleHints();
if (roleHints == null || roleHints.isEmpty())
{
listeners.remove(new Pair<Class<?>, String>(type, null), listener);
}
else
{
for ( String roleHint : roleHints )
{
listeners.remove(new Pair<Class<?>, String>(type, roleHint), listener);
}
}
}
private synchronized <T> List<ComponentDescriptorListener<T>> getListeners(ComponentDescriptor<T> descriptor)
{
List<ComponentDescriptorListener<T>> allHintListeners =
cast( listeners.get( new Pair<Class<?>, String>( descriptor.getRoleClass(), null ) ) );
List<ComponentDescriptorListener<T>> specificHintListeners =
cast( listeners.get( new Pair<Class<?>, String>( descriptor.getRoleClass(), descriptor.getRoleHint() ) ) );
return newArrayList( concat(allHintListeners, specificHintListeners) );
}
private <T> void fireComponentDescriptorAdded( ComponentDescriptor<T> componentDescriptor )
{
for ( ComponentDescriptorListener<T> listener : getListeners( componentDescriptor ))
{
try
{
listener.componentDescriptorAdded( componentDescriptor );
}
catch ( Throwable e )
{
logger.debug( "ComponentDescriptorListener threw exception while processing " + componentDescriptor, e );
}
}
}
private <T> void fireComponentDescriptorRemoved( ComponentDescriptor<T> componentDescriptor )
{
for ( ComponentDescriptorListener<T> listener : getListeners( componentDescriptor ))
{
try
{
listener.componentDescriptorRemoved( componentDescriptor );
}
catch ( Throwable e )
{
logger.error( "ComponentDescriptorListener threw exception while processing " + componentDescriptor, e );
}
}
}
public void release( Object component ) throws ComponentLifecycleException
{
if ( component == null )
{
return;
}
// get the component manager
ComponentManager<?> componentManager = componentManagersByComponent.get( component );
if ( componentManager == null )
{
// This needs to be tracked down but the user doesn't need to see this
// during the maven bootstrap this logger is null.
//logger.debug( "Component manager not found for returned component. Ignored. component=" + component );
return;
}
// release the component from the manager
componentManager.release( component );
}
public void removeComponentRealm( ClassRealm classRealm ) throws PlexusContainerException
{
try
{
// remove all component managers associated with the realm
LinkedHashSet<ComponentManager<?>> dispose;
synchronized ( index )
{
dispose = new LinkedHashSet<ComponentManager<?>>(index.removeAll( classRealm ));
for ( ComponentManager<?> componentManager : dispose )
{
ComponentDescriptor<?> descriptor = componentManager.getComponentDescriptor();
componentManagersByComponentDescriptor.remove( descriptor );
fireComponentDescriptorRemoved( descriptor );
}
}
// Call dispose callback outside of synchronized lock to avoid deadlocks
for ( ComponentManager<?> componentManager : dispose )
{
componentManager.dispose();
}
}
catch ( ComponentLifecycleException e )
{
throw new PlexusContainerException( "Failed to dissociate component realm: " + classRealm.getId(), e );
}
}
private <T> T getComponent( Class<T> type, String roleHint ) throws ComponentLookupException
{
ComponentManager<T> componentManager = (ComponentManager<T>) index.get( type, roleHint );
if ( componentManager == null )
{
throw new ComponentLookupException( "Component descriptor cannot be found", type, roleHint );
}
return getComponent( componentManager );
}
private <T> T getComponent( ComponentManager<T> componentManager ) throws ComponentLookupException
{
ComponentDescriptor<T> descriptor = componentManager.getComponentDescriptor();
// Get instance from manager... may result in creation
pushComponentStack( descriptor );
try
{
T component = componentManager.getComponent();
componentManagersByComponent.put( component, componentManager );
return component;
}
catch ( Exception e )
{
// get real cause
Throwable cause = e.getCause();
if ( cause == null )
{
cause = e;
}
// do not rewrap ComponentLookupException
if (cause instanceof ComponentLookupException )
{
throw (ComponentLookupException) cause;
}
throw new ComponentLookupException( e.getMessage(), descriptor, cause );
}
finally
{
popComponentStack();
}
}
private <T> void verifyComponentDescriptor( ComponentDescriptor<T> descriptor ) throws ComponentRepositoryException
{
ClassLoader classLoader = descriptor.getRealm();
if ( classLoader == null)
{
throw new ComponentRepositoryException( "ComponentDescriptor realm is null", descriptor);
}
Class<?> implementationClass = descriptor.getImplementationClass();
if (implementationClass.equals( Object.class ))
{
throw new ComponentRepositoryException( "ComponentDescriptor implementation class could not be loaded", descriptor);
}
String role = descriptor.getRole();
if (role == null)
{
throw new ComponentRepositoryException( "ComponentDescriptor role is null", descriptor);
}
Class<?> roleClass;
try
{
roleClass = classLoader.loadClass( role );
}
catch ( ClassNotFoundException e )
{
throw new ComponentRepositoryException( "ComponentDescriptor role class can not be loaded", descriptor);
}
if (!roleClass.isAssignableFrom( implementationClass ))
{
throw new ComponentRepositoryException( "ComponentDescriptor implementation class does not implement the role class:" +
" implementationClass=" + implementationClass.getName() + " roleClass=" + roleClass.getName(),
descriptor);
}
}
public static class Pair<L,R> {
private final L left;
private final R right;
public Pair( L left, R right )
{
this.left = left;
this.right = right;
}
public L getLeft()
{
return left;
}
public R getRight()
{
return right;
}
public boolean equals( Object o )
{
if ( this == o )
{
return true;
}
if ( !( o instanceof Pair ) )
{
return false;
}
Pair<?,?> pair = (Pair<?,?>) o;
return
( left == null ? pair.left == null : left.equals( pair.left ) ) &&
( right == null ? pair.right == null : right.equals( pair.right ) );
}
public int hashCode()
{
int result;
result = ( left != null ? left.hashCode() : 0 );
result = 31 * result + ( right != null ? right.hashCode() : 0 );
return result;
}
public String toString()
{
StringBuilder buf = new StringBuilder( );
buf.append("[").append(left).append(", ").append(right).append("]");
return buf.toString();
}
}
}