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