001package org.apache.maven.plugin.internal;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.BufferedInputStream;
023import java.io.ByteArrayOutputStream;
024import java.io.File;
025import java.io.FileInputStream;
026import java.io.IOException;
027import java.io.InputStream;
028import java.io.PrintStream;
029import java.io.Reader;
030import java.util.ArrayList;
031import java.util.Collection;
032import java.util.Collections;
033import java.util.HashMap;
034import java.util.Iterator;
035import java.util.List;
036import java.util.Map;
037import java.util.jar.JarFile;
038import java.util.zip.ZipEntry;
039
040import org.apache.maven.RepositoryUtils;
041import org.apache.maven.artifact.Artifact;
042import org.apache.maven.classrealm.ClassRealmManager;
043import org.apache.maven.execution.MavenSession;
044import org.apache.maven.model.Plugin;
045import org.apache.maven.monitor.logging.DefaultLog;
046import org.apache.maven.plugin.ContextEnabled;
047import org.apache.maven.plugin.DebugConfigurationListener;
048import org.apache.maven.plugin.InvalidPluginDescriptorException;
049import org.apache.maven.plugin.MavenPluginManager;
050import org.apache.maven.plugin.MavenPluginValidator;
051import org.apache.maven.plugin.Mojo;
052import org.apache.maven.plugin.MojoExecution;
053import org.apache.maven.plugin.MojoNotFoundException;
054import org.apache.maven.plugin.PluginConfigurationException;
055import org.apache.maven.plugin.PluginContainerException;
056import org.apache.maven.plugin.PluginDescriptorCache;
057import org.apache.maven.plugin.PluginDescriptorParsingException;
058import org.apache.maven.plugin.PluginIncompatibleException;
059import org.apache.maven.plugin.PluginParameterException;
060import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
061import org.apache.maven.plugin.PluginRealmCache;
062import org.apache.maven.plugin.PluginResolutionException;
063import org.apache.maven.plugin.descriptor.MojoDescriptor;
064import org.apache.maven.plugin.descriptor.Parameter;
065import org.apache.maven.plugin.descriptor.PluginDescriptor;
066import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder;
067import org.apache.maven.project.MavenProject;
068import org.apache.maven.rtinfo.RuntimeInformation;
069import org.codehaus.plexus.PlexusContainer;
070import org.codehaus.plexus.classworlds.realm.ClassRealm;
071import org.codehaus.plexus.component.annotations.Component;
072import org.codehaus.plexus.component.annotations.Requirement;
073import org.codehaus.plexus.component.composition.CycleDetectedInComponentGraphException;
074import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
075import org.codehaus.plexus.component.configurator.ComponentConfigurator;
076import org.codehaus.plexus.component.configurator.ConfigurationListener;
077import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
078import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
079import org.codehaus.plexus.component.repository.ComponentDescriptor;
080import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
081import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
082import org.codehaus.plexus.configuration.PlexusConfiguration;
083import org.codehaus.plexus.configuration.PlexusConfigurationException;
084import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
085import org.codehaus.plexus.logging.Logger;
086import org.codehaus.plexus.logging.LoggerManager;
087import org.codehaus.plexus.util.IOUtil;
088import org.codehaus.plexus.util.ReaderFactory;
089import org.codehaus.plexus.util.StringUtils;
090import org.codehaus.plexus.util.xml.Xpp3Dom;
091import org.eclipse.aether.RepositorySystemSession;
092import org.eclipse.aether.graph.DependencyFilter;
093import org.eclipse.aether.graph.DependencyNode;
094import org.eclipse.aether.repository.RemoteRepository;
095import org.eclipse.aether.util.filter.AndDependencyFilter;
096import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator;
097
098/**
099 * 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 )
107public 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}