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