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.lang.annotation.Annotation;
30 import java.lang.reflect.Field;
31 import java.lang.reflect.ParameterizedType;
32 import java.lang.reflect.Type;
33 import java.nio.file.Files;
34 import java.nio.file.Path;
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.HashMap;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Objects;
42 import java.util.jar.JarFile;
43 import java.util.stream.Collectors;
44 import java.util.zip.ZipEntry;
45
46 import org.apache.maven.RepositoryUtils;
47 import org.apache.maven.api.Dependency;
48 import org.apache.maven.api.Node;
49 import org.apache.maven.api.PathScope;
50 import org.apache.maven.api.PathType;
51 import org.apache.maven.api.Project;
52 import org.apache.maven.api.Session;
53 import org.apache.maven.api.plugin.descriptor.Resolution;
54 import org.apache.maven.api.services.DependencyResolver;
55 import org.apache.maven.api.services.DependencyResolverResult;
56 import org.apache.maven.api.services.PathScopeRegistry;
57 import org.apache.maven.api.xml.XmlNode;
58 import org.apache.maven.artifact.Artifact;
59 import org.apache.maven.classrealm.ClassRealmManager;
60 import org.apache.maven.di.Injector;
61 import org.apache.maven.di.Key;
62 import org.apache.maven.execution.MavenSession;
63 import org.apache.maven.execution.scope.internal.MojoExecutionScope;
64 import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
65 import org.apache.maven.internal.impl.DefaultLog;
66 import org.apache.maven.internal.impl.DefaultMojoExecution;
67 import org.apache.maven.internal.impl.InternalMavenSession;
68 import org.apache.maven.internal.xml.XmlPlexusConfiguration;
69 import org.apache.maven.model.Plugin;
70 import org.apache.maven.plugin.ContextEnabled;
71 import org.apache.maven.plugin.DebugConfigurationListener;
72 import org.apache.maven.plugin.ExtensionRealmCache;
73 import org.apache.maven.plugin.InvalidPluginDescriptorException;
74 import org.apache.maven.plugin.MavenPluginManager;
75 import org.apache.maven.plugin.MavenPluginPrerequisitesChecker;
76 import org.apache.maven.plugin.Mojo;
77 import org.apache.maven.plugin.MojoExecution;
78 import org.apache.maven.plugin.MojoNotFoundException;
79 import org.apache.maven.plugin.PluginArtifactsCache;
80 import org.apache.maven.plugin.PluginConfigurationException;
81 import org.apache.maven.plugin.PluginContainerException;
82 import org.apache.maven.plugin.PluginDescriptorCache;
83 import org.apache.maven.plugin.PluginDescriptorParsingException;
84 import org.apache.maven.plugin.PluginIncompatibleException;
85 import org.apache.maven.plugin.PluginManagerException;
86 import org.apache.maven.plugin.PluginParameterException;
87 import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
88 import org.apache.maven.plugin.PluginParameterExpressionEvaluatorV4;
89 import org.apache.maven.plugin.PluginRealmCache;
90 import org.apache.maven.plugin.PluginResolutionException;
91 import org.apache.maven.plugin.PluginValidationManager;
92 import org.apache.maven.plugin.descriptor.MojoDescriptor;
93 import org.apache.maven.plugin.descriptor.Parameter;
94 import org.apache.maven.plugin.descriptor.PluginDescriptor;
95 import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder;
96 import org.apache.maven.plugin.version.DefaultPluginVersionRequest;
97 import org.apache.maven.plugin.version.PluginVersionRequest;
98 import org.apache.maven.plugin.version.PluginVersionResolutionException;
99 import org.apache.maven.plugin.version.PluginVersionResolver;
100 import org.apache.maven.project.ExtensionDescriptor;
101 import org.apache.maven.project.ExtensionDescriptorBuilder;
102 import org.apache.maven.project.MavenProject;
103 import org.apache.maven.session.scope.internal.SessionScope;
104 import org.apache.maven.session.scope.internal.SessionScopeModule;
105 import org.codehaus.plexus.DefaultPlexusContainer;
106 import org.codehaus.plexus.PlexusContainer;
107 import org.codehaus.plexus.classworlds.realm.ClassRealm;
108 import org.codehaus.plexus.component.composition.CycleDetectedInComponentGraphException;
109 import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
110 import org.codehaus.plexus.component.configurator.ComponentConfigurator;
111 import org.codehaus.plexus.component.configurator.ConfigurationListener;
112 import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
113 import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
114 import org.codehaus.plexus.component.repository.ComponentDescriptor;
115 import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
116 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
117 import org.codehaus.plexus.configuration.DefaultPlexusConfiguration;
118 import org.codehaus.plexus.configuration.PlexusConfiguration;
119 import org.codehaus.plexus.configuration.PlexusConfigurationException;
120 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
121 import org.eclipse.aether.RepositorySystemSession;
122 import org.eclipse.aether.graph.DependencyFilter;
123 import org.eclipse.aether.repository.RemoteRepository;
124 import org.eclipse.aether.resolution.ArtifactResult;
125 import org.eclipse.aether.resolution.DependencyResult;
126 import org.eclipse.aether.util.filter.AndDependencyFilter;
127 import org.slf4j.Logger;
128 import org.slf4j.LoggerFactory;
129
130
131
132
133
134
135
136
137 @Named
138 @Singleton
139 public class DefaultMavenPluginManager implements MavenPluginManager {
140
141
142
143
144
145
146
147
148
149
150 public static final String KEY_EXTENSIONS_REALMS = DefaultMavenPluginManager.class.getName() + "/extensionsRealms";
151
152 private final Logger logger = LoggerFactory.getLogger(getClass());
153
154 private final PlexusContainer container;
155 private final ClassRealmManager classRealmManager;
156 private final PluginDescriptorCache pluginDescriptorCache;
157 private final PluginRealmCache pluginRealmCache;
158 private final PluginDependenciesResolver pluginDependenciesResolver;
159 private final ExtensionRealmCache extensionRealmCache;
160 private final PluginVersionResolver pluginVersionResolver;
161 private final PluginArtifactsCache pluginArtifactsCache;
162 private final MavenPluginValidator pluginValidator;
163 private final List<MavenPluginConfigurationValidator> configurationValidators;
164 private final PluginValidationManager pluginValidationManager;
165 private final List<MavenPluginPrerequisitesChecker> prerequisitesCheckers;
166 private final ExtensionDescriptorBuilder extensionDescriptorBuilder = new ExtensionDescriptorBuilder();
167 private final PluginDescriptorBuilder builder = new PluginDescriptorBuilder();
168
169 @Inject
170 @SuppressWarnings("checkstyle:ParameterNumber")
171 public DefaultMavenPluginManager(
172 PlexusContainer container,
173 ClassRealmManager classRealmManager,
174 PluginDescriptorCache pluginDescriptorCache,
175 PluginRealmCache pluginRealmCache,
176 PluginDependenciesResolver pluginDependenciesResolver,
177 ExtensionRealmCache extensionRealmCache,
178 PluginVersionResolver pluginVersionResolver,
179 PluginArtifactsCache pluginArtifactsCache,
180 MavenPluginValidator pluginValidator,
181 List<MavenPluginConfigurationValidator> configurationValidators,
182 PluginValidationManager pluginValidationManager,
183 List<MavenPluginPrerequisitesChecker> prerequisitesCheckers) {
184 this.container = container;
185 this.classRealmManager = classRealmManager;
186 this.pluginDescriptorCache = pluginDescriptorCache;
187 this.pluginRealmCache = pluginRealmCache;
188 this.pluginDependenciesResolver = pluginDependenciesResolver;
189 this.extensionRealmCache = extensionRealmCache;
190 this.pluginVersionResolver = pluginVersionResolver;
191 this.pluginArtifactsCache = pluginArtifactsCache;
192 this.pluginValidator = pluginValidator;
193 this.configurationValidators = configurationValidators;
194 this.pluginValidationManager = pluginValidationManager;
195 this.prerequisitesCheckers = prerequisitesCheckers;
196 }
197
198 public PluginDescriptor getPluginDescriptor(
199 Plugin plugin, List<RemoteRepository> repositories, RepositorySystemSession session)
200 throws PluginResolutionException, PluginDescriptorParsingException, InvalidPluginDescriptorException {
201 PluginDescriptorCache.Key cacheKey = pluginDescriptorCache.createKey(plugin, repositories, session);
202
203 PluginDescriptor pluginDescriptor = pluginDescriptorCache.get(cacheKey, () -> {
204 org.eclipse.aether.artifact.Artifact artifact =
205 pluginDependenciesResolver.resolve(plugin, repositories, session);
206
207 Artifact pluginArtifact = RepositoryUtils.toArtifact(artifact);
208
209 PluginDescriptor descriptor = extractPluginDescriptor(pluginArtifact, plugin);
210
211 boolean isBlankVersion = descriptor.getRequiredMavenVersion() == null
212 || descriptor.getRequiredMavenVersion().trim().isEmpty();
213
214 if (isBlankVersion) {
215
216 descriptor.setRequiredMavenVersion(artifact.getProperty("requiredMavenVersion", null));
217 }
218
219 return descriptor;
220 });
221
222 pluginDescriptor.setPlugin(plugin);
223
224 return pluginDescriptor;
225 }
226
227 private PluginDescriptor extractPluginDescriptor(Artifact pluginArtifact, Plugin plugin)
228 throws PluginDescriptorParsingException, InvalidPluginDescriptorException {
229 PluginDescriptor pluginDescriptor = null;
230
231 File pluginFile = pluginArtifact.getFile();
232
233 try {
234 if (pluginFile.isFile()) {
235 try (JarFile pluginJar = new JarFile(pluginFile, false)) {
236 ZipEntry pluginDescriptorEntry = pluginJar.getEntry(getPluginDescriptorLocation());
237
238 if (pluginDescriptorEntry != null) {
239 pluginDescriptor = parsePluginDescriptor(
240 () -> pluginJar.getInputStream(pluginDescriptorEntry),
241 plugin,
242 pluginFile.getAbsolutePath());
243 }
244 }
245 } else {
246 File pluginXml = new File(pluginFile, getPluginDescriptorLocation());
247
248 if (pluginXml.isFile()) {
249 pluginDescriptor = parsePluginDescriptor(
250 () -> Files.newInputStream(pluginXml.toPath()), plugin, pluginXml.getAbsolutePath());
251 }
252 }
253
254 if (pluginDescriptor == null) {
255 throw new IOException("No plugin descriptor found at " + getPluginDescriptorLocation());
256 }
257 } catch (IOException e) {
258 throw new PluginDescriptorParsingException(plugin, pluginFile.getAbsolutePath(), e);
259 }
260
261 List<String> errors = new ArrayList<>();
262 pluginValidator.validate(pluginArtifact, pluginDescriptor, errors);
263
264 if (!errors.isEmpty()) {
265 throw new InvalidPluginDescriptorException(
266 "Invalid plugin descriptor for " + plugin.getId() + " (" + pluginFile + ")", errors);
267 }
268
269 pluginDescriptor.setPluginArtifact(pluginArtifact);
270
271 return pluginDescriptor;
272 }
273
274 private String getPluginDescriptorLocation() {
275 return "META-INF/maven/plugin.xml";
276 }
277
278 private PluginDescriptor parsePluginDescriptor(
279 PluginDescriptorBuilder.StreamSupplier is, Plugin plugin, String descriptorLocation)
280 throws PluginDescriptorParsingException {
281 try {
282 return builder.build(is, descriptorLocation);
283 } catch (PlexusConfigurationException e) {
284 throw new PluginDescriptorParsingException(plugin, descriptorLocation, e);
285 }
286 }
287
288 public MojoDescriptor getMojoDescriptor(
289 Plugin plugin, String goal, List<RemoteRepository> repositories, RepositorySystemSession session)
290 throws MojoNotFoundException, PluginResolutionException, PluginDescriptorParsingException,
291 InvalidPluginDescriptorException {
292 PluginDescriptor pluginDescriptor = getPluginDescriptor(plugin, repositories, session);
293
294 MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo(goal);
295
296 if (mojoDescriptor == null) {
297 throw new MojoNotFoundException(goal, pluginDescriptor);
298 }
299
300 return mojoDescriptor;
301 }
302
303 @Override
304 public void checkPrerequisites(PluginDescriptor pluginDescriptor) throws PluginIncompatibleException {
305 List<IllegalStateException> prerequisiteExceptions = new ArrayList<>();
306 prerequisitesCheckers.forEach(c -> {
307 try {
308 c.accept(pluginDescriptor);
309 } catch (IllegalStateException e) {
310 prerequisiteExceptions.add(e);
311 }
312 });
313
314 if (!prerequisiteExceptions.isEmpty()) {
315 String messages = prerequisiteExceptions.stream()
316 .map(IllegalStateException::getMessage)
317 .collect(Collectors.joining(", "));
318 PluginIncompatibleException pie = new PluginIncompatibleException(
319 pluginDescriptor.getPlugin(),
320 "The plugin " + pluginDescriptor.getId() + " has unmet prerequisites: " + messages,
321 prerequisiteExceptions.get(0));
322
323 prerequisiteExceptions.stream().skip(1).forEach(pie::addSuppressed);
324 throw pie;
325 }
326 }
327
328 @Override
329 @Deprecated
330 public void checkRequiredMavenVersion(PluginDescriptor pluginDescriptor) throws PluginIncompatibleException {
331 checkPrerequisites(pluginDescriptor);
332 }
333
334 public void setupPluginRealm(
335 PluginDescriptor pluginDescriptor,
336 MavenSession session,
337 ClassLoader parent,
338 List<String> imports,
339 DependencyFilter filter)
340 throws PluginResolutionException, PluginContainerException {
341 Plugin plugin = pluginDescriptor.getPlugin();
342 MavenProject project = session.getCurrentProject();
343
344 if (plugin.isExtensions()) {
345 ExtensionRealmCache.CacheRecord extensionRecord;
346 try {
347 RepositorySystemSession repositorySession = session.getRepositorySession();
348 extensionRecord = setupExtensionsRealm(project, plugin, repositorySession);
349 } catch (PluginManagerException e) {
350
351
352 throw new IllegalStateException(e);
353 }
354
355 ClassRealm pluginRealm = extensionRecord.getRealm();
356 List<Artifact> pluginArtifacts = extensionRecord.getArtifacts();
357
358 for (ComponentDescriptor<?> componentDescriptor : pluginDescriptor.getComponents()) {
359 componentDescriptor.setRealm(pluginRealm);
360 }
361
362 pluginDescriptor.setClassRealm(pluginRealm);
363 pluginDescriptor.setArtifacts(pluginArtifacts);
364 } else {
365 boolean v4api = pluginDescriptor.getMojos().stream().anyMatch(MojoDescriptor::isV4Api);
366 Map<String, ClassLoader> foreignImports = calcImports(project, parent, imports, v4api);
367
368 PluginRealmCache.Key cacheKey = pluginRealmCache.createKey(
369 plugin,
370 parent,
371 foreignImports,
372 filter,
373 project.getRemotePluginRepositories(),
374 session.getRepositorySession());
375
376 PluginRealmCache.CacheRecord cacheRecord = pluginRealmCache.get(cacheKey, () -> {
377 createPluginRealm(pluginDescriptor, session, parent, foreignImports, filter);
378
379 return new PluginRealmCache.CacheRecord(
380 pluginDescriptor.getClassRealm(), pluginDescriptor.getArtifacts());
381 });
382
383 pluginDescriptor.setClassRealm(cacheRecord.getRealm());
384 pluginDescriptor.setArtifacts(new ArrayList<>(cacheRecord.getArtifacts()));
385 for (ComponentDescriptor<?> componentDescriptor : pluginDescriptor.getComponents()) {
386 componentDescriptor.setRealm(cacheRecord.getRealm());
387 }
388
389 pluginRealmCache.register(project, cacheKey, cacheRecord);
390 }
391 }
392
393 private void createPluginRealm(
394 PluginDescriptor pluginDescriptor,
395 MavenSession session,
396 ClassLoader parent,
397 Map<String, ClassLoader> foreignImports,
398 DependencyFilter filter)
399 throws PluginResolutionException, PluginContainerException {
400 Plugin plugin = Objects.requireNonNull(pluginDescriptor.getPlugin(), "pluginDescriptor.plugin cannot be null");
401
402 Artifact pluginArtifact = Objects.requireNonNull(
403 pluginDescriptor.getPluginArtifact(), "pluginDescriptor.pluginArtifact cannot be null");
404
405 MavenProject project = session.getCurrentProject();
406
407 final ClassRealm pluginRealm;
408 final List<Artifact> pluginArtifacts;
409
410 RepositorySystemSession repositorySession = session.getRepositorySession();
411 DependencyFilter dependencyFilter = project.getExtensionDependencyFilter();
412 dependencyFilter = AndDependencyFilter.newInstance(dependencyFilter, filter);
413
414 DependencyResult result = pluginDependenciesResolver.resolvePlugin(
415 plugin,
416 RepositoryUtils.toArtifact(pluginArtifact),
417 dependencyFilter,
418 project.getRemotePluginRepositories(),
419 repositorySession);
420
421 pluginArtifacts = toMavenArtifacts(result);
422
423 pluginRealm = classRealmManager.createPluginRealm(
424 plugin, parent, null, foreignImports, toAetherArtifacts(pluginArtifacts));
425
426 discoverPluginComponents(pluginRealm, plugin, pluginDescriptor);
427
428 pluginDescriptor.setDependencyNode(result.getRoot());
429 pluginDescriptor.setClassRealm(pluginRealm);
430 pluginDescriptor.setArtifacts(pluginArtifacts);
431 }
432
433 private void discoverPluginComponents(
434 final ClassRealm pluginRealm, Plugin plugin, PluginDescriptor pluginDescriptor)
435 throws PluginContainerException {
436 try {
437 if (pluginDescriptor != null) {
438 for (MojoDescriptor mojo : pluginDescriptor.getMojos()) {
439 if (!mojo.isV4Api()) {
440 mojo.setRealm(pluginRealm);
441 container.addComponentDescriptor(mojo);
442 }
443 }
444 }
445
446 ((DefaultPlexusContainer) container)
447 .discoverComponents(
448 pluginRealm,
449 new SessionScopeModule(container.lookup(SessionScope.class)),
450 new MojoExecutionScopeModule(container.lookup(MojoExecutionScope.class)),
451 new PluginConfigurationModule(plugin.getDelegate()));
452 } catch (ComponentLookupException | CycleDetectedInComponentGraphException e) {
453 throw new PluginContainerException(
454 plugin,
455 pluginRealm,
456 "Error in component graph of plugin " + plugin.getId() + ": " + e.getMessage(),
457 e);
458 }
459 }
460
461 private List<org.eclipse.aether.artifact.Artifact> toAetherArtifacts(final List<Artifact> pluginArtifacts) {
462 return new ArrayList<>(RepositoryUtils.toArtifacts(pluginArtifacts));
463 }
464
465 private List<Artifact> toMavenArtifacts(DependencyResult dependencyResult) {
466 List<Artifact> artifacts =
467 new ArrayList<>(dependencyResult.getArtifactResults().size());
468 dependencyResult.getArtifactResults().stream()
469 .filter(ArtifactResult::isResolved)
470 .forEach(a -> artifacts.add(RepositoryUtils.toArtifact(a.getArtifact())));
471 return Collections.unmodifiableList(artifacts);
472 }
473
474 private Map<String, ClassLoader> calcImports(
475 MavenProject project, ClassLoader parent, List<String> imports, boolean v4api) {
476 Map<String, ClassLoader> foreignImports = new HashMap<>();
477
478 ClassLoader projectRealm = project.getClassRealm();
479 if (projectRealm != null) {
480 foreignImports.put("", projectRealm);
481 } else {
482 foreignImports.put(
483 "", v4api ? classRealmManager.getMaven4ApiRealm() : classRealmManager.getMavenApiRealm());
484 }
485
486 if (parent != null && imports != null) {
487 for (String parentImport : imports) {
488 foreignImports.put(parentImport, parent);
489 }
490 }
491
492 return foreignImports;
493 }
494
495 public <T> T getConfiguredMojo(Class<T> mojoInterface, MavenSession session, MojoExecution mojoExecution)
496 throws PluginConfigurationException, PluginContainerException {
497 MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
498
499 PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor();
500
501 ClassRealm pluginRealm = pluginDescriptor.getClassRealm();
502
503 if (pluginRealm == null) {
504 try {
505 setupPluginRealm(pluginDescriptor, session, null, null, null);
506 } catch (PluginResolutionException e) {
507 String msg = "Cannot setup plugin realm [mojoDescriptor=" + mojoDescriptor.getId()
508 + ", pluginDescriptor=" + pluginDescriptor.getId() + "]";
509 throw new PluginConfigurationException(pluginDescriptor, msg, e);
510 }
511 pluginRealm = pluginDescriptor.getClassRealm();
512 }
513
514 if (logger.isDebugEnabled()) {
515 logger.debug("Loading mojo " + mojoDescriptor.getId() + " from plugin realm " + pluginRealm);
516 }
517
518
519
520
521 ClassRealm oldLookupRealm = container.setLookupRealm(pluginRealm);
522
523 ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
524 Thread.currentThread().setContextClassLoader(pluginRealm);
525
526 try {
527 if (mojoDescriptor.isV4Api()) {
528 return loadV4Mojo(mojoInterface, session, mojoExecution, mojoDescriptor, pluginDescriptor, pluginRealm);
529 } else {
530 return loadV3Mojo(mojoInterface, session, mojoExecution, mojoDescriptor, pluginDescriptor, pluginRealm);
531 }
532 } finally {
533 Thread.currentThread().setContextClassLoader(oldClassLoader);
534 container.setLookupRealm(oldLookupRealm);
535 }
536 }
537
538 private <T> T loadV4Mojo(
539 Class<T> mojoInterface,
540 MavenSession session,
541 MojoExecution mojoExecution,
542 MojoDescriptor mojoDescriptor,
543 PluginDescriptor pluginDescriptor,
544 ClassRealm pluginRealm)
545 throws PluginContainerException, PluginConfigurationException {
546 T mojo;
547
548 InternalMavenSession sessionV4 = InternalMavenSession.from(session.getSession());
549 Project project = sessionV4.getProject(session.getCurrentProject());
550
551 org.apache.maven.api.MojoExecution execution = new DefaultMojoExecution(sessionV4, mojoExecution);
552 org.apache.maven.api.plugin.Log log = new DefaultLog(
553 LoggerFactory.getLogger(mojoExecution.getMojoDescriptor().getFullGoalName()));
554 try {
555 Injector injector = Injector.create();
556 injector.discover(pluginRealm);
557
558
559 injector.bindInstance(Session.class, sessionV4);
560 injector.bindInstance(Project.class, project);
561 injector.bindInstance(org.apache.maven.api.MojoExecution.class, execution);
562 injector.bindInstance(org.apache.maven.api.plugin.Log.class, log);
563 mojo = mojoInterface.cast(injector.getInstance(
564 Key.of(mojoDescriptor.getImplementationClass(), mojoDescriptor.getRoleHint())));
565
566 } catch (Exception e) {
567 throw new PluginContainerException(mojoDescriptor, pluginRealm, "Unable to lookup Mojo", e);
568 }
569
570 XmlNode dom = mojoExecution.getConfiguration() != null
571 ? mojoExecution.getConfiguration().getDom()
572 : null;
573
574 PlexusConfiguration pomConfiguration;
575
576 if (dom == null) {
577 pomConfiguration = new DefaultPlexusConfiguration("configuration");
578 } else {
579 pomConfiguration = XmlPlexusConfiguration.toPlexusConfiguration(dom);
580 }
581
582 ExpressionEvaluator expressionEvaluator =
583 new PluginParameterExpressionEvaluatorV4(sessionV4, project, execution);
584
585 for (MavenPluginConfigurationValidator validator : configurationValidators) {
586 validator.validate(session, mojoDescriptor, mojo.getClass(), pomConfiguration, expressionEvaluator);
587 }
588
589 populateMojoExecutionFields(
590 mojo,
591 mojoExecution.getExecutionId(),
592 mojoDescriptor,
593 pluginRealm,
594 pomConfiguration,
595 expressionEvaluator);
596
597 for (Resolution resolution : mojoDescriptor.getMojoDescriptorV4().getResolutions()) {
598 Field field = null;
599 for (Class<?> clazz = mojo.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
600 try {
601 field = clazz.getDeclaredField(resolution.getField());
602 break;
603 } catch (NoSuchFieldException e) {
604
605 }
606 }
607 if (field == null) {
608 throw new PluginConfigurationException(
609 pluginDescriptor,
610 "Unable to find field '" + resolution.getField() + "' annotated with @Resolution");
611 }
612 field.setAccessible(true);
613 String pathScope = resolution.getPathScope();
614 Object result = null;
615 if (pathScope != null && !pathScope.isEmpty()) {
616
617 PathScope ps = sessionV4.getService(PathScopeRegistry.class).require(pathScope);
618 DependencyResolverResult res =
619 sessionV4.getService(DependencyResolver.class).resolve(sessionV4, project, ps);
620 if (field.getType() == DependencyResolverResult.class) {
621 result = res;
622 } else if (field.getType() == Node.class) {
623 result = res.getRoot();
624 } else if (field.getType() == List.class && field.getGenericType() instanceof ParameterizedType pt) {
625 Type t = pt.getActualTypeArguments()[0];
626 if (t == Node.class) {
627 result = res.getNodes();
628 } else if (t == Path.class) {
629 result = res.getPaths();
630 }
631 } else if (field.getType() == Map.class && field.getGenericType() instanceof ParameterizedType pt) {
632 Type k = pt.getActualTypeArguments()[0];
633 Type v = pt.getActualTypeArguments()[1];
634 if (k == PathType.class
635 && v instanceof ParameterizedType ptv
636 && ptv.getRawType() == List.class
637 && ptv.getActualTypeArguments()[0] == Path.class) {
638 result = res.getDispatchedPaths();
639 } else if (k == Dependency.class && v == Path.class) {
640 result = res.getDependencies();
641 }
642 }
643 } else {
644
645 DependencyResolverResult res = sessionV4
646 .getService(DependencyResolver.class)
647 .collect(sessionV4, project, PathScope.MAIN_RUNTIME);
648 if (field.getType() == DependencyResolverResult.class) {
649 result = res;
650 } else if (field.getType() == Node.class) {
651 result = res.getRoot();
652 }
653 }
654 if (result == null) {
655 throw new PluginConfigurationException(
656 pluginDescriptor,
657 "Unable to inject field '" + resolution.getField()
658 + "' annotated with @Dependencies. Unsupported type " + field.getGenericType());
659 }
660 try {
661 field.set(mojo, result);
662 } catch (IllegalAccessException e) {
663 throw new PluginConfigurationException(
664 pluginDescriptor,
665 "Unable to inject field '" + resolution.getField() + "' annotated with @Dependencies",
666 e);
667 }
668 }
669
670 return mojo;
671 }
672
673 private <T> T loadV3Mojo(
674 Class<T> mojoInterface,
675 MavenSession session,
676 MojoExecution mojoExecution,
677 MojoDescriptor mojoDescriptor,
678 PluginDescriptor pluginDescriptor,
679 ClassRealm pluginRealm)
680 throws PluginContainerException, PluginConfigurationException {
681 T mojo;
682
683 try {
684 mojo = container.lookup(mojoInterface, mojoDescriptor.getRoleHint());
685 } catch (ComponentLookupException e) {
686 Throwable cause = e.getCause();
687 while (cause != null && !(cause instanceof LinkageError) && !(cause instanceof ClassNotFoundException)) {
688 cause = cause.getCause();
689 }
690
691 if ((cause instanceof NoClassDefFoundError) || (cause instanceof ClassNotFoundException)) {
692 ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
693 PrintStream ps = new PrintStream(os);
694 ps.println("Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '"
695 + pluginDescriptor.getId() + "'. A required class is missing: "
696 + cause.getMessage());
697 pluginRealm.display(ps);
698
699 throw new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), cause);
700 } else if (cause instanceof LinkageError) {
701 ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
702 PrintStream ps = new PrintStream(os);
703 ps.println("Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '"
704 + pluginDescriptor.getId() + "' due to an API incompatibility: "
705 + e.getClass().getName() + ": " + cause.getMessage());
706 pluginRealm.display(ps);
707
708 throw new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), cause);
709 }
710
711 throw new PluginContainerException(
712 mojoDescriptor,
713 pluginRealm,
714 "Unable to load the mojo '" + mojoDescriptor.getGoal()
715 + "' (or one of its required components) from the plugin '"
716 + pluginDescriptor.getId() + "'",
717 e);
718 }
719
720 if (mojo instanceof ContextEnabled) {
721 MavenProject project = session.getCurrentProject();
722
723 Map<String, Object> pluginContext = session.getPluginContext(pluginDescriptor, project);
724
725 if (pluginContext != null) {
726 pluginContext.put("project", project);
727
728 pluginContext.put("pluginDescriptor", pluginDescriptor);
729
730 ((ContextEnabled) mojo).setPluginContext(pluginContext);
731 }
732 }
733
734 if (mojo instanceof Mojo) {
735 Logger mojoLogger = LoggerFactory.getLogger(mojoDescriptor.getImplementation());
736 ((Mojo) mojo).setLog(new MojoLogWrapper(mojoLogger));
737 }
738
739 if (mojo instanceof Contextualizable) {
740 pluginValidationManager.reportPluginMojoValidationIssue(
741 PluginValidationManager.IssueLocality.EXTERNAL,
742 session,
743 mojoDescriptor,
744 mojo.getClass(),
745 "Mojo implements `Contextualizable` interface from Plexus Container, which is EOL.");
746 }
747
748 XmlNode dom = mojoExecution.getConfiguration() != null
749 ? mojoExecution.getConfiguration().getDom()
750 : null;
751
752 PlexusConfiguration pomConfiguration;
753
754 if (dom == null) {
755 pomConfiguration = new DefaultPlexusConfiguration("configuration");
756 } else {
757 pomConfiguration = XmlPlexusConfiguration.toPlexusConfiguration(dom);
758 }
759
760 ExpressionEvaluator expressionEvaluator = new PluginParameterExpressionEvaluator(session, mojoExecution);
761
762 for (MavenPluginConfigurationValidator validator : configurationValidators) {
763 validator.validate(session, mojoDescriptor, mojo.getClass(), pomConfiguration, expressionEvaluator);
764 }
765
766 populateMojoExecutionFields(
767 mojo,
768 mojoExecution.getExecutionId(),
769 mojoDescriptor,
770 pluginRealm,
771 pomConfiguration,
772 expressionEvaluator);
773
774 return mojo;
775 }
776
777 private void populateMojoExecutionFields(
778 Object mojo,
779 String executionId,
780 MojoDescriptor mojoDescriptor,
781 ClassRealm pluginRealm,
782 PlexusConfiguration configuration,
783 ExpressionEvaluator expressionEvaluator)
784 throws PluginConfigurationException {
785 ComponentConfigurator configurator = null;
786
787 String configuratorId = mojoDescriptor.getComponentConfigurator();
788
789 if (configuratorId == null || configuratorId.isEmpty()) {
790 configuratorId = mojoDescriptor.isV4Api() ? "enhanced" : "basic";
791 }
792
793 try {
794
795
796 configurator = container.lookup(ComponentConfigurator.class, configuratorId);
797
798 ConfigurationListener listener = new DebugConfigurationListener(logger);
799
800 ValidatingConfigurationListener validator =
801 new ValidatingConfigurationListener(mojo, mojoDescriptor, listener);
802
803 if (logger.isDebugEnabled()) {
804 logger.debug("Configuring mojo execution '" + mojoDescriptor.getId() + ':' + executionId + "' with "
805 + configuratorId + " configurator -->");
806 }
807
808 configurator.configureComponent(mojo, configuration, expressionEvaluator, pluginRealm, validator);
809
810 logger.debug("-- end configuration --");
811
812 Collection<Parameter> missingParameters = validator.getMissingParameters();
813 if (!missingParameters.isEmpty()) {
814 if ("basic".equals(configuratorId)) {
815 throw new PluginParameterException(mojoDescriptor, new ArrayList<>(missingParameters));
816 } else {
817
818
819
820
821 validateParameters(mojoDescriptor, configuration, expressionEvaluator);
822 }
823 }
824
825 } catch (ComponentConfigurationException e) {
826 String message = "Unable to parse configuration of mojo " + mojoDescriptor.getId();
827 if (e.getFailedConfiguration() != null) {
828 message += " for parameter " + e.getFailedConfiguration().getName();
829 }
830 message += ": " + e.getMessage();
831
832 throw new PluginConfigurationException(mojoDescriptor.getPluginDescriptor(), message, e);
833 } catch (ComponentLookupException e) {
834 throw new PluginConfigurationException(
835 mojoDescriptor.getPluginDescriptor(),
836 "Unable to retrieve component configurator " + configuratorId + " for configuration of mojo "
837 + mojoDescriptor.getId(),
838 e);
839 } catch (NoClassDefFoundError e) {
840 ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
841 PrintStream ps = new PrintStream(os);
842 ps.println("A required class was missing during configuration of mojo " + mojoDescriptor.getId() + ": "
843 + e.getMessage());
844 pluginRealm.display(ps);
845
846 throw new PluginConfigurationException(mojoDescriptor.getPluginDescriptor(), os.toString(), e);
847 } catch (LinkageError e) {
848 ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
849 PrintStream ps = new PrintStream(os);
850 ps.println("An API incompatibility was encountered during configuration of mojo " + mojoDescriptor.getId()
851 + ": " + e.getClass().getName() + ": " + e.getMessage());
852 pluginRealm.display(ps);
853
854 throw new PluginConfigurationException(mojoDescriptor.getPluginDescriptor(), os.toString(), e);
855 } finally {
856 if (configurator != null) {
857 try {
858 container.release(configurator);
859 } catch (ComponentLifecycleException e) {
860 logger.debug("Failed to release mojo configurator - ignoring.");
861 }
862 }
863 }
864 }
865
866 private void validateParameters(
867 MojoDescriptor mojoDescriptor, PlexusConfiguration configuration, ExpressionEvaluator expressionEvaluator)
868 throws ComponentConfigurationException, PluginParameterException {
869 if (mojoDescriptor.getParameters() == null) {
870 return;
871 }
872
873 List<Parameter> invalidParameters = new ArrayList<>();
874
875 for (Parameter parameter : mojoDescriptor.getParameters()) {
876 if (!parameter.isRequired()) {
877 continue;
878 }
879
880 Object value = null;
881
882 PlexusConfiguration config = configuration.getChild(parameter.getName(), false);
883 if (config != null) {
884 String expression = config.getValue(null);
885
886 try {
887 value = expressionEvaluator.evaluate(expression);
888
889 if (value == null) {
890 value = config.getAttribute("default-value", null);
891 }
892 } catch (ExpressionEvaluationException e) {
893 String msg = "Error evaluating the expression '" + expression + "' for configuration value '"
894 + configuration.getName() + "'";
895 throw new ComponentConfigurationException(configuration, msg, e);
896 }
897 }
898
899 if (value == null && (config == null || config.getChildCount() <= 0)) {
900 invalidParameters.add(parameter);
901 }
902 }
903
904 if (!invalidParameters.isEmpty()) {
905 throw new PluginParameterException(mojoDescriptor, invalidParameters);
906 }
907 }
908
909 public void releaseMojo(Object mojo, MojoExecution mojoExecution) {
910 if (mojo != null) {
911 try {
912 container.release(mojo);
913 } catch (ComponentLifecycleException e) {
914 String goalExecId = mojoExecution.getGoal();
915
916 if (mojoExecution.getExecutionId() != null) {
917 logger.debug(
918 "Error releasing mojo for {} {execution: {}}",
919 goalExecId,
920 mojoExecution.getExecutionId(),
921 e);
922 } else {
923 logger.debug("Error releasing mojo for {}", goalExecId, e);
924 }
925 }
926 }
927 }
928
929 public ExtensionRealmCache.CacheRecord setupExtensionsRealm(
930 MavenProject project, Plugin plugin, RepositorySystemSession session) throws PluginManagerException {
931 @SuppressWarnings("unchecked")
932 Map<String, ExtensionRealmCache.CacheRecord> pluginRealms =
933 (Map<String, ExtensionRealmCache.CacheRecord>) project.getContextValue(KEY_EXTENSIONS_REALMS);
934 if (pluginRealms == null) {
935 pluginRealms = new HashMap<>();
936 project.setContextValue(KEY_EXTENSIONS_REALMS, pluginRealms);
937 }
938
939 final String pluginKey = plugin.getId();
940
941 ExtensionRealmCache.CacheRecord extensionRecord = pluginRealms.get(pluginKey);
942 if (extensionRecord != null) {
943 return extensionRecord;
944 }
945
946 final List<RemoteRepository> repositories = project.getRemotePluginRepositories();
947
948
949 if (plugin.getVersion() == null) {
950 PluginVersionRequest versionRequest = new DefaultPluginVersionRequest(plugin, session, repositories);
951 try {
952 plugin.setVersion(pluginVersionResolver.resolve(versionRequest).getVersion());
953 } catch (PluginVersionResolutionException e) {
954 throw new PluginManagerException(plugin, e.getMessage(), e);
955 }
956 }
957
958
959
960
961 List<Artifact> artifacts;
962 PluginArtifactsCache.Key cacheKey = pluginArtifactsCache.createKey(plugin, null, repositories, session);
963 PluginArtifactsCache.CacheRecord recordArtifacts;
964 try {
965 recordArtifacts = pluginArtifactsCache.get(cacheKey);
966 } catch (PluginResolutionException e) {
967 throw new PluginManagerException(plugin, e.getMessage(), e);
968 }
969 if (recordArtifacts != null) {
970 artifacts = recordArtifacts.getArtifacts();
971 } else {
972 try {
973 artifacts = resolveExtensionArtifacts(plugin, repositories, session);
974 recordArtifacts = pluginArtifactsCache.put(cacheKey, artifacts);
975 } catch (PluginResolutionException e) {
976 pluginArtifactsCache.put(cacheKey, e);
977 pluginArtifactsCache.register(project, cacheKey, recordArtifacts);
978 throw new PluginManagerException(plugin, e.getMessage(), e);
979 }
980 }
981 pluginArtifactsCache.register(project, cacheKey, recordArtifacts);
982
983
984 final ExtensionRealmCache.Key extensionKey = extensionRealmCache.createKey(artifacts);
985 extensionRecord = extensionRealmCache.get(extensionKey);
986 if (extensionRecord == null) {
987 ClassRealm extensionRealm = classRealmManager.createExtensionRealm(plugin, toAetherArtifacts(artifacts));
988
989
990
991 PluginDescriptor pluginDescriptor = null;
992 if (plugin.isExtensions() && !artifacts.isEmpty()) {
993
994
995 try {
996 pluginDescriptor = extractPluginDescriptor(artifacts.get(0), plugin);
997 } catch (PluginDescriptorParsingException | InvalidPluginDescriptorException e) {
998
999 }
1000 }
1001
1002 discoverPluginComponents(extensionRealm, plugin, pluginDescriptor);
1003
1004 ExtensionDescriptor extensionDescriptor = null;
1005 Artifact extensionArtifact = artifacts.get(0);
1006 try {
1007 extensionDescriptor = extensionDescriptorBuilder.build(extensionArtifact.getFile());
1008 } catch (IOException e) {
1009 String message = "Invalid extension descriptor for " + plugin.getId() + ": " + e.getMessage();
1010 if (logger.isDebugEnabled()) {
1011 logger.error(message, e);
1012 } else {
1013 logger.error(message);
1014 }
1015 }
1016 extensionRecord = extensionRealmCache.put(extensionKey, extensionRealm, extensionDescriptor, artifacts);
1017 }
1018 extensionRealmCache.register(project, extensionKey, extensionRecord);
1019 pluginRealms.put(pluginKey, extensionRecord);
1020
1021 return extensionRecord;
1022 }
1023
1024 private List<Artifact> resolveExtensionArtifacts(
1025 Plugin extensionPlugin, List<RemoteRepository> repositories, RepositorySystemSession session)
1026 throws PluginResolutionException {
1027 DependencyResult root =
1028 pluginDependenciesResolver.resolvePlugin(extensionPlugin, null, null, repositories, session);
1029 return toMavenArtifacts(root);
1030 }
1031
1032 static class NamedImpl implements Named {
1033 private final String value;
1034
1035 NamedImpl(String value) {
1036 this.value = value;
1037 }
1038
1039 public String value() {
1040 return this.value;
1041 }
1042
1043 public int hashCode() {
1044 return 127 * "value".hashCode() ^ this.value.hashCode();
1045 }
1046
1047 public boolean equals(Object o) {
1048 return o instanceof Named && this.value.equals(((Named) o).value());
1049 }
1050
1051 public Class<? extends Annotation> annotationType() {
1052 return Named.class;
1053 }
1054 }
1055 }