MNG-5771 user-defined core extensions

read ${maven.projectBasedir}/.mvn/extensions.xml and create core
extensions realms during maven runtime bootstrap. this required
short-lived bootstrap plexus container to resolve extensions.

individual extensions realms are wired to maven.ext realm according
to META-INF/maven/extension.xml exported packages specification

Signed-off-by: Igor Fedorenko <ifedorenko@apache.org>
This commit is contained in:
Igor Fedorenko 2015-02-14 09:59:02 -05:00
parent e2a0792840
commit 6efacdb3fc
11 changed files with 769 additions and 186 deletions

View File

@ -29,9 +29,7 @@ import javax.inject.Singleton;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ExclusionSetFilter;
import org.apache.maven.extension.internal.DefaultCoreExports;
import com.google.common.collect.ImmutableSet;
import org.apache.maven.extension.internal.CoreExportsProvider;
/**
* @author Jason van Zyl
@ -46,16 +44,24 @@ public class DefaultArtifactFilterManager
// this is a live injected collection
protected final List<ArtifactFilterManagerDelegate> delegates;
protected final Set<String> coreArtifacts;
protected Set<String> excludedArtifacts;
protected final Set<String> excludedArtifacts;
private final Set<String> coreArtifacts;
@Inject
public DefaultArtifactFilterManager( List<ArtifactFilterManagerDelegate> delegates, DefaultCoreExports extensions )
public DefaultArtifactFilterManager( List<ArtifactFilterManagerDelegate> delegates, CoreExportsProvider coreExports )
{
this.delegates = delegates;
this.coreArtifacts = ImmutableSet.copyOf( extensions.getExportedArtifacts() );
this.excludedArtifacts = new LinkedHashSet<String>( extensions.getExportedArtifacts() );
this.coreArtifacts = coreExports.get().getExportedArtifacts();
}
private synchronized Set<String> getExcludedArtifacts()
{
if ( excludedArtifacts == null )
{
excludedArtifacts = new LinkedHashSet<String>( coreArtifacts );
}
return excludedArtifacts;
}
/**
@ -65,7 +71,7 @@ public class DefaultArtifactFilterManager
*/
public ArtifactFilter getArtifactFilter()
{
Set<String> excludes = new LinkedHashSet<String>( excludedArtifacts );
Set<String> excludes = new LinkedHashSet<String>( getExcludedArtifacts() );
for ( ArtifactFilterManagerDelegate delegate : delegates )
{
@ -87,7 +93,7 @@ public class DefaultArtifactFilterManager
public void excludeArtifact( String artifactId )
{
excludedArtifacts.add( artifactId );
getExcludedArtifacts().add( artifactId );
}
public Set<String> getCoreArtifactExcludes()

View File

@ -23,7 +23,6 @@ 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;
@ -37,7 +36,7 @@ import javax.inject.Singleton;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.classrealm.ClassRealmRequest.RealmType;
import org.apache.maven.extension.internal.DefaultCoreExports;
import org.apache.maven.extension.internal.CoreExportsProvider;
import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
import org.codehaus.plexus.MutablePlexusContainer;
@ -49,8 +48,6 @@ import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.util.StringUtils;
import org.eclipse.aether.artifact.Artifact;
import com.google.common.collect.ImmutableMap;
/**
* Manages the class realms used by Maven. <strong>Warning:</strong> This is an internal utility class that is only
* public for technical reasons, it is not part of the public API. In particular, this class can be changed or deleted
@ -63,6 +60,7 @@ import com.google.common.collect.ImmutableMap;
public class DefaultClassRealmManager
implements ClassRealmManager
{
public static final String API_REALMID = "maven.api";
/**
* During normal command line build, ClassWorld is loaded by jvm system classloader, which only includes
@ -83,25 +81,22 @@ public class DefaultClassRealmManager
// this is a live injected collection
private final List<ClassRealmManagerDelegate> delegates;
private final Map<String, ClassLoader> coreImports;
private ClassRealm mavenRealm;
private final ClassRealm mavenApiRealm;
@Inject
public DefaultClassRealmManager( Logger logger, PlexusContainer container,
List<ClassRealmManagerDelegate> delegates, DefaultCoreExports coreExtensions )
List<ClassRealmManagerDelegate> delegates, CoreExportsProvider exports )
{
this.logger = logger;
this.world = ( (MutablePlexusContainer) container ).getClassWorld();
this.containerRealm = container.getContainerRealm();
this.delegates = delegates;
Map<String, ClassLoader> coreImports = new HashMap<String, ClassLoader>();
for ( String corePackage : coreExtensions.getExportedPackages() )
{
coreImports.put( corePackage, containerRealm );
}
this.coreImports = ImmutableMap.copyOf( coreImports );
Map<String, ClassLoader> foreignImports = exports.get().getExportedPackages();
this.mavenApiRealm =
createRealm( API_REALMID, RealmType.Core, null /* parent */, null /* parentImports */,
foreignImports, null /* artifacts */ );
}
private ClassRealm newRealm( String id )
@ -133,27 +128,9 @@ public class DefaultClassRealmManager
}
}
public synchronized ClassRealm getMavenApiRealm()
public ClassRealm getMavenApiRealm()
{
if ( mavenRealm == null )
{
mavenRealm = newRealm( "maven.api" );
List<ClassRealmConstituent> constituents = new ArrayList<ClassRealmConstituent>();
List<String> parentImports = new ArrayList<String>();
Map<String, ClassLoader> foreignImports = new HashMap<String, ClassLoader>( coreImports );
callDelegates( mavenRealm, RealmType.Core, mavenRealm.getParentClassLoader(), parentImports,
foreignImports, constituents );
wireRealm( mavenRealm, parentImports, foreignImports );
populateRealm( mavenRealm, constituents );
}
return mavenRealm;
return mavenApiRealm;
}
/**

View File

@ -0,0 +1,75 @@
package org.apache.maven.extension.internal;
/*
* 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 java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
/**
* Provides information about artifacts (identified by groupId:artifactId string key) and classpath elements exported by
* Maven core itself and loaded Maven core extensions.
*
* @since 3.2.6
*/
public class CoreExports
{
private final Set<String> artifacts;
private final Map<String, ClassLoader> packages;
public CoreExports( CoreExtensionEntry entry )
{
this( entry.getClassRealm(), entry.getExportedArtifacts(), entry.getExportedPackages() );
}
public CoreExports( ClassRealm realm, Set<String> exportedArtifacts, Set<String> exportedPackages )
{
Map<String, ClassLoader> packages = new LinkedHashMap<String, ClassLoader>();
for ( String pkg : exportedPackages )
{
packages.put( pkg, realm );
}
this.artifacts = ImmutableSet.copyOf( exportedArtifacts );
this.packages = ImmutableMap.copyOf( packages );
}
/**
* Returns artifacts exported by Maven core and core extensions. Artifacts are identified by their
* groupId:artifactId string key.
*/
public Set<String> getExportedArtifacts()
{
return artifacts;
}
/**
* Returns packages exported by Maven core and core extensions.
*/
public Map<String, ClassLoader> getExportedPackages()
{
return packages;
}
}

View File

@ -0,0 +1,53 @@
package org.apache.maven.extension.internal;
/*
* 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 javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.codehaus.plexus.PlexusContainer;
import org.eclipse.sisu.Nullable;
@Named
@Singleton
public class CoreExportsProvider
{
private final CoreExports exports;
@Inject
public CoreExportsProvider( PlexusContainer container, @Nullable CoreExports exports )
{
if ( exports == null )
{
this.exports = new CoreExports( CoreExtensionEntry.discoverFrom( container.getContainerRealm() ) );
}
else
{
this.exports = exports;
}
}
public CoreExports get()
{
return exports;
}
}

View File

@ -0,0 +1,141 @@
package org.apache.maven.extension.internal;
/*
* 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 java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;
import org.apache.maven.project.ExtensionDescriptor;
import org.apache.maven.project.ExtensionDescriptorBuilder;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.util.IOUtil;
import com.google.common.collect.ImmutableSet;
/**
* Provides information about artifacts (identified by groupId:artifactId string key) and classpath elements exported by
* Maven core itself or a Maven core extension.
*
* @since 3.2.6
*/
public class CoreExtensionEntry
{
private final ClassRealm realm;
private final Set<String> artifacts;
private final Set<String> packages;
public CoreExtensionEntry( ClassRealm realm, Collection<String> artifacts, Collection<String> packages )
{
this.realm = realm;
this.artifacts = ImmutableSet.copyOf( artifacts );
this.packages = ImmutableSet.copyOf( packages );
}
/**
* Returns ClassLoader used to load extension classes.
*/
public ClassRealm getClassRealm()
{
return realm;
}
/**
* Returns artifacts exported by the extension, identified by groupId:artifactId string key.
*/
public Set<String> getExportedArtifacts()
{
return artifacts;
}
/**
* Returns classpath elements exported by the extension.
*/
public Set<String> getExportedPackages()
{
return packages;
}
private static final ExtensionDescriptorBuilder builder = new ExtensionDescriptorBuilder();
public static CoreExtensionEntry discoverFrom( ClassRealm loader )
{
Set<String> artifacts = new LinkedHashSet<String>();
Set<String> packages = new LinkedHashSet<String>();
try
{
Enumeration<URL> urls = loader.getResources( builder.getExtensionDescriptorLocation() );
while ( urls.hasMoreElements() )
{
InputStream is = urls.nextElement().openStream();
try
{
ExtensionDescriptor descriptor = builder.build( is );
artifacts.addAll( descriptor.getExportedArtifacts() );
packages.addAll( descriptor.getExportedPackages() );
}
finally
{
IOUtil.close( is );
}
}
}
catch ( IOException ignored )
{
// exports descriptors are entirely optional
}
return new CoreExtensionEntry( loader, artifacts, packages );
}
public static CoreExtensionEntry discoverFrom( ClassRealm loader, Collection<File> classpath )
{
Set<String> artifacts = new LinkedHashSet<String>();
Set<String> packages = new LinkedHashSet<String>();
try
{
for ( File entry : classpath )
{
ExtensionDescriptor descriptor = builder.build( entry );
if ( descriptor != null )
{
artifacts.addAll( descriptor.getExportedArtifacts() );
packages.addAll( descriptor.getExportedPackages() );
}
}
}
catch ( IOException ignored )
{
// exports descriptors are entirely optional
}
return new CoreExtensionEntry( loader, artifacts, packages );
}
}

View File

@ -1,97 +0,0 @@
package org.apache.maven.extension.internal;
/*
* 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 java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.apache.maven.project.ExtensionDescriptor;
import org.apache.maven.project.ExtensionDescriptorBuilder;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.util.IOUtil;
import com.google.common.collect.ImmutableSet;
/**
* @since 3.2.6
*/
@Named
@Singleton
public class DefaultCoreExports
{
private static final ExtensionDescriptorBuilder builder = new ExtensionDescriptorBuilder();
private final Set<String> artifacts;
private final Set<String> packages;
@Inject
public DefaultCoreExports( PlexusContainer container )
throws IOException
{
Set<String> artifacts = new LinkedHashSet<String>();
Set<String> packages = new LinkedHashSet<String>();
Enumeration<URL> extensions =
container.getContainerRealm().getResources( builder.getExtensionDescriptorLocation() );
while ( extensions.hasMoreElements() )
{
InputStream is = extensions.nextElement().openStream();
try
{
ExtensionDescriptor descriptor = builder.build( is );
artifacts.addAll( descriptor.getExportedArtifacts() );
packages.addAll( descriptor.getExportedPackages() );
}
finally
{
IOUtil.close( is );
}
}
this.artifacts = ImmutableSet.copyOf( artifacts );
this.packages = ImmutableSet.copyOf( packages );
}
/**
* Returns artifacts exported by Maven core and core extensions. Artifacts are identified by their
* groupId:artifactId.
*/
public Set<String> getExportedArtifacts()
{
return artifacts;
}
/**
* Returns packages exported by Maven core and core extensions.
*/
public Set<String> getExportedPackages()
{
return packages;
}
}

View File

@ -136,9 +136,31 @@ public class DefaultPluginDependenciesResolver
return pluginArtifact;
}
/**
* @since 3.2.6
*/
public DependencyNode resolveCoreExtension( Plugin plugin, DependencyFilter dependencyFilter,
List<RemoteRepository> repositories, RepositorySystemSession session )
throws PluginResolutionException
{
return resolveInternal( plugin, null /* pluginArtifact */, dependencyFilter, null /* transformer */,
repositories, session );
}
public DependencyNode resolve( Plugin plugin, Artifact pluginArtifact, DependencyFilter dependencyFilter,
List<RemoteRepository> repositories, RepositorySystemSession session )
throws PluginResolutionException
{
DependencyFilter resolutionFilter =
new ExclusionsDependencyFilter( artifactFilterManager.getCoreArtifactExcludes() );
resolutionFilter = AndDependencyFilter.newInstance( resolutionFilter, dependencyFilter );
return resolveInternal( plugin, pluginArtifact, resolutionFilter, new PlexusUtilsInjector(), repositories, session );
}
private DependencyNode resolveInternal( Plugin plugin, Artifact pluginArtifact, DependencyFilter dependencyFilter,
DependencyGraphTransformer transformer,
List<RemoteRepository> repositories, RepositorySystemSession session )
throws PluginResolutionException
{
RequestTrace trace = RequestTrace.newChild( null, plugin );
@ -148,11 +170,7 @@ public class DefaultPluginDependenciesResolver
}
DependencyFilter collectionFilter = new ScopeDependencyFilter( "provided", "test" );
DependencyFilter resolutionFilter =
new ExclusionsDependencyFilter( artifactFilterManager.getCoreArtifactExcludes() );
resolutionFilter = AndDependencyFilter.newInstance( resolutionFilter, dependencyFilter );
resolutionFilter = new AndDependencyFilter( collectionFilter, resolutionFilter );
DependencyFilter resolutionFilter = AndDependencyFilter.newInstance( collectionFilter, dependencyFilter );
DependencyNode node;
@ -161,9 +179,8 @@ public class DefaultPluginDependenciesResolver
DependencySelector selector =
AndDependencySelector.newInstance( session.getDependencySelector(), new WagonExcluder() );
DependencyGraphTransformer transformer =
ChainedDependencyGraphTransformer.newInstance( session.getDependencyGraphTransformer(),
new PlexusUtilsInjector() );
transformer =
ChainedDependencyGraphTransformer.newInstance( session.getDependencyGraphTransformer(), transformer );
DefaultRepositorySystemSession pluginSession = new DefaultRepositorySystemSession( session );
pluginSession.setDependencySelector( selector );

View File

@ -121,10 +121,24 @@
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.eclipse.sisu</groupId>
<artifactId>sisu-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-component-metadata</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.modello</groupId>
<artifactId>modello-maven-plugin</artifactId>
<configuration>
<version>1.0.0</version>
<models>
<model>src/main/mdo/core-extensions.mdo</model>
</models>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -19,11 +19,14 @@ package org.apache.maven.cli;
* under the License.
*/
import java.io.BufferedInputStream;
import java.io.Console;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
@ -47,6 +50,9 @@ import org.apache.maven.building.Problem;
import org.apache.maven.building.Source;
import org.apache.maven.cli.event.DefaultEventSpyContext;
import org.apache.maven.cli.event.ExecutionEventLogger;
import org.apache.maven.cli.internal.BootstrapCoreExtensionManager;
import org.apache.maven.cli.internal.extension.model.CoreExtension;
import org.apache.maven.cli.internal.extension.model.io.xpp3.CoreExtensionsXpp3Reader;
import org.apache.maven.cli.logging.Slf4jConfiguration;
import org.apache.maven.cli.logging.Slf4jConfigurationFactory;
import org.apache.maven.cli.logging.Slf4jLoggerManager;
@ -64,6 +70,8 @@ import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionRequestPopulationException;
import org.apache.maven.execution.MavenExecutionRequestPopulator;
import org.apache.maven.execution.MavenExecutionResult;
import org.apache.maven.extension.internal.CoreExports;
import org.apache.maven.extension.internal.CoreExtensionEntry;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.model.building.ModelProcessor;
import org.apache.maven.project.MavenProject;
@ -87,7 +95,9 @@ import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.logging.LoggerManager;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.eclipse.aether.transfer.TransferListener;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
@ -134,6 +144,8 @@ public class MavenCli
private static final String EXT_CLASS_PATH = "maven.ext.class.path";
private static final String EXTENSIONS_FILENAME = ".mvn/extensions.xml";
private ClassWorld classWorld;
private LoggerManager plexusLoggerManager;
@ -485,21 +497,44 @@ public class MavenCli
cliRequest.classWorld = new ClassWorld( "plexus.core", Thread.currentThread().getContextClassLoader() );
}
DefaultPlexusContainer container;
ClassRealm coreRealm = cliRequest.classWorld.getClassRealm( "plexus.core" );
if ( coreRealm == null )
{
coreRealm = cliRequest.classWorld.getRealms().iterator().next();
}
List<File> extClassPath = parseExtClasspath( cliRequest );
CoreExtensionEntry coreEntry = CoreExtensionEntry.discoverFrom( coreRealm );
List<CoreExtensionEntry> extensions =
loadCoreExtensions( cliRequest, coreRealm, coreEntry.getExportedArtifacts() );
ClassRealm containerRealm = setupContainerRealm( cliRequest.classWorld, coreRealm, extClassPath, extensions );
ContainerConfiguration cc = new DefaultContainerConfiguration()
.setClassWorld( cliRequest.classWorld )
.setRealm( setupContainerRealm( cliRequest ) )
.setRealm( containerRealm )
.setClassPathScanning( PlexusConstants.SCANNING_INDEX )
.setAutoWiring( true )
.setName( "maven" );
container = new DefaultPlexusContainer( cc, new AbstractModule()
Set<String> exportedArtifacts = new HashSet<String>( coreEntry.getExportedArtifacts() );
Set<String> exportedPackages = new HashSet<String>( coreEntry.getExportedPackages() );
for ( CoreExtensionEntry extension : extensions )
{
exportedArtifacts.addAll( extension.getExportedArtifacts() );
exportedPackages.addAll( extension.getExportedPackages() );
}
final CoreExports exports = new CoreExports( containerRealm, exportedArtifacts, exportedPackages );
DefaultPlexusContainer container = new DefaultPlexusContainer( cc, new AbstractModule()
{
@Override
protected void configure()
{
bind( ILoggerFactory.class ).toInstance( slf4jLoggerFactory );
bind( CoreExports.class ).toInstance( exports );
}
} );
@ -508,6 +543,11 @@ public class MavenCli
container.setLoggerManager( plexusLoggerManager );
for ( CoreExtensionEntry extension : extensions )
{
container.discoverComponents( extension.getClassRealm() );
}
customizeContainer( container );
container.getLoggerManager().setThresholds( cliRequest.request.getLoggingLevel() );
@ -543,49 +583,170 @@ public class MavenCli
return container;
}
private ClassRealm setupContainerRealm( CliRequest cliRequest )
private List<CoreExtensionEntry> loadCoreExtensions( CliRequest cliRequest, ClassRealm containerRealm,
Set<String> providedArtifacts )
{
if ( cliRequest.projectBaseDirectory == null )
{
return Collections.emptyList();
}
File extensionsFile = new File( cliRequest.projectBaseDirectory, EXTENSIONS_FILENAME );
if ( !extensionsFile.isFile() )
{
return Collections.emptyList();
}
try
{
List<CoreExtension> extensions = readCoreExtensionsDescriptor( extensionsFile );
if ( extensions.isEmpty() )
{
return Collections.emptyList();
}
ContainerConfiguration cc = new DefaultContainerConfiguration() //
.setClassWorld( cliRequest.classWorld ) //
.setRealm( containerRealm ) //
.setClassPathScanning( PlexusConstants.SCANNING_INDEX ) //
.setAutoWiring( true ) //
.setName( "maven" );
DefaultPlexusContainer container = new DefaultPlexusContainer( cc, new AbstractModule()
{
@Override
protected void configure()
{
bind( ILoggerFactory.class ).toInstance( slf4jLoggerFactory );
}
} );
try
{
container.setLookupRealm( null );
container.setLoggerManager( plexusLoggerManager );
container.getLoggerManager().setThresholds( cliRequest.request.getLoggingLevel() );
Thread.currentThread().setContextClassLoader( container.getContainerRealm() );
executionRequestPopulator = container.lookup( MavenExecutionRequestPopulator.class );
settingsBuilder = container.lookup( SettingsBuilder.class );
MavenExecutionRequest request = DefaultMavenExecutionRequest.copy( cliRequest.request );
settings( cliRequest, request );
request = populateRequest( cliRequest, request );
request = executionRequestPopulator.populateDefaults( request );
BootstrapCoreExtensionManager resolver = container.lookup( BootstrapCoreExtensionManager.class );
return resolver.loadCoreExtensions( request, providedArtifacts, extensions );
}
finally
{
executionRequestPopulator = null;
settingsBuilder = null;
container.dispose();
}
}
catch ( RuntimeException e )
{
// runtime exceptions are most likely bugs in maven, let them bubble up to the user
throw e;
}
catch ( Exception e )
{
slf4jLogger.warn( "Failed to read extensions descriptor " + extensionsFile + ": " + e.getMessage() );
}
return Collections.emptyList();
}
private List<CoreExtension> readCoreExtensionsDescriptor( File extensionsFile )
throws IOException, XmlPullParserException
{
CoreExtensionsXpp3Reader parser = new CoreExtensionsXpp3Reader();
InputStream is = null;
try
{
is = new BufferedInputStream( new FileInputStream( extensionsFile ) );
return parser.read( is ).getExtensions();
}
finally
{
IOUtil.close( is );
}
}
private ClassRealm setupContainerRealm( ClassWorld classWorld, ClassRealm coreRealm, List<File> extClassPath,
List<CoreExtensionEntry> extensions )
throws Exception
{
ClassRealm containerRealm = null;
if ( !extClassPath.isEmpty() || !extensions.isEmpty() )
{
ClassRealm extRealm = classWorld.newRealm( "maven.ext", null );
extRealm.setParentRealm( coreRealm );
slf4jLogger.debug( "Populating class realm " + extRealm.getId() );
for ( File file : extClassPath )
{
slf4jLogger.debug( " Included " + file );
extRealm.addURL( file.toURI().toURL() );
}
for ( CoreExtensionEntry entry : reverse( extensions ) )
{
Set<String> exportedPackages = entry.getExportedPackages();
ClassRealm realm = entry.getClassRealm();
for ( String exportedPackage : exportedPackages )
{
extRealm.importFrom( realm, exportedPackage );
}
if ( exportedPackages.isEmpty() )
{
// sisu uses realm imports to establish component visibility
extRealm.importFrom( realm, realm.getId() );
}
}
return extRealm;
}
return coreRealm;
}
private static <T> List<T> reverse( List<T> list )
{
List<T> copy = new ArrayList<T>( list );
Collections.reverse( copy );
return copy;
}
private List<File> parseExtClasspath( CliRequest cliRequest )
{
String extClassPath = cliRequest.userProperties.getProperty( EXT_CLASS_PATH );
if ( extClassPath == null )
{
extClassPath = cliRequest.systemProperties.getProperty( EXT_CLASS_PATH );
}
List<File> jars = new ArrayList<File>();
if ( StringUtils.isNotEmpty( extClassPath ) )
{
String[] jars = StringUtils.split( extClassPath, File.pathSeparator );
if ( jars.length > 0 )
for ( String jar : StringUtils.split( extClassPath, File.pathSeparator ) )
{
ClassRealm coreRealm = cliRequest.classWorld.getClassRealm( "plexus.core" );
if ( coreRealm == null )
{
coreRealm = cliRequest.classWorld.getRealms().iterator().next();
}
File file = resolveFile( new File( jar ), cliRequest.workingDirectory );
ClassRealm extRealm = cliRequest.classWorld.newRealm( "maven.ext", null );
slf4jLogger.debug( " Included " + file );
slf4jLogger.debug( "Populating class realm " + extRealm.getId() );
for ( String jar : jars )
{
File file = resolveFile( new File( jar ), cliRequest.workingDirectory );
slf4jLogger.debug( " Included " + file );
extRealm.addURL( file.toURI().toURL() );
}
extRealm.setParentRealm( coreRealm );
containerRealm = extRealm;
jars.add( file );
}
}
return containerRealm;
return jars;
}
//
@ -813,6 +974,12 @@ public class MavenCli
@SuppressWarnings( "checkstyle:methodlength" )
private void settings( CliRequest cliRequest )
throws Exception
{
settings( cliRequest, cliRequest.request );
}
private void settings( CliRequest cliRequest, MavenExecutionRequest request )
throws Exception
{
File userSettingsFile;
@ -851,8 +1018,8 @@ public class MavenCli
globalSettingsFile = DEFAULT_GLOBAL_SETTINGS_FILE;
}
cliRequest.request.setGlobalSettingsFile( globalSettingsFile );
cliRequest.request.setUserSettingsFile( userSettingsFile );
request.setGlobalSettingsFile( globalSettingsFile );
request.setUserSettingsFile( userSettingsFile );
SettingsBuildingRequest settingsRequest = new DefaultSettingsBuildingRequest();
settingsRequest.setGlobalSettingsFile( globalSettingsFile );
@ -860,7 +1027,10 @@ public class MavenCli
settingsRequest.setSystemProperties( cliRequest.systemProperties );
settingsRequest.setUserProperties( cliRequest.userProperties );
eventSpyDispatcher.onEvent( settingsRequest );
if ( eventSpyDispatcher != null )
{
eventSpyDispatcher.onEvent( settingsRequest );
}
slf4jLogger.debug( "Reading global settings from "
+ getLocation( settingsRequest.getGlobalSettingsSource(),
@ -870,9 +1040,12 @@ public class MavenCli
SettingsBuildingResult settingsResult = settingsBuilder.build( settingsRequest );
eventSpyDispatcher.onEvent( settingsResult );
if ( eventSpyDispatcher != null )
{
eventSpyDispatcher.onEvent( settingsResult );
}
executionRequestPopulator.populateFromSettings( cliRequest.request, settingsResult.getEffectiveSettings() );
executionRequestPopulator.populateFromSettings( request, settingsResult.getEffectiveSettings() );
if ( !settingsResult.getProblems().isEmpty() && slf4jLogger.isWarnEnabled() )
{
@ -982,7 +1155,11 @@ public class MavenCli
private MavenExecutionRequest populateRequest( CliRequest cliRequest )
{
MavenExecutionRequest request = cliRequest.request;
return populateRequest( cliRequest, cliRequest.request );
}
private MavenExecutionRequest populateRequest( CliRequest cliRequest, MavenExecutionRequest request )
{
CommandLine commandLine = cliRequest.commandLine;
String workingDirectory = cliRequest.workingDirectory;
boolean quiet = cliRequest.quiet;
@ -1127,7 +1304,10 @@ public class MavenCli
}
ExecutionListener executionListener = new ExecutionEventLogger();
executionListener = eventSpyDispatcher.chainListener( executionListener );
if ( eventSpyDispatcher != null )
{
executionListener = eventSpyDispatcher.chainListener( executionListener );
}
String alternatePomFile = null;
if ( commandLine.hasOption( CLIManager.ALTERNATE_POM_FILE ) )
@ -1172,7 +1352,7 @@ public class MavenCli
request.setPom( pom );
}
else
else if ( modelProcessor != null )
{
File pom = modelProcessor.locatePom( baseDirectory );

View File

@ -0,0 +1,143 @@
package org.apache.maven.cli.internal;
/*
* 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 java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.maven.RepositoryUtils;
import org.apache.maven.cli.internal.extension.model.CoreExtension;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.extension.internal.CoreExtensionEntry;
import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.PluginResolutionException;
import org.apache.maven.plugin.internal.DefaultPluginDependenciesResolver;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.logging.Logger;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.graph.DependencyNode;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.util.filter.ExclusionsDependencyFilter;
import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator;
@Named
public class BootstrapCoreExtensionManager
{
private final Logger log;
private final DefaultPluginDependenciesResolver pluginDependenciesResolver;
private final DefaultRepositorySystemSessionFactory repositorySystemSessionFactory;
private final ClassWorld classWorld;
private final ClassRealm parentRealm;
@Inject
public BootstrapCoreExtensionManager( Logger log, DefaultPluginDependenciesResolver pluginDependenciesResolver,
DefaultRepositorySystemSessionFactory repositorySystemSessionFactory,
PlexusContainer container )
{
this.log = log;
this.pluginDependenciesResolver = pluginDependenciesResolver;
this.repositorySystemSessionFactory = repositorySystemSessionFactory;
this.classWorld = ( (DefaultPlexusContainer) container ).getClassWorld();
this.parentRealm = container.getContainerRealm();
}
public List<CoreExtensionEntry> loadCoreExtensions( MavenExecutionRequest request, Set<String> providedArtifacts,
List<CoreExtension> extensions )
throws Exception
{
RepositorySystemSession repoSession = repositorySystemSessionFactory.newRepositorySession( request );
List<RemoteRepository> repositories = RepositoryUtils.toRepos( request.getPluginArtifactRepositories() );
return resolveCoreExtensions( repoSession, repositories, providedArtifacts, extensions );
}
private List<CoreExtensionEntry> resolveCoreExtensions( RepositorySystemSession repoSession,
List<RemoteRepository> repositories,
Set<String> providedArtifacts,
List<CoreExtension> configuration )
throws Exception
{
List<CoreExtensionEntry> extensions = new ArrayList<CoreExtensionEntry>();
DependencyFilter dependencyFilter = new ExclusionsDependencyFilter( providedArtifacts );
for ( CoreExtension extension : configuration )
{
List<Artifact> artifacts = resolveExtension( extension, repoSession, repositories, dependencyFilter );
if ( !artifacts.isEmpty() )
{
extensions.add( createExtension( extension, artifacts ) );
}
}
return Collections.unmodifiableList( extensions );
}
private CoreExtensionEntry createExtension( CoreExtension extension, List<Artifact> artifacts )
throws Exception
{
String realmId =
"coreExtension>" + extension.getGroupId() + ":" + extension.getArtifactId() + ":" + extension.getVersion();
ClassRealm realm = classWorld.newRealm( realmId, null );
log.debug( "Populating class realm " + realm.getId() );
realm.setParentRealm( parentRealm );
for ( Artifact artifact : artifacts )
{
File file = artifact.getFile();
log.debug( " Included " + file );
realm.addURL( file.toURI().toURL() );
}
return CoreExtensionEntry.discoverFrom( realm, Collections.singleton( artifacts.get( 0 ).getFile() ) );
}
private List<Artifact> resolveExtension( CoreExtension extension, RepositorySystemSession repoSession,
List<RemoteRepository> repositories, DependencyFilter dependencyFilter )
throws PluginResolutionException
{
Plugin plugin = new Plugin();
plugin.setGroupId( extension.getGroupId() );
plugin.setArtifactId( extension.getArtifactId() );
plugin.setVersion( extension.getVersion() );
DependencyNode root =
pluginDependenciesResolver.resolveCoreExtension( plugin, dependencyFilter, repositories, repoSession );
PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
root.accept( nlg );
List<Artifact> artifacts = nlg.getArtifacts( false );
return artifacts;
}
}

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<model xmlns="http://modello.codehaus.org/MODELLO/1.4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<id>CoreExtensions</id>
<name>CoreExtensions</name>
<defaults>
<default>
<key>package</key>
<value>org.apache.maven.cli.internal.extension.model</value>
</default>
</defaults>
<classes>
<class rootElement="true" xml.tagName="extensions">
<name>CoreExtensions</name>
<version>1.0.0+</version>
<fields>
<field>
<name>extensions</name>
<version>1.0.0+</version>
<association xml.itemsStyle="flat">
<type>CoreExtension</type>
<multiplicity>*</multiplicity>
</association>
</field>
</fields>
</class>
<class xml.tagName="extension">
<name>CoreExtension</name>
<version>1.0.0+</version>
<fields>
<field>
<name>groupId</name>
<version>1.0.0+</version>
<required>true</required>
<type>String</type>
</field>
<field>
<name>artifactId</name>
<version>1.0.0+</version>
<required>true</required>
<type>String</type>
</field>
<field>
<name>version</name>
<version>1.0.0+</version>
<required>true</required>
<type>String</type>
</field>
</fields>
</class>
</classes>
</model>