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