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