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