blob: 52dedd3cda87dbdd6c9de8160c738a170840dac5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010-present Sonatype, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Stuart McCulloch (Sonatype, Inc.) - initial API and implementation
*******************************************************************************/
package org.eclipse.sisu.plexus;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.codehaus.plexus.component.annotations.Component;
import org.eclipse.sisu.inject.DeferredClass;
import org.eclipse.sisu.inject.Logs;
import org.eclipse.sisu.space.ClassSpace;
import org.eclipse.sisu.space.CloningClassSpace;
/**
* Enhanced Plexus component map with additional book-keeping.
*/
final class PlexusTypeRegistry
{
// ----------------------------------------------------------------------
// Constants
// ----------------------------------------------------------------------
private static final Component LOAD_ON_START_PLACEHOLDER =
new ComponentImpl( Object.class, "", Strategies.LOAD_ON_START, "" );
// ----------------------------------------------------------------------
// Implementation fields
// ----------------------------------------------------------------------
private final Map<String, Component> components = new HashMap<String, Component>();
private final Map<String, DeferredClass<?>> implementations = new HashMap<String, DeferredClass<?>>();
private final Set<String> deferredNames = new HashSet<String>();
private final ClassSpace space;
private CloningClassSpace clones;
// ----------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------
PlexusTypeRegistry( final ClassSpace space )
{
this.space = space;
}
// ----------------------------------------------------------------------
// Locally-shared methods
// ----------------------------------------------------------------------
/**
* @return Current class space
*/
ClassSpace getSpace()
{
return space;
}
/**
* Records that the given Plexus component should be loaded when the container starts.
*
* @param role The Plexus role
* @param hint The Plexus hint
*/
void loadOnStart( final String role, final String hint )
{
final String key = Roles.canonicalRoleHint( role, hint );
final Component c = components.get( key );
if ( null == c )
{
components.put( key, LOAD_ON_START_PLACEHOLDER );
}
else if ( !Strategies.LOAD_ON_START.equals( c.instantiationStrategy() ) )
{
components.put( key, new ComponentImpl( c.role(), c.hint(), Strategies.LOAD_ON_START, c.description() ) );
}
}
/**
* Registers the given component, automatically disambiguating between implementations bound multiple times.
*
* @param role The Plexus role
* @param hint The Plexus hint
* @param instantiationStrategy The instantiation strategy
* @param description The component description
* @param implementation The implementation
* @return The implementation the component was successfully registered with; otherwise {@code null}
*/
String addComponent( final String role, final String hint, final String instantiationStrategy,
final String description, final String implementation )
{
final Class<?> roleType = loadRole( role, implementation );
if ( null == roleType )
{
return null;
}
final String canonicalHint = Hints.canonicalHint( hint );
final String key = Roles.canonicalRoleHint( role, canonicalHint );
/*
* COMPONENT...
*/
final Component oldComponent = components.get( key );
if ( null == oldComponent )
{
components.put( key, new ComponentImpl( roleType, canonicalHint, instantiationStrategy, description ) );
}
else if ( LOAD_ON_START_PLACEHOLDER == oldComponent )
{
components.put( key, new ComponentImpl( roleType, canonicalHint, Strategies.LOAD_ON_START, description ) );
}
/*
* ...IMPLEMENTATION
*/
DeferredClass<?> implementationType = implementations.get( key );
if ( null == implementationType )
{
if ( deferredNames.add( implementation ) )
{
implementationType = space.deferLoadClass( implementation );
}
else
{
// type already used for another role, so we must clone it
implementationType = cloneImplementation( implementation );
}
implementations.put( key, implementationType );
return implementationType.getName();
}
final String oldImplementation = implementationType.getName();
if ( implementation.equals( CloningClassSpace.originalName( oldImplementation ) ) )
{
return oldImplementation; // merge configuration
}
Logs.trace( "Duplicate implementations for Plexus role: {}", key, null );
Logs.trace( "Using: {} ignoring: {}", oldImplementation, implementation );
return null;
}
/**
* @return Plexus component map
*/
Map<Component, DeferredClass<?>> getComponents()
{
final Map<Component, DeferredClass<?>> map = new HashMap<Component, DeferredClass<?>>();
for ( final Entry<String, DeferredClass<?>> i : implementations.entrySet() )
{
map.put( components.get( i.getKey() ), i.getValue() );
}
return map;
}
// ----------------------------------------------------------------------
// Implementation methods
// ----------------------------------------------------------------------
/**
* Attempts to load the given Plexus role, checks constructors for concrete types.
*
* @param role The Plexus role
* @param implementation The implementation
* @return Loaded Plexus role
*/
private Class<?> loadRole( final String role, final String implementation )
{
try
{
return space.loadClass( role );
}
catch ( final TypeNotPresentException e )
{
Logs.trace( "Ignoring Plexus role: {}", role, e );
}
return null;
}
/**
* Clones an implementation so it can be bound again with different configuration.
*
* @param implementation The implementation
* @return Cloned implementation
*/
private DeferredClass<?> cloneImplementation( final String implementation )
{
if ( null == clones )
{
clones = new CloningClassSpace( space );
}
return clones.cloneClass( implementation );
}
}