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