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