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