blob: 9949513d5e87c7b693e699bad274c230d71c18e4 [file] [log] [blame]
package org.apache.maven.extension;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.maven.container.ContainerUtils.findChildComponentHints;
import org.apache.maven.MavenArtifactFilterManager;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.handler.ArtifactHandler;
import org.apache.maven.artifact.manager.WagonManager;
import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.metadata.ResolutionGroup;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.model.Extension;
import org.apache.maven.plugin.DefaultPluginManager;
import org.apache.maven.project.MavenProject;
import org.apache.maven.wagon.Wagon;
import org.codehaus.classworlds.ClassRealm;
import org.codehaus.classworlds.ClassWorld;
import org.codehaus.classworlds.DuplicateRealmException;
import org.codehaus.classworlds.NoSuchRealmException;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.PlexusContainerException;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.context.Context;
import org.codehaus.plexus.context.ContextException;
import org.codehaus.plexus.logging.AbstractLogEnabled;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarFile;
/**
* Used to locate extensions.
*
* @author <a href="mailto:brett@apache.org">Brett Porter</a>
* @author Jason van Zyl
* @version $Id$
*/
public class DefaultExtensionManager
extends AbstractLogEnabled
implements ExtensionManager, Contextualizable
{
private ArtifactResolver artifactResolver;
private ArtifactFactory artifactFactory;
private ArtifactMetadataSource artifactMetadataSource;
private DefaultPlexusContainer container;
private ArtifactFilter artifactFilter = MavenArtifactFilterManager.createExtensionFilter();
private WagonManager wagonManager;
private PlexusContainer extensionContainer;
private static final String CONTAINER_NAME = "extensions";
public void addExtension( Extension extension,
MavenProject project,
ArtifactRepository localRepository )
throws ArtifactResolutionException, PlexusContainerException, ArtifactNotFoundException
{
String extensionId = ArtifactUtils.versionlessKey( extension.getGroupId(), extension.getArtifactId() );
getLogger().debug( "Initialising extension: " + extensionId );
Artifact artifact = (Artifact) project.getExtensionArtifactMap().get( extensionId );
if ( artifact != null )
{
ArtifactFilter filter = new ProjectArtifactExceptionFilter( artifactFilter, project.getArtifact() );
ResolutionGroup resolutionGroup;
try
{
resolutionGroup = artifactMetadataSource.retrieve( artifact, localRepository,
project.getRemoteArtifactRepositories() );
}
catch ( ArtifactMetadataRetrievalException e )
{
throw new ArtifactResolutionException( "Unable to download metadata from repository for plugin '" +
artifact.getId() + "': " + e.getMessage(), artifact, e );
}
// We use the same hack here to make sure that plexus 1.1 is available for extensions that do
// not declare plexus-utils but need it. MNG-2900
Set rgArtifacts = resolutionGroup.getArtifacts();
rgArtifacts = DefaultPluginManager.checkPlexusUtils( rgArtifacts, artifactFactory );
Set dependencies = new LinkedHashSet();
dependencies.add( artifact );
dependencies.addAll( rgArtifacts );
// Make sure that we do not influence the dependenecy resolution of extensions with the project's
// dependencyManagement
ArtifactResolutionResult result = artifactResolver.resolveTransitively( dependencies, project.getArtifact(),
Collections.EMPTY_MAP,
//project.getManagedVersionMap(),
localRepository,
project.getRemoteArtifactRepositories(),
artifactMetadataSource, filter );
// gross hack for some backwards compat (MNG-2749)
// if it is a lone artifact, then we assume it to be a resource package, and put it in the main container
// as before. If it has dependencies, that's when we risk conflict and exile to the child container
// jvz: we have to make this 2 because plexus is always added now.
Set artifacts = result.getArtifacts();
// Lifecycles are loaded by the Lifecycle executor by looking up lifecycle definitions from the
// core container. So we need to look if an extension has a lifecycle mapping and use the container
// and not an extension container. (MNG-2831)
if ( extensionContainsLifeycle( artifact.getFile() ) )
{
for ( Iterator i = artifacts.iterator(); i.hasNext(); )
{
Artifact a = (Artifact) i.next();
if ( artifactFilter.include( a ) )
{
getLogger().debug( "Adding extension to core container: " + a.getFile() );
container.addJarResource( a.getFile() );
}
}
}
else if ( artifacts.size() == 2 )
{
for ( Iterator i = artifacts.iterator(); i.hasNext(); )
{
Artifact a = (Artifact) i.next();
if ( !a.getArtifactId().equals( "plexus-utils" ) )
{
a = project.replaceWithActiveArtifact( a );
getLogger().debug( "Adding extension to core container: " + a.getFile() );
container.addJarResource( a.getFile() );
}
}
}
else
{
// create a child container for the extension
// TODO: this could surely be simpler/different on trunk with the new classworlds
if ( extensionContainer == null )
{
extensionContainer = createContainer();
}
for ( Iterator i = result.getArtifacts().iterator(); i.hasNext(); )
{
Artifact a = (Artifact) i.next();
a = project.replaceWithActiveArtifact( a );
getLogger().debug( "Adding to extension classpath: " + a.getFile() );
extensionContainer.addJarResource( a.getFile() );
}
if ( getLogger().isDebugEnabled() )
{
getLogger().debug( "Extension container contents:" );
extensionContainer.getContainerRealm().display();
}
}
}
}
private PlexusContainer createContainer()
throws PlexusContainerException
{
DefaultPlexusContainer child = new DefaultPlexusContainer();
ClassWorld classWorld = container.getClassWorld();
child.setClassWorld( classWorld );
ClassRealm childRealm = null;
// note: ideally extensions would live in their own realm, but this would mean that things like wagon-scm would
// have no way to obtain SCM extensions
String childRealmId = "plexus.core.child-container[" + CONTAINER_NAME + "]";
try
{
childRealm = classWorld.getRealm( childRealmId );
}
catch ( NoSuchRealmException e )
{
try
{
childRealm = classWorld.newRealm( childRealmId );
}
catch ( DuplicateRealmException impossibleError )
{
getLogger().error( "An impossible error has occurred. After getRealm() failed, newRealm() " +
"produced duplication error on same id!", impossibleError );
}
}
childRealm.setParent( container.getContainerRealm() );
child.setCoreRealm( childRealm );
child.setName( CONTAINER_NAME );
// This is what we are skipping - we use the parent realm, but not the parent container since otherwise
// we won't reload component descriptors that already exist in there
// child.setParentPlexusContainer( this );
// ----------------------------------------------------------------------
// Set all the child elements from the parent that were set
// programmatically.
// ----------------------------------------------------------------------
child.setLoggerManager( container.getLoggerManager() );
child.initialize();
child.start();
return child;
}
public void registerWagons()
{
if ( extensionContainer != null )
{
Set<String> wagons = findChildComponentHints( Wagon.ROLE, container, extensionContainer );
if ( wagons != null && !wagons.isEmpty() )
{
getLogger().debug( "Wagons to register: " + wagons );
wagonManager.registerWagons( wagons, extensionContainer );
}
}
else
{
getLogger().debug( "Wagons could not be registered as the extension container was never created" );
}
}
@SuppressWarnings( "unchecked" )
public Map<String, ArtifactHandler> getArtifactTypeHandlers()
{
Map<String, ArtifactHandler> result = new HashMap<String, ArtifactHandler>();
if ( extensionContainer != null )
{
try
{
result.putAll( extensionContainer.lookupMap( ArtifactHandler.ROLE ) );
}
catch ( ComponentLookupException e )
{
getLogger().debug( "ArtifactHandler extensions could not be loaded: " + e.getMessage(), e );
}
}
else
{
try
{
result.putAll( container.lookupMap( ArtifactHandler.ROLE ) );
}
catch ( ComponentLookupException e )
{
getLogger().debug( "ArtifactHandler extensions could not be loaded: " + e.getMessage(), e );
}
}
return result;
}
public void contextualize( Context context )
throws ContextException
{
container = (DefaultPlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
}
private static final class ProjectArtifactExceptionFilter
implements ArtifactFilter
{
private ArtifactFilter passThroughFilter;
private String projectDependencyConflictId;
ProjectArtifactExceptionFilter( ArtifactFilter passThroughFilter,
Artifact projectArtifact )
{
this.passThroughFilter = passThroughFilter;
projectDependencyConflictId = projectArtifact.getDependencyConflictId();
}
public boolean include( Artifact artifact )
{
String depConflictId = artifact.getDependencyConflictId();
return projectDependencyConflictId.equals( depConflictId ) || passThroughFilter.include( artifact );
}
}
private boolean extensionContainsLifeycle( File extension )
{
JarFile f;
try
{
f = new JarFile( extension );
InputStream is = f.getInputStream( f.getEntry( "META-INF/plexus/components.xml" ) );
if ( is == null )
{
return false;
}
Xpp3Dom dom = Xpp3DomBuilder.build( new InputStreamReader( is ) );
Xpp3Dom[] components = dom.getChild( "components" ).getChildren( "component" );
for ( int i = 0; i < components.length; i++ )
{
if ( components[i].getChild( "role" ).getValue().equals( "org.apache.maven.lifecycle.mapping.LifecycleMapping" ) )
{
return true;
}
}
}
catch( Exception e )
{
// do nothing
}
return false;
}
}