View Javadoc
1   package org.apache.maven.plugin.internal;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.BufferedInputStream;
23  import java.io.ByteArrayOutputStream;
24  import java.io.File;
25  import java.io.FileInputStream;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.io.PrintStream;
29  import java.io.Reader;
30  import java.util.ArrayList;
31  import java.util.Collection;
32  import java.util.Collections;
33  import java.util.HashMap;
34  import java.util.Iterator;
35  import java.util.List;
36  import java.util.Map;
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.InvalidPluginDescriptorException;
50  import org.apache.maven.plugin.MavenPluginManager;
51  import org.apache.maven.plugin.MavenPluginValidator;
52  import org.apache.maven.plugin.Mojo;
53  import org.apache.maven.plugin.MojoExecution;
54  import org.apache.maven.plugin.MojoNotFoundException;
55  import org.apache.maven.plugin.PluginConfigurationException;
56  import org.apache.maven.plugin.PluginContainerException;
57  import org.apache.maven.plugin.PluginDescriptorCache;
58  import org.apache.maven.plugin.PluginDescriptorParsingException;
59  import org.apache.maven.plugin.PluginIncompatibleException;
60  import org.apache.maven.plugin.PluginParameterException;
61  import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
62  import org.apache.maven.plugin.PluginRealmCache;
63  import org.apache.maven.plugin.PluginResolutionException;
64  import org.apache.maven.plugin.descriptor.MojoDescriptor;
65  import org.apache.maven.plugin.descriptor.Parameter;
66  import org.apache.maven.plugin.descriptor.PluginDescriptor;
67  import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder;
68  import org.apache.maven.project.MavenProject;
69  import org.apache.maven.rtinfo.RuntimeInformation;
70  import org.apache.maven.session.scope.internal.SessionScopeModule;
71  import org.codehaus.plexus.DefaultPlexusContainer;
72  import org.codehaus.plexus.PlexusContainer;
73  import org.codehaus.plexus.classworlds.realm.ClassRealm;
74  import org.codehaus.plexus.component.annotations.Component;
75  import org.codehaus.plexus.component.annotations.Requirement;
76  import org.codehaus.plexus.component.composition.CycleDetectedInComponentGraphException;
77  import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
78  import org.codehaus.plexus.component.configurator.ComponentConfigurator;
79  import org.codehaus.plexus.component.configurator.ConfigurationListener;
80  import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
81  import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
82  import org.codehaus.plexus.component.repository.ComponentDescriptor;
83  import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
84  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
85  import org.codehaus.plexus.configuration.PlexusConfiguration;
86  import org.codehaus.plexus.configuration.PlexusConfigurationException;
87  import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
88  import org.codehaus.plexus.logging.Logger;
89  import org.codehaus.plexus.logging.LoggerManager;
90  import org.codehaus.plexus.util.IOUtil;
91  import org.codehaus.plexus.util.ReaderFactory;
92  import org.codehaus.plexus.util.StringUtils;
93  import org.codehaus.plexus.util.xml.Xpp3Dom;
94  import org.eclipse.aether.RepositorySystemSession;
95  import org.eclipse.aether.graph.DependencyFilter;
96  import org.eclipse.aether.graph.DependencyNode;
97  import org.eclipse.aether.repository.RemoteRepository;
98  import org.eclipse.aether.util.filter.AndDependencyFilter;
99  import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator;
100 
101 /**
102  * Provides basic services to manage Maven plugins and their mojos. This component is kept general in its design such
103  * that the plugins/mojos can be used in arbitrary contexts. In particular, the mojos can be used for ordinary build
104  * plugins as well as special purpose plugins like reports.
105  *
106  * @since 3.0
107  * @author Benjamin Bentmann
108  */
109 @Component( role = MavenPluginManager.class )
110 public class DefaultMavenPluginManager
111     implements MavenPluginManager
112 {
113 
114     @Requirement
115     private Logger logger;
116 
117     @Requirement
118     private LoggerManager loggerManager;
119 
120     @Requirement
121     private PlexusContainer container;
122 
123     @Requirement
124     private ClassRealmManager classRealmManager;
125 
126     @Requirement
127     private PluginDescriptorCache pluginDescriptorCache;
128 
129     @Requirement
130     private PluginRealmCache pluginRealmCache;
131 
132     @Requirement
133     private PluginDependenciesResolver pluginDependenciesResolver;
134 
135     @Requirement
136     private RuntimeInformation runtimeInformation;
137 
138     private PluginDescriptorBuilder builder = new PluginDescriptorBuilder();
139 
140     public synchronized PluginDescriptor getPluginDescriptor( Plugin plugin, List<RemoteRepository> repositories,
141                                                               RepositorySystemSession session )
142         throws PluginResolutionException, PluginDescriptorParsingException, InvalidPluginDescriptorException
143     {
144         PluginDescriptorCache.Key cacheKey = pluginDescriptorCache.createKey( plugin, repositories, session );
145 
146         PluginDescriptor pluginDescriptor = pluginDescriptorCache.get( cacheKey );
147 
148         if ( pluginDescriptor == null )
149         {
150             org.eclipse.aether.artifact.Artifact artifact =
151                 pluginDependenciesResolver.resolve( plugin, repositories, session );
152 
153             Artifact pluginArtifact = RepositoryUtils.toArtifact( artifact );
154 
155             pluginDescriptor = extractPluginDescriptor( pluginArtifact, plugin );
156 
157             pluginDescriptor.setRequiredMavenVersion( artifact.getProperty( "requiredMavenVersion", null ) );
158 
159             pluginDescriptorCache.put( cacheKey, pluginDescriptor );
160         }
161 
162         pluginDescriptor.setPlugin( plugin );
163 
164         return pluginDescriptor;
165     }
166 
167     private PluginDescriptor extractPluginDescriptor( Artifact pluginArtifact, Plugin plugin )
168         throws PluginDescriptorParsingException, InvalidPluginDescriptorException
169     {
170         PluginDescriptor pluginDescriptor = null;
171 
172         File pluginFile = pluginArtifact.getFile();
173 
174         try
175         {
176             if ( pluginFile.isFile() )
177             {
178                 JarFile pluginJar = new JarFile( pluginFile, false );
179                 try
180                 {
181                     ZipEntry pluginDescriptorEntry = pluginJar.getEntry( getPluginDescriptorLocation() );
182 
183                     if ( pluginDescriptorEntry != null )
184                     {
185                         InputStream is = pluginJar.getInputStream( pluginDescriptorEntry );
186 
187                         pluginDescriptor = parsePluginDescriptor( is, plugin, pluginFile.getAbsolutePath() );
188                     }
189                 }
190                 finally
191                 {
192                     pluginJar.close();
193                 }
194             }
195             else
196             {
197                 File pluginXml = new File( pluginFile, getPluginDescriptorLocation() );
198 
199                 if ( pluginXml.isFile() )
200                 {
201                     InputStream is = new BufferedInputStream( new FileInputStream( pluginXml ) );
202                     try
203                     {
204                         pluginDescriptor = parsePluginDescriptor( is, plugin, pluginXml.getAbsolutePath() );
205                     }
206                     finally
207                     {
208                         IOUtil.close( is );
209                     }
210                 }
211             }
212 
213             if ( pluginDescriptor == null )
214             {
215                 throw new IOException( "No plugin descriptor found at " + getPluginDescriptorLocation() );
216             }
217         }
218         catch ( IOException e )
219         {
220             throw new PluginDescriptorParsingException( plugin, pluginFile.getAbsolutePath(), e );
221         }
222 
223         MavenPluginValidator validator = new MavenPluginValidator( pluginArtifact );
224 
225         validator.validate( pluginDescriptor );
226 
227         if ( validator.hasErrors() )
228         {
229             throw new InvalidPluginDescriptorException( "Invalid plugin descriptor for " + plugin.getId() + " ("
230                 + pluginFile + ")", validator.getErrors() );
231         }
232 
233         pluginDescriptor.setPluginArtifact( pluginArtifact );
234 
235         return pluginDescriptor;
236     }
237 
238     private String getPluginDescriptorLocation()
239     {
240         return "META-INF/maven/plugin.xml";
241     }
242 
243     private PluginDescriptor parsePluginDescriptor( InputStream is, Plugin plugin, String descriptorLocation )
244         throws PluginDescriptorParsingException
245     {
246         try
247         {
248             Reader reader = ReaderFactory.newXmlReader( is );
249 
250             PluginDescriptor pluginDescriptor = builder.build( reader, descriptorLocation );
251 
252             return pluginDescriptor;
253         }
254         catch ( IOException e )
255         {
256             throw new PluginDescriptorParsingException( plugin, descriptorLocation, e );
257         }
258         catch ( PlexusConfigurationException e )
259         {
260             throw new PluginDescriptorParsingException( plugin, descriptorLocation, e );
261         }
262     }
263 
264     public MojoDescriptor getMojoDescriptor( Plugin plugin, String goal, List<RemoteRepository> repositories,
265                                              RepositorySystemSession session )
266         throws MojoNotFoundException, PluginResolutionException, PluginDescriptorParsingException,
267         InvalidPluginDescriptorException
268     {
269         PluginDescriptor pluginDescriptor = getPluginDescriptor( plugin, repositories, session );
270 
271         MojoDescriptor mojoDescriptor = pluginDescriptor.getMojo( goal );
272 
273         if ( mojoDescriptor == null )
274         {
275             throw new MojoNotFoundException( goal, pluginDescriptor );
276         }
277 
278         return mojoDescriptor;
279     }
280 
281     public void checkRequiredMavenVersion( PluginDescriptor pluginDescriptor )
282         throws PluginIncompatibleException
283     {
284         String requiredMavenVersion = pluginDescriptor.getRequiredMavenVersion();
285         if ( StringUtils.isNotBlank( requiredMavenVersion ) )
286         {
287             try
288             {
289                 if ( !runtimeInformation.isMavenVersion( requiredMavenVersion ) )
290                 {
291                     throw new PluginIncompatibleException( pluginDescriptor.getPlugin(), "The plugin "
292                         + pluginDescriptor.getId() + " requires Maven version " + requiredMavenVersion );
293                 }
294             }
295             catch ( RuntimeException e )
296             {
297                 logger.warn( "Could not verify plugin's Maven prerequisite: " + e.getMessage() );
298             }
299         }
300     }
301 
302     public synchronized void setupPluginRealm( PluginDescriptor pluginDescriptor, MavenSession session,
303                                                ClassLoader parent, List<String> imports, DependencyFilter filter )
304         throws PluginResolutionException, PluginContainerException
305     {
306         Plugin plugin = pluginDescriptor.getPlugin();
307 
308         MavenProject project = session.getCurrentProject();
309 
310         Map<String, ClassLoader> foreignImports = calcImports( project, parent, imports );
311 
312         PluginRealmCache.Key cacheKey =
313             pluginRealmCache.createKey( plugin, parent, foreignImports, filter, project.getRemotePluginRepositories(),
314                                         session.getRepositorySession() );
315 
316         PluginRealmCache.CacheRecord cacheRecord = pluginRealmCache.get( cacheKey );
317 
318         if ( cacheRecord != null )
319         {
320             pluginDescriptor.setClassRealm( cacheRecord.realm );
321             pluginDescriptor.setArtifacts( new ArrayList<Artifact>( cacheRecord.artifacts ) );
322             for ( ComponentDescriptor<?> componentDescriptor : pluginDescriptor.getComponents() )
323             {
324                 componentDescriptor.setRealm( cacheRecord.realm );
325             }
326         }
327         else
328         {
329             createPluginRealm( pluginDescriptor, session, parent, foreignImports, filter );
330 
331             cacheRecord =
332                 pluginRealmCache.put( cacheKey, pluginDescriptor.getClassRealm(), pluginDescriptor.getArtifacts() );
333         }
334 
335         pluginRealmCache.register( project, cacheKey, cacheRecord );
336     }
337 
338     private void createPluginRealm( PluginDescriptor pluginDescriptor, MavenSession session, ClassLoader parent,
339                                     Map<String, ClassLoader> foreignImports, DependencyFilter filter )
340         throws PluginResolutionException, PluginContainerException
341     {
342         Plugin plugin = pluginDescriptor.getPlugin();
343 
344         if ( plugin == null )
345         {
346             throw new IllegalArgumentException( "incomplete plugin descriptor, plugin missing" );
347         }
348 
349         Artifact pluginArtifact = pluginDescriptor.getPluginArtifact();
350 
351         if ( pluginArtifact == null )
352         {
353             throw new IllegalArgumentException( "incomplete plugin descriptor, plugin artifact missing" );
354         }
355 
356         MavenProject project = session.getCurrentProject();
357 
358         DependencyFilter dependencyFilter = project.getExtensionDependencyFilter();
359         dependencyFilter = AndDependencyFilter.newInstance( dependencyFilter, filter );
360 
361         DependencyNode root =
362             pluginDependenciesResolver.resolve( plugin, RepositoryUtils.toArtifact( pluginArtifact ), dependencyFilter,
363                                                 project.getRemotePluginRepositories(), session.getRepositorySession() );
364 
365         PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
366         root.accept( nlg );
367 
368         List<Artifact> exposedPluginArtifacts = new ArrayList<Artifact>( nlg.getNodes().size() );
369         RepositoryUtils.toArtifacts( exposedPluginArtifacts, Collections.singleton( root ),
370                                      Collections.<String>emptyList(), null );
371         for ( Iterator<Artifact> it = exposedPluginArtifacts.iterator(); it.hasNext(); )
372         {
373             Artifact artifact = it.next();
374             if ( artifact.getFile() == null )
375             {
376                 it.remove();
377             }
378         }
379 
380         List<org.eclipse.aether.artifact.Artifact> pluginArtifacts = nlg.getArtifacts( true );
381 
382         ClassRealm pluginRealm =
383             classRealmManager.createPluginRealm( plugin, parent, null, foreignImports, pluginArtifacts );
384 
385         pluginDescriptor.setClassRealm( pluginRealm );
386         pluginDescriptor.setArtifacts( exposedPluginArtifacts );
387 
388         try
389         {
390             for ( ComponentDescriptor<?> componentDescriptor : pluginDescriptor.getComponents() )
391             {
392                 componentDescriptor.setRealm( pluginRealm );
393                 container.addComponentDescriptor( componentDescriptor );
394             }
395 
396             ( (DefaultPlexusContainer) container ).discoverComponents( pluginRealm, new SessionScopeModule( container ),
397                                                                        new MojoExecutionScopeModule( container ) );
398         }
399         catch ( ComponentLookupException e )
400         {
401             throw new PluginContainerException( plugin, pluginRealm, "Error in component graph of plugin "
402                 + plugin.getId() + ": " + e.getMessage(), e );
403         }
404         catch ( CycleDetectedInComponentGraphException e )
405         {
406             throw new PluginContainerException( plugin, pluginRealm, "Error in component graph of plugin "
407                 + plugin.getId() + ": " + e.getMessage(), e );
408         }
409     }
410 
411     private Map<String, ClassLoader> calcImports( MavenProject project, ClassLoader parent, List<String> imports )
412     {
413         Map<String, ClassLoader> foreignImports = new HashMap<String, ClassLoader>();
414 
415         ClassLoader projectRealm = project.getClassRealm();
416         if ( projectRealm != null )
417         {
418             foreignImports.put( "", projectRealm );
419         }
420         else
421         {
422             foreignImports.put( "", classRealmManager.getMavenApiRealm() );
423         }
424 
425         if ( parent != null && imports != null )
426         {
427             for ( String parentImport : imports )
428             {
429                 foreignImports.put( parentImport, parent );
430             }
431         }
432 
433         return foreignImports;
434     }
435 
436     public <T> T getConfiguredMojo( Class<T> mojoInterface, MavenSession session, MojoExecution mojoExecution )
437         throws PluginConfigurationException, PluginContainerException
438     {
439         MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
440 
441         PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor();
442 
443         ClassRealm pluginRealm = pluginDescriptor.getClassRealm();
444 
445         if ( logger.isDebugEnabled() )
446         {
447             logger.debug( "Configuring mojo " + mojoDescriptor.getId() + " from plugin realm " + pluginRealm );
448         }
449 
450         // We are forcing the use of the plugin realm for all lookups that might occur during
451         // the lifecycle that is part of the lookup. Here we are specifically trying to keep
452         // lookups that occur in contextualize calls in line with the right realm.
453         ClassRealm oldLookupRealm = container.setLookupRealm( pluginRealm );
454 
455         ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
456         Thread.currentThread().setContextClassLoader( pluginRealm );
457 
458         try
459         {
460             T mojo;
461 
462             try
463             {
464                 mojo = container.lookup( mojoInterface, mojoDescriptor.getRoleHint() );
465             }
466             catch ( ComponentLookupException e )
467             {
468                 Throwable cause = e.getCause();
469                 while ( cause != null && !( cause instanceof LinkageError )
470                     && !( cause instanceof ClassNotFoundException ) )
471                 {
472                     cause = cause.getCause();
473                 }
474 
475                 if ( ( cause instanceof NoClassDefFoundError ) || ( cause instanceof ClassNotFoundException ) )
476                 {
477                     ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 );
478                     PrintStream ps = new PrintStream( os );
479                     ps.println( "Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '"
480                         + pluginDescriptor.getId() + "'. A required class is missing: " + cause.getMessage() );
481                     pluginRealm.display( ps );
482 
483                     throw new PluginContainerException( mojoDescriptor, pluginRealm, os.toString(), cause );
484                 }
485                 else if ( cause instanceof LinkageError )
486                 {
487                     ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 );
488                     PrintStream ps = new PrintStream( os );
489                     ps.println( "Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '"
490                         + pluginDescriptor.getId() + "' due to an API incompatibility: " + e.getClass().getName()
491                         + ": " + cause.getMessage() );
492                     pluginRealm.display( ps );
493 
494                     throw new PluginContainerException( mojoDescriptor, pluginRealm, os.toString(), cause );
495                 }
496 
497                 throw new PluginContainerException( mojoDescriptor, pluginRealm, "Unable to load the mojo '"
498                     + mojoDescriptor.getGoal() + "' (or one of its required components) from the plugin '"
499                     + pluginDescriptor.getId() + "'", e );
500             }
501 
502             if ( mojo instanceof ContextEnabled )
503             {
504                 MavenProject project = session.getCurrentProject();
505 
506                 Map<String, Object> pluginContext = session.getPluginContext( pluginDescriptor, project );
507 
508                 if ( pluginContext != null )
509                 {
510                     pluginContext.put( "project", project );
511 
512                     pluginContext.put( "pluginDescriptor", pluginDescriptor );
513 
514                     ( (ContextEnabled) mojo ).setPluginContext( pluginContext );
515                 }
516             }
517 
518             if ( mojo instanceof Mojo )
519             {
520                 Logger mojoLogger = loggerManager.getLoggerForComponent( mojoDescriptor.getImplementation() );
521                 ( (Mojo) mojo ).setLog( new DefaultLog( mojoLogger ) );
522             }
523 
524             Xpp3Dom dom = mojoExecution.getConfiguration();
525 
526             PlexusConfiguration pomConfiguration;
527 
528             if ( dom == null )
529             {
530                 pomConfiguration = new XmlPlexusConfiguration( "configuration" );
531             }
532             else
533             {
534                 pomConfiguration = new XmlPlexusConfiguration( dom );
535             }
536 
537             ExpressionEvaluator expressionEvaluator = new PluginParameterExpressionEvaluator( session, mojoExecution );
538 
539             populatePluginFields( mojo, mojoDescriptor, pluginRealm, pomConfiguration, expressionEvaluator );
540 
541             return mojo;
542         }
543         finally
544         {
545             Thread.currentThread().setContextClassLoader( oldClassLoader );
546             container.setLookupRealm( oldLookupRealm );
547         }
548     }
549 
550     private void populatePluginFields( Object mojo, MojoDescriptor mojoDescriptor, ClassRealm pluginRealm,
551                                        PlexusConfiguration configuration, ExpressionEvaluator expressionEvaluator )
552         throws PluginConfigurationException
553     {
554         ComponentConfigurator configurator = null;
555 
556         String configuratorId = mojoDescriptor.getComponentConfigurator();
557 
558         if ( StringUtils.isEmpty( configuratorId ) )
559         {
560             configuratorId = "basic";
561         }
562 
563         try
564         {
565             // TODO: could the configuration be passed to lookup and the configurator known to plexus via the descriptor
566             // so that this method could entirely be handled by a plexus lookup?
567             configurator = container.lookup( ComponentConfigurator.class, configuratorId );
568 
569             ConfigurationListener listener = new DebugConfigurationListener( logger );
570 
571             ValidatingConfigurationListener validator =
572                 new ValidatingConfigurationListener( mojo, mojoDescriptor, listener );
573 
574             logger.debug( "Configuring mojo '" + mojoDescriptor.getId() + "' with " + configuratorId
575                 + " configurator -->" );
576 
577             configurator.configureComponent( mojo, configuration, expressionEvaluator, pluginRealm, validator );
578 
579             logger.debug( "-- end configuration --" );
580 
581             Collection<Parameter> missingParameters = validator.getMissingParameters();
582             if ( !missingParameters.isEmpty() )
583             {
584                 if ( "basic".equals( configuratorId ) )
585                 {
586                     throw new PluginParameterException( mojoDescriptor, new ArrayList<Parameter>( missingParameters ) );
587                 }
588                 else
589                 {
590                     /*
591                      * NOTE: Other configurators like the map-oriented one don't call into the listener, so do it the
592                      * hard way.
593                      */
594                     validateParameters( mojoDescriptor, configuration, expressionEvaluator );
595                 }
596             }
597         }
598         catch ( ComponentConfigurationException e )
599         {
600             String message = "Unable to parse configuration of mojo " + mojoDescriptor.getId();
601             if ( e.getFailedConfiguration() != null )
602             {
603                 message += " for parameter " + e.getFailedConfiguration().getName();
604             }
605             message += ": " + e.getMessage();
606 
607             throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(), message, e );
608         }
609         catch ( ComponentLookupException e )
610         {
611             throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(),
612                                                     "Unable to retrieve component configurator " + configuratorId
613                                                         + " for configuration of mojo " + mojoDescriptor.getId(), e );
614         }
615         catch ( NoClassDefFoundError e )
616         {
617             ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 );
618             PrintStream ps = new PrintStream( os );
619             ps.println( "A required class was missing during configuration of mojo " + mojoDescriptor.getId() + ": "
620                 + e.getMessage() );
621             pluginRealm.display( ps );
622 
623             throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(), os.toString(), e );
624         }
625         catch ( LinkageError e )
626         {
627             ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 );
628             PrintStream ps = new PrintStream( os );
629             ps.println( "An API incompatibility was encountered during configuration of mojo " + mojoDescriptor.getId()
630                 + ": " + e.getClass().getName() + ": " + e.getMessage() );
631             pluginRealm.display( ps );
632 
633             throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(), os.toString(), e );
634         }
635         finally
636         {
637             if ( configurator != null )
638             {
639                 try
640                 {
641                     container.release( configurator );
642                 }
643                 catch ( ComponentLifecycleException e )
644                 {
645                     logger.debug( "Failed to release mojo configurator - ignoring." );
646                 }
647             }
648         }
649     }
650 
651     private void validateParameters( MojoDescriptor mojoDescriptor, PlexusConfiguration configuration,
652                                      ExpressionEvaluator expressionEvaluator )
653         throws ComponentConfigurationException, PluginParameterException
654     {
655         if ( mojoDescriptor.getParameters() == null )
656         {
657             return;
658         }
659 
660         List<Parameter> invalidParameters = new ArrayList<Parameter>();
661 
662         for ( Parameter parameter : mojoDescriptor.getParameters() )
663         {
664             if ( !parameter.isRequired() )
665             {
666                 continue;
667             }
668 
669             Object value = null;
670 
671             PlexusConfiguration config = configuration.getChild( parameter.getName(), false );
672             if ( config != null )
673             {
674                 String expression = config.getValue( null );
675 
676                 try
677                 {
678                     value = expressionEvaluator.evaluate( expression );
679 
680                     if ( value == null )
681                     {
682                         value = config.getAttribute( "default-value", null );
683                     }
684                 }
685                 catch ( ExpressionEvaluationException e )
686                 {
687                     String msg =
688                         "Error evaluating the expression '" + expression + "' for configuration value '"
689                             + configuration.getName() + "'";
690                     throw new ComponentConfigurationException( configuration, msg, e );
691                 }
692             }
693 
694             if ( value == null && ( config == null || config.getChildCount() <= 0 ) )
695             {
696                 invalidParameters.add( parameter );
697             }
698         }
699 
700         if ( !invalidParameters.isEmpty() )
701         {
702             throw new PluginParameterException( mojoDescriptor, invalidParameters );
703         }
704     }
705 
706     public void releaseMojo( Object mojo, MojoExecution mojoExecution )
707     {
708         if ( mojo != null )
709         {
710             try
711             {
712                 container.release( mojo );
713             }
714             catch ( ComponentLifecycleException e )
715             {
716                 String goalExecId = mojoExecution.getGoal();
717 
718                 if ( mojoExecution.getExecutionId() != null )
719                 {
720                     goalExecId += " {execution: " + mojoExecution.getExecutionId() + "}";
721                 }
722 
723                 logger.debug( "Error releasing mojo for " + goalExecId, e );
724             }
725         }
726     }
727 
728 }