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