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