001    package 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    
022    import java.io.BufferedInputStream;
023    import java.io.ByteArrayOutputStream;
024    import java.io.File;
025    import java.io.FileInputStream;
026    import java.io.IOException;
027    import java.io.InputStream;
028    import java.io.PrintStream;
029    import java.io.Reader;
030    import java.util.ArrayList;
031    import java.util.Collection;
032    import java.util.Collections;
033    import java.util.HashMap;
034    import java.util.Iterator;
035    import java.util.List;
036    import java.util.Map;
037    import java.util.jar.JarFile;
038    import java.util.zip.ZipEntry;
039    
040    import org.apache.maven.RepositoryUtils;
041    import org.apache.maven.artifact.Artifact;
042    import org.apache.maven.classrealm.ClassRealmManager;
043    import org.apache.maven.execution.MavenSession;
044    import org.apache.maven.model.Plugin;
045    import org.apache.maven.monitor.logging.DefaultLog;
046    import org.apache.maven.plugin.ContextEnabled;
047    import org.apache.maven.plugin.DebugConfigurationListener;
048    import org.apache.maven.plugin.InvalidPluginDescriptorException;
049    import org.apache.maven.plugin.MavenPluginManager;
050    import org.apache.maven.plugin.MavenPluginValidator;
051    import org.apache.maven.plugin.Mojo;
052    import org.apache.maven.plugin.MojoExecution;
053    import org.apache.maven.plugin.MojoNotFoundException;
054    import org.apache.maven.plugin.PluginConfigurationException;
055    import org.apache.maven.plugin.PluginContainerException;
056    import org.apache.maven.plugin.PluginDescriptorCache;
057    import org.apache.maven.plugin.PluginDescriptorParsingException;
058    import org.apache.maven.plugin.PluginIncompatibleException;
059    import org.apache.maven.plugin.PluginParameterException;
060    import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
061    import org.apache.maven.plugin.PluginRealmCache;
062    import org.apache.maven.plugin.PluginResolutionException;
063    import org.apache.maven.plugin.descriptor.MojoDescriptor;
064    import org.apache.maven.plugin.descriptor.Parameter;
065    import org.apache.maven.plugin.descriptor.PluginDescriptor;
066    import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder;
067    import org.apache.maven.project.MavenProject;
068    import org.apache.maven.rtinfo.RuntimeInformation;
069    import org.codehaus.plexus.PlexusContainer;
070    import org.codehaus.plexus.classworlds.realm.ClassRealm;
071    import org.codehaus.plexus.component.annotations.Component;
072    import org.codehaus.plexus.component.annotations.Requirement;
073    import org.codehaus.plexus.component.composition.CycleDetectedInComponentGraphException;
074    import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
075    import org.codehaus.plexus.component.configurator.ComponentConfigurator;
076    import org.codehaus.plexus.component.configurator.ConfigurationListener;
077    import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
078    import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
079    import org.codehaus.plexus.component.repository.ComponentDescriptor;
080    import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
081    import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
082    import org.codehaus.plexus.configuration.PlexusConfiguration;
083    import org.codehaus.plexus.configuration.PlexusConfigurationException;
084    import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
085    import org.codehaus.plexus.logging.Logger;
086    import org.codehaus.plexus.util.IOUtil;
087    import org.codehaus.plexus.util.ReaderFactory;
088    import org.codehaus.plexus.util.StringUtils;
089    import org.codehaus.plexus.util.xml.Xpp3Dom;
090    import org.sonatype.aether.RepositorySystemSession;
091    import org.sonatype.aether.graph.DependencyFilter;
092    import org.sonatype.aether.graph.DependencyNode;
093    import org.sonatype.aether.repository.RemoteRepository;
094    import org.sonatype.aether.util.filter.AndDependencyFilter;
095    import org.sonatype.aether.util.graph.PreorderNodeListGenerator;
096    
097    /**
098     * Provides basic services to manage Maven plugins and their mojos. This component is kept general in its design such
099     * 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    }