View Javadoc

1   package org.apache.maven.plugin;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.MavenArtifactFilterManager;
23  import org.apache.maven.artifact.Artifact;
24  import org.apache.maven.artifact.factory.ArtifactFactory;
25  import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
26  import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
27  import org.apache.maven.artifact.metadata.ResolutionGroup;
28  import org.apache.maven.artifact.repository.ArtifactRepository;
29  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
30  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
31  import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
32  import org.apache.maven.artifact.resolver.ArtifactResolver;
33  import org.apache.maven.artifact.resolver.MultipleArtifactsNotFoundException;
34  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
35  import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
36  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
37  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
38  import org.apache.maven.artifact.versioning.VersionRange;
39  import org.apache.maven.execution.MavenSession;
40  import org.apache.maven.execution.RuntimeInformation;
41  import org.apache.maven.model.Plugin;
42  import org.apache.maven.model.ReportPlugin;
43  import org.apache.maven.monitor.event.EventDispatcher;
44  import org.apache.maven.monitor.event.MavenEvents;
45  import org.apache.maven.monitor.logging.DefaultLog;
46  import org.apache.maven.plugin.descriptor.MojoDescriptor;
47  import org.apache.maven.plugin.descriptor.Parameter;
48  import org.apache.maven.plugin.descriptor.PluginDescriptor;
49  import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder;
50  import org.apache.maven.plugin.logging.Log;
51  import org.apache.maven.plugin.version.PluginVersionManager;
52  import org.apache.maven.plugin.version.PluginVersionNotFoundException;
53  import org.apache.maven.plugin.version.PluginVersionResolutionException;
54  import org.apache.maven.project.MavenProject;
55  import org.apache.maven.project.MavenProjectBuilder;
56  import org.apache.maven.project.ProjectBuildingException;
57  import org.apache.maven.project.artifact.InvalidDependencyVersionException;
58  import org.apache.maven.project.artifact.MavenMetadataSource;
59  import org.apache.maven.project.path.PathTranslator;
60  import org.apache.maven.reporting.MavenReport;
61  import org.apache.maven.settings.Settings;
62  import org.codehaus.classworlds.ClassRealm;
63  import org.codehaus.classworlds.NoSuchRealmException;
64  import org.codehaus.plexus.PlexusConstants;
65  import org.codehaus.plexus.PlexusContainer;
66  import org.codehaus.plexus.PlexusContainerException;
67  import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
68  import org.codehaus.plexus.component.configurator.ComponentConfigurator;
69  import org.codehaus.plexus.component.configurator.ConfigurationListener;
70  import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
71  import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
72  import org.codehaus.plexus.component.discovery.ComponentDiscoveryListener;
73  import org.codehaus.plexus.component.repository.exception.ComponentLifecycleException;
74  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
75  import org.codehaus.plexus.configuration.PlexusConfiguration;
76  import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
77  import org.codehaus.plexus.context.Context;
78  import org.codehaus.plexus.context.ContextException;
79  import org.codehaus.plexus.logging.AbstractLogEnabled;
80  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
81  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
82  import org.codehaus.plexus.util.StringUtils;
83  import org.codehaus.plexus.util.xml.Xpp3Dom;
84  
85  import java.io.File;
86  import java.net.URL;
87  import java.util.ArrayList;
88  import java.util.Arrays;
89  import java.util.Collection;
90  import java.util.Collections;
91  import java.util.HashMap;
92  import java.util.HashSet;
93  import java.util.Iterator;
94  import java.util.LinkedHashMap;
95  import java.util.LinkedHashSet;
96  import java.util.List;
97  import java.util.Map;
98  import java.util.Set;
99  
100 public class DefaultPluginManager
101     extends AbstractLogEnabled
102     implements PluginManager, Initializable, Contextualizable
103 {
104     protected PlexusContainer container;
105 
106     protected PluginDescriptorBuilder pluginDescriptorBuilder;
107 
108     protected ArtifactFilter artifactFilter;
109 
110     private Log mojoLogger;
111 
112     private Map resolvedCoreArtifactFiles = new HashMap();
113 
114     // component requirements
115     protected PathTranslator pathTranslator;
116 
117     protected MavenPluginCollector pluginCollector;
118 
119     protected PluginVersionManager pluginVersionManager;
120 
121     protected ArtifactFactory artifactFactory;
122 
123     protected ArtifactResolver artifactResolver;
124 
125     protected ArtifactMetadataSource artifactMetadataSource;
126 
127     protected RuntimeInformation runtimeInformation;
128 
129     protected MavenProjectBuilder mavenProjectBuilder;
130     
131     protected PluginMappingManager pluginMappingManager;
132 
133     // END component requirements
134 
135     public DefaultPluginManager()
136     {
137         pluginDescriptorBuilder = new PluginDescriptorBuilder();
138     }
139 
140     // ----------------------------------------------------------------------
141     //
142     // ----------------------------------------------------------------------
143 
144     public PluginDescriptor getPluginDescriptorForPrefix( String prefix )
145     {
146         return pluginCollector.getPluginDescriptorForPrefix( prefix );
147     }
148 
149     public Plugin getPluginDefinitionForPrefix( String prefix,
150                                                 MavenSession session,
151                                                 MavenProject project )
152     {
153         // TODO: since this is only used in the lifecycle executor, maybe it should be moved there? There is no other
154         // use for the mapping manager in here
155         return pluginMappingManager.getByPrefix( prefix, session.getSettings().getPluginGroups(),
156                                                  project.getPluginArtifactRepositories(),
157                                                  session.getLocalRepository() );
158     }
159 
160     public PluginDescriptor verifyPlugin( Plugin plugin,
161                                           MavenProject project,
162                                           Settings settings,
163                                           ArtifactRepository localRepository )
164         throws ArtifactResolutionException, PluginVersionResolutionException, ArtifactNotFoundException,
165         InvalidVersionSpecificationException, InvalidPluginException, PluginManagerException, PluginNotFoundException,
166         PluginVersionNotFoundException
167     {
168         // TODO: this should be possibly outside
169         // All version-resolution logic has been moved to DefaultPluginVersionManager.
170         if ( plugin.getVersion() == null )
171         {
172             String version = pluginVersionManager.resolvePluginVersion( plugin.getGroupId(), plugin.getArtifactId(),
173                                                                         project, settings, localRepository );
174             plugin.setVersion( version );
175         }
176 
177         return verifyVersionedPlugin( plugin, project, localRepository );
178     }
179 
180     private PluginDescriptor verifyVersionedPlugin( Plugin plugin,
181                                                     MavenProject project,
182                                                     ArtifactRepository localRepository )
183         throws PluginVersionResolutionException, ArtifactNotFoundException, ArtifactResolutionException,
184         InvalidVersionSpecificationException, InvalidPluginException, PluginManagerException, PluginNotFoundException
185     {
186         // TODO: this might result in an artifact "RELEASE" being resolved continuously
187         // FIXME: need to find out how a plugin gets marked as 'installed'
188         // and no ChildContainer exists. The check for that below fixes
189         // the 'Can't find plexus container for plugin: xxx' error.
190         try
191         {
192             VersionRange versionRange = VersionRange.createFromVersionSpec( plugin.getVersion() );
193 
194             List remoteRepositories = new ArrayList();
195             remoteRepositories.addAll( project.getPluginArtifactRepositories() );
196             remoteRepositories.addAll( project.getRemoteArtifactRepositories() );
197 
198             checkRequiredMavenVersion( plugin, localRepository, remoteRepositories );
199 
200             Artifact pluginArtifact =
201                 artifactFactory.createPluginArtifact( plugin.getGroupId(), plugin.getArtifactId(), versionRange );
202 
203             pluginArtifact = project.replaceWithActiveArtifact( pluginArtifact );
204 
205             artifactResolver.resolve( pluginArtifact, project.getPluginArtifactRepositories(), localRepository );
206 
207             plugin.setVersion( pluginArtifact.getBaseVersion() );
208 
209             String pluginKey = PluginUtils.constructVersionedKey( plugin );
210 
211             PlexusContainer pluginContainer = container.getChildContainer( pluginKey );
212 
213             File pluginFile = pluginArtifact.getFile();
214 
215             if ( !pluginCollector.isPluginInstalled( plugin ) || ( pluginContainer == null ) )
216             {
217                 addPlugin( plugin, pluginArtifact, project, localRepository );
218             }
219             else if ( pluginFile.lastModified() > pluginContainer.getCreationDate().getTime() )
220             {
221                 getLogger().info(
222                     "Reloading plugin container for: " + pluginKey + ". The plugin artifact has changed." );
223 
224                 pluginContainer.dispose();
225 
226                 pluginCollector.flushPluginDescriptor( plugin );
227 
228                 addPlugin( plugin, pluginArtifact, project, localRepository );
229             }
230 
231             project.addPlugin( plugin );
232         }
233         catch ( ArtifactNotFoundException e )
234         {
235             String groupId = plugin.getGroupId();
236             String artifactId = plugin.getArtifactId();
237             String version = plugin.getVersion();
238 
239             if ( ( groupId == null ) || ( artifactId == null ) || ( version == null ) )
240             {
241                 throw new PluginNotFoundException( e );
242             }
243             else if ( groupId.equals( e.getGroupId() ) && artifactId.equals( e.getArtifactId() ) &&
244                 version.equals( e.getVersion() ) && "maven-plugin".equals( e.getType() ) )
245             {
246                 throw new PluginNotFoundException( e );
247             }
248             else
249             {
250                 throw e;
251             }
252         }
253 
254         return pluginCollector.getPluginDescriptor( plugin );
255     }
256 
257     /**
258      * @todo would be better to store this in the plugin descriptor, but then it won't be available to the version
259      * manager which executes before the plugin is instantiated
260      */
261     private void checkRequiredMavenVersion( Plugin plugin,
262                                             ArtifactRepository localRepository,
263                                             List remoteRepositories )
264         throws PluginVersionResolutionException, InvalidPluginException
265     {
266         try
267         {
268             Artifact artifact = artifactFactory.createProjectArtifact( plugin.getGroupId(), plugin.getArtifactId(),
269                                                                        plugin.getVersion() );
270             MavenProject project =
271                 mavenProjectBuilder.buildFromRepository( artifact, remoteRepositories, localRepository, false );
272             // if we don't have the required Maven version, then ignore an update
273             if ( ( project.getPrerequisites() != null ) && ( project.getPrerequisites().getMaven() != null ) )
274             {
275                 DefaultArtifactVersion requiredVersion =
276                     new DefaultArtifactVersion( project.getPrerequisites().getMaven() );
277                 if ( runtimeInformation.getApplicationVersion().compareTo( requiredVersion ) < 0 )
278                 {
279                     throw new PluginVersionResolutionException( plugin.getGroupId(), plugin.getArtifactId(),
280                                                                 "Plugin requires Maven version " + requiredVersion );
281                 }
282             }
283         }
284         catch ( ProjectBuildingException e )
285         {
286             throw new InvalidPluginException(
287                 "Unable to build project for plugin '" + plugin.getKey() + "': " + e.getMessage(), e );
288         }
289     }
290 
291     protected void addPlugin( Plugin plugin,
292                               Artifact pluginArtifact,
293                               MavenProject project,
294                               ArtifactRepository localRepository )
295         throws PluginManagerException, InvalidPluginException
296     {
297         PlexusContainer child;
298 
299         try
300         {
301             MavenPluginValidator validator = new MavenPluginValidator( pluginArtifact );
302 
303             String key = PluginUtils.constructVersionedKey( plugin ).intern();
304             child = container.createChildContainer( key,
305                                                     Collections.singletonList( pluginArtifact.getFile() ),
306                                                     Collections.EMPTY_MAP,
307                                                     Arrays.asList( new ComponentDiscoveryListener[] { validator, pluginCollector } ) );
308 
309             // remove listeners for efficiency since they are only needed for the initial stage and
310             // should not be applied to the plugin's dependencies
311             child.removeComponentDiscoveryListener( validator );
312             child.removeComponentDiscoveryListener( pluginCollector );
313 
314             if ( validator.hasErrors() )
315             {
316                 String msg = "Plugin '" + key + "' has an invalid descriptor:";
317                 int count = 1;
318                 for ( Iterator i = validator.getErrors().iterator(); i.hasNext(); )
319                 {
320                     msg += "\n" + count + ") " + i.next();
321                     count++;
322                 }
323                 throw new PluginManagerException( msg );
324             }
325 
326             try
327             {
328                 child.getContainerRealm().importFrom( "plexus.core", "org.codehaus.plexus.util.xml.Xpp3Dom" );
329                 child.getContainerRealm().importFrom( "plexus.core", "org.codehaus.plexus.util.xml.pull.XmlPullParser" );
330                 child.getContainerRealm().importFrom( "plexus.core", "org.codehaus.plexus.util.xml.pull.XmlPullParserException" );
331                 child.getContainerRealm().importFrom( "plexus.core", "org.codehaus.plexus.util.xml.pull.XmlSerializer" );
332 
333                 // MNG-2878
334                 child.getContainerRealm().importFrom( "plexus.core", "/default-report.xml" );
335             }
336             catch ( NoSuchRealmException e )
337             {
338                 // won't happen
339             }
340         }
341         catch ( PlexusContainerException e )
342         {
343             throw new PluginManagerException(
344                 "Failed to create plugin container for plugin '" + plugin + "': " + e.getMessage(), e );
345         }
346 
347         // this plugin's descriptor should have been discovered in the child creation, so we should be able to
348         // circle around and set the artifacts and class realm
349         PluginDescriptor addedPlugin = pluginCollector.getPluginDescriptor( plugin );
350 
351         if ( addedPlugin == null )
352         {
353             throw new IllegalStateException( "The plugin descriptor for the plugin " + plugin + " was not found."
354                 + " Please verify that the plugin JAR " + pluginArtifact.getFile() + " is intact." );
355         }
356 
357         addedPlugin.setClassRealm( child.getContainerRealm() );
358 
359         // we're only setting the plugin's artifact itself as the artifact list, to allow it to be retrieved
360         // later when the plugin is first invoked. Retrieving this artifact will in turn allow us to
361         // transitively resolve its dependencies, and add them to the plugin container...
362         addedPlugin.setArtifacts( Collections.singletonList( pluginArtifact ) );
363         addedPlugin.setPluginArtifact( pluginArtifact );
364 
365         try
366         {
367             // the only Plugin instance which will have dependencies is the one specified in the project.
368             // We need to look for a Plugin instance there, in case the instance we're using didn't come from
369             // the project.
370             Plugin projectPlugin = (Plugin) project.getBuild().getPluginsAsMap().get( plugin.getKey() );
371 
372             if ( projectPlugin == null )
373             {
374                 projectPlugin = plugin;
375             }
376 
377             Set artifacts = MavenMetadataSource.createArtifacts( artifactFactory, projectPlugin.getDependencies(), null,
378                                                                  null, project );
379 
380 //            Set artifacts =
381 //                MavenMetadataSource.createArtifacts( artifactFactory, plugin.getDependencies(), null, null, project );
382 
383             addedPlugin.setIntroducedDependencyArtifacts( artifacts );
384         }
385         catch ( InvalidDependencyVersionException e )
386         {
387             throw new InvalidPluginException( "Plugin '" + plugin + "' is invalid: " + e.getMessage(), e );
388         }
389     }
390 
391     // ----------------------------------------------------------------------
392     // Mojo execution
393     // ----------------------------------------------------------------------
394 
395     public void executeMojo( MavenProject project,
396                              MojoExecution mojoExecution,
397                              MavenSession session )
398         throws ArtifactResolutionException, MojoExecutionException, MojoFailureException, ArtifactNotFoundException,
399         InvalidDependencyVersionException, PluginManagerException, PluginConfigurationException
400     {
401         MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
402 
403         // NOTE: I'm putting these checks in here, since this is the central point of access for
404         // anything that wants to execute a mojo.
405         if ( mojoDescriptor.isProjectRequired() && !session.isUsingPOMsFromFilesystem() )
406         {
407             throw new MojoExecutionException( "Cannot execute mojo: " + mojoDescriptor.getGoal() +
408                 ". It requires a project with an existing pom.xml, but the build is not using one." );
409         }
410 
411         if ( mojoDescriptor.isOnlineRequired() && session.getSettings().isOffline() )
412         {
413             // TODO: Should we error out, or simply warn and skip??
414             throw new MojoExecutionException( "Mojo: " + mojoDescriptor.getGoal() +
415                 " requires online mode for execution. Maven is currently offline." );
416         }
417 
418         if ( mojoDescriptor.isDependencyResolutionRequired() != null )
419         {
420             Collection projects;
421 
422             if ( mojoDescriptor.isAggregator() )
423             {
424                 projects = session.getSortedProjects();
425             }
426             else
427             {
428                 projects = Collections.singleton( project );
429             }
430 
431             for ( Iterator i = projects.iterator(); i.hasNext(); )
432             {
433                 MavenProject p = (MavenProject) i.next();
434 
435                 resolveTransitiveDependencies( session, artifactResolver,
436                                                mojoDescriptor.isDependencyResolutionRequired(), artifactFactory, p, mojoDescriptor.isAggregator() );
437             }
438 
439             downloadDependencies( project, session, artifactResolver );
440         }
441 
442         String goalName = mojoDescriptor.getFullGoalName();
443 
444         Mojo plugin;
445 
446         PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor();
447 
448         String goalId = mojoDescriptor.getGoal();
449         String groupId = pluginDescriptor.getGroupId();
450         String artifactId = pluginDescriptor.getArtifactId();
451         String executionId = mojoExecution.getExecutionId();
452 
453         Xpp3Dom dom = project.getGoalConfiguration( groupId, artifactId, executionId, goalId );
454         Xpp3Dom reportDom = project.getReportConfiguration( groupId, artifactId, executionId );
455         dom = Xpp3Dom.mergeXpp3Dom( dom, reportDom );
456         if ( mojoExecution.getConfiguration() != null )
457         {
458             dom = Xpp3Dom.mergeXpp3Dom( dom, mojoExecution.getConfiguration() );
459         }
460 
461         plugin = getConfiguredMojo( session, dom, project, false, mojoExecution );
462 
463         // Event monitoring.
464         String event = MavenEvents.MOJO_EXECUTION;
465         EventDispatcher dispatcher = session.getEventDispatcher();
466 
467         String goalExecId = goalName;
468 
469         if ( mojoExecution.getExecutionId() != null )
470         {
471             goalExecId += " {execution: " + mojoExecution.getExecutionId() + "}";
472         }
473 
474         dispatcher.dispatchStart( event, goalExecId );
475 
476         ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
477 
478         try
479         {
480             Thread.currentThread().setContextClassLoader(
481                 mojoDescriptor.getPluginDescriptor().getClassRealm().getClassLoader() );
482 
483             plugin.execute();
484 
485             dispatcher.dispatchEnd( event, goalExecId );
486         }
487         catch ( MojoExecutionException e )
488         {
489             session.getEventDispatcher().dispatchError( event, goalExecId, e );
490 
491             throw e;
492         }
493         catch ( MojoFailureException e )
494         {
495             session.getEventDispatcher().dispatchError( event, goalExecId, e );
496 
497             throw e;
498         }
499         catch ( LinkageError e )
500         {
501             if ( getLogger().isFatalErrorEnabled() )
502             {
503                 getLogger().fatalError(
504                                         plugin.getClass().getName() + "#execute() caused a linkage error ("
505                                             + e.getClass().getName() + ") and may be out-of-date. Check the realms:" );
506 
507                 ClassRealm pluginRealm = mojoDescriptor.getPluginDescriptor().getClassRealm();
508                 StringBuffer sb = new StringBuffer();
509                 sb.append( "Plugin realm = " + pluginRealm.getId() ).append( '\n' );
510                 for ( int i = 0; i < pluginRealm.getConstituents().length; i++ )
511                 {
512                     sb.append( "urls[" + i + "] = " + pluginRealm.getConstituents()[i] );
513                     if ( i != ( pluginRealm.getConstituents().length - 1 ) )
514                     {
515                         sb.append( '\n' );
516                     }
517                 }
518                 getLogger().fatalError( sb.toString() );
519 
520                 ClassRealm containerRealm = container.getContainerRealm();
521                 sb = new StringBuffer();
522                 sb.append( "Container realm = " + containerRealm.getId() ).append( '\n' );
523                 for ( int i = 0; i < containerRealm.getConstituents().length; i++ )
524                 {
525                     sb.append( "urls[" + i + "] = " + containerRealm.getConstituents()[i] );
526                     if ( i != ( containerRealm.getConstituents().length - 1 ) )
527                     {
528                         sb.append( '\n' );
529                     }
530                 }
531                 getLogger().fatalError( sb.toString() );
532             }
533 
534             session.getEventDispatcher().dispatchError( event, goalExecId, e );
535 
536             throw e;
537         }
538         finally
539         {
540 
541             Thread.currentThread().setContextClassLoader( oldClassLoader );
542 
543             try
544             {
545                 PlexusContainer pluginContainer = getPluginContainer( mojoDescriptor.getPluginDescriptor() );
546 
547                 pluginContainer.release( plugin );
548             }
549             catch ( ComponentLifecycleException e )
550             {
551                 if ( getLogger().isErrorEnabled() )
552                 {
553                     getLogger().error( "Error releasing plugin - ignoring.", e );
554                 }
555             }
556         }
557     }
558 
559 
560     public MavenReport getReport( MavenProject project,
561                                   MojoExecution mojoExecution,
562                                   MavenSession session )
563         throws ArtifactNotFoundException, PluginConfigurationException, PluginManagerException,
564         ArtifactResolutionException
565     {
566         MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
567         PluginDescriptor descriptor = mojoDescriptor.getPluginDescriptor();
568 
569         Xpp3Dom dom = project.getReportConfiguration( descriptor.getGroupId(), descriptor.getArtifactId(),
570                                                       mojoExecution.getExecutionId() );
571         if ( mojoExecution.getConfiguration() != null )
572         {
573             dom = Xpp3Dom.mergeXpp3Dom( dom, mojoExecution.getConfiguration() );
574         }
575 
576         MavenReport report = (MavenReport) getConfiguredMojo( session, dom, project, true, mojoExecution );
577         
578         return report;
579     }
580 
581     public PluginDescriptor verifyReportPlugin( ReportPlugin reportPlugin,
582                                                 MavenProject project,
583                                                 MavenSession session )
584         throws PluginVersionResolutionException, ArtifactResolutionException, ArtifactNotFoundException,
585         InvalidVersionSpecificationException, InvalidPluginException, PluginManagerException, PluginNotFoundException,
586         PluginVersionNotFoundException
587     {
588         String version = reportPlugin.getVersion();
589 
590         if ( version == null )
591         {
592             version = pluginVersionManager.resolveReportPluginVersion( reportPlugin.getGroupId(),
593                                                                        reportPlugin.getArtifactId(), project,
594                                                                        session.getSettings(),
595                                                                        session.getLocalRepository() );
596             reportPlugin.setVersion( version );
597         }
598 
599         Plugin forLookup = new Plugin();
600 
601         forLookup.setGroupId( reportPlugin.getGroupId() );
602         forLookup.setArtifactId( reportPlugin.getArtifactId() );
603         forLookup.setVersion( version );
604 
605         return verifyVersionedPlugin( forLookup, project, session.getLocalRepository() );
606     }
607 
608     private PlexusContainer getPluginContainer( PluginDescriptor pluginDescriptor )
609         throws PluginManagerException
610     {
611         String pluginKey = PluginUtils.constructVersionedKey( pluginDescriptor );
612 
613         PlexusContainer pluginContainer = container.getChildContainer( pluginKey );
614 
615         if ( pluginContainer == null )
616         {
617             throw new PluginManagerException( "Cannot find Plexus container for plugin: " + pluginKey );
618         }
619 
620         return pluginContainer;
621     }
622 
623     private Mojo getConfiguredMojo( MavenSession session,
624                                     Xpp3Dom dom,
625                                     MavenProject project,
626                                     boolean report,
627                                     MojoExecution mojoExecution )
628         throws PluginConfigurationException, ArtifactNotFoundException, PluginManagerException,
629         ArtifactResolutionException
630     {
631         MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
632 
633         PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor();
634 
635         PlexusContainer pluginContainer = getPluginContainer( pluginDescriptor );
636 
637         // if this is the first time this plugin has been used, the plugin's container will only
638         // contain the plugin's artifact in isolation; we need to finish resolving the plugin's
639         // dependencies, and add them to the container.
640         ensurePluginContainerIsComplete( pluginDescriptor, pluginContainer, project, session );
641 
642         Mojo plugin;
643         try
644         {
645             plugin = (Mojo) pluginContainer.lookup( Mojo.ROLE, mojoDescriptor.getRoleHint() );
646             if ( report && !( plugin instanceof MavenReport ) )
647             {
648                 // TODO: the mojoDescriptor should actually capture this information so we don't get this far
649                 return null;
650             }
651         }
652         catch ( ComponentLookupException e )
653         {
654             Throwable cause = e.getCause();
655             while( cause != null && !(cause instanceof NoClassDefFoundError ) )
656             {
657                 cause = cause.getCause();
658             }
659             
660             if ( cause != null && ( cause instanceof NoClassDefFoundError ) )
661             {
662                 throw new PluginManagerException( "Unable to load the mojo '" + mojoDescriptor.getRoleHint()
663                                                   + "' in the plugin '" + pluginDescriptor.getPluginLookupKey() + "'. A required class is missing: "
664                                                   + cause.getMessage(), e );
665             }
666             
667             throw new PluginManagerException( "Unable to find the mojo '" + mojoDescriptor.getGoal() +
668                 "' (or one of its required components) in the plugin '" + pluginDescriptor.getPluginLookupKey() + "'", e );
669         }
670         catch ( NoClassDefFoundError e )
671         {
672             throw new PluginManagerException( "Unable to load the mojo '" + mojoDescriptor.getRoleHint()
673                 + "' in the plugin '" + pluginDescriptor.getPluginLookupKey() + "'. A required class is missing: "
674                 + e.getMessage(), e );
675         }
676 
677         if ( plugin instanceof ContextEnabled )
678         {
679             Map pluginContext = session.getPluginContext( pluginDescriptor, project );
680 
681             ( (ContextEnabled) plugin ).setPluginContext( pluginContext );
682         }
683 
684         plugin.setLog( mojoLogger );
685 
686         XmlPlexusConfiguration pomConfiguration;
687         if ( dom == null )
688         {
689             pomConfiguration = new XmlPlexusConfiguration( "configuration" );
690         }
691         else
692         {
693             pomConfiguration = new XmlPlexusConfiguration( dom );
694         }
695 
696         // Validate against non-editable (@readonly) parameters, to make sure users aren't trying to
697         // override in the POM.
698         validatePomConfiguration( mojoDescriptor, pomConfiguration );
699 
700         PlexusConfiguration mergedConfiguration = mergeMojoConfiguration( pomConfiguration, mojoDescriptor );
701 
702         // TODO: plexus changes to make this more like the component descriptor so this can be used instead
703         //            PlexusConfiguration mergedConfiguration = mergeConfiguration( pomConfiguration,
704         //                                                                          mojoDescriptor.getConfiguration() );
705 
706         ExpressionEvaluator expressionEvaluator = new PluginParameterExpressionEvaluator( session, mojoExecution,
707                                                                                           pathTranslator,
708                                                                                           getLogger(),
709                                                                                           project,
710                                                                                           session.getExecutionProperties() );
711 
712         PlexusConfiguration extractedMojoConfiguration =
713             extractMojoConfiguration( mergedConfiguration, mojoDescriptor );
714 
715         checkRequiredParameters( mojoDescriptor, extractedMojoConfiguration, expressionEvaluator );
716 
717         populatePluginFields( plugin, mojoDescriptor, extractedMojoConfiguration, pluginContainer,
718                               expressionEvaluator );
719         return plugin;
720     }
721 
722     private void ensurePluginContainerIsComplete( PluginDescriptor pluginDescriptor,
723                                                   PlexusContainer pluginContainer,
724                                                   MavenProject project,
725                                                   MavenSession session )
726         throws ArtifactNotFoundException, PluginManagerException, ArtifactResolutionException
727     {
728         // if the plugin's already been used once, don't re-do this step...
729         // otherwise, we have to finish resolving the plugin's classpath and start the container.
730         if ( ( pluginDescriptor.getArtifacts() != null ) && ( pluginDescriptor.getArtifacts().size() == 1 ) )
731         {
732             Artifact pluginArtifact = (Artifact) pluginDescriptor.getArtifacts().get( 0 );
733 
734             ArtifactRepository localRepository = session.getLocalRepository();
735 
736             ResolutionGroup resolutionGroup;
737             try
738             {
739                 resolutionGroup = artifactMetadataSource.retrieve( pluginArtifact, localRepository,
740                                                                    project.getPluginArtifactRepositories() );
741             }
742             catch ( ArtifactMetadataRetrievalException e )
743             {
744                 throw new ArtifactResolutionException( "Unable to download metadata from repository for plugin '" +
745                     pluginArtifact.getId() + "': " + e.getMessage(), pluginArtifact, e );
746             }
747 
748             Set rgArtifacts = resolutionGroup.getArtifacts();
749 
750             rgArtifacts = checkPlexusUtils( rgArtifacts, artifactFactory );
751 
752             // [jdcasey; 20-March-2008]:
753             // This is meant to eliminate the introduction of duplicated artifacts.
754             // Since much of the reasoning for reversing the order of introduction of
755             // plugin dependencies rests on the notion that we need to be able to
756             // introduce upgraded versions of plugin dependencies on a case-by-case
757             // basis, we need to remove the original version prior to artifact
758             // resolution. This is consistent with recent thinking on duplicated
759             // dependency specifications within a POM, where that case should
760             // throw a model validation exception.
761             //
762             // Here, we just want to remove any chance that the ArtifactCollector
763             // could make a bad choice, and use the old version in spite of our
764             // explicit preference otherwise.
765 
766             // First, we're going to accumulate plugin dependencies in an ordered map,
767             // keyed by dependencyConflictId (the ordered map is meant to preserve relative
768             // ordering of the dependencies that do make the cut).
769             Map dependencyMap = new LinkedHashMap();
770 
771             // Next, we need to accumulate all dependencies in a List, to make it
772             // simpler to iterate through them all and add them to the map.
773             List all = new ArrayList();
774 
775             // plugin-level dependencies from the consuming POM override dependencies
776             // from the plugin's own POM.
777             all.addAll( pluginDescriptor.getIntroducedDependencyArtifacts() );
778 
779             // add in the deps from the plugin POM now.
780             all.addAll( rgArtifacts );
781 
782             for ( Iterator it = all.iterator(); it.hasNext(); )
783             {
784                 Artifact artifact = (Artifact) it.next();
785                 String conflictId = artifact.getDependencyConflictId();
786 
787                 // if the map already contains this dependencyConflictId, it constitutes an
788                 // overridden dependency. Don't use the old one (we know it's old from the
789                 // order in which dependencies were added to this list).
790                 if ( !dependencyMap.containsKey( conflictId ) )
791                 {
792                     dependencyMap.put( conflictId, artifact );
793                 }
794             }
795 
796             // Create an ordered set of dependencies from the ordered map we used above, to feed into the resolver.
797             Set dependencies = new LinkedHashSet( dependencyMap.values() );
798 
799             if ( getLogger().isDebugEnabled() )
800             {
801                 // list all dependencies to be used by this plugin (first-level deps, not transitive ones).
802                 getLogger().debug( "Plugin dependencies for:\n\n" + pluginDescriptor.getId()
803                                    + "\n\nare:\n\n"
804                                    + StringUtils.join( dependencies.iterator(), "\n" ) + "\n\n" );
805             }
806 
807             List repositories = new ArrayList();
808             repositories.addAll( resolutionGroup.getResolutionRepositories() );
809             repositories.addAll( project.getRemoteArtifactRepositories() );
810 
811             /* get plugin managed versions */
812             Map pluginManagedDependencies = new HashMap();
813             try
814             {
815                 MavenProject pluginProject =
816                     mavenProjectBuilder.buildFromRepository( pluginArtifact, project.getRemoteArtifactRepositories(),
817                                                              localRepository );
818                 if ( pluginProject != null )
819                 {
820                     pluginManagedDependencies = pluginProject.getManagedVersionMap();
821                 }
822             }
823             catch ( ProjectBuildingException e )
824             {
825                 // this can't happen, it would have blowed up at artifactMetadataSource.retrieve()
826             }
827 
828             ArtifactResolutionResult result = artifactResolver.resolveTransitively( dependencies, pluginArtifact,
829                                                                                     pluginManagedDependencies,
830                                                                                     localRepository, repositories,
831                                                                                     artifactMetadataSource,
832                                                                                     artifactFilter );
833 
834             Set resolved = result.getArtifacts();
835 
836             for ( Iterator it = resolved.iterator(); it.hasNext(); )
837             {
838                 Artifact artifact = (Artifact) it.next();
839 
840                 if ( !artifact.equals( pluginArtifact ) )
841                 {
842                     artifact = project.replaceWithActiveArtifact( artifact );
843 
844                     try
845                     {
846                         pluginContainer.addJarResource( artifact.getFile() );
847                     }
848                     catch ( PlexusContainerException e )
849                     {
850                         throw new PluginManagerException( "Error adding plugin dependency '" +
851                             artifact.getDependencyConflictId() + "' into plugin manager: " + e.getMessage(), e );
852                     }
853                 }
854             }
855 
856             pluginDescriptor.setClassRealm( pluginContainer.getContainerRealm() );
857 
858             List unresolved = new ArrayList( dependencies );
859 
860             unresolved.removeAll( resolved );
861 
862             if ( getLogger().isDebugEnabled() )
863             {
864                 // list all artifacts that were filtered out during the resolution process.
865                 // these are already present in the core container.
866                 getLogger().debug( " The following artifacts were filtered out for plugin: "
867                                    + pluginDescriptor.getId()
868                                    + " because they're already in the core of Maven:\n\n"
869                                    + StringUtils.join( unresolved.iterator(), "\n" )
870                                    + "\n\nThese will use the artifact files already in the core ClassRealm instead, to allow them to be included in PluginDescriptor.getArtifacts().\n\n" );
871             }
872 
873             // Grab a file for all filtered artifacts, even if it means resolving them. This
874             // is necessary in order to present a full complement of a plugin's transitive
875             // dependencies to anyone who calls PluginDescriptor.getArtifacts().
876             resolveCoreArtifacts( unresolved, localRepository, resolutionGroup.getResolutionRepositories() );
877 
878             // Re-join resolved and filtered-but-now-resolved artifacts.
879             // NOTE: The process of filtering then re-adding some artifacts will
880             // result in different ordering within the PluginDescriptor.getArtifacts()
881             // List than should have happened if none had been filtered. All filtered
882             // artifacts will be listed last...
883             List allResolved = new ArrayList( resolved.size() + unresolved.size() );
884 
885             allResolved.addAll( resolved );
886             allResolved.addAll( unresolved );
887 
888             pluginDescriptor.setArtifacts( allResolved );
889         }
890     }
891 
892     public static Set checkPlexusUtils( Set dependencyArtifacts, ArtifactFactory artifactFactory )
893     {
894         // ----------------------------------------------------------------------------
895         // If the plugin already declares a dependency on plexus-utils then we're all
896         // set as the plugin author is aware of its use. If we don't have a dependency
897         // on plexus-utils then we must protect users from stupid plugin authors who
898         // did not declare a direct dependency on plexus-utils because the version
899         // Maven uses is hidden from downstream use. We will also bump up any
900         // anything below 1.1 to 1.1 as this mimics the behaviour in 2.0.5 where
901         // plexus-utils 1.1 was being forced into use.
902         // ----------------------------------------------------------------------------
903 
904         VersionRange vr = null;
905 
906         try
907         {
908             vr = VersionRange.createFromVersionSpec( "[1.1,)" );
909         }
910         catch ( InvalidVersionSpecificationException e )
911         {
912             // Won't happen
913         }
914 
915         boolean plexusUtilsPresent = false;
916 
917         for ( Iterator i = dependencyArtifacts.iterator(); i.hasNext(); )
918         {
919             Artifact a = (Artifact) i.next();
920 
921             if ( a.getArtifactId().equals( "plexus-utils" ) &&
922                 vr.containsVersion( new DefaultArtifactVersion( a.getVersion() ) ) )
923             {
924                 plexusUtilsPresent = true;
925 
926                 break;
927             }
928         }
929 
930         if ( !plexusUtilsPresent )
931         {
932             // We will add plexus-utils as every plugin was getting this anyway from Maven itself. We will set the
933             // version to the latest version we know that works as of the 2.0.6 release. We set the scope to runtime
934             // as this is what's implicitly happening in 2.0.6.
935 
936             Set result = new LinkedHashSet();
937             if ( !dependencyArtifacts.isEmpty() )
938             {
939                 result.addAll( dependencyArtifacts );
940             }
941 
942             result.add( artifactFactory.createArtifact( "org.codehaus.plexus", "plexus-utils", "1.1", Artifact.SCOPE_RUNTIME, "jar" ) );
943 
944             return result;
945         }
946         else
947         {
948             return dependencyArtifacts;
949         }
950     }
951 
952     private void resolveCoreArtifacts( List unresolved,
953                                        ArtifactRepository localRepository,
954                                        List resolutionRepositories )
955         throws ArtifactResolutionException, ArtifactNotFoundException
956     {
957         for ( Iterator it = unresolved.iterator(); it.hasNext(); )
958         {
959             Artifact artifact = (Artifact) it.next();
960 
961             File artifactFile = (File) resolvedCoreArtifactFiles.get( artifact.getId() );
962 
963             if ( artifactFile == null )
964             {
965                 String resource =
966                     "/META-INF/maven/" + artifact.getGroupId() + "/" + artifact.getArtifactId() + "/pom.xml";
967 
968                 URL resourceUrl = container.getContainerRealm().getResource( resource );
969 
970                 if ( resourceUrl == null )
971                 {
972                     artifactResolver.resolve( artifact, resolutionRepositories, localRepository );
973 
974                     artifactFile = artifact.getFile();
975                 }
976                 else
977                 {
978                     String artifactPath = resourceUrl.getPath();
979 
980                     if ( artifactPath.startsWith( "file:" ) )
981                     {
982                         artifactPath = artifactPath.substring( "file:".length() );
983                     }
984 
985                     artifactPath = artifactPath.substring( 0, artifactPath.length() - resource.length() );
986 
987                     if ( artifactPath.endsWith( "/" ) )
988                     {
989                         artifactPath = artifactPath.substring( 0, artifactPath.length() - 1 );
990                     }
991 
992                     if ( artifactPath.endsWith( "!" ) )
993                     {
994                         artifactPath = artifactPath.substring( 0, artifactPath.length() - 1 );
995                     }
996 
997                     artifactFile = new File( artifactPath ).getAbsoluteFile();
998                 }
999 
1000                 resolvedCoreArtifactFiles.put( artifact.getId(), artifactFile );
1001             }
1002 
1003             artifact.setFile( artifactFile );
1004         }
1005     }
1006 
1007     private PlexusConfiguration extractMojoConfiguration( PlexusConfiguration mergedConfiguration,
1008                                                           MojoDescriptor mojoDescriptor )
1009     {
1010         Map parameterMap = mojoDescriptor.getParameterMap();
1011 
1012         PlexusConfiguration[] mergedChildren = mergedConfiguration.getChildren();
1013 
1014         XmlPlexusConfiguration extractedConfiguration = new XmlPlexusConfiguration( "configuration" );
1015 
1016         for ( int i = 0; i < mergedChildren.length; i++ )
1017         {
1018             PlexusConfiguration child = mergedChildren[i];
1019 
1020             if ( parameterMap.containsKey( child.getName() ) )
1021             {
1022                 extractedConfiguration.addChild( copyConfiguration( child ) );
1023             }
1024             else
1025             {
1026                 // TODO: I defy anyone to find these messages in the '-X' output! Do we need a new log level?
1027                 // ideally, this would be elevated above the true debug output, but below the default INFO level...
1028                 // [BP] (2004-07-18): need to understand the context more but would prefer this could be either WARN or
1029                 // removed - shouldn't need DEBUG to diagnose a problem most of the time.
1030                 getLogger().debug( "*** WARNING: Configuration \'" + child.getName() + "\' is not used in goal \'" +
1031                     mojoDescriptor.getFullGoalName() + "; this may indicate a typo... ***" );
1032             }
1033         }
1034 
1035         return extractedConfiguration;
1036     }
1037 
1038     private void checkRequiredParameters( MojoDescriptor goal,
1039                                           PlexusConfiguration configuration,
1040                                           ExpressionEvaluator expressionEvaluator )
1041         throws PluginConfigurationException
1042     {
1043         // TODO: this should be built in to the configurator, as we presently double process the expressions
1044 
1045         List parameters = goal.getParameters();
1046 
1047         if ( parameters == null )
1048         {
1049             return;
1050         }
1051 
1052         List invalidParameters = new ArrayList();
1053 
1054         for ( int i = 0; i < parameters.size(); i++ )
1055         {
1056             Parameter parameter = (Parameter) parameters.get( i );
1057 
1058             if ( parameter.isRequired() )
1059             {
1060                 // the key for the configuration map we're building.
1061                 String key = parameter.getName();
1062 
1063                 Object fieldValue = null;
1064                 String expression = null;
1065                 PlexusConfiguration value = configuration.getChild( key, false );
1066                 try
1067                 {
1068                     if ( value != null )
1069                     {
1070                         expression = value.getValue( null );
1071 
1072                         fieldValue = expressionEvaluator.evaluate( expression );
1073 
1074                         if ( fieldValue == null )
1075                         {
1076                             fieldValue = value.getAttribute( "default-value", null );
1077                         }
1078                     }
1079 
1080                     if ( ( fieldValue == null ) && StringUtils.isNotEmpty( parameter.getAlias() ) )
1081                     {
1082                         value = configuration.getChild( parameter.getAlias(), false );
1083                         if ( value != null )
1084                         {
1085                             expression = value.getValue( null );
1086                             fieldValue = expressionEvaluator.evaluate( expression );
1087                             if ( fieldValue == null )
1088                             {
1089                                 fieldValue = value.getAttribute( "default-value", null );
1090                             }
1091                         }
1092                     }
1093                 }
1094                 catch ( ExpressionEvaluationException e )
1095                 {
1096                     throw new PluginConfigurationException( goal.getPluginDescriptor(), e.getMessage(), e );
1097                 }
1098 
1099                 // only mark as invalid if there are no child nodes
1100                 if ( ( fieldValue == null ) && ( ( value == null ) || ( value.getChildCount() == 0 ) ) )
1101                 {
1102                     parameter.setExpression( expression );
1103                     invalidParameters.add( parameter );
1104                 }
1105             }
1106         }
1107 
1108         if ( !invalidParameters.isEmpty() )
1109         {
1110             throw new PluginParameterException( goal, invalidParameters );
1111         }
1112     }
1113 
1114     private void validatePomConfiguration( MojoDescriptor goal,
1115                                            PlexusConfiguration pomConfiguration )
1116         throws PluginConfigurationException
1117     {
1118         List parameters = goal.getParameters();
1119 
1120         if ( parameters == null )
1121         {
1122             return;
1123         }
1124 
1125         for ( int i = 0; i < parameters.size(); i++ )
1126         {
1127             Parameter parameter = (Parameter) parameters.get( i );
1128 
1129             // the key for the configuration map we're building.
1130             String key = parameter.getName();
1131 
1132             PlexusConfiguration value = pomConfiguration.getChild( key, false );
1133 
1134             if ( ( value == null ) && StringUtils.isNotEmpty( parameter.getAlias() ) )
1135             {
1136                 key = parameter.getAlias();
1137                 value = pomConfiguration.getChild( key, false );
1138             }
1139 
1140             if ( value != null )
1141             {
1142                 // Make sure the parameter is either editable/configurable, or else is NOT specified in the POM
1143                 if ( !parameter.isEditable() )
1144                 {
1145                     StringBuffer errorMessage = new StringBuffer()
1146                         .append( "ERROR: Cannot override read-only parameter: " );
1147                     errorMessage.append( key );
1148                     errorMessage.append( " in goal: " ).append( goal.getFullGoalName() );
1149 
1150                     throw new PluginConfigurationException( goal.getPluginDescriptor(), errorMessage.toString() );
1151                 }
1152 
1153                 String deprecated = parameter.getDeprecated();
1154                 if ( StringUtils.isNotEmpty( deprecated ) )
1155                 {
1156                     getLogger().warn( "DEPRECATED [" + parameter.getName() + "]: " + deprecated );
1157                 }
1158             }
1159         }
1160     }
1161 
1162     private PlexusConfiguration mergeMojoConfiguration( XmlPlexusConfiguration fromPom,
1163                                                         MojoDescriptor mojoDescriptor )
1164     {
1165         XmlPlexusConfiguration result = new XmlPlexusConfiguration( fromPom.getName() );
1166         result.setValue( fromPom.getValue( null ) );
1167 
1168         if ( mojoDescriptor.getParameters() != null )
1169         {
1170             PlexusConfiguration fromMojo = mojoDescriptor.getMojoConfiguration();
1171 
1172             for ( Iterator it = mojoDescriptor.getParameters().iterator(); it.hasNext(); )
1173             {
1174                 Parameter parameter = (Parameter) it.next();
1175 
1176                 String paramName = parameter.getName();
1177                 String alias = parameter.getAlias();
1178                 String implementation = parameter.getImplementation();
1179 
1180                 PlexusConfiguration pomConfig = fromPom.getChild( paramName );
1181                 PlexusConfiguration aliased = null;
1182 
1183                 if ( alias != null )
1184                 {
1185                     aliased = fromPom.getChild( alias );
1186                 }
1187 
1188                 PlexusConfiguration mojoConfig = fromMojo.getChild( paramName, false );
1189 
1190                 // first we'll merge configurations from the aliased and real params.
1191                 // TODO: Is this the right thing to do?
1192                 if ( aliased != null )
1193                 {
1194                     if ( pomConfig == null )
1195                     {
1196                         pomConfig = new XmlPlexusConfiguration( paramName );
1197                     }
1198 
1199                     pomConfig = buildTopDownMergedConfiguration( pomConfig, aliased );
1200                 }
1201 
1202                 PlexusConfiguration toAdd = null;
1203 
1204                 if ( pomConfig != null )
1205                 {
1206                     pomConfig = buildTopDownMergedConfiguration( pomConfig, mojoConfig );
1207 
1208                     if ( StringUtils.isNotEmpty( pomConfig.getValue( null ) ) || ( pomConfig.getChildCount() > 0 ) )
1209                     {
1210                         toAdd = pomConfig;
1211                     }
1212                 }
1213 
1214                 if ( ( toAdd == null ) && ( mojoConfig != null ) )
1215                 {
1216                     toAdd = copyConfiguration( mojoConfig );
1217                 }
1218 
1219                 if ( toAdd != null )
1220                 {
1221                     if ( ( implementation != null ) && ( toAdd.getAttribute( "implementation", null ) == null ) )
1222                     {
1223 
1224                         XmlPlexusConfiguration implementationConf = new XmlPlexusConfiguration( paramName );
1225 
1226                         implementationConf.setAttribute( "implementation", parameter.getImplementation() );
1227 
1228                         toAdd = buildTopDownMergedConfiguration( toAdd, implementationConf );
1229                     }
1230 
1231                     result.addChild( toAdd );
1232                 }
1233             }
1234         }
1235         return result;
1236     }
1237 
1238     private XmlPlexusConfiguration buildTopDownMergedConfiguration( PlexusConfiguration dominant,
1239                                                                     PlexusConfiguration recessive )
1240     {
1241         XmlPlexusConfiguration result = new XmlPlexusConfiguration( dominant.getName() );
1242 
1243         String value = dominant.getValue( null );
1244 
1245         if ( StringUtils.isEmpty( value ) && ( recessive != null ) )
1246         {
1247             value = recessive.getValue( null );
1248         }
1249 
1250         if ( StringUtils.isNotEmpty( value ) )
1251         {
1252             result.setValue( value );
1253         }
1254 
1255         String[] attributeNames = dominant.getAttributeNames();
1256 
1257         for ( int i = 0; i < attributeNames.length; i++ )
1258         {
1259             String attributeValue = dominant.getAttribute( attributeNames[i], null );
1260 
1261             result.setAttribute( attributeNames[i], attributeValue );
1262         }
1263 
1264         if ( recessive != null )
1265         {
1266             attributeNames = recessive.getAttributeNames();
1267 
1268             for ( int i = 0; i < attributeNames.length; i++ )
1269             {
1270                 String attributeValue = recessive.getAttribute( attributeNames[i], null );
1271                 // TODO: recessive seems to be dominant here?
1272                 result.setAttribute( attributeNames[i], attributeValue );
1273             }
1274         }
1275 
1276         PlexusConfiguration[] children = dominant.getChildren();
1277 
1278         for ( int i = 0; i < children.length; i++ )
1279         {
1280             PlexusConfiguration childDom = children[i];
1281             PlexusConfiguration childRec = recessive == null ? null : recessive.getChild( childDom.getName(), false );
1282 
1283             if ( childRec != null )
1284             {
1285                 result.addChild( buildTopDownMergedConfiguration( childDom, childRec ) );
1286             }
1287             else
1288             {   // FIXME: copy, or use reference?
1289                 result.addChild( copyConfiguration( childDom ) );
1290             }
1291         }
1292 
1293         return result;
1294     }
1295 
1296     public static PlexusConfiguration copyConfiguration( PlexusConfiguration src )
1297     {
1298         // TODO: shouldn't be necessary
1299         XmlPlexusConfiguration dom = new XmlPlexusConfiguration( src.getName() );
1300         dom.setValue( src.getValue( null ) );
1301 
1302         String[] attributeNames = src.getAttributeNames();
1303         for ( int i = 0; i < attributeNames.length; i++ )
1304         {
1305             String attributeName = attributeNames[i];
1306             dom.setAttribute( attributeName, src.getAttribute( attributeName, null ) );
1307         }
1308 
1309         PlexusConfiguration[] children = src.getChildren();
1310         for ( int i = 0; i < children.length; i++ )
1311         {
1312             dom.addChild( copyConfiguration( children[i] ) );
1313         }
1314 
1315         return dom;
1316     }
1317 
1318     // ----------------------------------------------------------------------
1319     // Mojo Parameter Handling
1320     // ----------------------------------------------------------------------
1321 
1322     private void populatePluginFields( Mojo plugin,
1323                                        MojoDescriptor mojoDescriptor,
1324                                        PlexusConfiguration configuration,
1325                                        PlexusContainer pluginContainer,
1326                                        ExpressionEvaluator expressionEvaluator )
1327         throws PluginConfigurationException
1328     {
1329         ComponentConfigurator configurator = null;
1330 
1331         try
1332         {
1333             String configuratorId = mojoDescriptor.getComponentConfigurator();
1334 
1335             // TODO: could the configuration be passed to lookup and the configurator known to plexus via the descriptor
1336             // so that this meethod could entirely be handled by a plexus lookup?
1337             if ( StringUtils.isNotEmpty( configuratorId ) )
1338             {
1339                 configurator =
1340                     (ComponentConfigurator) pluginContainer.lookup( ComponentConfigurator.ROLE, configuratorId );
1341             }
1342             else
1343             {
1344                 configurator = (ComponentConfigurator) pluginContainer.lookup( ComponentConfigurator.ROLE );
1345             }
1346 
1347             ConfigurationListener listener = new DebugConfigurationListener( getLogger() );
1348 
1349             getLogger().debug( "Configuring mojo '" + mojoDescriptor.getId() + "' -->" );
1350             configurator.configureComponent( plugin, configuration, expressionEvaluator,
1351                                              pluginContainer.getContainerRealm(), listener );
1352             getLogger().debug( "-- end configuration --" );
1353         }
1354         catch ( ComponentConfigurationException e )
1355         {
1356             throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(),
1357                                                     "Unable to parse the created DOM for plugin configuration", e );
1358         }
1359         catch ( ComponentLookupException e )
1360         {
1361             throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(),
1362                                                     "Unable to retrieve component configurator for plugin configuration",
1363                                                     e );
1364         }
1365         catch ( NoClassDefFoundError e )
1366         {
1367             throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(),
1368                                                     "A required class was missing during mojo configuration: " + e.getMessage(), e );
1369         }
1370         catch ( LinkageError e )
1371         {
1372             if ( getLogger().isFatalErrorEnabled() )
1373             {
1374                 getLogger().fatalError(
1375                                         configurator.getClass().getName() + "#configureComponent(...) caused a linkage error ("
1376                                             + e.getClass().getName() + ") and may be out-of-date. Check the realms:" );
1377 
1378                 ClassRealm pluginRealm = mojoDescriptor.getPluginDescriptor().getClassRealm();
1379                 StringBuffer sb = new StringBuffer();
1380                 sb.append( "Plugin realm = " + pluginRealm.getId() ).append( '\n' );
1381                 for ( int i = 0; i < pluginRealm.getConstituents().length; i++ )
1382                 {
1383                     sb.append( "urls[" + i + "] = " + pluginRealm.getConstituents()[i] );
1384                     if ( i != ( pluginRealm.getConstituents().length - 1 ) )
1385                     {
1386                         sb.append( '\n' );
1387                     }
1388                 }
1389                 getLogger().fatalError( sb.toString() );
1390 
1391                 ClassRealm containerRealm = container.getContainerRealm();
1392                 sb = new StringBuffer();
1393                 sb.append( "Container realm = " + containerRealm.getId() ).append( '\n' );
1394                 for ( int i = 0; i < containerRealm.getConstituents().length; i++ )
1395                 {
1396                     sb.append( "urls[" + i + "] = " + containerRealm.getConstituents()[i] );
1397                     if ( i != ( containerRealm.getConstituents().length - 1 ) )
1398                     {
1399                         sb.append( '\n' );
1400                     }
1401                 }
1402                 getLogger().fatalError( sb.toString() );
1403             }
1404 
1405             throw new PluginConfigurationException( mojoDescriptor.getPluginDescriptor(),
1406                                                     e.getClass().getName() + ": " + e.getMessage(), e );
1407         }
1408         finally
1409         {
1410             if ( configurator != null )
1411             {
1412                 try
1413                 {
1414                     pluginContainer.release( configurator );
1415                 }
1416                 catch ( ComponentLifecycleException e )
1417                 {
1418                     getLogger().debug( "Failed to release plugin container - ignoring." );
1419                 }
1420             }
1421         }
1422     }
1423 
1424     public static String createPluginParameterRequiredMessage( MojoDescriptor mojo,
1425                                                                Parameter parameter,
1426                                                                String expression )
1427     {
1428         StringBuffer message = new StringBuffer();
1429 
1430         message.append( "The '" );
1431         message.append( parameter.getName() );
1432         message.append( "' parameter is required for the execution of the " );
1433         message.append( mojo.getFullGoalName() );
1434         message.append( " mojo and cannot be null." );
1435         if ( expression != null )
1436         {
1437             message.append( " The retrieval expression was: " ).append( expression );
1438         }
1439 
1440         return message.toString();
1441     }
1442 
1443     // ----------------------------------------------------------------------
1444     // Lifecycle
1445     // ----------------------------------------------------------------------
1446 
1447     public void contextualize( Context context )
1448         throws ContextException
1449     {
1450         container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
1451 
1452         mojoLogger = new DefaultLog( container.getLoggerManager().getLoggerForComponent( Mojo.ROLE ) );
1453     }
1454 
1455     public void initialize()
1456     {
1457         artifactFilter = MavenArtifactFilterManager.createStandardFilter();
1458     }
1459 
1460     // ----------------------------------------------------------------------
1461     // Artifact resolution
1462     // ----------------------------------------------------------------------
1463 
1464     private void resolveTransitiveDependencies( MavenSession context,
1465                                                 ArtifactResolver artifactResolver,
1466                                                 String scope,
1467                                                 ArtifactFactory artifactFactory,
1468                                                 MavenProject project, boolean isAggregator )
1469         throws ArtifactResolutionException, ArtifactNotFoundException, InvalidDependencyVersionException
1470     {
1471         ArtifactFilter filter = new ScopeArtifactFilter( scope );
1472 
1473         // TODO: such a call in MavenMetadataSource too - packaging not really the intention of type
1474         Artifact artifact = artifactFactory.createBuildArtifact( project.getGroupId(), project.getArtifactId(),
1475                                                                  project.getVersion(), project.getPackaging() );
1476 
1477         // TODO: we don't need to resolve over and over again, as long as we are sure that the parameters are the same
1478         // check this with yourkit as a hot spot.
1479         // Don't recreate if already created - for effeciency, and because clover plugin adds to it
1480         if ( project.getDependencyArtifacts() == null )
1481         {
1482             project.setDependencyArtifacts( project.createArtifacts( artifactFactory, null, null ) );
1483         }
1484         else
1485         {
1486             project.resolveActiveArtifacts();
1487         }
1488 
1489         Set resolvedArtifacts;
1490         try
1491         {
1492             ArtifactResolutionResult result = artifactResolver.resolveTransitively( project.getDependencyArtifacts(),
1493                                                                                 artifact,
1494                                                                                 project.getManagedVersionMap(),
1495                                                                                 context.getLocalRepository(),
1496                                                                                 project.getRemoteArtifactRepositories(),
1497                                                                                 artifactMetadataSource, filter );
1498             resolvedArtifacts = result.getArtifacts();
1499         }
1500         catch (MultipleArtifactsNotFoundException me)
1501         {
1502             /*only do this if we are an aggregating plugin: MNG-2277
1503             if the dependency doesn't yet exist but is in the reactor, then
1504             all we can do is warn and skip it. A better fix can be inserted into 2.1*/
1505             if (isAggregator && checkMissingArtifactsInReactor( context.getSortedProjects(), me.getMissingArtifacts() ))
1506             {
1507                 resolvedArtifacts = new HashSet(me.getResolvedArtifacts());
1508             }
1509             else
1510             {
1511                 //we can't find all the artifacts in the reactor so bubble the exception up.
1512                 throw me;
1513             }
1514         }
1515         project.setArtifacts( resolvedArtifacts );
1516     }
1517 
1518     /**
1519      * This method is checking to see if the artifacts that can't be resolved are all
1520      * part of this reactor. This is done to prevent a chicken or egg scenario with
1521      * fresh projects that have a plugin that is an aggregator and requires dependencies. See
1522      * MNG-2277 for more info.
1523      * @param projects the sibling projects in the reactor
1524      * @param missing the artifacts that can't be found
1525      * @return true if ALL missing artifacts are found in the reactor.
1526      */
1527     private boolean checkMissingArtifactsInReactor(Collection projects, Collection missing)
1528     {
1529         Collection foundInReactor = new HashSet();
1530         Iterator iter = missing.iterator();
1531         while (iter.hasNext())
1532         {
1533             Artifact mArtifact = (Artifact) iter.next();
1534             Iterator pIter = projects.iterator();
1535             while (pIter.hasNext())
1536             {
1537                 MavenProject p = (MavenProject) pIter.next();
1538                 if (p.getArtifactId().equals( mArtifact.getArtifactId()) &&
1539                     p.getGroupId().equals( mArtifact.getGroupId()) &&
1540                     p.getVersion().equals( mArtifact.getVersion()))
1541                 {
1542                     //TODO: the packaging could be different, but the exception doesn't contain that info
1543                     //most likely it would be produced by the project we just found in the reactor since all
1544                     //the other info matches. Assume it's ok.
1545                     getLogger().warn( "The dependency: "+ p.getId()+" can't be resolved but has been found in the reactor.\nThis dependency has been excluded from the plugin execution. You should rerun this mojo after executing mvn install.\n" );
1546 
1547                     //found it, move on.
1548                     foundInReactor.add( p );
1549                     break;
1550                 }
1551             }
1552         }
1553 
1554         //if all of them have been found, we can continue.
1555         return foundInReactor.size() == missing.size();
1556     }
1557 
1558 
1559     // ----------------------------------------------------------------------
1560     // Artifact downloading
1561     // ----------------------------------------------------------------------
1562 
1563     private void downloadDependencies( MavenProject project,
1564                                        MavenSession context,
1565                                        ArtifactResolver artifactResolver )
1566         throws ArtifactResolutionException, ArtifactNotFoundException
1567     {
1568         ArtifactRepository localRepository = context.getLocalRepository();
1569         List remoteArtifactRepositories = project.getRemoteArtifactRepositories();
1570 
1571         for ( Iterator it = project.getArtifacts().iterator(); it.hasNext(); )
1572         {
1573             Artifact artifact = (Artifact) it.next();
1574 
1575             artifactResolver.resolve( artifact, remoteArtifactRepositories, localRepository );
1576         }
1577     }
1578 
1579     public Object getPluginComponent( Plugin plugin,
1580                                       String role,
1581                                       String roleHint )
1582         throws PluginManagerException, ComponentLookupException
1583     {
1584         PluginDescriptor pluginDescriptor = pluginCollector.getPluginDescriptor( plugin );
1585 
1586         PlexusContainer pluginContainer = getPluginContainer( pluginDescriptor );
1587 
1588         return pluginContainer.lookup( role, roleHint );
1589     }
1590 
1591     public Map getPluginComponents( Plugin plugin,
1592                                     String role )
1593         throws ComponentLookupException, PluginManagerException
1594     {
1595         PluginDescriptor pluginDescriptor = pluginCollector.getPluginDescriptor( plugin );
1596 
1597         PlexusContainer pluginContainer = getPluginContainer( pluginDescriptor );
1598 
1599         return pluginContainer.lookupMap( role );
1600     }
1601 }