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