/******************************************************************************* * 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.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.inject.Inject; import org.codehaus.plexus.PlexusContainer; import org.codehaus.plexus.classworlds.realm.ClassRealm; import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.annotations.Configuration; import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.component.factory.ComponentFactory; import org.codehaus.plexus.component.repository.ComponentDescriptor; import org.codehaus.plexus.component.repository.ComponentRequirement; import org.eclipse.sisu.bean.BeanProperty; import org.eclipse.sisu.inject.DeferredClass; import org.eclipse.sisu.inject.DeferredProvider; import org.eclipse.sisu.space.ClassSpace; import org.eclipse.sisu.space.LoadedClass; import com.google.inject.Binder; import com.google.inject.Injector; import com.google.inject.ProvisionException; /** * {@link PlexusBeanModule} that binds Plexus components according to their {@link ComponentDescriptor}s. */ public final class ComponentDescriptorBeanModule implements PlexusBeanModule { // ---------------------------------------------------------------------- // Implementation fields // ---------------------------------------------------------------------- private final ClassSpace space; private final Map<Component, DeferredClass<?>> componentMap = new HashMap<Component, DeferredClass<?>>(); private final Map<String, PlexusBeanMetadata> metadataMap = new HashMap<String, PlexusBeanMetadata>(); // ---------------------------------------------------------------------- // Constructors // ---------------------------------------------------------------------- public ComponentDescriptorBeanModule( final ClassSpace space, final List<ComponentDescriptor<?>> descriptors ) { this.space = space; for ( int i = 0, size = descriptors.size(); i < size; i++ ) { final ComponentDescriptor<?> cd = descriptors.get( i ); final Component component = newComponent( cd ); final String factory = cd.getComponentFactory(); if ( null == factory || "java".equals( factory ) ) { try { componentMap.put( component, new LoadedClass<Object>( cd.getImplementationClass() ) ); } catch ( final TypeNotPresentException e ) { componentMap.put( component, space.deferLoadClass( cd.getImplementation() ) ); } } else { componentMap.put( component, new DeferredFactoryClass( cd, factory ) ); } final List<ComponentRequirement> requirements = cd.getRequirements(); if ( !requirements.isEmpty() ) { metadataMap.put( cd.getImplementation(), new ComponentMetadata( space, requirements ) ); } } } // ---------------------------------------------------------------------- // Public methods // ---------------------------------------------------------------------- public PlexusBeanSource configure( final Binder binder ) { final String source = space.toString(); final PlexusTypeBinder plexusTypeBinder = new PlexusTypeBinder( binder ); for ( final Entry<Component, DeferredClass<?>> entry : componentMap.entrySet() ) { plexusTypeBinder.hear( entry.getKey(), entry.getValue(), source ); } return new PlexusDescriptorBeanSource( metadataMap ); } // ---------------------------------------------------------------------- // Implementation methods // ---------------------------------------------------------------------- static Component newComponent( final ComponentDescriptor<?> cd ) { return new ComponentImpl( cd.getRoleClass(), cd.getRoleHint(), cd.getInstantiationStrategy(), cd.getDescription() ); } static Requirement newRequirement( final ClassSpace space, final ComponentRequirement cr ) { return new RequirementImpl( space.deferLoadClass( cr.getRole() ), cr.isOptional(), Collections.singletonList( cr.getRoleHint() ) ); } // ---------------------------------------------------------------------- // Implementation types // ---------------------------------------------------------------------- /** * {@link DeferredClass} backed by a {@link ComponentDescriptor} and {@link ComponentFactory} hint. */ private static final class DeferredFactoryClass implements DeferredClass<Object>, DeferredProvider<Object> { // ---------------------------------------------------------------------- // Implementation fields // ---------------------------------------------------------------------- @Inject private PlexusContainer container; @Inject private Injector injector; private final ComponentDescriptor<?> cd; private final String hint; // ---------------------------------------------------------------------- // Constructors // ---------------------------------------------------------------------- DeferredFactoryClass( final ComponentDescriptor<?> cd, final String hint ) { this.cd = cd; this.hint = hint; } // ---------------------------------------------------------------------- // Public methods // ---------------------------------------------------------------------- @SuppressWarnings( { "unchecked", "rawtypes" } ) public Class load() throws TypeNotPresentException { return cd.getImplementationClass(); } public String getName() { return cd.getImplementation(); } public DeferredProvider<Object> asProvider() { return this; } public Object get() { try { ClassRealm contextRealm = container.getLookupRealm(); if ( null == contextRealm ) { contextRealm = RealmManager.contextRealm(); } if ( null == contextRealm ) { contextRealm = container.getContainerRealm(); } final ComponentFactory factory = container.lookup( ComponentFactory.class, hint ); final Object o = factory.newInstance( cd, contextRealm, container ); if ( null != o ) { injector.injectMembers( o ); } return o; } catch ( final Exception e ) { throw new ProvisionException( "Error in ComponentFactory:" + hint, e ); } } public DeferredClass<Object> getImplementationClass() { return this; } } /** * {@link PlexusBeanMetadata} backed by list of {@link ComponentRequirement}s. */ private static final class ComponentMetadata implements PlexusBeanMetadata { // ---------------------------------------------------------------------- // Implementation fields // ---------------------------------------------------------------------- private Map<String, Requirement> requirementMap = new HashMap<String, Requirement>(); // ---------------------------------------------------------------------- // Constructors // ---------------------------------------------------------------------- ComponentMetadata( final ClassSpace space, final List<ComponentRequirement> requirements ) { for ( int i = 0, size = requirements.size(); i < size; i++ ) { final ComponentRequirement cr = requirements.get( i ); requirementMap.put( cr.getFieldName(), newRequirement( space, cr ) ); } } // ---------------------------------------------------------------------- // Public methods // ---------------------------------------------------------------------- public boolean isEmpty() { return requirementMap.isEmpty(); } public Requirement getRequirement( final BeanProperty<?> property ) { final Requirement requirement = requirementMap.get( property.getName() ); if ( null != requirement && requirementMap.isEmpty() ) { requirementMap = Collections.emptyMap(); } return requirement; } public Configuration getConfiguration( final BeanProperty<?> property ) { return null; } } /** * {@link PlexusBeanSource} backed by simple map of {@link PlexusBeanMetadata}. */ private static final class PlexusDescriptorBeanSource implements PlexusBeanSource { // ---------------------------------------------------------------------- // Implementation fields // ---------------------------------------------------------------------- private Map<String, PlexusBeanMetadata> metadataMap; // ---------------------------------------------------------------------- // Constructors // ---------------------------------------------------------------------- PlexusDescriptorBeanSource( final Map<String, PlexusBeanMetadata> metadataMap ) { this.metadataMap = metadataMap; } // ---------------------------------------------------------------------- // Public methods // ---------------------------------------------------------------------- public PlexusBeanMetadata getBeanMetadata( final Class<?> implementation ) { if ( null == metadataMap ) { return null; } final PlexusBeanMetadata metadata = metadataMap.remove( implementation.getName() ); if ( metadataMap.isEmpty() ) { metadataMap = null; } return metadata; } } }