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 <ifedore...@apache.org> Project: http://git-wip-us.apache.org/repos/asf/maven/repo Commit: http://git-wip-us.apache.org/repos/asf/maven/commit/6efacdb3 Tree: http://git-wip-us.apache.org/repos/asf/maven/tree/6efacdb3 Diff: http://git-wip-us.apache.org/repos/asf/maven/diff/6efacdb3 Branch: refs/heads/slf4j-log4j2 Commit: 6efacdb3fc5e8369fa3586c0603184dc785303da Parents: e2a0792 Author: Igor Fedorenko <ifedore...@apache.org> Authored: Sat Feb 14 09:59:02 2015 -0500 Committer: Igor Fedorenko <ifedore...@apache.org> Committed: Fri Feb 20 14:05:54 2015 -0500 ---------------------------------------------------------------------- .../maven/DefaultArtifactFilterManager.java | 26 +- .../classrealm/DefaultClassRealmManager.java | 45 +--- .../maven/extension/internal/CoreExports.java | 75 ++++++ .../extension/internal/CoreExportsProvider.java | 53 ++++ .../extension/internal/CoreExtensionEntry.java | 141 +++++++++++ .../extension/internal/DefaultCoreExports.java | 97 -------- .../DefaultPluginDependenciesResolver.java | 33 ++- maven-embedder/pom.xml | 14 ++ .../java/org/apache/maven/cli/MavenCli.java | 246 ++++++++++++++++--- .../internal/BootstrapCoreExtensionManager.java | 143 +++++++++++ maven-embedder/src/main/mdo/core-extensions.mdo | 74 ++++++ 11 files changed, 765 insertions(+), 182 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/maven/blob/6efacdb3/maven-core/src/main/java/org/apache/maven/DefaultArtifactFilterManager.java ---------------------------------------------------------------------- diff --git a/maven-core/src/main/java/org/apache/maven/DefaultArtifactFilterManager.java b/maven-core/src/main/java/org/apache/maven/DefaultArtifactFilterManager.java index 80bfd62..46f8af0 100644 --- a/maven-core/src/main/java/org/apache/maven/DefaultArtifactFilterManager.java +++ b/maven-core/src/main/java/org/apache/maven/DefaultArtifactFilterManager.java @@ -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() http://git-wip-us.apache.org/repos/asf/maven/blob/6efacdb3/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java ---------------------------------------------------------------------- diff --git a/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java b/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java index 206fa58..ab9607a 100644 --- a/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java +++ b/maven-core/src/main/java/org/apache/maven/classrealm/DefaultClassRealmManager.java @@ -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; } /** http://git-wip-us.apache.org/repos/asf/maven/blob/6efacdb3/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExports.java ---------------------------------------------------------------------- diff --git a/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExports.java b/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExports.java new file mode 100644 index 0000000..f1fa9e3 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExports.java @@ -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; + } +} http://git-wip-us.apache.org/repos/asf/maven/blob/6efacdb3/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExportsProvider.java ---------------------------------------------------------------------- diff --git a/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExportsProvider.java b/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExportsProvider.java new file mode 100644 index 0000000..e7e4534 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExportsProvider.java @@ -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; + } +} http://git-wip-us.apache.org/repos/asf/maven/blob/6efacdb3/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExtensionEntry.java ---------------------------------------------------------------------- diff --git a/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExtensionEntry.java b/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExtensionEntry.java new file mode 100644 index 0000000..d74c390 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/extension/internal/CoreExtensionEntry.java @@ -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 ); + } + +} http://git-wip-us.apache.org/repos/asf/maven/blob/6efacdb3/maven-core/src/main/java/org/apache/maven/extension/internal/DefaultCoreExports.java ---------------------------------------------------------------------- diff --git a/maven-core/src/main/java/org/apache/maven/extension/internal/DefaultCoreExports.java b/maven-core/src/main/java/org/apache/maven/extension/internal/DefaultCoreExports.java deleted file mode 100644 index c4253ee..0000000 --- a/maven-core/src/main/java/org/apache/maven/extension/internal/DefaultCoreExports.java +++ /dev/null @@ -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; - } -} http://git-wip-us.apache.org/repos/asf/maven/blob/6efacdb3/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java ---------------------------------------------------------------------- diff --git a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java index 5a0edf5..885c8ec 100644 --- a/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java +++ b/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java @@ -136,10 +136,32 @@ 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 ); if ( pluginArtifact == null ) @@ -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 ); http://git-wip-us.apache.org/repos/asf/maven/blob/6efacdb3/maven-embedder/pom.xml ---------------------------------------------------------------------- diff --git a/maven-embedder/pom.xml b/maven-embedder/pom.xml index cd3f583..2dc5fde 100644 --- a/maven-embedder/pom.xml +++ b/maven-embedder/pom.xml @@ -122,9 +122,23 @@ </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> http://git-wip-us.apache.org/repos/asf/maven/blob/6efacdb3/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java ---------------------------------------------------------------------- diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java b/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java index 3878dae..1c2ffc0 100644 --- a/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java +++ b/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java @@ -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 ) - throws Exception + private List<CoreExtensionEntry> loadCoreExtensions( CliRequest cliRequest, ClassRealm containerRealm, + Set<String> providedArtifacts ) { - ClassRealm containerRealm = null; + if ( cliRequest.projectBaseDirectory == null ) + { + return Collections.emptyList(); + } - String extClassPath = cliRequest.userProperties.getProperty( EXT_CLASS_PATH ); - if ( extClassPath == null ) + File extensionsFile = new File( cliRequest.projectBaseDirectory, EXTENSIONS_FILENAME ); + if ( !extensionsFile.isFile() ) { - extClassPath = cliRequest.systemProperties.getProperty( EXT_CLASS_PATH ); + return Collections.emptyList(); } - if ( StringUtils.isNotEmpty( extClassPath ) ) + try { - String[] jars = StringUtils.split( extClassPath, File.pathSeparator ); + 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" ); - if ( jars.length > 0 ) + DefaultPlexusContainer container = new DefaultPlexusContainer( cc, new AbstractModule() { - ClassRealm coreRealm = cliRequest.classWorld.getClassRealm( "plexus.core" ); - if ( coreRealm == null ) + @Override + protected void configure() { - coreRealm = cliRequest.classWorld.getRealms().iterator().next(); + bind( ILoggerFactory.class ).toInstance( slf4jLoggerFactory ); } + } ); - ClassRealm extRealm = cliRequest.classWorld.newRealm( "maven.ext", null ); + try + { + container.setLookupRealm( null ); - slf4jLogger.debug( "Populating class realm " + extRealm.getId() ); + container.setLoggerManager( plexusLoggerManager ); - for ( String jar : jars ) - { - File file = resolveFile( new File( jar ), cliRequest.workingDirectory ); + container.getLoggerManager().setThresholds( cliRequest.request.getLoggingLevel() ); + + Thread.currentThread().setContextClassLoader( container.getContainerRealm() ); - slf4jLogger.debug( " Included " + file ); + executionRequestPopulator = container.lookup( MavenExecutionRequestPopulator.class ); + settingsBuilder = container.lookup( SettingsBuilder.class ); - extRealm.addURL( file.toURI().toURL() ); + 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 + { + 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; + } - extRealm.setParentRealm( coreRealm ); + private static <T> List<T> reverse( List<T> list ) + { + List<T> copy = new ArrayList<T>( list ); + Collections.reverse( copy ); + return copy; + } - containerRealm = extRealm; + 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 ) ) + { + for ( String jar : StringUtils.split( extClassPath, File.pathSeparator ) ) + { + File file = resolveFile( new File( jar ), cliRequest.workingDirectory ); + + slf4jLogger.debug( " Included " + file ); + + jars.add( file ); } } - return containerRealm; + return jars; } // @@ -814,6 +975,12 @@ public class MavenCli private void settings( CliRequest cliRequest ) throws Exception { + settings( cliRequest, cliRequest.request ); + } + + private void settings( CliRequest cliRequest, MavenExecutionRequest request ) + throws Exception + { File userSettingsFile; if ( cliRequest.commandLine.hasOption( CLIManager.ALTERNATE_USER_SETTINGS ) ) @@ -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 ); http://git-wip-us.apache.org/repos/asf/maven/blob/6efacdb3/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java ---------------------------------------------------------------------- diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java b/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java new file mode 100644 index 0000000..a431bde --- /dev/null +++ b/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java @@ -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; + } +} http://git-wip-us.apache.org/repos/asf/maven/blob/6efacdb3/maven-embedder/src/main/mdo/core-extensions.mdo ---------------------------------------------------------------------- diff --git a/maven-embedder/src/main/mdo/core-extensions.mdo b/maven-embedder/src/main/mdo/core-extensions.mdo new file mode 100644 index 0000000..f4b0215 --- /dev/null +++ b/maven-embedder/src/main/mdo/core-extensions.mdo @@ -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>