| package org.apache.maven.plugin.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 org.apache.maven.RepositoryUtils; |
| import org.apache.maven.artifact.Artifact; |
| import org.apache.maven.classrealm.ClassRealmManager; |
| import org.apache.maven.execution.MavenSession; |
| import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule; |
| import org.apache.maven.model.Plugin; |
| import org.apache.maven.plugin.ContextEnabled; |
| import org.apache.maven.plugin.DebugConfigurationListener; |
| import org.apache.maven.plugin.ExtensionRealmCache; |
| import org.apache.maven.plugin.InvalidPluginDescriptorException; |
| import org.apache.maven.plugin.MavenPluginManager; |
| import org.apache.maven.plugin.Mojo; |
| import org.apache.maven.plugin.MojoExecution; |
| import org.apache.maven.plugin.MojoNotFoundException; |
| import org.apache.maven.plugin.PluginArtifactsCache; |
| import org.apache.maven.plugin.PluginConfigurationException; |
| import org.apache.maven.plugin.PluginContainerException; |
| import org.apache.maven.plugin.PluginDescriptorCache; |
| import org.apache.maven.plugin.PluginDescriptorParsingException; |
| import org.apache.maven.plugin.PluginIncompatibleException; |
| import org.apache.maven.plugin.PluginManagerException; |
| import org.apache.maven.plugin.PluginParameterException; |
| import org.apache.maven.plugin.PluginParameterExpressionEvaluator; |
| import org.apache.maven.plugin.PluginRealmCache; |
| import org.apache.maven.plugin.PluginResolutionException; |
| import org.apache.maven.plugin.descriptor.MojoDescriptor; |
| import org.apache.maven.plugin.descriptor.Parameter; |
| import org.apache.maven.plugin.descriptor.PluginDescriptor; |
| import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder; |
| import org.apache.maven.plugin.version.DefaultPluginVersionRequest; |
| import org.apache.maven.plugin.version.PluginVersionRequest; |
| import org.apache.maven.plugin.version.PluginVersionResolutionException; |
| import org.apache.maven.plugin.version.PluginVersionResolver; |
| import org.apache.maven.project.ExtensionDescriptor; |
| import org.apache.maven.project.ExtensionDescriptorBuilder; |
| import org.apache.maven.project.MavenProject; |
| import org.apache.maven.rtinfo.RuntimeInformation; |
| import org.apache.maven.session.scope.internal.SessionScopeModule; |
| import org.codehaus.plexus.DefaultPlexusContainer; |
| import org.codehaus.plexus.PlexusContainer; |
| import org.codehaus.plexus.classworlds.realm.ClassRealm; |
| import org.codehaus.plexus.component.composition.CycleDetectedInComponentGraphException; |
| import org.codehaus.plexus.component.configurator.ComponentConfigurationException; |
| import org.codehaus.plexus.component.configurator.ComponentConfigurator; |
| import org.codehaus.plexus.component.configurator.ConfigurationListener; |
| import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; |
| import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; |
| import org.codehaus.plexus.component.repository.ComponentDescriptor; |
| import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException; |
| import org.codehaus.plexus.component.repository.exception.ComponentLookupException; |
| import org.codehaus.plexus.configuration.PlexusConfiguration; |
| import org.codehaus.plexus.configuration.PlexusConfigurationException; |
| import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration; |
| import org.codehaus.plexus.util.ReaderFactory; |
| import org.codehaus.plexus.util.StringUtils; |
| import org.codehaus.plexus.util.xml.Xpp3Dom; |
| import org.eclipse.aether.RepositorySystemSession; |
| import org.eclipse.aether.graph.DependencyFilter; |
| import org.eclipse.aether.graph.DependencyNode; |
| import org.eclipse.aether.repository.RemoteRepository; |
| import org.eclipse.aether.util.filter.AndDependencyFilter; |
| import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.io.BufferedInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.PrintStream; |
| import java.io.Reader; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.jar.JarFile; |
| import java.util.zip.ZipEntry; |
| |
| import javax.inject.Inject; |
| import javax.inject.Named; |
| import javax.inject.Singleton; |
| |
| /** |
| * Provides basic services to manage Maven plugins and their mojos. This component is kept general in its design such |
| * that the plugins/mojos can be used in arbitrary contexts. In particular, the mojos can be used for ordinary build |
| * plugins as well as special purpose plugins like reports. |
| * |
| * @author Benjamin Bentmann |
| * @since 3.0 |
| */ |
| @Named |
| @Singleton |
| public class DefaultMavenPluginManager |
| implements MavenPluginManager |
| { |
| |
| /** |
| * <p> |
| * PluginId => ExtensionRealmCache.CacheRecord map MavenProject context value key. The map is used to ensure the |
| * same class realm is used to load build extensions and load mojos for extensions=true plugins. |
| * </p> |
| * <strong>Note:</strong> This is part of internal implementation and may be changed or removed without notice |
| * |
| * @since 3.3.0 |
| */ |
| public static final String KEY_EXTENSIONS_REALMS = DefaultMavenPluginManager.class.getName() + "/extensionsRealms"; |
| |
| private final Logger logger = LoggerFactory.getLogger( getClass() ); |
| |
| private PlexusContainer container; |
| private ClassRealmManager classRealmManager; |
| private PluginDescriptorCache pluginDescriptorCache; |
| private PluginRealmCache pluginRealmCache; |
| private PluginDependenciesResolver pluginDependenciesResolver; |
| private RuntimeInformation runtimeInformation; |
| private ExtensionRealmCache extensionRealmCache; |
| private PluginVersionResolver pluginVersionResolver; |
| private PluginArtifactsCache pluginArtifactsCache; |
| private MavenPluginValidator pluginValidator; |
| |
| private final ExtensionDescriptorBuilder extensionDescriptorBuilder = new ExtensionDescriptorBuilder(); |
| private final PluginDescriptorBuilder builder = new PluginDescriptorBuilder(); |
| |
| @Inject |
| public DefaultMavenPluginManager( |
| PlexusContainer container, |
| ClassRealmManager classRealmManager, |
| PluginDescriptorCache pluginDescriptorCache, |
| PluginRealmCache pluginRealmCache, |
| PluginDependenciesResolver pluginDependenciesResolver, |
| RuntimeInformation runtimeInformation, |
| ExtensionRealmCache extensionRealmCache, |
| PluginVersionResolver pluginVersionResolver, |
| PluginArtifactsCache pluginArtifactsCache, |
| MavenPluginValidator pluginValidator ) |
| { |
| this.container = container; |
| this.classRealmManager = classRealmManager; |
| this.pluginDescriptorCache = pluginDescriptorCache; |
| this.pluginRealmCache = pluginRealmCache; |
| this.pluginDependenciesResolver = pluginDependenciesResolver; |
| this.runtimeInformation = runtimeInformation; |
| this.extensionRealmCache = extensionRealmCache; |
| this.pluginVersionResolver = pluginVersionResolver; |
| this.pluginArtifactsCache = pluginArtifactsCache; |
| this.pluginValidator = pluginValidator; |
| } |
| |
| public synchronized PluginDescriptor getPluginDescriptor( Plugin plugin, List<RemoteRepository> repositories, |
| RepositorySystemSession session ) |
| throws PluginResolutionException, PluginDescriptorParsingException, InvalidPluginDescriptorException |
| { |
| PluginDescriptorCache.Key cacheKey = pluginDescriptorCache.createKey( plugin, repositories, session ); |
| |
| PluginDescriptor pluginDescriptor = pluginDescriptorCache.get( cacheKey ); |
| |
| if ( pluginDescriptor == null ) |
| { |
| org.eclipse.aether.artifact.Artifact artifact = |
| pluginDependenciesResolver.resolve( plugin, repositories, session ); |
| |
| Artifact pluginArtifact = RepositoryUtils.toArtifact( artifact ); |
| |
| pluginDescriptor = extractPluginDescriptor( pluginArtifact, plugin ); |
| |
| pluginDescriptor.setRequiredMavenVersion( artifact.getProperty( "requiredMavenVersion", null ) ); |
| |
| pluginDescriptorCache.put( cacheKey, pluginDescriptor ); |
| } |
| |
| pluginDescriptor.setPlugin( plugin ); |
| |
| return pluginDescriptor; |
| } |
| |
| private PluginDescriptor extractPluginDescriptor( Artifact pluginArtifact, Plugin plugin ) |
| throws PluginDescriptorParsingException, InvalidPluginDescriptorException |
| { |
| PluginDescriptor pluginDescriptor = null; |
| |
| File pluginFile = pluginArtifact.getFile(); |
| |
| try |
| { |
| if ( pluginFile.isFile() ) |
| { |
| try ( JarFile pluginJar = new JarFile( pluginFile, false ) ) |
| { |
| ZipEntry pluginDescriptorEntry = pluginJar.getEntry( getPluginDescriptorLocation() ); |
| |
| if ( pluginDescriptorEntry != null ) |
| { |
| InputStream is = pluginJar.getInputStream( pluginDescriptorEntry ); |
| |
| pluginDescriptor = parsePluginDescriptor( is, plugin, pluginFile.getAbsolutePath() ); |
| } |
| } |
| } |
| else |
| { |
| File pluginXml = new File( pluginFile, getPluginDescriptorLocation() ); |
| |
| if ( pluginXml.isFile() ) |
| { |
| try ( InputStream is = new BufferedInputStream( new FileInputStream( pluginXml ) ) ) |
| { |
| pluginDescriptor = parsePluginDescriptor( is, plugin, pluginXml.getAbsolutePath() ); |
| } |
| } |
| } |
| |
| if ( pluginDescriptor == null ) |
| { |
| throw new IOException( "No plugin descriptor found at " + getPluginDescriptorLocation() ); |
| } |
| } |
| catch ( IOException e ) |
| { |
| throw new PluginDescriptorParsingException( plugin, pluginFile.getAbsolutePath(), e ); |
| } |
| |
| List<String> errors = new ArrayList<>(); |
| pluginValidator.validate( pluginArtifact, pluginDescriptor, errors ); |
| |
| if ( !errors.isEmpty() ) |
| { |
| throw new InvalidPluginDescriptorException( |
| "Invalid plugin descriptor for " + plugin.getId() + " (" + pluginFile + ")", errors ); |
| } |
| |
| pluginDescriptor.setPluginArtifact( pluginArtifact ); |
| |
| return pluginDescriptor; |
| } |
| |
| private String getPluginDescriptorLocation() |
| { |
| return "META-INF/maven/plugin.xml"; |
| } |
| |
| private PluginDescriptor parsePluginDescriptor( InputStream is, Plugin plugin, String descriptorLocation ) |
| throws PluginDescriptorParsingException |
| { |
| try |
| { |
| Reader reader = ReaderFactory.newXmlReader( is ); |
| |
| PluginDescriptor pluginDescriptor = builder.build( reader, descriptorLocation ); |
| |
| return pluginDescriptor; |
| } |
| catch ( IOException | PlexusConfigurationException e ) |
| { |
| throw new PluginDescriptorParsingException( plugin, descriptorLocation, e ); |
| } |
| } |
| |
| public MojoDescriptor getMojoDescriptor( Plugin plugin, String goal, List<RemoteRepository> repositories, |
| RepositorySystemSession session ) |
| throws MojoNotFoundException, PluginResolutionException, PluginDescriptorParsingException, |
| InvalidPluginDescriptorException |
| { |
| PluginDescriptor pluginDescriptor = getPluginDescriptor( plugin, repositories, session ); |
| |
| MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( goal ); |
| |
| if ( mojoDescriptor == null ) |
| { |
| throw new MojoNotFoundException( goal, pluginDescriptor ); |
| } |
| |
| return mojoDescriptor; |
| } |
| |
| public void checkRequiredMavenVersion( PluginDescriptor pluginDescriptor ) |
| throws PluginIncompatibleException |
| { |
| String requiredMavenVersion = pluginDescriptor.getRequiredMavenVersion(); |
| if ( StringUtils.isNotBlank( requiredMavenVersion ) ) |
| { |
| try |
| { |
| if ( !runtimeInformation.isMavenVersion( requiredMavenVersion ) ) |
| { |
| throw new PluginIncompatibleException( pluginDescriptor.getPlugin(), |
| "The plugin " + pluginDescriptor.getId() |
| + " requires Maven version " + requiredMavenVersion ); |
| } |
| } |
| catch ( RuntimeException e ) |
| { |
| logger.warn( "Could not verify plugin's Maven prerequisite: " + e.getMessage() ); |
| } |
| } |
| } |
| |
| public synchronized void setupPluginRealm( PluginDescriptor pluginDescriptor, MavenSession session, |
| ClassLoader parent, List<String> imports, DependencyFilter filter ) |
| throws PluginResolutionException, PluginContainerException |
| { |
| Plugin plugin = pluginDescriptor.getPlugin(); |
| MavenProject project = session.getCurrentProject(); |
| |
| if ( plugin.isExtensions() ) |
| { |
| ExtensionRealmCache.CacheRecord extensionRecord; |
| try |
| { |
| RepositorySystemSession repositorySession = session.getRepositorySession(); |
| extensionRecord = setupExtensionsRealm( project, plugin, repositorySession ); |
| } |
| catch ( PluginManagerException e ) |
| { |
| // extensions realm is expected to be fully setup at this point |
| // any exception means a problem in maven code, not a user error |
| throw new IllegalStateException( e ); |
| } |
| |
| ClassRealm pluginRealm = extensionRecord.getRealm(); |
| List<Artifact> pluginArtifacts = extensionRecord.getArtifacts(); |
| |
| for ( ComponentDescriptor<?> componentDescriptor : pluginDescriptor.getComponents() ) |
| { |
| componentDescriptor.setRealm( pluginRealm ); |
| } |
| |
| pluginDescriptor.setClassRealm( pluginRealm ); |
| pluginDescriptor.setArtifacts( pluginArtifacts ); |
| } |
| else |
| { |
| Map<String, ClassLoader> foreignImports = calcImports( project, parent, imports ); |
| |
| PluginRealmCache.Key cacheKey = pluginRealmCache.createKey( plugin, parent, foreignImports, filter, |
| project.getRemotePluginRepositories(), |
| session.getRepositorySession() ); |
| |
| PluginRealmCache.CacheRecord cacheRecord = pluginRealmCache.get( cacheKey ); |
| |
| if ( cacheRecord != null ) |
| { |
| pluginDescriptor.setClassRealm( cacheRecord.getRealm() ); |
| pluginDescriptor.setArtifacts( new ArrayList<>( cacheRecord.getArtifacts() ) ); |
| for ( ComponentDescriptor<?> componentDescriptor : pluginDescriptor.getComponents() ) |
| { |
| componentDescriptor.setRealm( cacheRecord.getRealm() ); |
| } |
| } |
| else |
| { |
| createPluginRealm( pluginDescriptor, session, parent, foreignImports, filter ); |
| |
| cacheRecord = |
| pluginRealmCache.put( cacheKey, pluginDescriptor.getClassRealm(), pluginDescriptor.getArtifacts() ); |
| } |
| |
| pluginRealmCache.register( project, cacheKey, cacheRecord ); |
| } |
| } |
| |
| private void createPluginRealm( PluginDescriptor pluginDescriptor, MavenSession session, ClassLoader parent, |
| Map<String, ClassLoader> foreignImports, DependencyFilter filter ) |
| throws PluginResolutionException, PluginContainerException |
| { |
| Plugin plugin = |
| Objects.requireNonNull( pluginDescriptor.getPlugin(), "pluginDescriptor.plugin cannot be null" ); |
| |
| Artifact pluginArtifact = Objects.requireNonNull( pluginDescriptor.getPluginArtifact(), |
| "pluginDescriptor.pluginArtifact cannot be null" ); |
| |
| MavenProject project = session.getCurrentProject(); |
| |
| final ClassRealm pluginRealm; |
| final List<Artifact> pluginArtifacts; |
| |
| RepositorySystemSession repositorySession = session.getRepositorySession(); |
| DependencyFilter dependencyFilter = project.getExtensionDependencyFilter(); |
| dependencyFilter = AndDependencyFilter.newInstance( dependencyFilter, filter ); |
| |
| DependencyNode root = |
| pluginDependenciesResolver.resolve( plugin, RepositoryUtils.toArtifact( pluginArtifact ), dependencyFilter, |
| project.getRemotePluginRepositories(), repositorySession ); |
| |
| PreorderNodeListGenerator nlg = new PreorderNodeListGenerator(); |
| root.accept( nlg ); |
| |
| pluginArtifacts = toMavenArtifacts( root, nlg ); |
| |
| pluginRealm = classRealmManager.createPluginRealm( plugin, parent, null, foreignImports, |
| toAetherArtifacts( pluginArtifacts ) ); |
| |
| discoverPluginComponents( pluginRealm, plugin, pluginDescriptor ); |
| |
| pluginDescriptor.setClassRealm( pluginRealm ); |
| pluginDescriptor.setArtifacts( pluginArtifacts ); |
| } |
| |
| private void discoverPluginComponents( final ClassRealm pluginRealm, Plugin plugin, |
| PluginDescriptor pluginDescriptor ) |
| throws PluginContainerException |
| { |
| try |
| { |
| if ( pluginDescriptor != null ) |
| { |
| for ( ComponentDescriptor<?> componentDescriptor : pluginDescriptor.getComponents() ) |
| { |
| componentDescriptor.setRealm( pluginRealm ); |
| container.addComponentDescriptor( componentDescriptor ); |
| } |
| } |
| |
| ( (DefaultPlexusContainer) container ).discoverComponents( pluginRealm, new SessionScopeModule( container ), |
| new MojoExecutionScopeModule( container ) ); |
| } |
| catch ( ComponentLookupException | CycleDetectedInComponentGraphException e ) |
| { |
| throw new PluginContainerException( plugin, pluginRealm, |
| "Error in component graph of plugin " + plugin.getId() + ": " |
| + e.getMessage(), e ); |
| } |
| } |
| |
| private List<org.eclipse.aether.artifact.Artifact> toAetherArtifacts( final List<Artifact> pluginArtifacts ) |
| { |
| return new ArrayList<>( RepositoryUtils.toArtifacts( pluginArtifacts ) ); |
| } |
| |
| private List<Artifact> toMavenArtifacts( DependencyNode root, PreorderNodeListGenerator nlg ) |
| { |
| List<Artifact> artifacts = new ArrayList<>( nlg.getNodes().size() ); |
| RepositoryUtils.toArtifacts( artifacts, Collections.singleton( root ), Collections.<String>emptyList(), null ); |
| artifacts.removeIf( artifact -> artifact.getFile() == null ); |
| return Collections.unmodifiableList( artifacts ); |
| } |
| |
| private Map<String, ClassLoader> calcImports( MavenProject project, ClassLoader parent, List<String> imports ) |
| { |
| Map<String, ClassLoader> foreignImports = new HashMap<>(); |
| |
| ClassLoader projectRealm = project.getClassRealm(); |
| if ( projectRealm != null ) |
| { |
| foreignImports.put( "", projectRealm ); |
| } |
| else |
| { |
| foreignImports.put( "", classRealmManager.getMavenApiRealm() ); |
| } |
| |
| if ( parent != null && imports != null ) |
| { |
| for ( String parentImport : imports ) |
| { |
| foreignImports.put( parentImport, parent ); |
| } |
| } |
| |
| return foreignImports; |
| } |
| |
| public <T> T getConfiguredMojo( Class<T> mojoInterface, MavenSession session, MojoExecution mojoExecution ) |
| throws PluginConfigurationException, PluginContainerException |
| { |
| MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); |
| |
| PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor(); |
| |
| ClassRealm pluginRealm = pluginDescriptor.getClassRealm(); |
| |
| if ( logger.isDebugEnabled() ) |
| { |
| logger.debug( "Configuring mojo " + mojoDescriptor.getId() + " from plugin realm " + pluginRealm ); |
| } |
| |
| // We are forcing the use of the plugin realm for all lookups that might occur during |
| // the lifecycle that is part of the lookup. Here we are specifically trying to keep |
| // lookups that occur in contextualize calls in line with the right realm. |
| ClassRealm oldLookupRealm = container.setLookupRealm( pluginRealm ); |
| |
| ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); |
| Thread.currentThread().setContextClassLoader( pluginRealm ); |
| |
| try |
| { |
| T mojo; |
| |
| try |
| { |
| mojo = container.lookup( mojoInterface, mojoDescriptor.getRoleHint() ); |
| } |
| catch ( ComponentLookupException e ) |
| { |
| Throwable cause = e.getCause(); |
| while ( cause != null && !( cause instanceof LinkageError ) |
| && !( cause instanceof ClassNotFoundException ) ) |
| { |
| cause = cause.getCause(); |
| } |
| |
| if ( ( cause instanceof NoClassDefFoundError ) || ( cause instanceof ClassNotFoundException ) ) |
| { |
| ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 ); |
| PrintStream ps = new PrintStream( os ); |
| ps.println( "Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '" |
| + pluginDescriptor.getId() + "'. A required class is missing: " |
| + cause.getMessage() ); |
| pluginRealm.display( ps ); |
| |
| throw new PluginContainerException( mojoDescriptor, pluginRealm, os.toString(), cause ); |
| } |
| else if ( cause instanceof LinkageError ) |
| { |
| ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 ); |
| PrintStream ps = new PrintStream( os ); |
| ps.println( "Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '" |
| + pluginDescriptor.getId() + "' due to an API incompatibility: " |
| + e.getClass().getName() + ": " + cause.getMessage() ); |
| pluginRealm.display( ps ); |
| |
| throw new PluginContainerException( mojoDescriptor, pluginRealm, os.toString(), cause ); |
| } |
| |
| throw new PluginContainerException( mojoDescriptor, pluginRealm, |
| "Unable to load the mojo '" + mojoDescriptor.getGoal() |
| + "' (or one of its required components) from the plugin '" |
| + pluginDescriptor.getId() + "'", e ); |
| } |
| |
| if ( mojo instanceof ContextEnabled ) |
| { |
| MavenProject project = session.getCurrentProject(); |
| |
| Map<String, Object> pluginContext = session.getPluginContext( pluginDescriptor, project ); |
| |
| if ( pluginContext != null ) |
| { |
| pluginContext.put( "project", project ); |
| |
| pluginContext.put( "pluginDescriptor", pluginDescriptor ); |
| |
| ( (ContextEnabled) mojo ).setPluginContext( pluginContext ); |
| } |
| } |
| |
| if ( mojo instanceof Mojo ) |
| { |
| Logger mojoLogger = LoggerFactory.getLogger( mojoDescriptor.getImplementation() ); |
| ( (Mojo) mojo ).setLog( new MojoLogWrapper( mojoLogger ) ); |
| } |
| |
| Xpp3Dom dom = mojoExecution.getConfiguration(); |
| |
| PlexusConfiguration pomConfiguration; |
| |
| if ( dom == null ) |
| { |
| pomConfiguration = new XmlPlexusConfiguration( "configuration" ); |
| } |
| else |
| { |
| pomConfiguration = new XmlPlexusConfiguration( dom ); |
| } |
| |
| ExpressionEvaluator expressionEvaluator = new PluginParameterExpressionEvaluator( session, mojoExecution ); |
| |
| populatePluginFields( mojo, mojoDescriptor, pluginRealm, pomConfiguration, expressionEvaluator ); |
| |
| return mojo; |
| } |
| finally |
| { |
| Thread.currentThread().setContextClassLoader( oldClassLoader ); |
| container.setLookupRealm( oldLookupRealm ); |
| } |
| } |
| |
| private void populatePluginFields( Object mojo, MojoDescriptor mojoDescriptor, ClassRealm pluginRealm, |
| PlexusConfiguration configuration, ExpressionEvaluator expressionEvaluator ) |
| throws PluginConfigurationException |
| { |
| ComponentConfigurator configurator = null; |
| |
| String configuratorId = mojoDescriptor.getComponentConfigurator(); |
| |
| if ( StringUtils.isEmpty( configuratorId ) ) |
| { |
| configuratorId = "basic"; |
| } |
| |
| try |
| { |
| // TODO could the configuration be passed to lookup and the configurator known to plexus via the descriptor |
| // so that this method could entirely be handled by a plexus lookup? |
| configurator = container.lookup( ComponentConfigurator.class, configuratorId ); |
| |
| ConfigurationListener listener = new DebugConfigurationListener( logger ); |
| |
| ValidatingConfigurationListener validator = |
| new ValidatingConfigurationListener( mojo, mojoDescriptor, listener ); |
| |
| logger.debug( |
| "Configuring mojo '" + mojoDescriptor.getId() + "' with " + configuratorId + " configurator -->" ); |
| |
| configurator.configureComponent( mojo, configuration, expressionEvaluator, pluginRealm, validator ); |
| |
| logger.debug( "-- end configuration --" ); |
| |
| Collection<Parameter> missingParameters = validator.getMissingParameters(); |
| if ( !missingParameters.isEmpty() ) |
| { |
| if ( "basic".equals( configuratorId ) ) |
| { |
| throw new PluginParameterException( mojoDescriptor, new ArrayList<>( missingParameters ) ); |
| } |
| else |
| { |
| /* |
| * NOTE: Other configurators like the map-oriented one don't call into the listener, so do it the |
| * hard way. |
| */ |
| validateParameters( mojoDescriptor, configuration, expressionEvaluator ); |
| } |
| } |
| } |
| catch ( ComponentConfigurationException e ) |
| { |
| String message = "Unable to parse configuration of mojo " + mojoDescriptor.getId(); |
| if ( e.getFailedConfiguration() != null ) |
| { |
| message += " for parameter " + e.getFailedConfiguration().getName(); |
| } |
| message += ": " + e.getMessage(); |
| |
| throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(), message, e ); |
| } |
| catch ( ComponentLookupException e ) |
| { |
| throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(), |
| "Unable to retrieve component configurator " + configuratorId |
| + " for configuration of mojo " + mojoDescriptor.getId(), e ); |
| } |
| catch ( NoClassDefFoundError e ) |
| { |
| ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 ); |
| PrintStream ps = new PrintStream( os ); |
| ps.println( "A required class was missing during configuration of mojo " + mojoDescriptor.getId() + ": " |
| + e.getMessage() ); |
| pluginRealm.display( ps ); |
| |
| throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(), os.toString(), e ); |
| } |
| catch ( LinkageError e ) |
| { |
| ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 ); |
| PrintStream ps = new PrintStream( os ); |
| ps.println( |
| "An API incompatibility was encountered during configuration of mojo " + mojoDescriptor.getId() + ": " |
| + e.getClass().getName() + ": " + e.getMessage() ); |
| pluginRealm.display( ps ); |
| |
| throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(), os.toString(), e ); |
| } |
| finally |
| { |
| if ( configurator != null ) |
| { |
| try |
| { |
| container.release( configurator ); |
| } |
| catch ( ComponentLifecycleException e ) |
| { |
| logger.debug( "Failed to release mojo configurator - ignoring." ); |
| } |
| } |
| } |
| } |
| |
| private void validateParameters( MojoDescriptor mojoDescriptor, PlexusConfiguration configuration, |
| ExpressionEvaluator expressionEvaluator ) |
| throws ComponentConfigurationException, PluginParameterException |
| { |
| if ( mojoDescriptor.getParameters() == null ) |
| { |
| return; |
| } |
| |
| List<Parameter> invalidParameters = new ArrayList<>(); |
| |
| for ( Parameter parameter : mojoDescriptor.getParameters() ) |
| { |
| if ( !parameter.isRequired() ) |
| { |
| continue; |
| } |
| |
| Object value = null; |
| |
| PlexusConfiguration config = configuration.getChild( parameter.getName(), false ); |
| if ( config != null ) |
| { |
| String expression = config.getValue( null ); |
| |
| try |
| { |
| value = expressionEvaluator.evaluate( expression ); |
| |
| if ( value == null ) |
| { |
| value = config.getAttribute( "default-value", null ); |
| } |
| } |
| catch ( ExpressionEvaluationException e ) |
| { |
| String msg = "Error evaluating the expression '" + expression + "' for configuration value '" |
| + configuration.getName() + "'"; |
| throw new ComponentConfigurationException( configuration, msg, e ); |
| } |
| } |
| |
| if ( value == null && ( config == null || config.getChildCount() <= 0 ) ) |
| { |
| invalidParameters.add( parameter ); |
| } |
| } |
| |
| if ( !invalidParameters.isEmpty() ) |
| { |
| throw new PluginParameterException( mojoDescriptor, invalidParameters ); |
| } |
| } |
| |
| public void releaseMojo( Object mojo, MojoExecution mojoExecution ) |
| { |
| if ( mojo != null ) |
| { |
| try |
| { |
| container.release( mojo ); |
| } |
| catch ( ComponentLifecycleException e ) |
| { |
| String goalExecId = mojoExecution.getGoal(); |
| |
| if ( mojoExecution.getExecutionId() != null ) |
| { |
| goalExecId += " {execution: " + mojoExecution.getExecutionId() + "}"; |
| } |
| |
| logger.debug( "Error releasing mojo for " + goalExecId, e ); |
| } |
| } |
| } |
| |
| public ExtensionRealmCache.CacheRecord setupExtensionsRealm( MavenProject project, Plugin plugin, |
| RepositorySystemSession session ) |
| throws PluginManagerException |
| { |
| @SuppressWarnings( "unchecked" ) Map<String, ExtensionRealmCache.CacheRecord> pluginRealms = |
| (Map<String, ExtensionRealmCache.CacheRecord>) project.getContextValue( KEY_EXTENSIONS_REALMS ); |
| if ( pluginRealms == null ) |
| { |
| pluginRealms = new HashMap<>(); |
| project.setContextValue( KEY_EXTENSIONS_REALMS, pluginRealms ); |
| } |
| |
| final String pluginKey = plugin.getId(); |
| |
| ExtensionRealmCache.CacheRecord extensionRecord = pluginRealms.get( pluginKey ); |
| if ( extensionRecord != null ) |
| { |
| return extensionRecord; |
| } |
| |
| final List<RemoteRepository> repositories = project.getRemotePluginRepositories(); |
| |
| // resolve plugin version as necessary |
| if ( plugin.getVersion() == null ) |
| { |
| PluginVersionRequest versionRequest = new DefaultPluginVersionRequest( plugin, session, repositories ); |
| try |
| { |
| plugin.setVersion( pluginVersionResolver.resolve( versionRequest ).getVersion() ); |
| } |
| catch ( PluginVersionResolutionException e ) |
| { |
| throw new PluginManagerException( plugin, e.getMessage(), e ); |
| } |
| } |
| |
| // resolve plugin artifacts |
| List<Artifact> artifacts; |
| PluginArtifactsCache.Key cacheKey = pluginArtifactsCache.createKey( plugin, null, repositories, session ); |
| PluginArtifactsCache.CacheRecord recordArtifacts; |
| try |
| { |
| recordArtifacts = pluginArtifactsCache.get( cacheKey ); |
| } |
| catch ( PluginResolutionException e ) |
| { |
| throw new PluginManagerException( plugin, e.getMessage(), e ); |
| } |
| if ( recordArtifacts != null ) |
| { |
| artifacts = recordArtifacts.getArtifacts(); |
| } |
| else |
| { |
| try |
| { |
| artifacts = resolveExtensionArtifacts( plugin, repositories, session ); |
| recordArtifacts = pluginArtifactsCache.put( cacheKey, artifacts ); |
| } |
| catch ( PluginResolutionException e ) |
| { |
| pluginArtifactsCache.put( cacheKey, e ); |
| pluginArtifactsCache.register( project, cacheKey, recordArtifacts ); |
| throw new PluginManagerException( plugin, e.getMessage(), e ); |
| } |
| } |
| pluginArtifactsCache.register( project, cacheKey, recordArtifacts ); |
| |
| // create and cache extensions realms |
| final ExtensionRealmCache.Key extensionKey = extensionRealmCache.createKey( artifacts ); |
| extensionRecord = extensionRealmCache.get( extensionKey ); |
| if ( extensionRecord == null ) |
| { |
| ClassRealm extensionRealm = |
| classRealmManager.createExtensionRealm( plugin, toAetherArtifacts( artifacts ) ); |
| |
| // TODO figure out how to use the same PluginDescriptor when running mojos |
| |
| PluginDescriptor pluginDescriptor = null; |
| if ( plugin.isExtensions() && !artifacts.isEmpty() ) |
| { |
| // ignore plugin descriptor parsing errors at this point |
| // these errors will reported during calculation of project build execution plan |
| try |
| { |
| pluginDescriptor = extractPluginDescriptor( artifacts.get( 0 ), plugin ); |
| } |
| catch ( PluginDescriptorParsingException | InvalidPluginDescriptorException e ) |
| { |
| // ignore, see above |
| } |
| } |
| |
| discoverPluginComponents( extensionRealm, plugin, pluginDescriptor ); |
| |
| ExtensionDescriptor extensionDescriptor = null; |
| Artifact extensionArtifact = artifacts.get( 0 ); |
| try |
| { |
| extensionDescriptor = extensionDescriptorBuilder.build( extensionArtifact.getFile() ); |
| } |
| catch ( IOException e ) |
| { |
| String message = "Invalid extension descriptor for " + plugin.getId() + ": " + e.getMessage(); |
| if ( logger.isDebugEnabled() ) |
| { |
| logger.error( message, e ); |
| } |
| else |
| { |
| logger.error( message ); |
| } |
| } |
| extensionRecord = extensionRealmCache.put( extensionKey, extensionRealm, extensionDescriptor, artifacts ); |
| } |
| extensionRealmCache.register( project, extensionKey, extensionRecord ); |
| pluginRealms.put( pluginKey, extensionRecord ); |
| |
| return extensionRecord; |
| } |
| |
| private List<Artifact> resolveExtensionArtifacts( Plugin extensionPlugin, List<RemoteRepository> repositories, |
| RepositorySystemSession session ) |
| throws PluginResolutionException |
| { |
| DependencyNode root = pluginDependenciesResolver.resolve( extensionPlugin, null, null, repositories, session ); |
| PreorderNodeListGenerator nlg = new PreorderNodeListGenerator(); |
| root.accept( nlg ); |
| return toMavenArtifacts( root, nlg ); |
| } |
| |
| } |