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