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