[MNG-4895] Plugins depending on 3rd party JARs that contain the Maven API can't be configured/run due to type incompatibilities

git-svn-id: https://svn.apache.org/repos/asf/maven/maven-3/trunk@1034880 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Benjamin Bentmann 2010-11-13 21:09:45 +00:00
parent a77e4c2ebf
commit 0fb027ea44
7 changed files with 279 additions and 139 deletions

View File

@ -20,6 +20,7 @@ package org.apache.maven.classrealm;
*/
import java.util.List;
import java.util.Map;
import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
@ -43,6 +44,13 @@ public interface ClassRealmManager
*/
ClassRealm getCoreRealm();
/**
* Gets the class realm exposing the Maven API. This is basically a restricted view on the Maven core realm.
*
* @return The class realm exposing the Maven API, never {@code null}.
*/
ClassRealm getMavenApiRealm();
/**
* Creates a new class realm for the specified project and its build extensions.
*
@ -67,12 +75,14 @@ public interface ClassRealmManager
* Creates a new class realm for the specified plugin.
*
* @param plugin The plugin for which to create a realm, must not be {@code null}.
* @param parent The parent realm for the new realm, may be {@code null} to use the Maven core realm.
* @param imports The packages/types to import from the parent realm, may be {@code null}.
* @param parent The parent realm for the new realm, may be {@code null}.
* @param parentImports The packages/types to import from the parent realm, may be {@code null}.
* @param foreignImports The packages/types to import from foreign realms, may be {@code null}.
* @param artifacts The artifacts to add to the class realm, may be {@code null}. Unresolved artifacts (i.e. with a
* missing file) will automatically be excluded from the realm.
* @return The new plugin realm, never {@code null}.
*/
ClassRealm createPluginRealm( Plugin plugin, ClassLoader parent, List<String> imports, List<Artifact> artifacts );
ClassRealm createPluginRealm( Plugin plugin, ClassLoader parent, List<String> parentImports,
Map<String, ClassLoader> foreignImports, List<Artifact> artifacts );
}

View File

@ -20,6 +20,7 @@ package org.apache.maven.classrealm;
*/
import java.util.List;
import java.util.Map;
/**
* Describes the requirements for a new class realm.
@ -65,16 +66,29 @@ public interface ClassRealmRequest
/**
* Gets the parent class realm (if any).
*
* @return The parent class realm or {@code null} if using the Maven core realm as parent.
* @return The parent class realm or {@code null} if using the default parent.
*/
ClassLoader getParent();
/**
* @deprecated Use {@link #getParentImports()} instead.
*/
@Deprecated
List<String> getImports();
/**
* Gets the packages/types to import from the parent realm.
*
* @return The modifiable list of packages/types to import from the parent realm, never {@code null}.
*/
List<String> getImports();
List<String> getParentImports();
/**
* Gets the packages/types to import from foreign realms.
*
* @return The modifiable map of packages/types to import from foreign realms, never {@code null}.
*/
Map<String, ClassLoader> getForeignImports();
/**
* Gets the constituents for the class realm.

View File

@ -23,10 +23,13 @@ import java.io.File;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.classrealm.ClassRealmRequest.RealmType;
@ -100,48 +103,116 @@ public class DefaultClassRealmManager
}
}
private synchronized ClassRealm getMavenRealm()
public synchronized ClassRealm getMavenApiRealm()
{
if ( mavenRealm == null )
{
mavenRealm = newRealm( "maven.api" );
importMavenApi( mavenRealm );
List<ClassRealmConstituent> constituents = new ArrayList<ClassRealmConstituent>();
mavenRealm.setParentClassLoader( ClassLoader.getSystemClassLoader() );
List<String> parentImports = new ArrayList<String>();
List<ClassRealmManagerDelegate> delegates = getDelegates();
if ( !delegates.isEmpty() )
{
List<ClassRealmConstituent> constituents = new ArrayList<ClassRealmConstituent>();
Map<String, ClassLoader> foreignImports = new HashMap<String, ClassLoader>();
importMavenApi( foreignImports );
ClassRealmRequest request =
new DefaultClassRealmRequest( RealmType.Core, null, new ArrayList<String>(), constituents );
callDelegates( mavenRealm, RealmType.Core, mavenRealm.getParentClassLoader(), parentImports,
foreignImports, constituents );
for ( ClassRealmManagerDelegate delegate : delegates )
{
delegate.setupRealm( mavenRealm, request );
}
wireRealm( mavenRealm, parentImports, foreignImports );
populateRealm( mavenRealm, constituents );
}
populateRealm( mavenRealm, constituents );
}
return mavenRealm;
}
private void importMavenApi( Map<String, ClassLoader> imports )
{
ClassRealm coreRealm = getCoreRealm();
// maven-*
imports.put( "org.apache.maven.*", coreRealm );
imports.put( "org.apache.maven.artifact", coreRealm );
imports.put( "org.apache.maven.classrealm", coreRealm );
imports.put( "org.apache.maven.cli", coreRealm );
imports.put( "org.apache.maven.configuration", coreRealm );
imports.put( "org.apache.maven.exception", coreRealm );
imports.put( "org.apache.maven.execution", coreRealm );
imports.put( "org.apache.maven.lifecycle", coreRealm );
imports.put( "org.apache.maven.model", coreRealm );
imports.put( "org.apache.maven.monitor", coreRealm );
imports.put( "org.apache.maven.plugin", coreRealm );
imports.put( "org.apache.maven.profiles", coreRealm );
imports.put( "org.apache.maven.project", coreRealm );
imports.put( "org.apache.maven.reporting", coreRealm );
imports.put( "org.apache.maven.repository", coreRealm );
imports.put( "org.apache.maven.settings", coreRealm );
imports.put( "org.apache.maven.toolchain", coreRealm );
imports.put( "org.apache.maven.usability", coreRealm );
// wagon-api
imports.put( "org.apache.maven.wagon.*", coreRealm );
imports.put( "org.apache.maven.wagon.authentication", coreRealm );
imports.put( "org.apache.maven.wagon.authorization", coreRealm );
imports.put( "org.apache.maven.wagon.events", coreRealm );
imports.put( "org.apache.maven.wagon.observers", coreRealm );
imports.put( "org.apache.maven.wagon.proxy", coreRealm );
imports.put( "org.apache.maven.wagon.repository", coreRealm );
imports.put( "org.apache.maven.wagon.resource", coreRealm );
// aether-api, aether-spi, aether-impl
imports.put( "org.sonatype.aether.*", coreRealm );
imports.put( "org.sonatype.aether.artifact", coreRealm );
imports.put( "org.sonatype.aether.collection", coreRealm );
imports.put( "org.sonatype.aether.deployment", coreRealm );
imports.put( "org.sonatype.aether.graph", coreRealm );
imports.put( "org.sonatype.aether.impl", coreRealm );
imports.put( "org.sonatype.aether.installation", coreRealm );
imports.put( "org.sonatype.aether.metadata", coreRealm );
imports.put( "org.sonatype.aether.repository", coreRealm );
imports.put( "org.sonatype.aether.resolution", coreRealm );
imports.put( "org.sonatype.aether.spi", coreRealm );
imports.put( "org.sonatype.aether.transfer", coreRealm );
imports.put( "org.sonatype.aether.version", coreRealm );
// plexus-classworlds
imports.put( "org.codehaus.plexus.classworlds", coreRealm );
// classworlds (for legacy code)
imports.put( "org.codehaus.classworlds", coreRealm );
// plexus-container, plexus-component-annotations
imports.put( "org.codehaus.plexus.*", coreRealm );
imports.put( "org.codehaus.plexus.component", coreRealm );
imports.put( "org.codehaus.plexus.configuration", coreRealm );
imports.put( "org.codehaus.plexus.container", coreRealm );
imports.put( "org.codehaus.plexus.context", coreRealm );
imports.put( "org.codehaus.plexus.lifecycle", coreRealm );
imports.put( "org.codehaus.plexus.logging", coreRealm );
imports.put( "org.codehaus.plexus.personality", coreRealm );
// plexus-utils (for maven-model)
imports.put( "org.codehaus.plexus.util.xml.Xpp3Dom", coreRealm );
imports.put( "org.codehaus.plexus.util.xml.pull.XmlPullParser", coreRealm );
imports.put( "org.codehaus.plexus.util.xml.pull.XmlPullParserException", coreRealm );
imports.put( "org.codehaus.plexus.util.xml.pull.XmlSerializer", coreRealm );
}
/**
* Creates a new class realm with the specified parent and imports.
*
* @param baseRealmId The base id to use for the new realm, must not be {@code null}.
* @param type The type of the class realm, must not be {@code null}.
* @param parent The parent realm for the new realm, may be {@code null} to use the Maven core realm.
* @param imports The packages/types to import from the parent realm, may be {@code null}.
* @param parent The parent realm for the new realm, may be {@code null}.
* @param parentImports The packages/types to import from the parent realm, may be {@code null}.
* @param foreignImports The packages/types to import from foreign realms, may be {@code null}.
* @param artifacts The artifacts to add to the realm, may be {@code null}. Unresolved artifacts (i.e. with a
* missing file) will automatically be excluded from the realm.
* @return The created class realm, never {@code null}.
*/
private ClassRealm createRealm( String baseRealmId, RealmType type, ClassLoader parent, List<String> imports,
boolean importXpp3Dom, List<Artifact> artifacts )
private ClassRealm createRealm( String baseRealmId, RealmType type, ClassLoader parent, List<String> parentImports,
Map<String, ClassLoader> foreignImports, List<Artifact> artifacts )
{
Set<String> artifactIds = new LinkedHashSet<String>();
@ -159,13 +230,22 @@ public class DefaultClassRealmManager
}
}
if ( imports != null )
if ( parentImports != null )
{
imports = new ArrayList<String>( imports );
parentImports = new ArrayList<String>( parentImports );
}
else
{
imports = new ArrayList<String>();
parentImports = new ArrayList<String>();
}
if ( foreignImports != null )
{
foreignImports = new TreeMap<String, ClassLoader>( foreignImports );
}
else
{
foreignImports = new TreeMap<String, ClassLoader>();
}
ClassRealm classRealm = newRealm( baseRealmId );
@ -174,46 +254,10 @@ public class DefaultClassRealmManager
{
classRealm.setParentClassLoader( parent );
}
else
{
classRealm.setParentRealm( getMavenRealm() );
}
List<ClassRealmManagerDelegate> delegates = getDelegates();
if ( !delegates.isEmpty() )
{
ClassRealmRequest request = new DefaultClassRealmRequest( type, parent, imports, constituents );
callDelegates( classRealm, type, parent, parentImports, foreignImports, constituents );
for ( ClassRealmManagerDelegate delegate : delegates )
{
delegate.setupRealm( classRealm, request );
}
}
if ( importXpp3Dom )
{
importXpp3Dom( classRealm );
}
if ( !imports.isEmpty() )
{
ClassLoader importedRealm = classRealm.getParentClassLoader();
if ( logger.isDebugEnabled() )
{
logger.debug( "Importing packages into class realm " + classRealm.getId() );
}
for ( String imp : imports )
{
if ( logger.isDebugEnabled() )
{
logger.debug( " Imported: " + imp );
}
classRealm.importFrom( importedRealm, imp );
}
}
wireRealm( classRealm, parentImports, foreignImports );
Set<String> includedIds = populateRealm( classRealm, constituents );
@ -230,64 +274,6 @@ public class DefaultClassRealmManager
return classRealm;
}
/**
* Imports Xpp3Dom and associated types into the specified realm. Unlike the other archives that constitute the API
* realm, plexus-utils is not excluded from the plugin/project realm, yet we must ensure this class is loaded from
* the API realm and not from the plugin/project realm.
*
* @param importingRealm The realm into which to import Xpp3Dom, must not be {@code null}.
*/
private void importXpp3Dom( ClassRealm importingRealm )
{
ClassRealm coreRealm = getCoreRealm();
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.util.xml.Xpp3Dom" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.util.xml.pull.XmlPullParser" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.util.xml.pull.XmlPullParserException" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.util.xml.pull.XmlSerializer" );
}
/**
* Imports the classes/resources constituting the Maven API into the specified realm.
*
* @param importingRealm The realm into which to import the Maven API, must not be {@code null}.
*/
private void importMavenApi( ClassRealm importingRealm )
{
ClassRealm coreRealm = getCoreRealm();
// maven-*
importingRealm.importFrom( coreRealm, "org.apache.maven" );
// aether
importingRealm.importFrom( coreRealm, "org.sonatype.aether" );
// plexus-classworlds
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.classworlds" );
// classworlds (for legacy code)
importingRealm.importFrom( coreRealm, "org.codehaus.classworlds" );
// plexus-container, plexus-component-annotations
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.component" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.configuration" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.container" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.context" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.lifecycle" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.logging" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.personality" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.ComponentRegistry" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.ContainerConfiguration" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.DefaultComponentRegistry" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.DefaultContainerConfiguration" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.DefaultPlexusContainer" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.DuplicateChildContainerException" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.MutablePlexusContainer" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.PlexusConstants" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.PlexusContainer" );
importingRealm.importFrom( coreRealm, "org.codehaus.plexus.PlexusContainerException" );
}
public ClassRealm getCoreRealm()
{
return container.getContainerRealm();
@ -300,7 +286,9 @@ public class DefaultClassRealmManager
throw new IllegalArgumentException( "model missing" );
}
return createRealm( getKey( model ), RealmType.Project, null, null, false, artifacts );
ClassLoader parent = getMavenApiRealm();
return createRealm( getKey( model ), RealmType.Project, parent, null, null, artifacts );
}
private static String getKey( Model model )
@ -315,18 +303,28 @@ public class DefaultClassRealmManager
throw new IllegalArgumentException( "extension plugin missing" );
}
return createRealm( getKey( plugin, true ), RealmType.Extension, null, null, true, artifacts );
ClassLoader parent = ClassLoader.getSystemClassLoader();
Map<String, ClassLoader> foreignImports =
Collections.<String, ClassLoader> singletonMap( "", getMavenApiRealm() );
return createRealm( getKey( plugin, true ), RealmType.Extension, parent, null, foreignImports, artifacts );
}
public ClassRealm createPluginRealm( Plugin plugin, ClassLoader parent, List<String> imports,
List<Artifact> artifacts )
public ClassRealm createPluginRealm( Plugin plugin, ClassLoader parent, List<String> parentImports,
Map<String, ClassLoader> foreignImports, List<Artifact> artifacts )
{
if ( plugin == null )
{
throw new IllegalArgumentException( "plugin missing" );
}
return createRealm( getKey( plugin, false ), RealmType.Plugin, parent, imports, true, artifacts );
if ( parent == null )
{
parent = ClassLoader.getSystemClassLoader();
}
return createRealm( getKey( plugin, false ), RealmType.Plugin, parent, parentImports, foreignImports, artifacts );
}
private static String getKey( Plugin plugin, boolean extension )
@ -338,8 +336,8 @@ public class DefaultClassRealmManager
private static String getId( Artifact artifact )
{
return getId( artifact.getGroupId(), artifact.getArtifactId(), artifact.getExtension(), artifact.getClassifier(),
artifact.getBaseVersion() );
return getId( artifact.getGroupId(), artifact.getArtifactId(), artifact.getExtension(),
artifact.getClassifier(), artifact.getBaseVersion() );
}
private static String getId( ClassRealmConstituent constituent )
@ -367,6 +365,31 @@ public class DefaultClassRealmManager
}
}
private void callDelegates( ClassRealm classRealm, RealmType type, ClassLoader parent, List<String> parentImports,
Map<String, ClassLoader> foreignImports, List<ClassRealmConstituent> constituents )
{
List<ClassRealmManagerDelegate> delegates = getDelegates();
if ( !delegates.isEmpty() )
{
ClassRealmRequest request =
new DefaultClassRealmRequest( type, parent, parentImports, foreignImports, constituents );
for ( ClassRealmManagerDelegate delegate : delegates )
{
try
{
delegate.setupRealm( classRealm, request );
}
catch ( Exception e )
{
logger.error( delegate.getClass().getName() + " failed to setup class realm " + classRealm + ": "
+ e.getMessage(), e );
}
}
}
}
private Set<String> populateRealm( ClassRealm classRealm, List<ClassRealmConstituent> constituents )
{
Set<String> includedIds = new LinkedHashSet<String>();
@ -402,4 +425,55 @@ public class DefaultClassRealmManager
return includedIds;
}
private void wireRealm( ClassRealm classRealm, List<String> parentImports, Map<String, ClassLoader> foreignImports )
{
if ( foreignImports != null && !foreignImports.isEmpty() )
{
if ( logger.isDebugEnabled() )
{
logger.debug( "Importing foreign packages into class realm " + classRealm.getId() );
}
for ( Map.Entry<String, ClassLoader> entry : foreignImports.entrySet() )
{
ClassLoader importedRealm = entry.getValue();
String imp = entry.getKey();
if ( logger.isDebugEnabled() )
{
logger.debug( " Imported: " + imp + " < " + getId( importedRealm ) );
}
classRealm.importFrom( importedRealm, imp );
}
}
if ( parentImports != null && !parentImports.isEmpty() )
{
if ( logger.isDebugEnabled() )
{
logger.debug( "Importing parent packages into class realm " + classRealm.getId() );
}
for ( String imp : parentImports )
{
if ( logger.isDebugEnabled() )
{
logger.debug( " Imported: " + imp + " < " + getId( classRealm.getParentClassLoader() ) );
}
classRealm.importFromParent( imp );
}
}
}
private String getId( ClassLoader classLoader )
{
if ( classLoader instanceof ClassRealm )
{
return ( (ClassRealm) classLoader ).getId();
}
return String.valueOf( classLoader );
}
}

View File

@ -20,6 +20,7 @@ package org.apache.maven.classrealm;
*/
import java.util.List;
import java.util.Map;
/**
* @author Benjamin Bentmann
@ -32,16 +33,19 @@ class DefaultClassRealmRequest
private final ClassLoader parent;
private final List<String> imports;
private final List<String> parentImports;
private final Map<String, ClassLoader> foreignImports;
private final List<ClassRealmConstituent> constituents;
public DefaultClassRealmRequest( RealmType type, ClassLoader parent, List<String> imports,
List<ClassRealmConstituent> constituents )
public DefaultClassRealmRequest( RealmType type, ClassLoader parent, List<String> parentImports,
Map<String, ClassLoader> foreignImports, List<ClassRealmConstituent> constituents )
{
this.type = type;
this.parent = parent;
this.imports = imports;
this.parentImports = parentImports;
this.foreignImports = foreignImports;
this.constituents = constituents;
}
@ -57,7 +61,17 @@ class DefaultClassRealmRequest
public List<String> getImports()
{
return imports;
return getParentImports();
}
public List<String> getParentImports()
{
return parentImports;
}
public Map<String, ClassLoader> getForeignImports()
{
return foreignImports;
}
public List<ClassRealmConstituent> getConstituents()

View File

@ -183,8 +183,7 @@ public class DefaultBuildPluginManager
return pluginRealm;
}
mavenPluginManager.setupPluginRealm( pluginDescriptor, session, session.getCurrentProject().getClassRealm(),
null, null );
mavenPluginManager.setupPluginRealm( pluginDescriptor, session, null, null, null );
return pluginDescriptor.getClassRealm();
}

View File

@ -30,6 +30,7 @@ import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -334,7 +335,10 @@ public class DefaultMavenPluginManager
List<org.sonatype.aether.artifact.Artifact> pluginArtifacts = nlg.getArtifacts( true );
ClassRealm pluginRealm = classRealmManager.createPluginRealm( plugin, parent, imports, pluginArtifacts );
Map<String, ClassLoader> foreignImports = calcImports( project, parent, imports );
ClassRealm pluginRealm =
classRealmManager.createPluginRealm( plugin, parent, null, foreignImports, pluginArtifacts );
pluginDescriptor.setClassRealm( pluginRealm );
pluginDescriptor.setArtifacts( exposedPluginArtifacts );
@ -361,6 +365,31 @@ public class DefaultMavenPluginManager
}
}
private Map<String, ClassLoader> calcImports( MavenProject project, ClassLoader parent, List<String> imports )
{
Map<String, ClassLoader> foreignImports = new HashMap<String, ClassLoader>();
ClassLoader projectRealm = project.getClassRealm();
if ( projectRealm != null )
{
foreignImports.put( "", projectRealm );
}
else
{
foreignImports.put( "", classRealmManager.getMavenApiRealm() );
}
if ( parent != null && imports != null )
{
for ( String parentImport : imports )
{
foreignImports.put( parentImport, parent );
}
}
return foreignImports;
}
public <T> T getConfiguredMojo( Class<T> mojoInterface, MavenSession session, MojoExecution mojoExecution )
throws PluginConfigurationException, PluginContainerException
{

View File

@ -37,7 +37,7 @@
<inceptionYear>2001</inceptionYear>
<properties>
<classWorldsVersion>2.2.3</classWorldsVersion>
<classWorldsVersion>2.4</classWorldsVersion>
<commonsCliVersion>1.2</commonsCliVersion>
<easyMockVersion>1.2_Java1.3</easyMockVersion>
<junitVersion>3.8.2</junitVersion>