001package org.apache.maven.plugin.internal; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.commons.lang3.Validate; 023import org.apache.maven.RepositoryUtils; 024import org.apache.maven.artifact.Artifact; 025import org.apache.maven.classrealm.ClassRealmManager; 026import org.apache.maven.execution.MavenSession; 027import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule; 028import org.apache.maven.model.Plugin; 029import org.apache.maven.monitor.logging.DefaultLog; 030import org.apache.maven.plugin.ContextEnabled; 031import org.apache.maven.plugin.DebugConfigurationListener; 032import org.apache.maven.plugin.ExtensionRealmCache; 033import org.apache.maven.plugin.InvalidPluginDescriptorException; 034import org.apache.maven.plugin.MavenPluginManager; 035import org.apache.maven.plugin.MavenPluginValidator; 036import org.apache.maven.plugin.Mojo; 037import org.apache.maven.plugin.MojoExecution; 038import org.apache.maven.plugin.MojoNotFoundException; 039import org.apache.maven.plugin.PluginArtifactsCache; 040import org.apache.maven.plugin.PluginConfigurationException; 041import org.apache.maven.plugin.PluginContainerException; 042import org.apache.maven.plugin.PluginDescriptorCache; 043import org.apache.maven.plugin.PluginDescriptorParsingException; 044import org.apache.maven.plugin.PluginIncompatibleException; 045import org.apache.maven.plugin.PluginManagerException; 046import org.apache.maven.plugin.PluginParameterException; 047import org.apache.maven.plugin.PluginParameterExpressionEvaluator; 048import org.apache.maven.plugin.PluginRealmCache; 049import org.apache.maven.plugin.PluginResolutionException; 050import org.apache.maven.plugin.descriptor.MojoDescriptor; 051import org.apache.maven.plugin.descriptor.Parameter; 052import org.apache.maven.plugin.descriptor.PluginDescriptor; 053import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder; 054import org.apache.maven.plugin.version.DefaultPluginVersionRequest; 055import org.apache.maven.plugin.version.PluginVersionRequest; 056import org.apache.maven.plugin.version.PluginVersionResolutionException; 057import org.apache.maven.plugin.version.PluginVersionResolver; 058import org.apache.maven.project.ExtensionDescriptor; 059import org.apache.maven.project.ExtensionDescriptorBuilder; 060import org.apache.maven.project.MavenProject; 061import org.apache.maven.rtinfo.RuntimeInformation; 062import org.apache.maven.session.scope.internal.SessionScopeModule; 063import org.codehaus.plexus.DefaultPlexusContainer; 064import org.codehaus.plexus.PlexusContainer; 065import org.codehaus.plexus.classworlds.realm.ClassRealm; 066import org.codehaus.plexus.component.annotations.Component; 067import org.codehaus.plexus.component.annotations.Requirement; 068import org.codehaus.plexus.component.composition.CycleDetectedInComponentGraphException; 069import org.codehaus.plexus.component.configurator.ComponentConfigurationException; 070import org.codehaus.plexus.component.configurator.ComponentConfigurator; 071import org.codehaus.plexus.component.configurator.ConfigurationListener; 072import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; 073import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator; 074import org.codehaus.plexus.component.repository.ComponentDescriptor; 075import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException; 076import org.codehaus.plexus.component.repository.exception.ComponentLookupException; 077import org.codehaus.plexus.configuration.PlexusConfiguration; 078import org.codehaus.plexus.configuration.PlexusConfigurationException; 079import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration; 080import org.codehaus.plexus.logging.Logger; 081import org.codehaus.plexus.logging.LoggerManager; 082import org.codehaus.plexus.util.ReaderFactory; 083import org.codehaus.plexus.util.StringUtils; 084import org.codehaus.plexus.util.xml.Xpp3Dom; 085import org.eclipse.aether.RepositorySystemSession; 086import org.eclipse.aether.graph.DependencyFilter; 087import org.eclipse.aether.graph.DependencyNode; 088import org.eclipse.aether.repository.RemoteRepository; 089import org.eclipse.aether.util.filter.AndDependencyFilter; 090import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator; 091 092import java.io.BufferedInputStream; 093import java.io.ByteArrayOutputStream; 094import java.io.File; 095import java.io.FileInputStream; 096import java.io.IOException; 097import java.io.InputStream; 098import java.io.PrintStream; 099import java.io.Reader; 100import java.util.ArrayList; 101import java.util.Collection; 102import java.util.Collections; 103import java.util.HashMap; 104import java.util.Iterator; 105import java.util.List; 106import java.util.Map; 107import java.util.jar.JarFile; 108import java.util.zip.ZipEntry; 109 110/** 111 * Provides basic services to manage Maven plugins and their mojos. This component is kept general in its design such 112 * that the plugins/mojos can be used in arbitrary contexts. In particular, the mojos can be used for ordinary build 113 * plugins as well as special purpose plugins like reports. 114 * 115 * @author Benjamin Bentmann 116 * @since 3.0 117 */ 118@Component( role = MavenPluginManager.class ) 119public class DefaultMavenPluginManager 120 implements MavenPluginManager 121{ 122 123 /** 124 * PluginId=>ExtensionRealmCache.CacheRecord map MavenProject context value key. The map is used to ensure the same 125 * class realm is used to load build extensions and load mojos for extensions=true plugins. 126 * 127 * @noreference this is part of internal implementation and may be changed or removed without notice 128 * @since 3.3.0 129 */ 130 public static final String KEY_EXTENSIONS_REALMS = DefaultMavenPluginManager.class.getName() + "/extensionsRealms"; 131 132 @Requirement 133 private Logger logger; 134 135 @Requirement 136 private LoggerManager loggerManager; 137 138 @Requirement 139 private PlexusContainer container; 140 141 @Requirement 142 private ClassRealmManager classRealmManager; 143 144 @Requirement 145 private PluginDescriptorCache pluginDescriptorCache; 146 147 @Requirement 148 private PluginRealmCache pluginRealmCache; 149 150 @Requirement 151 private PluginDependenciesResolver pluginDependenciesResolver; 152 153 @Requirement 154 private RuntimeInformation runtimeInformation; 155 156 @Requirement 157 private ExtensionRealmCache extensionRealmCache; 158 159 @Requirement 160 private PluginVersionResolver pluginVersionResolver; 161 162 @Requirement 163 private PluginArtifactsCache pluginArtifactsCache; 164 165 private ExtensionDescriptorBuilder extensionDescriptorBuilder = new ExtensionDescriptorBuilder(); 166 167 private PluginDescriptorBuilder builder = new PluginDescriptorBuilder(); 168 169 public synchronized PluginDescriptor getPluginDescriptor( Plugin plugin, List<RemoteRepository> repositories, 170 RepositorySystemSession session ) 171 throws PluginResolutionException, PluginDescriptorParsingException, InvalidPluginDescriptorException 172 { 173 PluginDescriptorCache.Key cacheKey = pluginDescriptorCache.createKey( plugin, repositories, session ); 174 175 PluginDescriptor pluginDescriptor = pluginDescriptorCache.get( cacheKey ); 176 177 if ( pluginDescriptor == null ) 178 { 179 org.eclipse.aether.artifact.Artifact artifact = 180 pluginDependenciesResolver.resolve( plugin, repositories, session ); 181 182 Artifact pluginArtifact = RepositoryUtils.toArtifact( artifact ); 183 184 pluginDescriptor = extractPluginDescriptor( pluginArtifact, plugin ); 185 186 pluginDescriptor.setRequiredMavenVersion( artifact.getProperty( "requiredMavenVersion", null ) ); 187 188 pluginDescriptorCache.put( cacheKey, pluginDescriptor ); 189 } 190 191 pluginDescriptor.setPlugin( plugin ); 192 193 return pluginDescriptor; 194 } 195 196 private PluginDescriptor extractPluginDescriptor( Artifact pluginArtifact, Plugin plugin ) 197 throws PluginDescriptorParsingException, InvalidPluginDescriptorException 198 { 199 PluginDescriptor pluginDescriptor = null; 200 201 File pluginFile = pluginArtifact.getFile(); 202 203 try 204 { 205 if ( pluginFile.isFile() ) 206 { 207 try ( JarFile pluginJar = new JarFile( pluginFile, false ) ) 208 { 209 ZipEntry pluginDescriptorEntry = pluginJar.getEntry( getPluginDescriptorLocation() ); 210 211 if ( pluginDescriptorEntry != null ) 212 { 213 InputStream is = pluginJar.getInputStream( pluginDescriptorEntry ); 214 215 pluginDescriptor = parsePluginDescriptor( is, plugin, pluginFile.getAbsolutePath() ); 216 } 217 } 218 } 219 else 220 { 221 File pluginXml = new File( pluginFile, getPluginDescriptorLocation() ); 222 223 if ( pluginXml.isFile() ) 224 { 225 try ( InputStream is = new BufferedInputStream( new FileInputStream( pluginXml ) ) ) 226 { 227 pluginDescriptor = parsePluginDescriptor( is, plugin, pluginXml.getAbsolutePath() ); 228 } 229 } 230 } 231 232 if ( pluginDescriptor == null ) 233 { 234 throw new IOException( "No plugin descriptor found at " + getPluginDescriptorLocation() ); 235 } 236 } 237 catch ( IOException e ) 238 { 239 throw new PluginDescriptorParsingException( plugin, pluginFile.getAbsolutePath(), e ); 240 } 241 242 MavenPluginValidator validator = new MavenPluginValidator( pluginArtifact ); 243 244 validator.validate( pluginDescriptor ); 245 246 if ( validator.hasErrors() ) 247 { 248 throw new InvalidPluginDescriptorException( 249 "Invalid plugin descriptor for " + plugin.getId() + " (" + pluginFile + ")", validator.getErrors() ); 250 } 251 252 pluginDescriptor.setPluginArtifact( pluginArtifact ); 253 254 return pluginDescriptor; 255 } 256 257 private String getPluginDescriptorLocation() 258 { 259 return "META-INF/maven/plugin.xml"; 260 } 261 262 private PluginDescriptor parsePluginDescriptor( InputStream is, Plugin plugin, String descriptorLocation ) 263 throws PluginDescriptorParsingException 264 { 265 try 266 { 267 Reader reader = ReaderFactory.newXmlReader( is ); 268 269 PluginDescriptor pluginDescriptor = builder.build( reader, descriptorLocation ); 270 271 return pluginDescriptor; 272 } 273 catch ( IOException | PlexusConfigurationException e ) 274 { 275 throw new PluginDescriptorParsingException( plugin, descriptorLocation, e ); 276 } 277 } 278 279 public MojoDescriptor getMojoDescriptor( Plugin plugin, String goal, List<RemoteRepository> repositories, 280 RepositorySystemSession session ) 281 throws MojoNotFoundException, PluginResolutionException, PluginDescriptorParsingException, 282 InvalidPluginDescriptorException 283 { 284 PluginDescriptor pluginDescriptor = getPluginDescriptor( plugin, repositories, session ); 285 286 MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( goal ); 287 288 if ( mojoDescriptor == null ) 289 { 290 throw new MojoNotFoundException( goal, pluginDescriptor ); 291 } 292 293 return mojoDescriptor; 294 } 295 296 public void checkRequiredMavenVersion( PluginDescriptor pluginDescriptor ) 297 throws PluginIncompatibleException 298 { 299 String requiredMavenVersion = pluginDescriptor.getRequiredMavenVersion(); 300 if ( StringUtils.isNotBlank( requiredMavenVersion ) ) 301 { 302 try 303 { 304 if ( !runtimeInformation.isMavenVersion( requiredMavenVersion ) ) 305 { 306 throw new PluginIncompatibleException( pluginDescriptor.getPlugin(), 307 "The plugin " + pluginDescriptor.getId() 308 + " requires Maven version " + requiredMavenVersion ); 309 } 310 } 311 catch ( RuntimeException e ) 312 { 313 logger.warn( "Could not verify plugin's Maven prerequisite: " + e.getMessage() ); 314 } 315 } 316 } 317 318 public synchronized void setupPluginRealm( PluginDescriptor pluginDescriptor, MavenSession session, 319 ClassLoader parent, List<String> imports, DependencyFilter filter ) 320 throws PluginResolutionException, PluginContainerException 321 { 322 Plugin plugin = pluginDescriptor.getPlugin(); 323 MavenProject project = session.getCurrentProject(); 324 325 if ( plugin.isExtensions() ) 326 { 327 ExtensionRealmCache.CacheRecord extensionRecord; 328 try 329 { 330 RepositorySystemSession repositorySession = session.getRepositorySession(); 331 extensionRecord = setupExtensionsRealm( project, plugin, repositorySession ); 332 } 333 catch ( PluginManagerException e ) 334 { 335 // extensions realm is expected to be fully setup at this point 336 // any exception means a problem in maven code, not a user error 337 throw new IllegalStateException( e ); 338 } 339 340 ClassRealm pluginRealm = extensionRecord.realm; 341 List<Artifact> pluginArtifacts = extensionRecord.artifacts; 342 343 for ( ComponentDescriptor<?> componentDescriptor : pluginDescriptor.getComponents() ) 344 { 345 componentDescriptor.setRealm( pluginRealm ); 346 } 347 348 pluginDescriptor.setClassRealm( pluginRealm ); 349 pluginDescriptor.setArtifacts( pluginArtifacts ); 350 } 351 else 352 { 353 Map<String, ClassLoader> foreignImports = calcImports( project, parent, imports ); 354 355 PluginRealmCache.Key cacheKey = pluginRealmCache.createKey( plugin, parent, foreignImports, filter, 356 project.getRemotePluginRepositories(), 357 session.getRepositorySession() ); 358 359 PluginRealmCache.CacheRecord cacheRecord = pluginRealmCache.get( cacheKey ); 360 361 if ( cacheRecord != null ) 362 { 363 pluginDescriptor.setClassRealm( cacheRecord.realm ); 364 pluginDescriptor.setArtifacts( new ArrayList<>( cacheRecord.artifacts ) ); 365 for ( ComponentDescriptor<?> componentDescriptor : pluginDescriptor.getComponents() ) 366 { 367 componentDescriptor.setRealm( cacheRecord.realm ); 368 } 369 } 370 else 371 { 372 createPluginRealm( pluginDescriptor, session, parent, foreignImports, filter ); 373 374 cacheRecord = 375 pluginRealmCache.put( cacheKey, pluginDescriptor.getClassRealm(), pluginDescriptor.getArtifacts() ); 376 } 377 378 pluginRealmCache.register( project, cacheKey, cacheRecord ); 379 } 380 } 381 382 private void createPluginRealm( PluginDescriptor pluginDescriptor, MavenSession session, ClassLoader parent, 383 Map<String, ClassLoader> foreignImports, DependencyFilter filter ) 384 throws PluginResolutionException, PluginContainerException 385 { 386 Plugin plugin = Validate.notNull( pluginDescriptor.getPlugin(), "pluginDescriptor.plugin cannot be null" ); 387 388 Artifact pluginArtifact = 389 Validate.notNull( pluginDescriptor.getPluginArtifact(), "pluginDescriptor.pluginArtifact cannot be null" ); 390 391 MavenProject project = session.getCurrentProject(); 392 393 final ClassRealm pluginRealm; 394 final List<Artifact> pluginArtifacts; 395 396 RepositorySystemSession repositorySession = session.getRepositorySession(); 397 DependencyFilter dependencyFilter = project.getExtensionDependencyFilter(); 398 dependencyFilter = AndDependencyFilter.newInstance( dependencyFilter, filter ); 399 400 DependencyNode root = 401 pluginDependenciesResolver.resolve( plugin, RepositoryUtils.toArtifact( pluginArtifact ), dependencyFilter, 402 project.getRemotePluginRepositories(), repositorySession ); 403 404 PreorderNodeListGenerator nlg = new PreorderNodeListGenerator(); 405 root.accept( nlg ); 406 407 pluginArtifacts = toMavenArtifacts( root, nlg ); 408 409 pluginRealm = classRealmManager.createPluginRealm( plugin, parent, null, foreignImports, 410 toAetherArtifacts( pluginArtifacts ) ); 411 412 discoverPluginComponents( pluginRealm, plugin, pluginDescriptor ); 413 414 pluginDescriptor.setClassRealm( pluginRealm ); 415 pluginDescriptor.setArtifacts( pluginArtifacts ); 416 } 417 418 private void discoverPluginComponents( final ClassRealm pluginRealm, Plugin plugin, 419 PluginDescriptor pluginDescriptor ) 420 throws PluginContainerException 421 { 422 try 423 { 424 if ( pluginDescriptor != null ) 425 { 426 for ( ComponentDescriptor<?> componentDescriptor : pluginDescriptor.getComponents() ) 427 { 428 componentDescriptor.setRealm( pluginRealm ); 429 container.addComponentDescriptor( componentDescriptor ); 430 } 431 } 432 433 ( (DefaultPlexusContainer) container ).discoverComponents( pluginRealm, new SessionScopeModule( container ), 434 new MojoExecutionScopeModule( container ) ); 435 } 436 catch ( ComponentLookupException | CycleDetectedInComponentGraphException e ) 437 { 438 throw new PluginContainerException( plugin, pluginRealm, 439 "Error in component graph of plugin " + plugin.getId() + ": " 440 + e.getMessage(), e ); 441 } 442 } 443 444 private List<org.eclipse.aether.artifact.Artifact> toAetherArtifacts( final List<Artifact> pluginArtifacts ) 445 { 446 return new ArrayList<>( RepositoryUtils.toArtifacts( pluginArtifacts ) ); 447 } 448 449 private List<Artifact> toMavenArtifacts( DependencyNode root, PreorderNodeListGenerator nlg ) 450 { 451 List<Artifact> artifacts = new ArrayList<>( nlg.getNodes().size() ); 452 RepositoryUtils.toArtifacts( artifacts, Collections.singleton( root ), Collections.<String>emptyList(), null ); 453 for ( Iterator<Artifact> it = artifacts.iterator(); it.hasNext(); ) 454 { 455 Artifact artifact = it.next(); 456 if ( artifact.getFile() == null ) 457 { 458 it.remove(); 459 } 460 } 461 return artifacts; 462 } 463 464 private Map<String, ClassLoader> calcImports( MavenProject project, ClassLoader parent, List<String> imports ) 465 { 466 Map<String, ClassLoader> foreignImports = new HashMap<>(); 467 468 ClassLoader projectRealm = project.getClassRealm(); 469 if ( projectRealm != null ) 470 { 471 foreignImports.put( "", projectRealm ); 472 } 473 else 474 { 475 foreignImports.put( "", classRealmManager.getMavenApiRealm() ); 476 } 477 478 if ( parent != null && imports != null ) 479 { 480 for ( String parentImport : imports ) 481 { 482 foreignImports.put( parentImport, parent ); 483 } 484 } 485 486 return foreignImports; 487 } 488 489 public <T> T getConfiguredMojo( Class<T> mojoInterface, MavenSession session, MojoExecution mojoExecution ) 490 throws PluginConfigurationException, PluginContainerException 491 { 492 MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); 493 494 PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor(); 495 496 ClassRealm pluginRealm = pluginDescriptor.getClassRealm(); 497 498 if ( logger.isDebugEnabled() ) 499 { 500 logger.debug( "Configuring mojo " + mojoDescriptor.getId() + " from plugin realm " + pluginRealm ); 501 } 502 503 // We are forcing the use of the plugin realm for all lookups that might occur during 504 // the lifecycle that is part of the lookup. Here we are specifically trying to keep 505 // lookups that occur in contextualize calls in line with the right realm. 506 ClassRealm oldLookupRealm = container.setLookupRealm( pluginRealm ); 507 508 ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); 509 Thread.currentThread().setContextClassLoader( pluginRealm ); 510 511 try 512 { 513 T mojo; 514 515 try 516 { 517 mojo = container.lookup( mojoInterface, mojoDescriptor.getRoleHint() ); 518 } 519 catch ( ComponentLookupException e ) 520 { 521 Throwable cause = e.getCause(); 522 while ( cause != null && !( cause instanceof LinkageError ) 523 && !( cause instanceof ClassNotFoundException ) ) 524 { 525 cause = cause.getCause(); 526 } 527 528 if ( ( cause instanceof NoClassDefFoundError ) || ( cause instanceof ClassNotFoundException ) ) 529 { 530 ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 ); 531 PrintStream ps = new PrintStream( os ); 532 ps.println( "Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '" 533 + pluginDescriptor.getId() + "'. A required class is missing: " 534 + cause.getMessage() ); 535 pluginRealm.display( ps ); 536 537 throw new PluginContainerException( mojoDescriptor, pluginRealm, os.toString(), cause ); 538 } 539 else if ( cause instanceof LinkageError ) 540 { 541 ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 ); 542 PrintStream ps = new PrintStream( os ); 543 ps.println( "Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '" 544 + pluginDescriptor.getId() + "' due to an API incompatibility: " 545 + e.getClass().getName() + ": " + cause.getMessage() ); 546 pluginRealm.display( ps ); 547 548 throw new PluginContainerException( mojoDescriptor, pluginRealm, os.toString(), cause ); 549 } 550 551 throw new PluginContainerException( mojoDescriptor, pluginRealm, 552 "Unable to load the mojo '" + mojoDescriptor.getGoal() 553 + "' (or one of its required components) from the plugin '" 554 + pluginDescriptor.getId() + "'", e ); 555 } 556 557 if ( mojo instanceof ContextEnabled ) 558 { 559 MavenProject project = session.getCurrentProject(); 560 561 Map<String, Object> pluginContext = session.getPluginContext( pluginDescriptor, project ); 562 563 if ( pluginContext != null ) 564 { 565 pluginContext.put( "project", project ); 566 567 pluginContext.put( "pluginDescriptor", pluginDescriptor ); 568 569 ( (ContextEnabled) mojo ).setPluginContext( pluginContext ); 570 } 571 } 572 573 if ( mojo instanceof Mojo ) 574 { 575 Logger mojoLogger = loggerManager.getLoggerForComponent( mojoDescriptor.getImplementation() ); 576 ( (Mojo) mojo ).setLog( new DefaultLog( mojoLogger ) ); 577 } 578 579 Xpp3Dom dom = mojoExecution.getConfiguration(); 580 581 PlexusConfiguration pomConfiguration; 582 583 if ( dom == null ) 584 { 585 pomConfiguration = new XmlPlexusConfiguration( "configuration" ); 586 } 587 else 588 { 589 pomConfiguration = new XmlPlexusConfiguration( dom ); 590 } 591 592 ExpressionEvaluator expressionEvaluator = new PluginParameterExpressionEvaluator( session, mojoExecution ); 593 594 populatePluginFields( mojo, mojoDescriptor, pluginRealm, pomConfiguration, expressionEvaluator ); 595 596 return mojo; 597 } 598 finally 599 { 600 Thread.currentThread().setContextClassLoader( oldClassLoader ); 601 container.setLookupRealm( oldLookupRealm ); 602 } 603 } 604 605 private void populatePluginFields( Object mojo, MojoDescriptor mojoDescriptor, ClassRealm pluginRealm, 606 PlexusConfiguration configuration, ExpressionEvaluator expressionEvaluator ) 607 throws PluginConfigurationException 608 { 609 ComponentConfigurator configurator = null; 610 611 String configuratorId = mojoDescriptor.getComponentConfigurator(); 612 613 if ( StringUtils.isEmpty( configuratorId ) ) 614 { 615 configuratorId = "basic"; 616 } 617 618 try 619 { 620 // TODO: could the configuration be passed to lookup and the configurator known to plexus via the descriptor 621 // so that this method could entirely be handled by a plexus lookup? 622 configurator = container.lookup( ComponentConfigurator.class, configuratorId ); 623 624 ConfigurationListener listener = new DebugConfigurationListener( logger ); 625 626 ValidatingConfigurationListener validator = 627 new ValidatingConfigurationListener( mojo, mojoDescriptor, listener ); 628 629 logger.debug( 630 "Configuring mojo '" + mojoDescriptor.getId() + "' with " + configuratorId + " configurator -->" ); 631 632 configurator.configureComponent( mojo, configuration, expressionEvaluator, pluginRealm, validator ); 633 634 logger.debug( "-- end configuration --" ); 635 636 Collection<Parameter> missingParameters = validator.getMissingParameters(); 637 if ( !missingParameters.isEmpty() ) 638 { 639 if ( "basic".equals( configuratorId ) ) 640 { 641 throw new PluginParameterException( mojoDescriptor, new ArrayList<>( missingParameters ) ); 642 } 643 else 644 { 645 /* 646 * NOTE: Other configurators like the map-oriented one don't call into the listener, so do it the 647 * hard way. 648 */ 649 validateParameters( mojoDescriptor, configuration, expressionEvaluator ); 650 } 651 } 652 } 653 catch ( ComponentConfigurationException e ) 654 { 655 String message = "Unable to parse configuration of mojo " + mojoDescriptor.getId(); 656 if ( e.getFailedConfiguration() != null ) 657 { 658 message += " for parameter " + e.getFailedConfiguration().getName(); 659 } 660 message += ": " + e.getMessage(); 661 662 throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(), message, e ); 663 } 664 catch ( ComponentLookupException e ) 665 { 666 throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(), 667 "Unable to retrieve component configurator " + configuratorId 668 + " for configuration of mojo " + mojoDescriptor.getId(), e ); 669 } 670 catch ( NoClassDefFoundError e ) 671 { 672 ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 ); 673 PrintStream ps = new PrintStream( os ); 674 ps.println( "A required class was missing during configuration of mojo " + mojoDescriptor.getId() + ": " 675 + e.getMessage() ); 676 pluginRealm.display( ps ); 677 678 throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(), os.toString(), e ); 679 } 680 catch ( LinkageError e ) 681 { 682 ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 ); 683 PrintStream ps = new PrintStream( os ); 684 ps.println( 685 "An API incompatibility was encountered during configuration of mojo " + mojoDescriptor.getId() + ": " 686 + e.getClass().getName() + ": " + e.getMessage() ); 687 pluginRealm.display( ps ); 688 689 throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(), os.toString(), e ); 690 } 691 finally 692 { 693 if ( configurator != null ) 694 { 695 try 696 { 697 container.release( configurator ); 698 } 699 catch ( ComponentLifecycleException e ) 700 { 701 logger.debug( "Failed to release mojo configurator - ignoring." ); 702 } 703 } 704 } 705 } 706 707 private void validateParameters( MojoDescriptor mojoDescriptor, PlexusConfiguration configuration, 708 ExpressionEvaluator expressionEvaluator ) 709 throws ComponentConfigurationException, PluginParameterException 710 { 711 if ( mojoDescriptor.getParameters() == null ) 712 { 713 return; 714 } 715 716 List<Parameter> invalidParameters = new ArrayList<>(); 717 718 for ( Parameter parameter : mojoDescriptor.getParameters() ) 719 { 720 if ( !parameter.isRequired() ) 721 { 722 continue; 723 } 724 725 Object value = null; 726 727 PlexusConfiguration config = configuration.getChild( parameter.getName(), false ); 728 if ( config != null ) 729 { 730 String expression = config.getValue( null ); 731 732 try 733 { 734 value = expressionEvaluator.evaluate( expression ); 735 736 if ( value == null ) 737 { 738 value = config.getAttribute( "default-value", null ); 739 } 740 } 741 catch ( ExpressionEvaluationException e ) 742 { 743 String msg = "Error evaluating the expression '" + expression + "' for configuration value '" 744 + configuration.getName() + "'"; 745 throw new ComponentConfigurationException( configuration, msg, e ); 746 } 747 } 748 749 if ( value == null && ( config == null || config.getChildCount() <= 0 ) ) 750 { 751 invalidParameters.add( parameter ); 752 } 753 } 754 755 if ( !invalidParameters.isEmpty() ) 756 { 757 throw new PluginParameterException( mojoDescriptor, invalidParameters ); 758 } 759 } 760 761 public void releaseMojo( Object mojo, MojoExecution mojoExecution ) 762 { 763 if ( mojo != null ) 764 { 765 try 766 { 767 container.release( mojo ); 768 } 769 catch ( ComponentLifecycleException e ) 770 { 771 String goalExecId = mojoExecution.getGoal(); 772 773 if ( mojoExecution.getExecutionId() != null ) 774 { 775 goalExecId += " {execution: " + mojoExecution.getExecutionId() + "}"; 776 } 777 778 logger.debug( "Error releasing mojo for " + goalExecId, e ); 779 } 780 } 781 } 782 783 public ExtensionRealmCache.CacheRecord setupExtensionsRealm( MavenProject project, Plugin plugin, 784 RepositorySystemSession session ) 785 throws PluginManagerException 786 { 787 @SuppressWarnings( "unchecked" ) Map<String, ExtensionRealmCache.CacheRecord> pluginRealms = 788 (Map<String, ExtensionRealmCache.CacheRecord>) project.getContextValue( KEY_EXTENSIONS_REALMS ); 789 if ( pluginRealms == null ) 790 { 791 pluginRealms = new HashMap<>(); 792 project.setContextValue( KEY_EXTENSIONS_REALMS, pluginRealms ); 793 } 794 795 final String pluginKey = plugin.getId(); 796 797 ExtensionRealmCache.CacheRecord extensionRecord = pluginRealms.get( pluginKey ); 798 if ( extensionRecord != null ) 799 { 800 return extensionRecord; 801 } 802 803 final List<RemoteRepository> repositories = project.getRemotePluginRepositories(); 804 805 // resolve plugin version as necessary 806 if ( plugin.getVersion() == null ) 807 { 808 PluginVersionRequest versionRequest = new DefaultPluginVersionRequest( plugin, session, repositories ); 809 try 810 { 811 plugin.setVersion( pluginVersionResolver.resolve( versionRequest ).getVersion() ); 812 } 813 catch ( PluginVersionResolutionException e ) 814 { 815 throw new PluginManagerException( plugin, e.getMessage(), e ); 816 } 817 } 818 819 // resolve plugin artifacts 820 List<Artifact> artifacts; 821 PluginArtifactsCache.Key cacheKey = pluginArtifactsCache.createKey( plugin, null, repositories, session ); 822 PluginArtifactsCache.CacheRecord recordArtifacts; 823 try 824 { 825 recordArtifacts = pluginArtifactsCache.get( cacheKey ); 826 } 827 catch ( PluginResolutionException e ) 828 { 829 throw new PluginManagerException( plugin, e.getMessage(), e ); 830 } 831 if ( recordArtifacts != null ) 832 { 833 artifacts = recordArtifacts.artifacts; 834 } 835 else 836 { 837 try 838 { 839 artifacts = resolveExtensionArtifacts( plugin, repositories, session ); 840 recordArtifacts = pluginArtifactsCache.put( cacheKey, artifacts ); 841 } 842 catch ( PluginResolutionException e ) 843 { 844 pluginArtifactsCache.put( cacheKey, e ); 845 pluginArtifactsCache.register( project, cacheKey, recordArtifacts ); 846 throw new PluginManagerException( plugin, e.getMessage(), e ); 847 } 848 } 849 pluginArtifactsCache.register( project, cacheKey, recordArtifacts ); 850 851 // create and cache extensions realms 852 final ExtensionRealmCache.Key extensionKey = extensionRealmCache.createKey( artifacts ); 853 extensionRecord = extensionRealmCache.get( extensionKey ); 854 if ( extensionRecord == null ) 855 { 856 ClassRealm extensionRealm = 857 classRealmManager.createExtensionRealm( plugin, toAetherArtifacts( artifacts ) ); 858 859 // TODO figure out how to use the same PluginDescriptor when running mojos 860 861 PluginDescriptor pluginDescriptor = null; 862 if ( plugin.isExtensions() && !artifacts.isEmpty() ) 863 { 864 // ignore plugin descriptor parsing errors at this point 865 // these errors will reported during calculation of project build execution plan 866 try 867 { 868 pluginDescriptor = extractPluginDescriptor( artifacts.get( 0 ), plugin ); 869 } 870 catch ( PluginDescriptorParsingException | InvalidPluginDescriptorException e ) 871 { 872 // ignore, see above 873 } 874 } 875 876 discoverPluginComponents( extensionRealm, plugin, pluginDescriptor ); 877 878 ExtensionDescriptor extensionDescriptor = null; 879 Artifact extensionArtifact = artifacts.get( 0 ); 880 try 881 { 882 extensionDescriptor = extensionDescriptorBuilder.build( extensionArtifact.getFile() ); 883 } 884 catch ( IOException e ) 885 { 886 String message = "Invalid extension descriptor for " + plugin.getId() + ": " + e.getMessage(); 887 if ( logger.isDebugEnabled() ) 888 { 889 logger.error( message, e ); 890 } 891 else 892 { 893 logger.error( message ); 894 } 895 } 896 extensionRecord = extensionRealmCache.put( extensionKey, extensionRealm, extensionDescriptor, artifacts ); 897 } 898 extensionRealmCache.register( project, extensionKey, extensionRecord ); 899 pluginRealms.put( pluginKey, extensionRecord ); 900 901 return extensionRecord; 902 } 903 904 private List<Artifact> resolveExtensionArtifacts( Plugin extensionPlugin, List<RemoteRepository> repositories, 905 RepositorySystemSession session ) 906 throws PluginResolutionException 907 { 908 DependencyNode root = pluginDependenciesResolver.resolve( extensionPlugin, null, null, repositories, session ); 909 PreorderNodeListGenerator nlg = new PreorderNodeListGenerator(); 910 root.accept( nlg ); 911 return toMavenArtifacts( root, nlg ); 912 } 913 914}