1 package org.apache.maven.plugin.internal;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.RepositoryUtils;
23 import org.apache.maven.api.xml.Dom;
24 import org.apache.maven.artifact.Artifact;
25 import org.apache.maven.classrealm.ClassRealmManager;
26 import org.apache.maven.execution.MavenSession;
27 import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
28 import org.apache.maven.internal.impl.DefaultSession;
29 import org.apache.maven.model.Plugin;
30 import org.apache.maven.plugin.ContextEnabled;
31 import org.apache.maven.plugin.DebugConfigurationListener;
32 import org.apache.maven.plugin.ExtensionRealmCache;
33 import org.apache.maven.plugin.InvalidPluginDescriptorException;
34 import org.apache.maven.plugin.MavenPluginManager;
35 import org.apache.maven.plugin.Mojo;
36 import org.apache.maven.plugin.MojoExecution;
37 import org.apache.maven.plugin.MojoNotFoundException;
38 import org.apache.maven.plugin.PluginArtifactsCache;
39 import org.apache.maven.plugin.PluginConfigurationException;
40 import org.apache.maven.plugin.PluginContainerException;
41 import org.apache.maven.plugin.PluginDescriptorCache;
42 import org.apache.maven.plugin.PluginDescriptorParsingException;
43 import org.apache.maven.plugin.PluginIncompatibleException;
44 import org.apache.maven.plugin.PluginManagerException;
45 import org.apache.maven.plugin.PluginParameterException;
46 import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
47 import org.apache.maven.plugin.PluginParameterExpressionEvaluatorV4;
48 import org.apache.maven.plugin.PluginRealmCache;
49 import org.apache.maven.plugin.PluginResolutionException;
50 import org.apache.maven.plugin.descriptor.MojoDescriptor;
51 import org.apache.maven.plugin.descriptor.Parameter;
52 import org.apache.maven.plugin.descriptor.PluginDescriptor;
53 import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder;
54 import org.apache.maven.plugin.version.DefaultPluginVersionRequest;
55 import org.apache.maven.plugin.version.PluginVersionRequest;
56 import org.apache.maven.plugin.version.PluginVersionResolutionException;
57 import org.apache.maven.plugin.version.PluginVersionResolver;
58 import org.apache.maven.project.ExtensionDescriptor;
59 import org.apache.maven.project.ExtensionDescriptorBuilder;
60 import org.apache.maven.project.MavenProject;
61 import org.apache.maven.rtinfo.RuntimeInformation;
62 import org.apache.maven.session.scope.internal.SessionScopeModule;
63 import org.apache.maven.internal.xml.XmlPlexusConfiguration;
64 import org.codehaus.plexus.DefaultPlexusContainer;
65 import org.codehaus.plexus.PlexusContainer;
66 import org.codehaus.plexus.classworlds.realm.ClassRealm;
67 import org.codehaus.plexus.component.composition.CycleDetectedInComponentGraphException;
68 import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
69 import org.codehaus.plexus.component.configurator.ComponentConfigurator;
70 import org.codehaus.plexus.component.configurator.ConfigurationListener;
71 import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
72 import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
73 import org.codehaus.plexus.component.repository.ComponentDescriptor;
74 import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
75 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
76 import org.codehaus.plexus.configuration.DefaultPlexusConfiguration;
77 import org.codehaus.plexus.configuration.PlexusConfiguration;
78 import org.codehaus.plexus.configuration.PlexusConfigurationException;
79 import org.codehaus.plexus.util.ReaderFactory;
80 import org.codehaus.plexus.util.StringUtils;
81 import org.eclipse.aether.RepositorySystemSession;
82 import org.eclipse.aether.graph.DependencyFilter;
83 import org.eclipse.aether.graph.DependencyNode;
84 import org.eclipse.aether.repository.RemoteRepository;
85 import org.eclipse.aether.util.filter.AndDependencyFilter;
86 import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator;
87 import org.slf4j.Logger;
88 import org.slf4j.LoggerFactory;
89
90 import java.io.BufferedInputStream;
91 import java.io.ByteArrayOutputStream;
92 import java.io.File;
93 import java.io.FileInputStream;
94 import java.io.IOException;
95 import java.io.InputStream;
96 import java.io.PrintStream;
97 import java.io.Reader;
98 import java.util.ArrayList;
99 import java.util.Collection;
100 import java.util.Collections;
101 import java.util.HashMap;
102 import java.util.List;
103 import java.util.Map;
104 import java.util.Objects;
105 import java.util.jar.JarFile;
106 import java.util.zip.ZipEntry;
107
108 import javax.inject.Inject;
109 import javax.inject.Named;
110 import javax.inject.Singleton;
111
112
113
114
115
116
117
118
119
120 @Named
121 @Singleton
122 public class DefaultMavenPluginManager
123 implements MavenPluginManager
124 {
125
126
127
128
129
130
131
132
133
134
135 public static final String KEY_EXTENSIONS_REALMS = DefaultMavenPluginManager.class.getName() + "/extensionsRealms";
136
137 private final Logger logger = LoggerFactory.getLogger( getClass() );
138
139 private PlexusContainer container;
140 private ClassRealmManager classRealmManager;
141 private PluginDescriptorCache pluginDescriptorCache;
142 private PluginRealmCache pluginRealmCache;
143 private PluginDependenciesResolver pluginDependenciesResolver;
144 private RuntimeInformation runtimeInformation;
145 private ExtensionRealmCache extensionRealmCache;
146 private PluginVersionResolver pluginVersionResolver;
147 private PluginArtifactsCache pluginArtifactsCache;
148 private MavenPluginValidator pluginValidator;
149 private List<MavenPluginConfigurationValidator> configurationValidators;
150
151 private final ExtensionDescriptorBuilder extensionDescriptorBuilder = new ExtensionDescriptorBuilder();
152 private final PluginDescriptorBuilder builder = new PluginDescriptorBuilder();
153
154 @Inject
155 @SuppressWarnings( "checkstyle:ParameterNumber" )
156 public DefaultMavenPluginManager(
157 PlexusContainer container,
158 ClassRealmManager classRealmManager,
159 PluginDescriptorCache pluginDescriptorCache,
160 PluginRealmCache pluginRealmCache,
161 PluginDependenciesResolver pluginDependenciesResolver,
162 RuntimeInformation runtimeInformation,
163 ExtensionRealmCache extensionRealmCache,
164 PluginVersionResolver pluginVersionResolver,
165 PluginArtifactsCache pluginArtifactsCache,
166 MavenPluginValidator pluginValidator,
167 List<MavenPluginConfigurationValidator> configurationValidators )
168 {
169 this.container = container;
170 this.classRealmManager = classRealmManager;
171 this.pluginDescriptorCache = pluginDescriptorCache;
172 this.pluginRealmCache = pluginRealmCache;
173 this.pluginDependenciesResolver = pluginDependenciesResolver;
174 this.runtimeInformation = runtimeInformation;
175 this.extensionRealmCache = extensionRealmCache;
176 this.pluginVersionResolver = pluginVersionResolver;
177 this.pluginArtifactsCache = pluginArtifactsCache;
178 this.pluginValidator = pluginValidator;
179 this.configurationValidators = configurationValidators;
180 }
181
182 public synchronized PluginDescriptor getPluginDescriptor( Plugin plugin, List<RemoteRepository> repositories,
183 RepositorySystemSession session )
184 throws PluginResolutionException, PluginDescriptorParsingException, InvalidPluginDescriptorException
185 {
186 PluginDescriptorCache.Key cacheKey = pluginDescriptorCache.createKey( plugin, repositories, session );
187
188 PluginDescriptor pluginDescriptor = pluginDescriptorCache.get( cacheKey );
189
190 if ( pluginDescriptor == null )
191 {
192 org.eclipse.aether.artifact.Artifact artifact =
193 pluginDependenciesResolver.resolve( plugin, repositories, session );
194
195 Artifact pluginArtifact = RepositoryUtils.toArtifact( artifact );
196
197 pluginDescriptor = extractPluginDescriptor( pluginArtifact, plugin );
198
199 pluginDescriptor.setRequiredMavenVersion( artifact.getProperty( "requiredMavenVersion", null ) );
200
201 pluginDescriptorCache.put( cacheKey, pluginDescriptor );
202 }
203
204 pluginDescriptor.setPlugin( plugin );
205
206 return pluginDescriptor;
207 }
208
209 private PluginDescriptor extractPluginDescriptor( Artifact pluginArtifact, Plugin plugin )
210 throws PluginDescriptorParsingException, InvalidPluginDescriptorException
211 {
212 PluginDescriptor pluginDescriptor = null;
213
214 File pluginFile = pluginArtifact.getFile();
215
216 try
217 {
218 if ( pluginFile.isFile() )
219 {
220 try ( JarFile pluginJar = new JarFile( pluginFile, false ) )
221 {
222 ZipEntry pluginDescriptorEntry = pluginJar.getEntry( getPluginDescriptorLocation() );
223
224 if ( pluginDescriptorEntry != null )
225 {
226 InputStream is = pluginJar.getInputStream( pluginDescriptorEntry );
227
228 pluginDescriptor = parsePluginDescriptor( is, plugin, pluginFile.getAbsolutePath() );
229 }
230 }
231 }
232 else
233 {
234 File pluginXml = new File( pluginFile, getPluginDescriptorLocation() );
235
236 if ( pluginXml.isFile() )
237 {
238 try ( InputStream is = new BufferedInputStream( new FileInputStream( pluginXml ) ) )
239 {
240 pluginDescriptor = parsePluginDescriptor( is, plugin, pluginXml.getAbsolutePath() );
241 }
242 }
243 }
244
245 if ( pluginDescriptor == null )
246 {
247 throw new IOException( "No plugin descriptor found at " + getPluginDescriptorLocation() );
248 }
249 }
250 catch ( IOException e )
251 {
252 throw new PluginDescriptorParsingException( plugin, pluginFile.getAbsolutePath(), e );
253 }
254
255 List<String> errors = new ArrayList<>();
256 pluginValidator.validate( pluginArtifact, pluginDescriptor, errors );
257
258 if ( !errors.isEmpty() )
259 {
260 throw new InvalidPluginDescriptorException(
261 "Invalid plugin descriptor for " + plugin.getId() + " (" + pluginFile + ")", errors );
262 }
263
264 pluginDescriptor.setPluginArtifact( pluginArtifact );
265
266 return pluginDescriptor;
267 }
268
269 private String getPluginDescriptorLocation()
270 {
271 return "META-INF/maven/plugin.xml";
272 }
273
274 private PluginDescriptor parsePluginDescriptor( InputStream is, Plugin plugin, String descriptorLocation )
275 throws PluginDescriptorParsingException
276 {
277 try
278 {
279 Reader reader = ReaderFactory.newXmlReader( is );
280
281 return builder.build( reader, descriptorLocation );
282 }
283 catch ( IOException | PlexusConfigurationException e )
284 {
285 throw new PluginDescriptorParsingException( plugin, descriptorLocation, e );
286 }
287 }
288
289 public MojoDescriptor getMojoDescriptor( Plugin plugin, String goal, List<RemoteRepository> repositories,
290 RepositorySystemSession session )
291 throws MojoNotFoundException, PluginResolutionException, PluginDescriptorParsingException,
292 InvalidPluginDescriptorException
293 {
294 PluginDescriptor pluginDescriptor = getPluginDescriptor( plugin, repositories, session );
295
296 MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( goal );
297
298 if ( mojoDescriptor == null )
299 {
300 throw new MojoNotFoundException( goal, pluginDescriptor );
301 }
302
303 return mojoDescriptor;
304 }
305
306 public void checkRequiredMavenVersion( PluginDescriptor pluginDescriptor )
307 throws PluginIncompatibleException
308 {
309 String requiredMavenVersion = pluginDescriptor.getRequiredMavenVersion();
310 if ( StringUtils.isNotBlank( requiredMavenVersion ) )
311 {
312 try
313 {
314 if ( !runtimeInformation.isMavenVersion( requiredMavenVersion ) )
315 {
316 throw new PluginIncompatibleException( pluginDescriptor.getPlugin(),
317 "The plugin " + pluginDescriptor.getId()
318 + " requires Maven version " + requiredMavenVersion );
319 }
320 }
321 catch ( RuntimeException e )
322 {
323 logger.warn( "Could not verify plugin's Maven prerequisite: " + e.getMessage() );
324 }
325 }
326 }
327
328 public synchronized void setupPluginRealm( PluginDescriptor pluginDescriptor, MavenSession session,
329 ClassLoader parent, List<String> imports, DependencyFilter filter )
330 throws PluginResolutionException, PluginContainerException
331 {
332 Plugin plugin = pluginDescriptor.getPlugin();
333 MavenProject project = session.getCurrentProject();
334
335 if ( plugin.isExtensions() )
336 {
337 ExtensionRealmCache.CacheRecord extensionRecord;
338 try
339 {
340 RepositorySystemSession repositorySession = session.getRepositorySession();
341 extensionRecord = setupExtensionsRealm( project, plugin, repositorySession );
342 }
343 catch ( PluginManagerException e )
344 {
345
346
347 throw new IllegalStateException( e );
348 }
349
350 ClassRealm pluginRealm = extensionRecord.getRealm();
351 List<Artifact> pluginArtifacts = extensionRecord.getArtifacts();
352
353 for ( ComponentDescriptor<?> componentDescriptor : pluginDescriptor.getComponents() )
354 {
355 componentDescriptor.setRealm( pluginRealm );
356 }
357
358 pluginDescriptor.setClassRealm( pluginRealm );
359 pluginDescriptor.setArtifacts( pluginArtifacts );
360 }
361 else
362 {
363 Map<String, ClassLoader> foreignImports = calcImports( project, parent, imports );
364
365 PluginRealmCache.Key cacheKey = pluginRealmCache.createKey( plugin, parent, foreignImports, filter,
366 project.getRemotePluginRepositories(),
367 session.getRepositorySession() );
368
369 PluginRealmCache.CacheRecord cacheRecord = pluginRealmCache.get( cacheKey );
370
371 if ( cacheRecord != null )
372 {
373 pluginDescriptor.setClassRealm( cacheRecord.getRealm() );
374 pluginDescriptor.setArtifacts( new ArrayList<>( cacheRecord.getArtifacts() ) );
375 for ( ComponentDescriptor<?> componentDescriptor : pluginDescriptor.getComponents() )
376 {
377 componentDescriptor.setRealm( cacheRecord.getRealm() );
378 }
379 }
380 else
381 {
382 createPluginRealm( pluginDescriptor, session, parent, foreignImports, filter );
383
384 cacheRecord =
385 pluginRealmCache.put( cacheKey, pluginDescriptor.getClassRealm(), pluginDescriptor.getArtifacts() );
386 }
387
388 pluginRealmCache.register( project, cacheKey, cacheRecord );
389 }
390 }
391
392 private void createPluginRealm( PluginDescriptor pluginDescriptor, MavenSession session, ClassLoader parent,
393 Map<String, ClassLoader> foreignImports, DependencyFilter filter )
394 throws PluginResolutionException, PluginContainerException
395 {
396 Plugin plugin =
397 Objects.requireNonNull( pluginDescriptor.getPlugin(), "pluginDescriptor.plugin cannot be null" );
398
399 Artifact pluginArtifact = Objects.requireNonNull( pluginDescriptor.getPluginArtifact(),
400 "pluginDescriptor.pluginArtifact cannot be null" );
401
402 MavenProject project = session.getCurrentProject();
403
404 final ClassRealm pluginRealm;
405 final List<Artifact> pluginArtifacts;
406
407 RepositorySystemSession repositorySession = session.getRepositorySession();
408 DependencyFilter dependencyFilter = project.getExtensionDependencyFilter();
409 dependencyFilter = AndDependencyFilter.newInstance( dependencyFilter, filter );
410
411 DependencyNode root =
412 pluginDependenciesResolver.resolve( plugin, RepositoryUtils.toArtifact( pluginArtifact ), dependencyFilter,
413 project.getRemotePluginRepositories(), repositorySession );
414
415 PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
416 root.accept( nlg );
417
418 pluginArtifacts = toMavenArtifacts( root, nlg );
419
420 pluginRealm = classRealmManager.createPluginRealm( plugin, parent, null, foreignImports,
421 toAetherArtifacts( pluginArtifacts ) );
422
423 discoverPluginComponents( pluginRealm, plugin, pluginDescriptor );
424
425 pluginDescriptor.setClassRealm( pluginRealm );
426 pluginDescriptor.setArtifacts( pluginArtifacts );
427 }
428
429 private void discoverPluginComponents( final ClassRealm pluginRealm, Plugin plugin,
430 PluginDescriptor pluginDescriptor )
431 throws PluginContainerException
432 {
433 try
434 {
435 if ( pluginDescriptor != null )
436 {
437 for ( ComponentDescriptor<?> componentDescriptor : pluginDescriptor.getComponents() )
438 {
439 componentDescriptor.setRealm( pluginRealm );
440 container.addComponentDescriptor( componentDescriptor );
441 }
442 }
443
444 ( (DefaultPlexusContainer) container ).discoverComponents( pluginRealm, new SessionScopeModule( container ),
445 new MojoExecutionScopeModule( container ) );
446 }
447 catch ( ComponentLookupException | CycleDetectedInComponentGraphException e )
448 {
449 throw new PluginContainerException( plugin, pluginRealm,
450 "Error in component graph of plugin " + plugin.getId() + ": "
451 + e.getMessage(), e );
452 }
453 }
454
455 private List<org.eclipse.aether.artifact.Artifact> toAetherArtifacts( final List<Artifact> pluginArtifacts )
456 {
457 return new ArrayList<>( RepositoryUtils.toArtifacts( pluginArtifacts ) );
458 }
459
460 private List<Artifact> toMavenArtifacts( DependencyNode root, PreorderNodeListGenerator nlg )
461 {
462 List<Artifact> artifacts = new ArrayList<>( nlg.getNodes().size() );
463 RepositoryUtils.toArtifacts( artifacts, Collections.singleton( root ), Collections.emptyList(), null );
464 artifacts.removeIf( artifact -> artifact.getFile() == null );
465 return Collections.unmodifiableList( artifacts );
466 }
467
468 private Map<String, ClassLoader> calcImports( MavenProject project, ClassLoader parent, List<String> imports )
469 {
470 Map<String, ClassLoader> foreignImports = new HashMap<>();
471
472 ClassLoader projectRealm = project.getClassRealm();
473 if ( projectRealm != null )
474 {
475 foreignImports.put( "", projectRealm );
476 }
477 else
478 {
479 foreignImports.put( "", classRealmManager.getMavenApiRealm() );
480 }
481
482 if ( parent != null && imports != null )
483 {
484 for ( String parentImport : imports )
485 {
486 foreignImports.put( parentImport, parent );
487 }
488 }
489
490 return foreignImports;
491 }
492
493 public <T> T getConfiguredMojo( Class<T> mojoInterface, MavenSession session, MojoExecution mojoExecution )
494 throws PluginConfigurationException, PluginContainerException
495 {
496 MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
497
498 PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor();
499
500 ClassRealm pluginRealm = pluginDescriptor.getClassRealm();
501
502 if ( pluginRealm == null )
503 {
504 try
505 {
506 setupPluginRealm( pluginDescriptor, session, null, null, null );
507 }
508 catch ( PluginResolutionException e )
509 {
510 String msg = "Cannot setup plugin realm [mojoDescriptor=" + mojoDescriptor.getId()
511 + ", pluginDescriptor=" + pluginDescriptor.getId() + "]";
512 throw new PluginConfigurationException( pluginDescriptor, msg, e );
513 }
514 pluginRealm = pluginDescriptor.getClassRealm();
515 }
516
517 if ( logger.isDebugEnabled() )
518 {
519 logger.debug( "Loading mojo " + mojoDescriptor.getId() + " from plugin realm " + pluginRealm );
520 }
521
522
523
524
525 ClassRealm oldLookupRealm = container.setLookupRealm( pluginRealm );
526
527 ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
528 Thread.currentThread().setContextClassLoader( pluginRealm );
529
530 try
531 {
532 T mojo;
533
534 try
535 {
536 mojo = container.lookup( mojoInterface, mojoDescriptor.getRoleHint() );
537 }
538 catch ( ComponentLookupException e )
539 {
540 Throwable cause = e.getCause();
541 while ( cause != null && !( cause instanceof LinkageError )
542 && !( cause instanceof ClassNotFoundException ) )
543 {
544 cause = cause.getCause();
545 }
546
547 if ( ( cause instanceof NoClassDefFoundError ) || ( cause instanceof ClassNotFoundException ) )
548 {
549 ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 );
550 PrintStream ps = new PrintStream( os );
551 ps.println( "Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '"
552 + pluginDescriptor.getId() + "'. A required class is missing: "
553 + cause.getMessage() );
554 pluginRealm.display( ps );
555
556 throw new PluginContainerException( mojoDescriptor, pluginRealm, os.toString(), cause );
557 }
558 else if ( cause instanceof LinkageError )
559 {
560 ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 );
561 PrintStream ps = new PrintStream( os );
562 ps.println( "Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '"
563 + pluginDescriptor.getId() + "' due to an API incompatibility: "
564 + e.getClass().getName() + ": " + cause.getMessage() );
565 pluginRealm.display( ps );
566
567 throw new PluginContainerException( mojoDescriptor, pluginRealm, os.toString(), cause );
568 }
569
570 throw new PluginContainerException( mojoDescriptor, pluginRealm,
571 "Unable to load the mojo '" + mojoDescriptor.getGoal()
572 + "' (or one of its required components) from the plugin '"
573 + pluginDescriptor.getId() + "'", e );
574 }
575
576 if ( mojo instanceof ContextEnabled )
577 {
578 MavenProject project = session.getCurrentProject();
579
580 Map<String, Object> pluginContext = session.getPluginContext( pluginDescriptor, project );
581
582 if ( pluginContext != null )
583 {
584 pluginContext.put( "project", project );
585
586 pluginContext.put( "pluginDescriptor", pluginDescriptor );
587
588 ( (ContextEnabled) mojo ).setPluginContext( pluginContext );
589 }
590 }
591
592 if ( mojo instanceof Mojo )
593 {
594 Logger mojoLogger = LoggerFactory.getLogger( mojoDescriptor.getImplementation() );
595 ( (Mojo) mojo ).setLog( new MojoLogWrapper( mojoLogger ) );
596 }
597
598 Dom dom = mojoExecution.getConfiguration() != null
599 ? mojoExecution.getConfiguration().getDom() : null;
600
601 PlexusConfiguration pomConfiguration;
602
603 if ( dom == null )
604 {
605 pomConfiguration = new DefaultPlexusConfiguration( "configuration" );
606 }
607 else
608 {
609 pomConfiguration = XmlPlexusConfiguration.toPlexusConfiguration( dom );
610 }
611
612 ExpressionEvaluator expressionEvaluator;
613 if ( mojoDescriptor.isV4Api() )
614 {
615 expressionEvaluator = new PluginParameterExpressionEvaluatorV4(
616 session.getSession(),
617 ( ( DefaultSession ) session.getSession() ).getProject( session.getCurrentProject() ),
618 mojoExecution );
619 }
620 else
621 {
622 expressionEvaluator = new PluginParameterExpressionEvaluator( session, mojoExecution );
623 }
624
625 for ( MavenPluginConfigurationValidator validator: configurationValidators )
626 {
627 validator.validate( mojoDescriptor, pomConfiguration, expressionEvaluator );
628 }
629
630 populateMojoExecutionFields( mojo, mojoExecution.getExecutionId(), mojoDescriptor, pluginRealm,
631 pomConfiguration, expressionEvaluator );
632
633 return mojo;
634 }
635 finally
636 {
637 Thread.currentThread().setContextClassLoader( oldClassLoader );
638 container.setLookupRealm( oldLookupRealm );
639 }
640 }
641
642 private void populateMojoExecutionFields( Object mojo, String executionId, MojoDescriptor mojoDescriptor,
643 ClassRealm pluginRealm, PlexusConfiguration configuration,
644 ExpressionEvaluator expressionEvaluator )
645 throws PluginConfigurationException
646 {
647 ComponentConfigurator configurator = null;
648
649 String configuratorId = mojoDescriptor.getComponentConfigurator();
650
651 if ( StringUtils.isEmpty( configuratorId ) )
652 {
653 configuratorId = mojoDescriptor.isV4Api() ? "enhanced" : "basic";
654 }
655
656 try
657 {
658
659
660 configurator = container.lookup( ComponentConfigurator.class, configuratorId );
661
662 ConfigurationListener listener = new DebugConfigurationListener( logger );
663
664 ValidatingConfigurationListener validator =
665 new ValidatingConfigurationListener( mojo, mojoDescriptor, listener );
666
667 logger.debug( "Configuring mojo execution '" + mojoDescriptor.getId() + ':' + executionId + "' with "
668 + configuratorId + " configurator -->" );
669
670 configurator.configureComponent( mojo, configuration, expressionEvaluator, pluginRealm, validator );
671
672 logger.debug( "-- end configuration --" );
673
674 Collection<Parameter> missingParameters = validator.getMissingParameters();
675 if ( !missingParameters.isEmpty() )
676 {
677 if ( "basic".equals( configuratorId ) )
678 {
679 throw new PluginParameterException( mojoDescriptor, new ArrayList<>( missingParameters ) );
680 }
681 else
682 {
683
684
685
686
687 validateParameters( mojoDescriptor, configuration, expressionEvaluator );
688 }
689 }
690 }
691 catch ( ComponentConfigurationException e )
692 {
693 String message = "Unable to parse configuration of mojo " + mojoDescriptor.getId();
694 if ( e.getFailedConfiguration() != null )
695 {
696 message += " for parameter " + e.getFailedConfiguration().getName();
697 }
698 message += ": " + e.getMessage();
699
700 throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(), message, e );
701 }
702 catch ( ComponentLookupException e )
703 {
704 throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(),
705 "Unable to retrieve component configurator " + configuratorId
706 + " for configuration of mojo " + mojoDescriptor.getId(), e );
707 }
708 catch ( NoClassDefFoundError e )
709 {
710 ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 );
711 PrintStream ps = new PrintStream( os );
712 ps.println( "A required class was missing during configuration of mojo " + mojoDescriptor.getId() + ": "
713 + e.getMessage() );
714 pluginRealm.display( ps );
715
716 throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(), os.toString(), e );
717 }
718 catch ( LinkageError e )
719 {
720 ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 );
721 PrintStream ps = new PrintStream( os );
722 ps.println(
723 "An API incompatibility was encountered during configuration of mojo " + mojoDescriptor.getId() + ": "
724 + e.getClass().getName() + ": " + e.getMessage() );
725 pluginRealm.display( ps );
726
727 throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(), os.toString(), e );
728 }
729 finally
730 {
731 if ( configurator != null )
732 {
733 try
734 {
735 container.release( configurator );
736 }
737 catch ( ComponentLifecycleException e )
738 {
739 logger.debug( "Failed to release mojo configurator - ignoring." );
740 }
741 }
742 }
743 }
744
745 private void validateParameters( MojoDescriptor mojoDescriptor, PlexusConfiguration configuration,
746 ExpressionEvaluator expressionEvaluator )
747 throws ComponentConfigurationException, PluginParameterException
748 {
749 if ( mojoDescriptor.getParameters() == null )
750 {
751 return;
752 }
753
754 List<Parameter> invalidParameters = new ArrayList<>();
755
756 for ( Parameter parameter : mojoDescriptor.getParameters() )
757 {
758 if ( !parameter.isRequired() )
759 {
760 continue;
761 }
762
763 Object value = null;
764
765 PlexusConfiguration config = configuration.getChild( parameter.getName(), false );
766 if ( config != null )
767 {
768 String expression = config.getValue( null );
769
770 try
771 {
772 value = expressionEvaluator.evaluate( expression );
773
774 if ( value == null )
775 {
776 value = config.getAttribute( "default-value", null );
777 }
778 }
779 catch ( ExpressionEvaluationException e )
780 {
781 String msg = "Error evaluating the expression '" + expression + "' for configuration value '"
782 + configuration.getName() + "'";
783 throw new ComponentConfigurationException( configuration, msg, e );
784 }
785 }
786
787 if ( value == null && ( config == null || config.getChildCount() <= 0 ) )
788 {
789 invalidParameters.add( parameter );
790 }
791 }
792
793 if ( !invalidParameters.isEmpty() )
794 {
795 throw new PluginParameterException( mojoDescriptor, invalidParameters );
796 }
797 }
798
799 public void releaseMojo( Object mojo, MojoExecution mojoExecution )
800 {
801 if ( mojo != null )
802 {
803 try
804 {
805 container.release( mojo );
806 }
807 catch ( ComponentLifecycleException e )
808 {
809 String goalExecId = mojoExecution.getGoal();
810
811 if ( mojoExecution.getExecutionId() != null )
812 {
813 goalExecId += " {execution: " + mojoExecution.getExecutionId() + "}";
814 }
815
816 logger.debug( "Error releasing mojo for " + goalExecId, e );
817 }
818 }
819 }
820
821 public ExtensionRealmCache.CacheRecord setupExtensionsRealm( MavenProject project, Plugin plugin,
822 RepositorySystemSession session )
823 throws PluginManagerException
824 {
825 @SuppressWarnings( "unchecked" ) Map<String, ExtensionRealmCache.CacheRecord> pluginRealms =
826 (Map<String, ExtensionRealmCache.CacheRecord>) project.getContextValue( KEY_EXTENSIONS_REALMS );
827 if ( pluginRealms == null )
828 {
829 pluginRealms = new HashMap<>();
830 project.setContextValue( KEY_EXTENSIONS_REALMS, pluginRealms );
831 }
832
833 final String pluginKey = plugin.getId();
834
835 ExtensionRealmCache.CacheRecord extensionRecord = pluginRealms.get( pluginKey );
836 if ( extensionRecord != null )
837 {
838 return extensionRecord;
839 }
840
841 final List<RemoteRepository> repositories = project.getRemotePluginRepositories();
842
843
844 if ( plugin.getVersion() == null )
845 {
846 PluginVersionRequest versionRequest = new DefaultPluginVersionRequest( plugin, session, repositories );
847 try
848 {
849 plugin.setVersion( pluginVersionResolver.resolve( versionRequest ).getVersion() );
850 }
851 catch ( PluginVersionResolutionException e )
852 {
853 throw new PluginManagerException( plugin, e.getMessage(), e );
854 }
855 }
856
857
858
859
860 List<Artifact> artifacts;
861 PluginArtifactsCache.Key cacheKey = pluginArtifactsCache.createKey( plugin, null, repositories, session );
862 PluginArtifactsCache.CacheRecord recordArtifacts;
863 try
864 {
865 recordArtifacts = pluginArtifactsCache.get( cacheKey );
866 }
867 catch ( PluginResolutionException e )
868 {
869 throw new PluginManagerException( plugin, e.getMessage(), e );
870 }
871 if ( recordArtifacts != null )
872 {
873 artifacts = recordArtifacts.getArtifacts();
874 }
875 else
876 {
877 try
878 {
879 artifacts = resolveExtensionArtifacts( plugin, repositories, session );
880 recordArtifacts = pluginArtifactsCache.put( cacheKey, artifacts );
881 }
882 catch ( PluginResolutionException e )
883 {
884 pluginArtifactsCache.put( cacheKey, e );
885 pluginArtifactsCache.register( project, cacheKey, recordArtifacts );
886 throw new PluginManagerException( plugin, e.getMessage(), e );
887 }
888 }
889 pluginArtifactsCache.register( project, cacheKey, recordArtifacts );
890
891
892 final ExtensionRealmCache.Key extensionKey = extensionRealmCache.createKey( artifacts );
893 extensionRecord = extensionRealmCache.get( extensionKey );
894 if ( extensionRecord == null )
895 {
896 ClassRealm extensionRealm =
897 classRealmManager.createExtensionRealm( plugin, toAetherArtifacts( artifacts ) );
898
899
900
901 PluginDescriptor pluginDescriptor = null;
902 if ( plugin.isExtensions() && !artifacts.isEmpty() )
903 {
904
905
906 try
907 {
908 pluginDescriptor = extractPluginDescriptor( artifacts.get( 0 ), plugin );
909 }
910 catch ( PluginDescriptorParsingException | InvalidPluginDescriptorException e )
911 {
912
913 }
914 }
915
916 discoverPluginComponents( extensionRealm, plugin, pluginDescriptor );
917
918 ExtensionDescriptor extensionDescriptor = null;
919 Artifact extensionArtifact = artifacts.get( 0 );
920 try
921 {
922 extensionDescriptor = extensionDescriptorBuilder.build( extensionArtifact.getFile() );
923 }
924 catch ( IOException e )
925 {
926 String message = "Invalid extension descriptor for " + plugin.getId() + ": " + e.getMessage();
927 if ( logger.isDebugEnabled() )
928 {
929 logger.error( message, e );
930 }
931 else
932 {
933 logger.error( message );
934 }
935 }
936 extensionRecord = extensionRealmCache.put( extensionKey, extensionRealm, extensionDescriptor, artifacts );
937 }
938 extensionRealmCache.register( project, extensionKey, extensionRecord );
939 pluginRealms.put( pluginKey, extensionRecord );
940
941 return extensionRecord;
942 }
943
944 private List<Artifact> resolveExtensionArtifacts( Plugin extensionPlugin, List<RemoteRepository> repositories,
945 RepositorySystemSession session )
946 throws PluginResolutionException
947 {
948 DependencyNode root = pluginDependenciesResolver.resolve( extensionPlugin, null, null, repositories, session );
949 PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
950 root.accept( nlg );
951 return toMavenArtifacts( root, nlg );
952 }
953
954 }