[MNG-7160] Ability to customize core extensions classloaders (#616)

This commit is contained in:
Guillaume Nodet 2022-06-15 10:43:17 +02:00 committed by Hervé Boutemy
parent bb6bbf882d
commit 115febf29b
6 changed files with 69 additions and 24 deletions

View File

@ -29,7 +29,7 @@ import javax.inject.Singleton;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter; import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ExclusionSetFilter; import org.apache.maven.artifact.resolver.filter.ExclusionSetFilter;
import org.apache.maven.extension.internal.CoreExportsProvider; import org.apache.maven.extension.internal.CoreExports;
/** /**
* @author Jason van Zyl * @author Jason van Zyl
@ -50,10 +50,10 @@ public class DefaultArtifactFilterManager
@Inject @Inject
public DefaultArtifactFilterManager( List<ArtifactFilterManagerDelegate> delegates, public DefaultArtifactFilterManager( List<ArtifactFilterManagerDelegate> delegates,
CoreExportsProvider coreExports ) CoreExports coreExports )
{ {
this.delegates = delegates; this.delegates = delegates;
this.coreArtifacts = coreExports.get().getExportedArtifacts(); this.coreArtifacts = coreExports.getExportedArtifacts();
} }
private synchronized Set<String> getExcludedArtifacts() private synchronized Set<String> getExcludedArtifacts()

View File

@ -37,7 +37,7 @@ import javax.inject.Singleton;
import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.classrealm.ClassRealmRequest.RealmType; import org.apache.maven.classrealm.ClassRealmRequest.RealmType;
import org.apache.maven.extension.internal.CoreExportsProvider; import org.apache.maven.extension.internal.CoreExports;
import org.apache.maven.model.Model; import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin; import org.apache.maven.model.Plugin;
import org.codehaus.plexus.MutablePlexusContainer; import org.codehaus.plexus.MutablePlexusContainer;
@ -92,20 +92,20 @@ public class DefaultClassRealmManager
@Inject @Inject
public DefaultClassRealmManager( Logger logger, PlexusContainer container, public DefaultClassRealmManager( Logger logger, PlexusContainer container,
List<ClassRealmManagerDelegate> delegates, CoreExportsProvider exports ) List<ClassRealmManagerDelegate> delegates, CoreExports exports )
{ {
this.logger = logger; this.logger = logger;
this.world = ( (MutablePlexusContainer) container ).getClassWorld(); this.world = ( (MutablePlexusContainer) container ).getClassWorld();
this.containerRealm = container.getContainerRealm(); this.containerRealm = container.getContainerRealm();
this.delegates = delegates; this.delegates = delegates;
Map<String, ClassLoader> foreignImports = exports.get().getExportedPackages(); Map<String, ClassLoader> foreignImports = exports.getExportedPackages();
this.mavenApiRealm = this.mavenApiRealm =
createRealm( API_REALMID, RealmType.Core, null /* parent */, null /* parentImports */, createRealm( API_REALMID, RealmType.Core, null /* parent */, null /* parentImports */,
foreignImports, null /* artifacts */ ); foreignImports, null /* artifacts */ );
this.providedArtifacts = exports.get().getExportedArtifacts(); this.providedArtifacts = exports.getExportedArtifacts();
} }
private ClassRealm newRealm( String id ) private ClassRealm newRealm( String id )

View File

@ -19,34 +19,34 @@ package org.apache.maven.extension.internal;
* under the License. * under the License.
*/ */
import java.util.Objects;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.codehaus.plexus.PlexusContainer; import org.codehaus.plexus.PlexusContainer;
import org.eclipse.sisu.Nullable;
/** /**
* CoreExportsProvider * CoreExportsProvider
*/ */
@Named @Named
@Singleton @Singleton
public class CoreExportsProvider public class CoreExportsProvider implements Provider<CoreExports>
{ {
private final CoreExports exports; private final CoreExports exports;
@Inject @Inject
public CoreExportsProvider( PlexusContainer container, @Nullable CoreExports exports ) public CoreExportsProvider( PlexusContainer container )
{ {
if ( exports == null ) this( new CoreExports( CoreExtensionEntry.discoverFrom( container.getContainerRealm() ) ) );
{
this.exports = new CoreExports( CoreExtensionEntry.discoverFrom( container.getContainerRealm() ) );
} }
else
public CoreExportsProvider( CoreExports exports )
{ {
this.exports = exports; this.exports = Objects.requireNonNull( exports );
}
} }
public CoreExports get() public CoreExports get()

View File

@ -188,7 +188,7 @@ under the License.
<groupId>org.codehaus.modello</groupId> <groupId>org.codehaus.modello</groupId>
<artifactId>modello-maven-plugin</artifactId> <artifactId>modello-maven-plugin</artifactId>
<configuration> <configuration>
<version>1.0.0</version> <version>1.1.0</version>
<models> <models>
<model>src/main/mdo/core-extensions.mdo</model> <model>src/main/mdo/core-extensions.mdo</model>
</models> </models>

View File

@ -31,6 +31,7 @@ import javax.inject.Named;
import org.apache.maven.RepositoryUtils; import org.apache.maven.RepositoryUtils;
import org.apache.maven.cli.internal.extension.model.CoreExtension; import org.apache.maven.cli.internal.extension.model.CoreExtension;
import org.apache.maven.execution.MavenExecutionRequest; import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.extension.internal.CoreExports;
import org.apache.maven.extension.internal.CoreExtensionEntry; import org.apache.maven.extension.internal.CoreExtensionEntry;
import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory; import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory;
import org.apache.maven.model.Plugin; import org.apache.maven.model.Plugin;
@ -59,12 +60,18 @@ import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator;
@Named @Named
public class BootstrapCoreExtensionManager public class BootstrapCoreExtensionManager
{ {
public static final String STRATEGY_PARENT_FIRST = "parent-first";
public static final String STRATEGY_PLUGIN = "plugin";
public static final String STRATEGY_SELF_FIRST = "self-first";
private final Logger log; private final Logger log;
private final DefaultPluginDependenciesResolver pluginDependenciesResolver; private final DefaultPluginDependenciesResolver pluginDependenciesResolver;
private final DefaultRepositorySystemSessionFactory repositorySystemSessionFactory; private final DefaultRepositorySystemSessionFactory repositorySystemSessionFactory;
private final CoreExports coreExports;
private final ClassWorld classWorld; private final ClassWorld classWorld;
private final ClassRealm parentRealm; private final ClassRealm parentRealm;
@ -72,11 +79,13 @@ public class BootstrapCoreExtensionManager
@Inject @Inject
public BootstrapCoreExtensionManager( Logger log, DefaultPluginDependenciesResolver pluginDependenciesResolver, public BootstrapCoreExtensionManager( Logger log, DefaultPluginDependenciesResolver pluginDependenciesResolver,
DefaultRepositorySystemSessionFactory repositorySystemSessionFactory, DefaultRepositorySystemSessionFactory repositorySystemSessionFactory,
CoreExports coreExports,
PlexusContainer container ) PlexusContainer container )
{ {
this.log = log; this.log = log;
this.pluginDependenciesResolver = pluginDependenciesResolver; this.pluginDependenciesResolver = pluginDependenciesResolver;
this.repositorySystemSessionFactory = repositorySystemSessionFactory; this.repositorySystemSessionFactory = repositorySystemSessionFactory;
this.coreExports = coreExports;
this.classWorld = ( (DefaultPlexusContainer) container ).getClassWorld(); this.classWorld = ( (DefaultPlexusContainer) container ).getClassWorld();
this.parentRealm = container.getContainerRealm(); this.parentRealm = container.getContainerRealm();
} }
@ -121,15 +130,43 @@ public class BootstrapCoreExtensionManager
{ {
String realmId = String realmId =
"coreExtension>" + extension.getGroupId() + ":" + extension.getArtifactId() + ":" + extension.getVersion(); "coreExtension>" + extension.getGroupId() + ":" + extension.getArtifactId() + ":" + extension.getVersion();
ClassRealm realm = classWorld.newRealm( realmId, null ); final ClassRealm realm = classWorld.newRealm( realmId, null );
log.debug( "Populating class realm " + realm.getId() ); Set<String> providedArtifacts = Collections.emptySet();
String classLoadingStrategy = extension.getClassLoadingStrategy();
if ( STRATEGY_PARENT_FIRST.equals( classLoadingStrategy ) )
{
realm.importFrom( parentRealm, "" );
}
else if ( STRATEGY_PLUGIN.equals( classLoadingStrategy ) )
{
coreExports.getExportedPackages().forEach( ( p, cl ) -> realm.importFrom( cl, p ) );
providedArtifacts = coreExports.getExportedArtifacts();
}
else if ( STRATEGY_SELF_FIRST.equals( classLoadingStrategy ) )
{
realm.setParentRealm( parentRealm ); realm.setParentRealm( parentRealm );
}
else
{
throw new IllegalArgumentException( "Unsupported class-loading strategy '"
+ classLoadingStrategy + "'. Supported values are: " + STRATEGY_PARENT_FIRST
+ ", " + STRATEGY_PLUGIN + " and " + STRATEGY_SELF_FIRST );
}
log.debug( "Populating class realm " + realm.getId() );
for ( Artifact artifact : artifacts ) for ( Artifact artifact : artifacts )
{
String id = artifact.getGroupId() + ":" + artifact.getArtifactId();
if ( providedArtifacts.contains( id ) )
{
log.debug( " Excluded " + id );
}
else
{ {
File file = artifact.getFile(); File file = artifact.getFile();
log.debug( " Included " + file ); log.debug( " Included " + id + " located at " + file );
realm.addURL( file.toURI().toURL() ); realm.addURL( file.toURI().toURL() );
} }
}
return CoreExtensionEntry.discoverFrom( realm, Collections.singleton( artifacts.get( 0 ).getFile() ) ); return CoreExtensionEntry.discoverFrom( realm, Collections.singleton( artifacts.get( 0 ).getFile() ) );
} }

View File

@ -82,6 +82,14 @@
<required>true</required> <required>true</required>
<type>String</type> <type>String</type>
</field> </field>
<field>
<name>classLoadingStrategy</name>
<description>The class loading strategy: 'self-first' (the default), 'parent-first' (loads classes from the parent, then from the extension) or 'plugin' (follows the rules from extensions defined as plugins).</description>
<version>1.1.0+</version>
<defaultValue>self-first</defaultValue>
<required>false</required>
<type>String</type>
</field>
</fields> </fields>
<codeSegments> <codeSegments>
<codeSegment> <codeSegment>