| /******************************************************************************* |
| * 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 ); |
| } |
| } |