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.util.ArrayList;
33 import java.util.Collection;
34 import java.util.Collections;
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.api.xml.XmlNode;
45 import org.apache.maven.artifact.Artifact;
46 import org.apache.maven.classrealm.ClassRealmManager;
47 import org.apache.maven.execution.MavenSession;
48 import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
49 import org.apache.maven.internal.impl.DefaultSession;
50 import org.apache.maven.internal.xml.XmlPlexusConfiguration;
51 import org.apache.maven.model.Plugin;
52 import org.apache.maven.plugin.ContextEnabled;
53 import org.apache.maven.plugin.DebugConfigurationListener;
54 import org.apache.maven.plugin.ExtensionRealmCache;
55 import org.apache.maven.plugin.InvalidPluginDescriptorException;
56 import org.apache.maven.plugin.MavenPluginManager;
57 import org.apache.maven.plugin.MavenPluginPrerequisitesChecker;
58 import org.apache.maven.plugin.Mojo;
59 import org.apache.maven.plugin.MojoExecution;
60 import org.apache.maven.plugin.MojoNotFoundException;
61 import org.apache.maven.plugin.PluginArtifactsCache;
62 import org.apache.maven.plugin.PluginConfigurationException;
63 import org.apache.maven.plugin.PluginContainerException;
64 import org.apache.maven.plugin.PluginDescriptorCache;
65 import org.apache.maven.plugin.PluginDescriptorParsingException;
66 import org.apache.maven.plugin.PluginIncompatibleException;
67 import org.apache.maven.plugin.PluginManagerException;
68 import org.apache.maven.plugin.PluginParameterException;
69 import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
70 import org.apache.maven.plugin.PluginParameterExpressionEvaluatorV4;
71 import org.apache.maven.plugin.PluginRealmCache;
72 import org.apache.maven.plugin.PluginResolutionException;
73 import org.apache.maven.plugin.PluginValidationManager;
74 import org.apache.maven.plugin.descriptor.MojoDescriptor;
75 import org.apache.maven.plugin.descriptor.Parameter;
76 import org.apache.maven.plugin.descriptor.PluginDescriptor;
77 import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder;
78 import org.apache.maven.plugin.version.DefaultPluginVersionRequest;
79 import org.apache.maven.plugin.version.PluginVersionRequest;
80 import org.apache.maven.plugin.version.PluginVersionResolutionException;
81 import org.apache.maven.plugin.version.PluginVersionResolver;
82 import org.apache.maven.project.ExtensionDescriptor;
83 import org.apache.maven.project.ExtensionDescriptorBuilder;
84 import org.apache.maven.project.MavenProject;
85 import org.apache.maven.rtinfo.RuntimeInformation;
86 import org.apache.maven.session.scope.internal.SessionScopeModule;
87 import org.codehaus.plexus.DefaultPlexusContainer;
88 import org.codehaus.plexus.PlexusContainer;
89 import org.codehaus.plexus.classworlds.realm.ClassRealm;
90 import org.codehaus.plexus.component.composition.CycleDetectedInComponentGraphException;
91 import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
92 import org.codehaus.plexus.component.configurator.ComponentConfigurator;
93 import org.codehaus.plexus.component.configurator.ConfigurationListener;
94 import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
95 import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
96 import org.codehaus.plexus.component.repository.ComponentDescriptor;
97 import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
98 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
99 import org.codehaus.plexus.configuration.DefaultPlexusConfiguration;
100 import org.codehaus.plexus.configuration.PlexusConfiguration;
101 import org.codehaus.plexus.configuration.PlexusConfigurationException;
102 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
103 import org.codehaus.plexus.util.StringUtils;
104 import org.eclipse.aether.RepositorySystemSession;
105 import org.eclipse.aether.graph.DependencyFilter;
106 import org.eclipse.aether.graph.DependencyNode;
107 import org.eclipse.aether.repository.RemoteRepository;
108 import org.eclipse.aether.util.filter.AndDependencyFilter;
109 import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator;
110 import org.slf4j.Logger;
111 import org.slf4j.LoggerFactory;
112
113
114
115
116
117
118
119
120
121 @Named
122 @Singleton
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 private final Logger logger = LoggerFactory.getLogger(getClass());
137
138 private PlexusContainer container;
139 private ClassRealmManager classRealmManager;
140 private PluginDescriptorCache pluginDescriptorCache;
141 private PluginRealmCache pluginRealmCache;
142 private PluginDependenciesResolver pluginDependenciesResolver;
143 private RuntimeInformation runtimeInformation;
144 private ExtensionRealmCache extensionRealmCache;
145 private PluginVersionResolver pluginVersionResolver;
146 private PluginArtifactsCache pluginArtifactsCache;
147 private MavenPluginValidator pluginValidator;
148 private List<MavenPluginConfigurationValidator> configurationValidators;
149 private PluginValidationManager pluginValidationManager;
150 private List<MavenPluginPrerequisitesChecker> prerequisitesCheckers;
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 PluginValidationManager pluginValidationManager,
169 List<MavenPluginPrerequisitesChecker> prerequisitesCheckers) {
170 this.container = container;
171 this.classRealmManager = classRealmManager;
172 this.pluginDescriptorCache = pluginDescriptorCache;
173 this.pluginRealmCache = pluginRealmCache;
174 this.pluginDependenciesResolver = pluginDependenciesResolver;
175 this.runtimeInformation = runtimeInformation;
176 this.extensionRealmCache = extensionRealmCache;
177 this.pluginVersionResolver = pluginVersionResolver;
178 this.pluginArtifactsCache = pluginArtifactsCache;
179 this.pluginValidator = pluginValidator;
180 this.configurationValidators = configurationValidators;
181 this.pluginValidationManager = pluginValidationManager;
182 this.prerequisitesCheckers = prerequisitesCheckers;
183 }
184
185 public PluginDescriptor getPluginDescriptor(
186 Plugin plugin, List<RemoteRepository> repositories, RepositorySystemSession session)
187 throws PluginResolutionException, PluginDescriptorParsingException, InvalidPluginDescriptorException {
188 PluginDescriptorCache.Key cacheKey = pluginDescriptorCache.createKey(plugin, repositories, session);
189
190 PluginDescriptor pluginDescriptor = pluginDescriptorCache.get(cacheKey, () -> {
191 org.eclipse.aether.artifact.Artifact artifact =
192 pluginDependenciesResolver.resolve(plugin, repositories, session);
193
194 Artifact pluginArtifact = RepositoryUtils.toArtifact(artifact);
195
196 PluginDescriptor descriptor = extractPluginDescriptor(pluginArtifact, plugin);
197
198 if (StringUtils.isBlank(descriptor.getRequiredMavenVersion())) {
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 = new BufferedInputStream(new FileInputStream(pluginXml))) {
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, new SessionScopeModule(container), new MojoExecutionScopeModule(container));
431 } catch (ComponentLookupException | CycleDetectedInComponentGraphException e) {
432 throw new PluginContainerException(
433 plugin,
434 pluginRealm,
435 "Error in component graph of plugin " + plugin.getId() + ": " + e.getMessage(),
436 e);
437 }
438 }
439
440 private List<org.eclipse.aether.artifact.Artifact> toAetherArtifacts(final List<Artifact> pluginArtifacts) {
441 return new ArrayList<>(RepositoryUtils.toArtifacts(pluginArtifacts));
442 }
443
444 private List<Artifact> toMavenArtifacts(DependencyNode root, PreorderNodeListGenerator nlg) {
445 List<Artifact> artifacts = new ArrayList<>(nlg.getNodes().size());
446 RepositoryUtils.toArtifacts(artifacts, Collections.singleton(root), Collections.emptyList(), null);
447 artifacts.removeIf(artifact -> artifact.getFile() == null);
448 return Collections.unmodifiableList(artifacts);
449 }
450
451 private Map<String, ClassLoader> calcImports(MavenProject project, ClassLoader parent, List<String> imports) {
452 Map<String, ClassLoader> foreignImports = new HashMap<>();
453
454 ClassLoader projectRealm = project.getClassRealm();
455 if (projectRealm != null) {
456 foreignImports.put("", projectRealm);
457 } else {
458 foreignImports.put("", classRealmManager.getMavenApiRealm());
459 }
460
461 if (parent != null && imports != null) {
462 for (String parentImport : imports) {
463 foreignImports.put(parentImport, parent);
464 }
465 }
466
467 return foreignImports;
468 }
469
470 public <T> T getConfiguredMojo(Class<T> mojoInterface, MavenSession session, MojoExecution mojoExecution)
471 throws PluginConfigurationException, PluginContainerException {
472 MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
473
474 PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor();
475
476 ClassRealm pluginRealm = pluginDescriptor.getClassRealm();
477
478 if (pluginRealm == null) {
479 try {
480 setupPluginRealm(pluginDescriptor, session, null, null, null);
481 } catch (PluginResolutionException e) {
482 String msg = "Cannot setup plugin realm [mojoDescriptor=" + mojoDescriptor.getId()
483 + ", pluginDescriptor=" + pluginDescriptor.getId() + "]";
484 throw new PluginConfigurationException(pluginDescriptor, msg, e);
485 }
486 pluginRealm = pluginDescriptor.getClassRealm();
487 }
488
489 if (logger.isDebugEnabled()) {
490 logger.debug("Loading mojo " + mojoDescriptor.getId() + " from plugin realm " + pluginRealm);
491 }
492
493
494
495
496 ClassRealm oldLookupRealm = container.setLookupRealm(pluginRealm);
497
498 ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
499 Thread.currentThread().setContextClassLoader(pluginRealm);
500
501 try {
502 T mojo;
503
504 try {
505 mojo = container.lookup(mojoInterface, mojoDescriptor.getRoleHint());
506 } catch (ComponentLookupException e) {
507 Throwable cause = e.getCause();
508 while (cause != null
509 && !(cause instanceof LinkageError)
510 && !(cause instanceof ClassNotFoundException)) {
511 cause = cause.getCause();
512 }
513
514 if ((cause instanceof NoClassDefFoundError) || (cause instanceof ClassNotFoundException)) {
515 ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
516 PrintStream ps = new PrintStream(os);
517 ps.println("Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '"
518 + pluginDescriptor.getId() + "'. A required class is missing: "
519 + cause.getMessage());
520 pluginRealm.display(ps);
521
522 throw new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), cause);
523 } else if (cause instanceof LinkageError) {
524 ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
525 PrintStream ps = new PrintStream(os);
526 ps.println("Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '"
527 + pluginDescriptor.getId() + "' due to an API incompatibility: "
528 + e.getClass().getName() + ": " + cause.getMessage());
529 pluginRealm.display(ps);
530
531 throw new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), cause);
532 }
533
534 throw new PluginContainerException(
535 mojoDescriptor,
536 pluginRealm,
537 "Unable to load the mojo '" + mojoDescriptor.getGoal()
538 + "' (or one of its required components) from the plugin '"
539 + pluginDescriptor.getId() + "'",
540 e);
541 }
542
543 if (mojo instanceof ContextEnabled) {
544 MavenProject project = session.getCurrentProject();
545
546 Map<String, Object> pluginContext = session.getPluginContext(pluginDescriptor, project);
547
548 if (pluginContext != null) {
549 pluginContext.put("project", project);
550
551 pluginContext.put("pluginDescriptor", pluginDescriptor);
552
553 ((ContextEnabled) mojo).setPluginContext(pluginContext);
554 }
555 }
556
557 if (mojo instanceof Mojo) {
558 Logger mojoLogger = LoggerFactory.getLogger(mojoDescriptor.getImplementation());
559 ((Mojo) mojo).setLog(new MojoLogWrapper(mojoLogger));
560 }
561
562 if (mojo instanceof Contextualizable) {
563 pluginValidationManager.reportPluginMojoValidationIssue(
564 PluginValidationManager.IssueLocality.EXTERNAL,
565 session,
566 mojoDescriptor,
567 mojo.getClass(),
568 "Mojo implements `Contextualizable` interface from Plexus Container, which is EOL.");
569 }
570
571 XmlNode dom = mojoExecution.getConfiguration() != null
572 ? mojoExecution.getConfiguration().getDom()
573 : null;
574
575 PlexusConfiguration pomConfiguration;
576
577 if (dom == null) {
578 pomConfiguration = new DefaultPlexusConfiguration("configuration");
579 } else {
580 pomConfiguration = XmlPlexusConfiguration.toPlexusConfiguration(dom);
581 }
582
583 ExpressionEvaluator expressionEvaluator;
584 if (mojoDescriptor.isV4Api()) {
585 expressionEvaluator = new PluginParameterExpressionEvaluatorV4(
586 session.getSession(),
587 ((DefaultSession) session.getSession()).getProject(session.getCurrentProject()),
588 mojoExecution);
589 } else {
590 expressionEvaluator = new PluginParameterExpressionEvaluator(session, mojoExecution);
591 }
592
593 for (MavenPluginConfigurationValidator validator : configurationValidators) {
594 validator.validate(session, mojoDescriptor, mojo.getClass(), pomConfiguration, expressionEvaluator);
595 }
596
597 populateMojoExecutionFields(
598 mojo,
599 mojoExecution.getExecutionId(),
600 mojoDescriptor,
601 pluginRealm,
602 pomConfiguration,
603 expressionEvaluator);
604
605 return mojo;
606 } finally {
607 Thread.currentThread().setContextClassLoader(oldClassLoader);
608 container.setLookupRealm(oldLookupRealm);
609 }
610 }
611
612 private void populateMojoExecutionFields(
613 Object mojo,
614 String executionId,
615 MojoDescriptor mojoDescriptor,
616 ClassRealm pluginRealm,
617 PlexusConfiguration configuration,
618 ExpressionEvaluator expressionEvaluator)
619 throws PluginConfigurationException {
620 ComponentConfigurator configurator = null;
621
622 String configuratorId = mojoDescriptor.getComponentConfigurator();
623
624 if (configuratorId == null || configuratorId.isEmpty()) {
625 configuratorId = mojoDescriptor.isV4Api() ? "enhanced" : "basic";
626 }
627
628 try {
629
630
631 configurator = container.lookup(ComponentConfigurator.class, configuratorId);
632
633 ConfigurationListener listener = new DebugConfigurationListener(logger);
634
635 ValidatingConfigurationListener validator =
636 new ValidatingConfigurationListener(mojo, mojoDescriptor, listener);
637
638 logger.debug("Configuring mojo execution '" + mojoDescriptor.getId() + ':' + executionId + "' with "
639 + configuratorId + " configurator -->");
640
641 configurator.configureComponent(mojo, configuration, expressionEvaluator, pluginRealm, validator);
642
643 logger.debug("-- end configuration --");
644
645 Collection<Parameter> missingParameters = validator.getMissingParameters();
646 if (!missingParameters.isEmpty()) {
647 if ("basic".equals(configuratorId)) {
648 throw new PluginParameterException(mojoDescriptor, new ArrayList<>(missingParameters));
649 } else {
650
651
652
653
654 validateParameters(mojoDescriptor, configuration, expressionEvaluator);
655 }
656 }
657 } catch (ComponentConfigurationException e) {
658 String message = "Unable to parse configuration of mojo " + mojoDescriptor.getId();
659 if (e.getFailedConfiguration() != null) {
660 message += " for parameter " + e.getFailedConfiguration().getName();
661 }
662 message += ": " + e.getMessage();
663
664 throw new PluginConfigurationException(mojoDescriptor.getPluginDescriptor(), message, e);
665 } catch (ComponentLookupException e) {
666 throw new PluginConfigurationException(
667 mojoDescriptor.getPluginDescriptor(),
668 "Unable to retrieve component configurator " + configuratorId + " for configuration of mojo "
669 + mojoDescriptor.getId(),
670 e);
671 } catch (NoClassDefFoundError e) {
672 ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
673 PrintStream ps = new PrintStream(os);
674 ps.println("A required class was missing during configuration of mojo " + mojoDescriptor.getId() + ": "
675 + e.getMessage());
676 pluginRealm.display(ps);
677
678 throw new PluginConfigurationException(mojoDescriptor.getPluginDescriptor(), os.toString(), e);
679 } catch (LinkageError e) {
680 ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
681 PrintStream ps = new PrintStream(os);
682 ps.println("An API incompatibility was encountered during configuration of mojo " + mojoDescriptor.getId()
683 + ": " + e.getClass().getName() + ": " + e.getMessage());
684 pluginRealm.display(ps);
685
686 throw new PluginConfigurationException(mojoDescriptor.getPluginDescriptor(), os.toString(), e);
687 } finally {
688 if (configurator != null) {
689 try {
690 container.release(configurator);
691 } catch (ComponentLifecycleException e) {
692 logger.debug("Failed to release mojo configurator - ignoring.");
693 }
694 }
695 }
696 }
697
698 private void validateParameters(
699 MojoDescriptor mojoDescriptor, PlexusConfiguration configuration, ExpressionEvaluator expressionEvaluator)
700 throws ComponentConfigurationException, PluginParameterException {
701 if (mojoDescriptor.getParameters() == null) {
702 return;
703 }
704
705 List<Parameter> invalidParameters = new ArrayList<>();
706
707 for (Parameter parameter : mojoDescriptor.getParameters()) {
708 if (!parameter.isRequired()) {
709 continue;
710 }
711
712 Object value = null;
713
714 PlexusConfiguration config = configuration.getChild(parameter.getName(), false);
715 if (config != null) {
716 String expression = config.getValue(null);
717
718 try {
719 value = expressionEvaluator.evaluate(expression);
720
721 if (value == null) {
722 value = config.getAttribute("default-value", null);
723 }
724 } catch (ExpressionEvaluationException e) {
725 String msg = "Error evaluating the expression '" + expression + "' for configuration value '"
726 + configuration.getName() + "'";
727 throw new ComponentConfigurationException(configuration, msg, e);
728 }
729 }
730
731 if (value == null && (config == null || config.getChildCount() <= 0)) {
732 invalidParameters.add(parameter);
733 }
734 }
735
736 if (!invalidParameters.isEmpty()) {
737 throw new PluginParameterException(mojoDescriptor, invalidParameters);
738 }
739 }
740
741 public void releaseMojo(Object mojo, MojoExecution mojoExecution) {
742 if (mojo != null) {
743 try {
744 container.release(mojo);
745 } catch (ComponentLifecycleException e) {
746 String goalExecId = mojoExecution.getGoal();
747
748 if (mojoExecution.getExecutionId() != null) {
749 goalExecId += " {execution: " + mojoExecution.getExecutionId() + "}";
750 }
751
752 logger.debug("Error releasing mojo for " + goalExecId, e);
753 }
754 }
755 }
756
757 public ExtensionRealmCache.CacheRecord setupExtensionsRealm(
758 MavenProject project, Plugin plugin, RepositorySystemSession session) throws PluginManagerException {
759 @SuppressWarnings("unchecked")
760 Map<String, ExtensionRealmCache.CacheRecord> pluginRealms =
761 (Map<String, ExtensionRealmCache.CacheRecord>) project.getContextValue(KEY_EXTENSIONS_REALMS);
762 if (pluginRealms == null) {
763 pluginRealms = new HashMap<>();
764 project.setContextValue(KEY_EXTENSIONS_REALMS, pluginRealms);
765 }
766
767 final String pluginKey = plugin.getId();
768
769 ExtensionRealmCache.CacheRecord extensionRecord = pluginRealms.get(pluginKey);
770 if (extensionRecord != null) {
771 return extensionRecord;
772 }
773
774 final List<RemoteRepository> repositories = project.getRemotePluginRepositories();
775
776
777 if (plugin.getVersion() == null) {
778 PluginVersionRequest versionRequest = new DefaultPluginVersionRequest(plugin, session, repositories);
779 try {
780 plugin.setVersion(pluginVersionResolver.resolve(versionRequest).getVersion());
781 } catch (PluginVersionResolutionException e) {
782 throw new PluginManagerException(plugin, e.getMessage(), e);
783 }
784 }
785
786
787
788
789 List<Artifact> artifacts;
790 PluginArtifactsCache.Key cacheKey = pluginArtifactsCache.createKey(plugin, null, repositories, session);
791 PluginArtifactsCache.CacheRecord recordArtifacts;
792 try {
793 recordArtifacts = pluginArtifactsCache.get(cacheKey);
794 } catch (PluginResolutionException e) {
795 throw new PluginManagerException(plugin, e.getMessage(), e);
796 }
797 if (recordArtifacts != null) {
798 artifacts = recordArtifacts.getArtifacts();
799 } else {
800 try {
801 artifacts = resolveExtensionArtifacts(plugin, repositories, session);
802 recordArtifacts = pluginArtifactsCache.put(cacheKey, artifacts);
803 } catch (PluginResolutionException e) {
804 pluginArtifactsCache.put(cacheKey, e);
805 pluginArtifactsCache.register(project, cacheKey, recordArtifacts);
806 throw new PluginManagerException(plugin, e.getMessage(), e);
807 }
808 }
809 pluginArtifactsCache.register(project, cacheKey, recordArtifacts);
810
811
812 final ExtensionRealmCache.Key extensionKey = extensionRealmCache.createKey(artifacts);
813 extensionRecord = extensionRealmCache.get(extensionKey);
814 if (extensionRecord == null) {
815 ClassRealm extensionRealm = classRealmManager.createExtensionRealm(plugin, toAetherArtifacts(artifacts));
816
817
818
819 PluginDescriptor pluginDescriptor = null;
820 if (plugin.isExtensions() && !artifacts.isEmpty()) {
821
822
823 try {
824 pluginDescriptor = extractPluginDescriptor(artifacts.get(0), plugin);
825 } catch (PluginDescriptorParsingException | InvalidPluginDescriptorException e) {
826
827 }
828 }
829
830 discoverPluginComponents(extensionRealm, plugin, pluginDescriptor);
831
832 ExtensionDescriptor extensionDescriptor = null;
833 Artifact extensionArtifact = artifacts.get(0);
834 try {
835 extensionDescriptor = extensionDescriptorBuilder.build(extensionArtifact.getFile());
836 } catch (IOException e) {
837 String message = "Invalid extension descriptor for " + plugin.getId() + ": " + e.getMessage();
838 if (logger.isDebugEnabled()) {
839 logger.error(message, e);
840 } else {
841 logger.error(message);
842 }
843 }
844 extensionRecord = extensionRealmCache.put(extensionKey, extensionRealm, extensionDescriptor, artifacts);
845 }
846 extensionRealmCache.register(project, extensionKey, extensionRecord);
847 pluginRealms.put(pluginKey, extensionRecord);
848
849 return extensionRecord;
850 }
851
852 private List<Artifact> resolveExtensionArtifacts(
853 Plugin extensionPlugin, List<RemoteRepository> repositories, RepositorySystemSession session)
854 throws PluginResolutionException {
855 DependencyNode root = pluginDependenciesResolver.resolve(extensionPlugin, null, null, repositories, session);
856 PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
857 root.accept(nlg);
858 return toMavenArtifacts(root, nlg);
859 }
860 }