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