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.reflect.Field;
30 import java.lang.reflect.ParameterizedType;
31 import java.lang.reflect.Type;
32 import java.nio.file.Files;
33 import java.nio.file.Path;
34 import java.util.ArrayList;
35 import java.util.Collection;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Objects;
40 import java.util.jar.JarFile;
41 import java.util.stream.Collectors;
42 import java.util.zip.ZipEntry;
43
44 import org.apache.maven.RepositoryUtils;
45 import org.apache.maven.api.Dependency;
46 import org.apache.maven.api.Node;
47 import org.apache.maven.api.PathScope;
48 import org.apache.maven.api.PathType;
49 import org.apache.maven.api.Project;
50 import org.apache.maven.api.Session;
51 import org.apache.maven.api.plugin.descriptor.Resolution;
52 import org.apache.maven.api.services.DependencyResolver;
53 import org.apache.maven.api.services.DependencyResolverResult;
54 import org.apache.maven.api.services.PathScopeRegistry;
55 import org.apache.maven.api.xml.XmlNode;
56 import org.apache.maven.artifact.Artifact;
57 import org.apache.maven.classrealm.ClassRealmManager;
58 import org.apache.maven.di.Injector;
59 import org.apache.maven.di.Key;
60 import org.apache.maven.execution.MavenSession;
61 import org.apache.maven.execution.scope.internal.MojoExecutionScope;
62 import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
63 import org.apache.maven.internal.impl.DefaultLog;
64 import org.apache.maven.internal.impl.DefaultMojoExecution;
65 import org.apache.maven.internal.impl.InternalMavenSession;
66 import org.apache.maven.internal.impl.SisuDiBridgeModule;
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 @Override
197 public PluginDescriptor getPluginDescriptor(
198 Plugin plugin, List<RemoteRepository> repositories, RepositorySystemSession session)
199 throws PluginResolutionException, PluginDescriptorParsingException, InvalidPluginDescriptorException {
200 PluginDescriptorCache.Key cacheKey = pluginDescriptorCache.createKey(plugin, repositories, session);
201
202 PluginDescriptor pluginDescriptor = pluginDescriptorCache.get(cacheKey, () -> {
203 org.eclipse.aether.artifact.Artifact artifact =
204 pluginDependenciesResolver.resolve(plugin, repositories, session);
205
206 Artifact pluginArtifact = RepositoryUtils.toArtifact(artifact);
207
208 PluginDescriptor descriptor = extractPluginDescriptor(pluginArtifact, plugin);
209
210 boolean isBlankVersion = descriptor.getRequiredMavenVersion() == null
211 || descriptor.getRequiredMavenVersion().trim().isEmpty();
212
213 if (isBlankVersion) {
214
215 descriptor.setRequiredMavenVersion(artifact.getProperty("requiredMavenVersion", null));
216 }
217
218 return descriptor;
219 });
220
221 pluginDescriptor.setPlugin(plugin);
222
223 return pluginDescriptor;
224 }
225
226 private PluginDescriptor extractPluginDescriptor(Artifact pluginArtifact, Plugin plugin)
227 throws PluginDescriptorParsingException, InvalidPluginDescriptorException {
228 PluginDescriptor pluginDescriptor = null;
229
230 File pluginFile = pluginArtifact.getFile();
231
232 try {
233 if (pluginFile.isFile()) {
234 try (JarFile pluginJar = new JarFile(pluginFile, false)) {
235 ZipEntry pluginDescriptorEntry = pluginJar.getEntry(getPluginDescriptorLocation());
236
237 if (pluginDescriptorEntry != null) {
238 pluginDescriptor = parsePluginDescriptor(
239 () -> pluginJar.getInputStream(pluginDescriptorEntry),
240 plugin,
241 pluginFile.getAbsolutePath());
242 }
243 }
244 } else {
245 File pluginXml = new File(pluginFile, getPluginDescriptorLocation());
246
247 if (pluginXml.isFile()) {
248 pluginDescriptor = parsePluginDescriptor(
249 () -> Files.newInputStream(pluginXml.toPath()), plugin, pluginXml.getAbsolutePath());
250 }
251 }
252
253 if (pluginDescriptor == null) {
254 throw new IOException("No plugin descriptor found at " + getPluginDescriptorLocation());
255 }
256 } catch (IOException e) {
257 throw new PluginDescriptorParsingException(plugin, pluginFile.getAbsolutePath(), e);
258 }
259
260 List<String> errors = new ArrayList<>();
261 pluginValidator.validate(pluginArtifact, pluginDescriptor, errors);
262
263 if (!errors.isEmpty()) {
264 throw new InvalidPluginDescriptorException(
265 "Invalid plugin descriptor for " + plugin.getId() + " (" + pluginFile + ")", errors);
266 }
267
268 pluginDescriptor.setPluginArtifact(pluginArtifact);
269
270 return pluginDescriptor;
271 }
272
273 private String getPluginDescriptorLocation() {
274 return "META-INF/maven/plugin.xml";
275 }
276
277 private PluginDescriptor parsePluginDescriptor(
278 PluginDescriptorBuilder.StreamSupplier is, Plugin plugin, String descriptorLocation)
279 throws PluginDescriptorParsingException {
280 try {
281 return builder.build(is, descriptorLocation);
282 } catch (PlexusConfigurationException e) {
283 throw new PluginDescriptorParsingException(plugin, descriptorLocation, e);
284 }
285 }
286
287 @Override
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 @Override
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 ClassLoader prevTccl = Thread.currentThread().getContextClassLoader();
438 try {
439 if (pluginDescriptor != null) {
440 for (MojoDescriptor mojo : pluginDescriptor.getMojos()) {
441 if (!mojo.isV4Api()) {
442 mojo.setRealm(pluginRealm);
443 container.addComponentDescriptor(mojo);
444 }
445 }
446 }
447
448 Thread.currentThread().setContextClassLoader(pluginRealm);
449 ((DefaultPlexusContainer) container)
450 .discoverComponents(
451 pluginRealm,
452 new SessionScopeModule(container.lookup(SessionScope.class)),
453 new MojoExecutionScopeModule(container.lookup(MojoExecutionScope.class)),
454 new PluginConfigurationModule(plugin.getDelegate()),
455 new SisuDiBridgeModule(true));
456 } catch (ComponentLookupException | CycleDetectedInComponentGraphException e) {
457 throw new PluginContainerException(
458 plugin,
459 pluginRealm,
460 "Error in component graph of plugin " + plugin.getId() + ": " + e.getMessage(),
461 e);
462 } finally {
463 Thread.currentThread().setContextClassLoader(prevTccl);
464 }
465 }
466
467 private List<org.eclipse.aether.artifact.Artifact> toAetherArtifacts(final List<Artifact> pluginArtifacts) {
468 return new ArrayList<>(RepositoryUtils.toArtifacts(pluginArtifacts));
469 }
470
471 private List<Artifact> toMavenArtifacts(DependencyResult dependencyResult) {
472 return dependencyResult.getDependencyNodeResults().stream()
473 .filter(n -> n.getArtifact().getPath() != null)
474 .map(n -> RepositoryUtils.toArtifact(n.getDependency()))
475 .toList();
476 }
477
478 private Map<String, ClassLoader> calcImports(
479 MavenProject project, ClassLoader parent, List<String> imports, boolean v4api) {
480 Map<String, ClassLoader> foreignImports = new HashMap<>();
481
482 ClassLoader projectRealm = project.getClassRealm();
483 if (projectRealm != null) {
484 foreignImports.put("", projectRealm);
485 } else {
486 foreignImports.put(
487 "", v4api ? classRealmManager.getMaven4ApiRealm() : classRealmManager.getMavenApiRealm());
488 }
489
490 if (parent != null && imports != null) {
491 for (String parentImport : imports) {
492 foreignImports.put(parentImport, parent);
493 }
494 }
495
496 return foreignImports;
497 }
498
499 @Override
500 public <T> T getConfiguredMojo(Class<T> mojoInterface, MavenSession session, MojoExecution mojoExecution)
501 throws PluginConfigurationException, PluginContainerException {
502 MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
503
504 PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor();
505
506 ClassRealm pluginRealm = pluginDescriptor.getClassRealm();
507
508 if (pluginRealm == null) {
509 try {
510 setupPluginRealm(pluginDescriptor, session, null, null, null);
511 } catch (PluginResolutionException e) {
512 String msg = "Cannot setup plugin realm [mojoDescriptor=" + mojoDescriptor.getId()
513 + ", pluginDescriptor=" + pluginDescriptor.getId() + "]";
514 throw new PluginConfigurationException(pluginDescriptor, msg, e);
515 }
516 pluginRealm = pluginDescriptor.getClassRealm();
517 }
518
519 if (logger.isDebugEnabled()) {
520 logger.debug("Loading mojo " + mojoDescriptor.getId() + " from plugin realm " + pluginRealm);
521 }
522
523
524
525
526 ClassRealm oldLookupRealm = container.setLookupRealm(pluginRealm);
527
528 ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
529 Thread.currentThread().setContextClassLoader(pluginRealm);
530
531 try {
532 if (mojoDescriptor.isV4Api()) {
533 return loadV4Mojo(mojoInterface, session, mojoExecution, mojoDescriptor, pluginDescriptor, pluginRealm);
534 } else {
535 return loadV3Mojo(mojoInterface, session, mojoExecution, mojoDescriptor, pluginDescriptor, pluginRealm);
536 }
537 } finally {
538 Thread.currentThread().setContextClassLoader(oldClassLoader);
539 container.setLookupRealm(oldLookupRealm);
540 }
541 }
542
543 private <T> T loadV4Mojo(
544 Class<T> mojoInterface,
545 MavenSession session,
546 MojoExecution mojoExecution,
547 MojoDescriptor mojoDescriptor,
548 PluginDescriptor pluginDescriptor,
549 ClassRealm pluginRealm)
550 throws PluginContainerException, PluginConfigurationException {
551 T mojo;
552
553 InternalMavenSession sessionV4 = InternalMavenSession.from(session.getSession());
554 Project project = sessionV4.getProject(session.getCurrentProject());
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 = sessionV4
651 .getService(DependencyResolver.class)
652 .collect(sessionV4, project, PathScope.MAIN_RUNTIME);
653 if (field.getType() == DependencyResolverResult.class) {
654 result = res;
655 } else if (field.getType() == Node.class) {
656 result = res.getRoot();
657 }
658 }
659 if (result == null) {
660 throw new PluginConfigurationException(
661 pluginDescriptor,
662 "Unable to inject field '" + resolution.getField()
663 + "' annotated with @Dependencies. Unsupported type " + field.getGenericType());
664 }
665 try {
666 field.set(mojo, result);
667 } catch (IllegalAccessException e) {
668 throw new PluginConfigurationException(
669 pluginDescriptor,
670 "Unable to inject field '" + resolution.getField() + "' annotated with @Dependencies",
671 e);
672 }
673 }
674
675 return mojo;
676 }
677
678 private <T> T loadV3Mojo(
679 Class<T> mojoInterface,
680 MavenSession session,
681 MojoExecution mojoExecution,
682 MojoDescriptor mojoDescriptor,
683 PluginDescriptor pluginDescriptor,
684 ClassRealm pluginRealm)
685 throws PluginContainerException, PluginConfigurationException {
686 T mojo;
687
688 try {
689 mojo = container.lookup(mojoInterface, mojoDescriptor.getRoleHint());
690 } catch (ComponentLookupException e) {
691 Throwable cause = e.getCause();
692 while (cause != null && !(cause instanceof LinkageError) && !(cause instanceof ClassNotFoundException)) {
693 cause = cause.getCause();
694 }
695
696 if ((cause instanceof NoClassDefFoundError) || (cause instanceof ClassNotFoundException)) {
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() + "'. A required class is missing: "
701 + cause.getMessage());
702 pluginRealm.display(ps);
703
704 throw new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), cause);
705 } else if (cause instanceof LinkageError) {
706 ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
707 PrintStream ps = new PrintStream(os);
708 ps.println("Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '"
709 + pluginDescriptor.getId() + "' due to an API incompatibility: "
710 + e.getClass().getName() + ": " + cause.getMessage());
711 pluginRealm.display(ps);
712
713 throw new PluginContainerException(mojoDescriptor, pluginRealm, os.toString(), cause);
714 }
715
716 throw new PluginContainerException(
717 mojoDescriptor,
718 pluginRealm,
719 "Unable to load the mojo '" + mojoDescriptor.getGoal()
720 + "' (or one of its required components) from the plugin '"
721 + pluginDescriptor.getId() + "'",
722 e);
723 }
724
725 if (mojo instanceof ContextEnabled contextEnabledMojo) {
726 MavenProject project = session.getCurrentProject();
727
728 Map<String, Object> pluginContext = session.getPluginContext(pluginDescriptor, project);
729
730 if (pluginContext != null) {
731 pluginContext.put("project", project);
732
733 pluginContext.put("pluginDescriptor", pluginDescriptor);
734
735 contextEnabledMojo.setPluginContext(pluginContext);
736 }
737 }
738
739 if (mojo instanceof Mojo mojoInstance) {
740 Logger mojoLogger = LoggerFactory.getLogger(mojoDescriptor.getImplementation());
741 mojoInstance.setLog(new MojoLogWrapper(mojoLogger));
742 }
743
744 if (mojo instanceof Contextualizable) {
745 pluginValidationManager.reportPluginMojoValidationIssue(
746 PluginValidationManager.IssueLocality.EXTERNAL,
747 session,
748 mojoDescriptor,
749 mojo.getClass(),
750 "Mojo implements `Contextualizable` interface from Plexus Container, which is EOL.");
751 }
752
753 XmlNode dom = mojoExecution.getConfiguration() != null
754 ? mojoExecution.getConfiguration().getDom()
755 : null;
756
757 PlexusConfiguration pomConfiguration;
758
759 if (dom == null) {
760 pomConfiguration = new DefaultPlexusConfiguration("configuration");
761 } else {
762 pomConfiguration = XmlPlexusConfiguration.toPlexusConfiguration(dom);
763 }
764
765 ExpressionEvaluator expressionEvaluator = new PluginParameterExpressionEvaluator(session, mojoExecution);
766
767 for (MavenPluginConfigurationValidator validator : configurationValidators) {
768 validator.validate(session, mojoDescriptor, mojo.getClass(), pomConfiguration, expressionEvaluator);
769 }
770
771 populateMojoExecutionFields(
772 mojo,
773 mojoExecution.getExecutionId(),
774 mojoDescriptor,
775 pluginRealm,
776 pomConfiguration,
777 expressionEvaluator);
778
779 return mojo;
780 }
781
782 private void populateMojoExecutionFields(
783 Object mojo,
784 String executionId,
785 MojoDescriptor mojoDescriptor,
786 ClassRealm pluginRealm,
787 PlexusConfiguration configuration,
788 ExpressionEvaluator expressionEvaluator)
789 throws PluginConfigurationException {
790 ComponentConfigurator configurator = null;
791
792 String configuratorId = mojoDescriptor.getComponentConfigurator();
793
794 if (configuratorId == null || configuratorId.isEmpty()) {
795 configuratorId = mojoDescriptor.isV4Api() ? "enhanced" : "basic";
796 }
797
798 try {
799
800
801 configurator = container.lookup(ComponentConfigurator.class, configuratorId);
802
803 ConfigurationListener listener = new DebugConfigurationListener(logger);
804
805 ValidatingConfigurationListener validator =
806 new ValidatingConfigurationListener(mojo, mojoDescriptor, listener);
807
808 if (logger.isDebugEnabled()) {
809 logger.debug("Configuring mojo execution '" + mojoDescriptor.getId() + ':' + executionId + "' with "
810 + configuratorId + " configurator -->");
811 }
812
813 configurator.configureComponent(mojo, configuration, expressionEvaluator, pluginRealm, validator);
814
815 logger.debug("-- end configuration --");
816
817 Collection<Parameter> missingParameters = validator.getMissingParameters();
818 if (!missingParameters.isEmpty()) {
819 if ("basic".equals(configuratorId)) {
820 throw new PluginParameterException(mojoDescriptor, new ArrayList<>(missingParameters));
821 } else {
822
823
824
825
826 validateParameters(mojoDescriptor, configuration, expressionEvaluator);
827 }
828 }
829
830 } catch (ComponentConfigurationException e) {
831 String message = "Unable to parse configuration of mojo " + mojoDescriptor.getId();
832 if (e.getFailedConfiguration() != null) {
833 message += " for parameter " + e.getFailedConfiguration().getName();
834 }
835 message += ": " + e.getMessage();
836
837 throw new PluginConfigurationException(mojoDescriptor.getPluginDescriptor(), message, e);
838 } catch (ComponentLookupException e) {
839 throw new PluginConfigurationException(
840 mojoDescriptor.getPluginDescriptor(),
841 "Unable to retrieve component configurator " + configuratorId + " for configuration of mojo "
842 + mojoDescriptor.getId(),
843 e);
844 } catch (NoClassDefFoundError e) {
845 ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
846 PrintStream ps = new PrintStream(os);
847 ps.println("A required class was missing during configuration of mojo " + mojoDescriptor.getId() + ": "
848 + e.getMessage());
849 pluginRealm.display(ps);
850
851 throw new PluginConfigurationException(mojoDescriptor.getPluginDescriptor(), os.toString(), e);
852 } catch (LinkageError e) {
853 ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
854 PrintStream ps = new PrintStream(os);
855 ps.println("An API incompatibility was encountered during configuration of mojo " + mojoDescriptor.getId()
856 + ": " + e.getClass().getName() + ": " + e.getMessage());
857 pluginRealm.display(ps);
858
859 throw new PluginConfigurationException(mojoDescriptor.getPluginDescriptor(), os.toString(), e);
860 } finally {
861 if (configurator != null) {
862 try {
863 container.release(configurator);
864 } catch (ComponentLifecycleException e) {
865 logger.debug("Failed to release mojo configurator - ignoring.");
866 }
867 }
868 }
869 }
870
871 private void validateParameters(
872 MojoDescriptor mojoDescriptor, PlexusConfiguration configuration, ExpressionEvaluator expressionEvaluator)
873 throws ComponentConfigurationException, PluginParameterException {
874 if (mojoDescriptor.getParameters() == null) {
875 return;
876 }
877
878 List<Parameter> invalidParameters = new ArrayList<>();
879
880 for (Parameter parameter : mojoDescriptor.getParameters()) {
881 if (!parameter.isRequired()) {
882 continue;
883 }
884
885 Object value = null;
886
887 PlexusConfiguration config = configuration.getChild(parameter.getName(), false);
888 if (config != null) {
889 String expression = config.getValue(null);
890
891 try {
892 value = expressionEvaluator.evaluate(expression);
893
894 if (value == null) {
895 value = config.getAttribute("default-value", null);
896 }
897 } catch (ExpressionEvaluationException e) {
898 String msg = "Error evaluating the expression '" + expression + "' for configuration value '"
899 + configuration.getName() + "'";
900 throw new ComponentConfigurationException(configuration, msg, e);
901 }
902 }
903
904 if (value == null && (config == null || config.getChildCount() <= 0)) {
905 invalidParameters.add(parameter);
906 }
907 }
908
909 if (!invalidParameters.isEmpty()) {
910 throw new PluginParameterException(mojoDescriptor, invalidParameters);
911 }
912 }
913
914 @Override
915 public void releaseMojo(Object mojo, MojoExecution mojoExecution) {
916 if (mojo != null) {
917 try {
918 container.release(mojo);
919 } catch (ComponentLifecycleException e) {
920 String goalExecId = mojoExecution.getGoal();
921
922 if (mojoExecution.getExecutionId() != null) {
923 logger.debug(
924 "Error releasing mojo for {} {execution: {}}",
925 goalExecId,
926 mojoExecution.getExecutionId(),
927 e);
928 } else {
929 logger.debug("Error releasing mojo for {}", goalExecId, e);
930 }
931 }
932 }
933 }
934
935 @Override
936 public ExtensionRealmCache.CacheRecord setupExtensionsRealm(
937 MavenProject project, Plugin plugin, RepositorySystemSession session) throws PluginManagerException {
938 @SuppressWarnings("unchecked")
939 Map<String, ExtensionRealmCache.CacheRecord> pluginRealms =
940 (Map<String, ExtensionRealmCache.CacheRecord>) project.getContextValue(KEY_EXTENSIONS_REALMS);
941 if (pluginRealms == null) {
942 pluginRealms = new HashMap<>();
943 project.setContextValue(KEY_EXTENSIONS_REALMS, pluginRealms);
944 }
945
946 final String pluginKey = plugin.getId();
947
948 ExtensionRealmCache.CacheRecord extensionRecord = pluginRealms.get(pluginKey);
949 if (extensionRecord != null) {
950 return extensionRecord;
951 }
952
953 final List<RemoteRepository> repositories = project.getRemotePluginRepositories();
954
955
956 if (plugin.getVersion() == null) {
957 PluginVersionRequest versionRequest = new DefaultPluginVersionRequest(plugin, session, repositories);
958 try {
959 plugin.setVersion(pluginVersionResolver.resolve(versionRequest).getVersion());
960 } catch (PluginVersionResolutionException e) {
961 throw new PluginManagerException(plugin, e.getMessage(), e);
962 }
963 }
964
965
966
967
968 List<Artifact> artifacts;
969 PluginArtifactsCache.Key cacheKey = pluginArtifactsCache.createKey(plugin, null, repositories, session);
970 PluginArtifactsCache.CacheRecord recordArtifacts;
971 try {
972 recordArtifacts = pluginArtifactsCache.get(cacheKey);
973 } catch (PluginResolutionException e) {
974 throw new PluginManagerException(plugin, e.getMessage(), e);
975 }
976 if (recordArtifacts != null) {
977 artifacts = recordArtifacts.getArtifacts();
978 } else {
979 try {
980 artifacts = resolveExtensionArtifacts(plugin, repositories, session);
981 recordArtifacts = pluginArtifactsCache.put(cacheKey, artifacts);
982 } catch (PluginResolutionException e) {
983 pluginArtifactsCache.put(cacheKey, e);
984 pluginArtifactsCache.register(project, cacheKey, recordArtifacts);
985 throw new PluginManagerException(plugin, e.getMessage(), e);
986 }
987 }
988 pluginArtifactsCache.register(project, cacheKey, recordArtifacts);
989
990
991 final ExtensionRealmCache.Key extensionKey = extensionRealmCache.createKey(artifacts);
992 extensionRecord = extensionRealmCache.get(extensionKey);
993 if (extensionRecord == null) {
994 ClassRealm extensionRealm = classRealmManager.createExtensionRealm(plugin, toAetherArtifacts(artifacts));
995
996
997
998 PluginDescriptor pluginDescriptor = null;
999 if (plugin.isExtensions() && !artifacts.isEmpty()) {
1000
1001
1002 try {
1003 pluginDescriptor = extractPluginDescriptor(artifacts.get(0), plugin);
1004 } catch (PluginDescriptorParsingException | InvalidPluginDescriptorException e) {
1005
1006 }
1007 }
1008
1009 discoverPluginComponents(extensionRealm, plugin, pluginDescriptor);
1010
1011 ExtensionDescriptor extensionDescriptor = null;
1012 Artifact extensionArtifact = artifacts.get(0);
1013 try {
1014 extensionDescriptor = extensionDescriptorBuilder.build(extensionArtifact.getFile());
1015 } catch (IOException e) {
1016 String message = "Invalid extension descriptor for " + plugin.getId() + ": " + e.getMessage();
1017 if (logger.isDebugEnabled()) {
1018 logger.error(message, e);
1019 } else {
1020 logger.error(message);
1021 }
1022 }
1023 extensionRecord = extensionRealmCache.put(extensionKey, extensionRealm, extensionDescriptor, artifacts);
1024 }
1025 extensionRealmCache.register(project, extensionKey, extensionRecord);
1026 pluginRealms.put(pluginKey, extensionRecord);
1027
1028 return extensionRecord;
1029 }
1030
1031 private List<Artifact> resolveExtensionArtifacts(
1032 Plugin extensionPlugin, List<RemoteRepository> repositories, RepositorySystemSession session)
1033 throws PluginResolutionException {
1034 DependencyResult root =
1035 pluginDependenciesResolver.resolvePlugin(extensionPlugin, null, null, repositories, session);
1036 return toMavenArtifacts(root);
1037 }
1038 }