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