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