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