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