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