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