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         Map<String, ClassLoader> foreignImports = calcImports( project, parent, imports );
303 
304         PluginRealmCache.Key cacheKey =
305             pluginRealmCache.createKey( plugin, parent, foreignImports, filter, project.getRemotePluginRepositories(),
306                                         session.getRepositorySession() );
307 
308         PluginRealmCache.CacheRecord cacheRecord = pluginRealmCache.get( cacheKey );
309 
310         if ( cacheRecord != null )
311         {
312             pluginDescriptor.setClassRealm( cacheRecord.realm );
313             pluginDescriptor.setArtifacts( new ArrayList<Artifact>( cacheRecord.artifacts ) );
314             for ( ComponentDescriptor<?> componentDescriptor : pluginDescriptor.getComponents() )
315             {
316                 componentDescriptor.setRealm( cacheRecord.realm );
317             }
318         }
319         else
320         {
321             createPluginRealm( pluginDescriptor, session, parent, foreignImports, filter );
322 
323             cacheRecord =
324                 pluginRealmCache.put( cacheKey, pluginDescriptor.getClassRealm(), pluginDescriptor.getArtifacts() );
325         }
326 
327         pluginRealmCache.register( project, cacheRecord );
328     }
329 
330     private void createPluginRealm( PluginDescriptor pluginDescriptor, MavenSession session, ClassLoader parent,
331                                     Map<String, ClassLoader> foreignImports, DependencyFilter filter )
332         throws PluginResolutionException, PluginContainerException
333     {
334         Plugin plugin = pluginDescriptor.getPlugin();
335 
336         if ( plugin == null )
337         {
338             throw new IllegalArgumentException( "incomplete plugin descriptor, plugin missing" );
339         }
340 
341         Artifact pluginArtifact = pluginDescriptor.getPluginArtifact();
342 
343         if ( pluginArtifact == null )
344         {
345             throw new IllegalArgumentException( "incomplete plugin descriptor, plugin artifact missing" );
346         }
347 
348         MavenProject project = session.getCurrentProject();
349 
350         DependencyFilter dependencyFilter = project.getExtensionDependencyFilter();
351         dependencyFilter = AndDependencyFilter.newInstance( dependencyFilter, filter );
352 
353         DependencyNode root =
354             pluginDependenciesResolver.resolve( plugin, RepositoryUtils.toArtifact( pluginArtifact ), dependencyFilter,
355                                                 project.getRemotePluginRepositories(), session.getRepositorySession() );
356 
357         PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
358         root.accept( nlg );
359 
360         List<Artifact> exposedPluginArtifacts = new ArrayList<Artifact>( nlg.getNodes().size() );
361         RepositoryUtils.toArtifacts( exposedPluginArtifacts, Collections.singleton( root ),
362                                      Collections.<String> emptyList(), null );
363         for ( Iterator<Artifact> it = exposedPluginArtifacts.iterator(); it.hasNext(); )
364         {
365             Artifact artifact = it.next();
366             if ( artifact.getFile() == null )
367             {
368                 it.remove();
369             }
370         }
371 
372         List<org.sonatype.aether.artifact.Artifact> pluginArtifacts = nlg.getArtifacts( true );
373 
374         ClassRealm pluginRealm =
375             classRealmManager.createPluginRealm( plugin, parent, null, foreignImports, pluginArtifacts );
376 
377         pluginDescriptor.setClassRealm( pluginRealm );
378         pluginDescriptor.setArtifacts( exposedPluginArtifacts );
379 
380         try
381         {
382             for ( ComponentDescriptor<?> componentDescriptor : pluginDescriptor.getComponents() )
383             {
384                 componentDescriptor.setRealm( pluginRealm );
385                 container.addComponentDescriptor( componentDescriptor );
386             }
387 
388             container.discoverComponents( pluginRealm );
389         }
390         catch ( PlexusConfigurationException e )
391         {
392             throw new PluginContainerException( plugin, pluginRealm, "Error in component graph of plugin "
393                 + plugin.getId() + ": " + e.getMessage(), e );
394         }
395         catch ( CycleDetectedInComponentGraphException e )
396         {
397             throw new PluginContainerException( plugin, pluginRealm, "Error in component graph of plugin "
398                 + plugin.getId() + ": " + e.getMessage(), e );
399         }
400     }
401 
402     private Map<String, ClassLoader> calcImports( MavenProject project, ClassLoader parent, List<String> imports )
403     {
404         Map<String, ClassLoader> foreignImports = new HashMap<String, ClassLoader>();
405 
406         ClassLoader projectRealm = project.getClassRealm();
407         if ( projectRealm != null )
408         {
409             foreignImports.put( "", projectRealm );
410         }
411         else
412         {
413             foreignImports.put( "", classRealmManager.getMavenApiRealm() );
414         }
415 
416         if ( parent != null && imports != null )
417         {
418             for ( String parentImport : imports )
419             {
420                 foreignImports.put( parentImport, parent );
421             }
422         }
423 
424         return foreignImports;
425     }
426 
427     public <T> T getConfiguredMojo( Class<T> mojoInterface, MavenSession session, MojoExecution mojoExecution )
428         throws PluginConfigurationException, PluginContainerException
429     {
430         MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
431 
432         PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor();
433 
434         ClassRealm pluginRealm = pluginDescriptor.getClassRealm();
435 
436         if ( logger.isDebugEnabled() )
437         {
438             logger.debug( "Configuring mojo " + mojoDescriptor.getId() + " from plugin realm " + pluginRealm );
439         }
440 
441         // We are forcing the use of the plugin realm for all lookups that might occur during
442         // the lifecycle that is part of the lookup. Here we are specifically trying to keep
443         // lookups that occur in contextualize calls in line with the right realm.
444         ClassRealm oldLookupRealm = container.setLookupRealm( pluginRealm );
445 
446         ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
447         Thread.currentThread().setContextClassLoader( pluginRealm );
448 
449         try
450         {
451             T mojo;
452 
453             try
454             {
455                 mojo = container.lookup( mojoInterface, mojoDescriptor.getRoleHint() );
456             }
457             catch ( ComponentLookupException e )
458             {
459                 Throwable cause = e.getCause();
460                 while ( cause != null && !( cause instanceof LinkageError )
461                     && !( cause instanceof ClassNotFoundException ) )
462                 {
463                     cause = cause.getCause();
464                 }
465 
466                 if ( ( cause instanceof NoClassDefFoundError ) || ( cause instanceof ClassNotFoundException ) )
467                 {
468                     ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 );
469                     PrintStream ps = new PrintStream( os );
470                     ps.println( "Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '"
471                         + pluginDescriptor.getId() + "'. A required class is missing: " + cause.getMessage() );
472                     pluginRealm.display( ps );
473 
474                     throw new PluginContainerException( mojoDescriptor, pluginRealm, os.toString(), cause );
475                 }
476                 else if ( cause instanceof LinkageError )
477                 {
478                     ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 );
479                     PrintStream ps = new PrintStream( os );
480                     ps.println( "Unable to load the mojo '" + mojoDescriptor.getGoal() + "' in the plugin '"
481                         + pluginDescriptor.getId() + "' due to an API incompatibility: " + e.getClass().getName()
482                         + ": " + cause.getMessage() );
483                     pluginRealm.display( ps );
484 
485                     throw new PluginContainerException( mojoDescriptor, pluginRealm, os.toString(), cause );
486                 }
487 
488                 throw new PluginContainerException( mojoDescriptor, pluginRealm, "Unable to load the mojo '"
489                     + mojoDescriptor.getGoal() + "' (or one of its required components) from the plugin '"
490                     + pluginDescriptor.getId() + "'", e );
491             }
492 
493             if ( mojo instanceof ContextEnabled )
494             {
495                 MavenProject project = session.getCurrentProject();
496 
497                 Map<String, Object> pluginContext = session.getPluginContext( pluginDescriptor, project );
498 
499                 if ( pluginContext != null )
500                 {
501                     pluginContext.put( "project", project );
502 
503                     pluginContext.put( "pluginDescriptor", pluginDescriptor );
504 
505                     ( (ContextEnabled) mojo ).setPluginContext( pluginContext );
506                 }
507             }
508 
509             if ( mojo instanceof Mojo )
510             {
511                 ( (Mojo) mojo ).setLog( new DefaultLog( logger ) );
512             }
513 
514             Xpp3Dom dom = mojoExecution.getConfiguration();
515 
516             PlexusConfiguration pomConfiguration;
517 
518             if ( dom == null )
519             {
520                 pomConfiguration = new XmlPlexusConfiguration( "configuration" );
521             }
522             else
523             {
524                 pomConfiguration = new XmlPlexusConfiguration( dom );
525             }
526 
527             ExpressionEvaluator expressionEvaluator = new PluginParameterExpressionEvaluator( session, mojoExecution );
528 
529             populatePluginFields( mojo, mojoDescriptor, pluginRealm, pomConfiguration, expressionEvaluator );
530 
531             return mojo;
532         }
533         finally
534         {
535             Thread.currentThread().setContextClassLoader( oldClassLoader );
536             container.setLookupRealm( oldLookupRealm );
537         }
538     }
539 
540     private void populatePluginFields( Object mojo, MojoDescriptor mojoDescriptor, ClassRealm pluginRealm,
541                                        PlexusConfiguration configuration, ExpressionEvaluator expressionEvaluator )
542         throws PluginConfigurationException
543     {
544         ComponentConfigurator configurator = null;
545 
546         String configuratorId = mojoDescriptor.getComponentConfigurator();
547 
548         if ( StringUtils.isEmpty( configuratorId ) )
549         {
550             configuratorId = "basic";
551         }
552 
553         try
554         {
555             // TODO: could the configuration be passed to lookup and the configurator known to plexus via the descriptor
556             // so that this method could entirely be handled by a plexus lookup?
557             configurator = container.lookup( ComponentConfigurator.class, configuratorId );
558 
559             ConfigurationListener listener = new DebugConfigurationListener( logger );
560 
561             ValidatingConfigurationListener validator =
562                 new ValidatingConfigurationListener( mojo, mojoDescriptor, listener );
563 
564             logger.debug( "Configuring mojo '" + mojoDescriptor.getId() + "' with " + configuratorId
565                 + " configurator -->" );
566 
567             configurator.configureComponent( mojo, configuration, expressionEvaluator, pluginRealm, validator );
568 
569             logger.debug( "-- end configuration --" );
570 
571             Collection<Parameter> missingParameters = validator.getMissingParameters();
572             if ( !missingParameters.isEmpty() )
573             {
574                 if ( "basic".equals( configuratorId ) )
575                 {
576                     throw new PluginParameterException( mojoDescriptor, new ArrayList<Parameter>( missingParameters ) );
577                 }
578                 else
579                 {
580                     /*
581                      * NOTE: Other configurators like the map-oriented one don't call into the listener, so do it the
582                      * hard way.
583                      */
584                     validateParameters( mojoDescriptor, configuration, expressionEvaluator );
585                 }
586             }
587         }
588         catch ( ComponentConfigurationException e )
589         {
590             String message = "Unable to parse configuration of mojo " + mojoDescriptor.getId();
591             if ( e.getFailedConfiguration() != null )
592             {
593                 message += " for parameter " + e.getFailedConfiguration().getName();
594             }
595             message += ": " + e.getMessage();
596 
597             throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(), message, e );
598         }
599         catch ( ComponentLookupException e )
600         {
601             throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(),
602                                                     "Unable to retrieve component configurator " + configuratorId
603                                                         + " for configuration of mojo " + mojoDescriptor.getId(), e );
604         }
605         catch ( NoClassDefFoundError e )
606         {
607             ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 );
608             PrintStream ps = new PrintStream( os );
609             ps.println( "A required class was missing during configuration of mojo " + mojoDescriptor.getId() + ": "
610                 + e.getMessage() );
611             pluginRealm.display( ps );
612 
613             throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(), os.toString(), e );
614         }
615         catch ( LinkageError e )
616         {
617             ByteArrayOutputStream os = new ByteArrayOutputStream( 1024 );
618             PrintStream ps = new PrintStream( os );
619             ps.println( "An API incompatibility was encountered during configuration of mojo " + mojoDescriptor.getId()
620                 + ": " + e.getClass().getName() + ": " + e.getMessage() );
621             pluginRealm.display( ps );
622 
623             throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(), os.toString(), e );
624         }
625         finally
626         {
627             if ( configurator != null )
628             {
629                 try
630                 {
631                     container.release( configurator );
632                 }
633                 catch ( ComponentLifecycleException e )
634                 {
635                     logger.debug( "Failed to release mojo configurator - ignoring." );
636                 }
637             }
638         }
639     }
640 
641     private void validateParameters( MojoDescriptor mojoDescriptor, PlexusConfiguration configuration,
642                                      ExpressionEvaluator expressionEvaluator )
643         throws ComponentConfigurationException, PluginParameterException
644     {
645         if ( mojoDescriptor.getParameters() == null )
646         {
647             return;
648         }
649 
650         List<Parameter> invalidParameters = new ArrayList<Parameter>();
651 
652         for ( Parameter parameter : mojoDescriptor.getParameters() )
653         {
654             if ( !parameter.isRequired() )
655             {
656                 continue;
657             }
658 
659             Object value = null;
660 
661             PlexusConfiguration config = configuration.getChild( parameter.getName(), false );
662             if ( config != null )
663             {
664                 String expression = config.getValue( null );
665 
666                 try
667                 {
668                     value = expressionEvaluator.evaluate( expression );
669 
670                     if ( value == null )
671                     {
672                         value = config.getAttribute( "default-value", null );
673                     }
674                 }
675                 catch ( ExpressionEvaluationException e )
676                 {
677                     String msg =
678                         "Error evaluating the expression '" + expression + "' for configuration value '"
679                             + configuration.getName() + "'";
680                     throw new ComponentConfigurationException( configuration, msg, e );
681                 }
682             }
683 
684             if ( value == null && ( config == null || config.getChildCount() <= 0 ) )
685             {
686                 invalidParameters.add( parameter );
687             }
688         }
689 
690         if ( !invalidParameters.isEmpty() )
691         {
692             throw new PluginParameterException( mojoDescriptor, invalidParameters );
693         }
694     }
695 
696     public void releaseMojo( Object mojo, MojoExecution mojoExecution )
697     {
698         if ( mojo != null )
699         {
700             try
701             {
702                 container.release( mojo );
703             }
704             catch ( ComponentLifecycleException e )
705             {
706                 String goalExecId = mojoExecution.getGoal();
707 
708                 if ( mojoExecution.getExecutionId() != null )
709                 {
710                     goalExecId += " {execution: " + mojoExecution.getExecutionId() + "}";
711                 }
712 
713                 logger.debug( "Error releasing mojo for " + goalExecId, e );
714             }
715         }
716     }
717 
718 }