View Javadoc
1   package org.apache.maven;
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 java.io.File;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.text.SimpleDateFormat;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.Collection;
29  import java.util.Collections;
30  import java.util.Date;
31  import java.util.HashMap;
32  import java.util.HashSet;
33  import java.util.LinkedHashMap;
34  import java.util.LinkedHashSet;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.Properties;
38  
39  import org.apache.maven.artifact.ArtifactUtils;
40  import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
41  import org.apache.maven.eventspy.internal.EventSpyDispatcher;
42  import org.apache.maven.execution.DefaultMavenExecutionResult;
43  import org.apache.maven.execution.ExecutionEvent;
44  import org.apache.maven.execution.MavenExecutionRequest;
45  import org.apache.maven.execution.MavenExecutionRequestPopulationException;
46  import org.apache.maven.execution.MavenExecutionRequestPopulator;
47  import org.apache.maven.execution.MavenExecutionResult;
48  import org.apache.maven.execution.MavenSession;
49  import org.apache.maven.execution.ProjectDependencyGraph;
50  import org.apache.maven.lifecycle.internal.ExecutionEventCatapult;
51  import org.apache.maven.lifecycle.internal.LifecycleStarter;
52  import org.apache.maven.model.Plugin;
53  import org.apache.maven.model.building.ModelProblem;
54  import org.apache.maven.model.building.ModelProblemUtils;
55  import org.apache.maven.model.building.ModelSource;
56  import org.apache.maven.model.building.UrlModelSource;
57  import org.apache.maven.plugin.LegacySupport;
58  import org.apache.maven.project.MavenProject;
59  import org.apache.maven.project.ProjectBuilder;
60  import org.apache.maven.project.ProjectBuildingException;
61  import org.apache.maven.project.ProjectBuildingRequest;
62  import org.apache.maven.project.ProjectBuildingResult;
63  import org.apache.maven.repository.LocalRepositoryNotAccessibleException;
64  import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
65  import org.apache.maven.settings.Mirror;
66  import org.apache.maven.settings.Proxy;
67  import org.apache.maven.settings.Server;
68  import org.apache.maven.settings.building.SettingsProblem;
69  import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
70  import org.apache.maven.settings.crypto.SettingsDecrypter;
71  import org.apache.maven.settings.crypto.SettingsDecryptionResult;
72  import org.codehaus.plexus.PlexusContainer;
73  import org.codehaus.plexus.component.annotations.Component;
74  import org.codehaus.plexus.component.annotations.Requirement;
75  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
76  import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
77  import org.codehaus.plexus.logging.Logger;
78  import org.codehaus.plexus.util.IOUtil;
79  import org.codehaus.plexus.util.StringUtils;
80  import org.codehaus.plexus.util.dag.CycleDetectedException;
81  import org.codehaus.plexus.util.xml.Xpp3Dom;
82  import org.eclipse.aether.ConfigurationProperties;
83  import org.eclipse.aether.DefaultRepositorySystemSession;
84  import org.eclipse.aether.RepositorySystem;
85  import org.eclipse.aether.RepositorySystemSession;
86  import org.eclipse.aether.repository.LocalRepository;
87  import org.eclipse.aether.repository.NoLocalRepositoryManagerException;
88  import org.eclipse.aether.repository.RepositoryPolicy;
89  import org.eclipse.aether.repository.WorkspaceReader;
90  import org.eclipse.aether.resolution.ResolutionErrorPolicy;
91  import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory;
92  import org.eclipse.aether.util.repository.AuthenticationBuilder;
93  import org.eclipse.aether.util.repository.ChainedWorkspaceReader;
94  import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
95  import org.eclipse.aether.util.repository.DefaultMirrorSelector;
96  import org.eclipse.aether.util.repository.DefaultProxySelector;
97  import org.eclipse.aether.util.repository.SimpleResolutionErrorPolicy;
98  
99  /**
100  * @author Jason van Zyl
101  */
102 @Component( role = Maven.class )
103 public class DefaultMaven
104     implements Maven
105 {
106 
107     @Requirement
108     private Logger logger;
109 
110     @Requirement
111     protected ProjectBuilder projectBuilder;
112 
113     @Requirement
114     private LifecycleStarter lifecycleStarter;
115 
116     @Requirement
117     protected PlexusContainer container;
118 
119     @Requirement
120     MavenExecutionRequestPopulator populator;
121 
122     @Requirement
123     private ExecutionEventCatapult eventCatapult;
124 
125     @Requirement
126     private ArtifactHandlerManager artifactHandlerManager;
127 
128     @Requirement( optional = true, hint = "ide" )
129     private WorkspaceReader workspaceRepository;
130 
131     @Requirement
132     private RepositorySystem repoSystem;
133 
134     @Requirement( optional = true, hint = "simple" )
135     private LocalRepositoryManagerFactory simpleLocalRepositoryManagerFactory;
136 
137     @Requirement
138     private SettingsDecrypter settingsDecrypter;
139 
140     @Requirement
141     private LegacySupport legacySupport;
142 
143     @Requirement
144     private EventSpyDispatcher eventSpyDispatcher;
145 
146     @Requirement
147     private SessionScope sessionScope;
148         
149     public MavenExecutionResult execute( MavenExecutionRequest request )
150     {
151         MavenExecutionResult result;
152 
153         try
154         {
155             result = doExecute( populator.populateDefaults( request ) );
156         }
157         catch ( OutOfMemoryError e )
158         {
159             result = addExceptionToResult( new DefaultMavenExecutionResult(), e );
160         }
161         catch ( MavenExecutionRequestPopulationException e )
162         {
163             result = addExceptionToResult( new DefaultMavenExecutionResult(), e );
164         }
165         catch ( RuntimeException e )
166         {
167             result =
168                 addExceptionToResult( new DefaultMavenExecutionResult(), new InternalErrorException( "Internal error: "
169                     + e, e ) );
170         }
171         finally
172         {
173             legacySupport.setSession( null );
174         }
175 
176         return result;
177     }
178 
179     // 
180     // 1) Setup initial properties.
181     //
182     // 2) Validate local repository directory is accessible.
183     //
184     // 3) Create RepositorySystemSession.
185     //
186     // 4) Create MavenSession.
187     //
188     // 5) Execute AbstractLifecycleParticipant.afterSessionStart(session)
189     //
190     // 6) Get reactor projects looking for general POM errors
191     //
192     // 7) Create ProjectDependencyGraph using trimming which takes into account --projects and reactor mode. This ensures
193     //    that the projects passed into the ReactorReader are only those specified.
194     //
195     // 8) Create ReactorReader with the getProjectMap( projects ). NOTE that getProjectMap(projects) is the code that
196     //    checks for duplicate projects definitions in the build. Ideally this type of duplicate checking should be part of
197     //    getting the reactor projects in 6). The duplicate checking is conflated with getProjectMap(projects).
198     //
199     // 9) Execute AbstractLifecycleParticipant.afterProjectsRead(session)
200     //
201     // 10) Create ProjectDependencyGraph without trimming (as trimming was done in 7). A new topological sort is required after
202     //     the execution of 9) as the AbstractLifecycleParticipants are free to mutate the MavenProject instances, which may change
203     //     dependencies which can, in turn, affect the build order.
204     // 
205     // 11) Execute LifecycleStarter.start()
206     //    
207     private MavenExecutionResult doExecute( MavenExecutionRequest request )
208     {
209         if ( request.getStartTime() != null )
210         {
211             request.getSystemProperties().put( "${build.timestamp}",
212                                                new SimpleDateFormat( "yyyyMMdd-hhmm" ).format( request.getStartTime() ) );
213         }
214 
215         request.setStartTime( new Date() );
216 
217         MavenExecutionResult result = new DefaultMavenExecutionResult();
218 
219         try
220         {
221             validateLocalRepository( request );
222         }
223         catch ( LocalRepositoryNotAccessibleException e )
224         {
225             return addExceptionToResult( result, e );
226         }
227 
228         DefaultRepositorySystemSession repoSession = (DefaultRepositorySystemSession) newRepositorySession( request );
229 
230         MavenSession session = new MavenSession( container, repoSession, request, result );
231         legacySupport.setSession( session );
232 
233         try
234         {
235             for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( Collections.<MavenProject> emptyList() ) )
236             {
237                 listener.afterSessionStart( session );
238             }
239         }
240         catch ( MavenExecutionException e )
241         {
242             return addExceptionToResult( result, e );
243         }
244 
245         eventCatapult.fire( ExecutionEvent.Type.ProjectDiscoveryStarted, session, null );
246 
247         List<MavenProject> projects;
248         try
249         {
250             projects = getProjectsForMavenReactor( session );
251             //
252             // Capture the full set of projects before any potential constraining is performed by --projects
253             //
254             session.setAllProjects( projects );
255         }
256         catch ( ProjectBuildingException e )
257         {
258             return addExceptionToResult( result, e );
259         }
260 
261         validateProjects( projects );
262 
263         //
264         // This creates the graph and trims the projects down based on the user request using something like:
265         //
266         // -pl project0,project2 eclipse:eclipse
267         //
268         ProjectDependencyGraph projectDependencyGraph = createProjectDependencyGraph( projects, request, result, true );
269 
270         session.setProjects( projectDependencyGraph.getSortedProjects() );
271         
272         if ( result.hasExceptions() )
273         {
274             return result;
275         }
276 
277         try
278         {
279             session.setProjectMap( getProjectMap( session.getProjects() ) );
280         }
281         catch ( DuplicateProjectException e )
282         {
283             return addExceptionToResult( result, e );
284         }
285         
286         WorkspaceReader reactorWorkspace;
287         sessionScope.enter();
288         sessionScope.seed( MavenSession.class, session );
289         try
290         {
291             reactorWorkspace = container.lookup( WorkspaceReader.class );
292         }
293         catch ( ComponentLookupException e )
294         {
295             return addExceptionToResult( result, e );
296         }
297         
298         //
299         // Desired order of precedence for local artifact repositories
300         //
301         // Reactor
302         // Workspace
303         // User Local Repository
304         //        
305         repoSession.setWorkspaceReader( ChainedWorkspaceReader.newInstance( reactorWorkspace,
306                                                                             repoSession.getWorkspaceReader() ) );
307 
308         repoSession.setReadOnly();
309 
310         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
311         try
312         {
313             for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( projects ) )
314             {
315                 Thread.currentThread().setContextClassLoader( listener.getClass().getClassLoader() );
316 
317                 listener.afterProjectsRead( session );
318             }
319         }
320         catch ( MavenExecutionException e )
321         {
322             return addExceptionToResult( result, e );
323         }
324         finally
325         {
326             Thread.currentThread().setContextClassLoader( originalClassLoader );
327         }
328 
329         //
330         // The projects need to be topologically after the participants have run their afterProjectsRead(session)
331         // because the participant is free to change the dependencies of a project which can potentially change the
332         // topological order of the projects, and therefore can potentially change the build order.
333         //
334         // Note that participants may affect the topological order of the projects but it is
335         // not expected that a participant will add or remove projects from the session.
336         //
337         projectDependencyGraph = createProjectDependencyGraph( session.getProjects(), request, result, false );
338 
339         if ( result.hasExceptions() )
340         {
341             try 
342             {
343                 afterSessionEnd( projects, session );
344             } 
345             catch ( MavenExecutionException e )
346             {
347                 return addExceptionToResult( result, e );
348             }
349 
350             return result;
351         }
352         
353         session.setProjects( projectDependencyGraph.getSortedProjects() );
354 
355         session.setProjectDependencyGraph( projectDependencyGraph );
356 
357         result.setTopologicallySortedProjects( session.getProjects() );
358 
359         result.setProject( session.getTopLevelProject() );
360 
361         lifecycleStarter.execute( session );
362 
363         validateActivatedProfiles( session.getProjects(), request.getActiveProfiles() );
364 
365         if ( session.getResult().hasExceptions() )
366         {
367             return addExceptionToResult( result, session.getResult().getExceptions().get( 0 ) );
368         }
369 
370         try 
371         {
372             afterSessionEnd( projects, session );
373         } 
374         catch ( MavenExecutionException e )
375         {
376             return addExceptionToResult( result, e );
377         }
378 
379         sessionScope.exit();
380 
381         return result;
382     }
383 
384     private void afterSessionEnd( Collection<MavenProject> projects, MavenSession session ) 
385         throws MavenExecutionException
386     {
387         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
388         try
389         {
390             for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( projects ) )
391             {
392                 Thread.currentThread().setContextClassLoader( listener.getClass().getClassLoader() );
393 
394                 listener.afterSessionEnd( session );
395             }
396         }
397         finally
398         {
399             Thread.currentThread().setContextClassLoader( originalClassLoader );
400         }
401     }
402 
403     public RepositorySystemSession newRepositorySession( MavenExecutionRequest request )
404     {
405         DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
406 
407         session.setCache( request.getRepositoryCache() );
408 
409         Map<Object, Object> configProps = new LinkedHashMap<Object, Object>();
410         configProps.put( ConfigurationProperties.USER_AGENT, getUserAgent() );
411         configProps.put( ConfigurationProperties.INTERACTIVE, request.isInteractiveMode() );
412         configProps.putAll( request.getSystemProperties() );
413         configProps.putAll( request.getUserProperties() );
414 
415         session.setOffline( request.isOffline() );
416         session.setChecksumPolicy( request.getGlobalChecksumPolicy() );
417         if ( request.isNoSnapshotUpdates() )
418         {
419             session.setUpdatePolicy( RepositoryPolicy.UPDATE_POLICY_NEVER );
420         }
421         else if ( request.isUpdateSnapshots() )
422         {
423             session.setUpdatePolicy( RepositoryPolicy.UPDATE_POLICY_ALWAYS );
424         }
425         else
426         {
427             session.setUpdatePolicy( null );
428         }
429 
430         int errorPolicy = 0;
431         errorPolicy |= request.isCacheNotFound() ? ResolutionErrorPolicy.CACHE_NOT_FOUND : 0;
432         errorPolicy |= request.isCacheTransferError() ? ResolutionErrorPolicy.CACHE_TRANSFER_ERROR : 0;
433         session.setResolutionErrorPolicy( new SimpleResolutionErrorPolicy( errorPolicy, errorPolicy
434             | ResolutionErrorPolicy.CACHE_NOT_FOUND ) );
435 
436         session.setArtifactTypeRegistry( RepositoryUtils.newArtifactTypeRegistry( artifactHandlerManager ) );
437 
438         LocalRepository localRepo = new LocalRepository( request.getLocalRepository().getBasedir() );
439 
440         if ( request.isUseLegacyLocalRepository() )
441         {
442             logger.warn( "Disabling enhanced local repository: using legacy is strongly discouraged to ensure build reproducibility." );
443             try
444             {
445                 session.setLocalRepositoryManager( simpleLocalRepositoryManagerFactory.newInstance( session, localRepo ) );
446             }
447             catch ( NoLocalRepositoryManagerException e )
448             {
449 
450                 logger.warn( "Failed to configure legacy local repository: back to default" );
451                 session.setLocalRepositoryManager( repoSystem.newLocalRepositoryManager( session, localRepo ) );
452             }
453         }
454         else
455         {
456             session.setLocalRepositoryManager( repoSystem.newLocalRepositoryManager( session, localRepo ) );
457         }
458 
459         if ( request.getWorkspaceReader() != null )
460         {
461             session.setWorkspaceReader( request.getWorkspaceReader() );
462         }
463         else
464         {
465             session.setWorkspaceReader( workspaceRepository );
466         }
467 
468         DefaultSettingsDecryptionRequest decrypt = new DefaultSettingsDecryptionRequest();
469         decrypt.setProxies( request.getProxies() );
470         decrypt.setServers( request.getServers() );
471         SettingsDecryptionResult decrypted = settingsDecrypter.decrypt( decrypt );
472 
473         if ( logger.isDebugEnabled() )
474         {
475             for ( SettingsProblem problem : decrypted.getProblems() )
476             {
477                 logger.debug( problem.getMessage(), problem.getException() );
478             }
479         }
480 
481         DefaultMirrorSelector mirrorSelector = new DefaultMirrorSelector();
482         for ( Mirror mirror : request.getMirrors() )
483         {
484             mirrorSelector.add( mirror.getId(), mirror.getUrl(), mirror.getLayout(), false, mirror.getMirrorOf(),
485                                 mirror.getMirrorOfLayouts() );
486         }
487         session.setMirrorSelector( mirrorSelector );
488 
489         DefaultProxySelector proxySelector = new DefaultProxySelector();
490         for ( Proxy proxy : decrypted.getProxies() )
491         {
492             AuthenticationBuilder authBuilder = new AuthenticationBuilder();
493             authBuilder.addUsername( proxy.getUsername() ).addPassword( proxy.getPassword() );
494             proxySelector.add( new org.eclipse.aether.repository.Proxy( proxy.getProtocol(), proxy.getHost(),
495                                                                         proxy.getPort(), authBuilder.build() ),
496                                proxy.getNonProxyHosts() );
497         }
498         session.setProxySelector( proxySelector );
499 
500         DefaultAuthenticationSelector authSelector = new DefaultAuthenticationSelector();
501         for ( Server server : decrypted.getServers() )
502         {
503             AuthenticationBuilder authBuilder = new AuthenticationBuilder();
504             authBuilder.addUsername( server.getUsername() ).addPassword( server.getPassword() );
505             authBuilder.addPrivateKey( server.getPrivateKey(), server.getPassphrase() );
506             authSelector.add( server.getId(), authBuilder.build() );
507 
508             if ( server.getConfiguration() != null )
509             {
510                 Xpp3Dom dom = (Xpp3Dom) server.getConfiguration();
511                 for ( int i = dom.getChildCount() - 1; i >= 0; i-- )
512                 {
513                     Xpp3Dom child = dom.getChild( i );
514                     if ( "wagonProvider".equals( child.getName() ) )
515                     {
516                         dom.removeChild( i );
517                     }
518                 }
519 
520                 XmlPlexusConfiguration config = new XmlPlexusConfiguration( dom );
521                 configProps.put( "aether.connector.wagon.config." + server.getId(), config );
522             }
523 
524             configProps.put( "aether.connector.perms.fileMode." + server.getId(), server.getFilePermissions() );
525             configProps.put( "aether.connector.perms.dirMode." + server.getId(), server.getDirectoryPermissions() );
526         }
527         session.setAuthenticationSelector( authSelector );
528 
529         session.setTransferListener( request.getTransferListener() );
530 
531         session.setRepositoryListener( eventSpyDispatcher.chainListener( new LoggingRepositoryListener( logger ) ) );
532 
533         session.setUserProperties( request.getUserProperties() );
534         session.setSystemProperties( request.getSystemProperties() );
535         session.setConfigProperties( configProps );
536 
537         return session;
538     }
539 
540     private String getUserAgent()
541     {
542         return "Apache-Maven/" + getMavenVersion() + " (Java " + System.getProperty( "java.version" ) + "; "
543             + System.getProperty( "os.name" ) + " " + System.getProperty( "os.version" ) + ")";
544     }
545 
546     private String getMavenVersion()
547     {
548         Properties props = new Properties();
549 
550         InputStream is = getClass().getResourceAsStream( "/META-INF/maven/org.apache.maven/maven-core/pom.properties" );
551         if ( is != null )
552         {
553             try
554             {
555                 props.load( is );
556             }
557             catch ( IOException e )
558             {
559                 logger.debug( "Failed to read Maven version", e );
560             }
561             IOUtil.close( is );
562         }
563 
564         return props.getProperty( "version", "unknown-version" );
565     }
566 
567     private void validateLocalRepository( MavenExecutionRequest request )
568         throws LocalRepositoryNotAccessibleException
569     {
570         File localRepoDir = request.getLocalRepositoryPath();
571 
572         logger.debug( "Using local repository at " + localRepoDir );
573 
574         localRepoDir.mkdirs();
575 
576         if ( !localRepoDir.isDirectory() )
577         {
578             throw new LocalRepositoryNotAccessibleException( "Could not create local repository at " + localRepoDir );
579         }
580     }
581 
582     private Collection<AbstractMavenLifecycleParticipant> getLifecycleParticipants( Collection<MavenProject> projects )
583     {
584         Collection<AbstractMavenLifecycleParticipant> lifecycleListeners =
585             new LinkedHashSet<AbstractMavenLifecycleParticipant>();
586 
587         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
588         try
589         {
590             try
591             {
592                 lifecycleListeners.addAll( container.lookupList( AbstractMavenLifecycleParticipant.class ) );
593             }
594             catch ( ComponentLookupException e )
595             {
596                 // this is just silly, lookupList should return an empty list!
597                 logger.warn( "Failed to lookup lifecycle participants: " + e.getMessage() );
598             }
599 
600             Collection<ClassLoader> scannedRealms = new HashSet<ClassLoader>();
601 
602             for ( MavenProject project : projects )
603             {
604                 ClassLoader projectRealm = project.getClassRealm();
605 
606                 if ( projectRealm != null && scannedRealms.add( projectRealm ) )
607                 {
608                     Thread.currentThread().setContextClassLoader( projectRealm );
609 
610                     try
611                     {
612                         lifecycleListeners.addAll( container.lookupList( AbstractMavenLifecycleParticipant.class ) );
613                     }
614                     catch ( ComponentLookupException e )
615                     {
616                         // this is just silly, lookupList should return an empty list!
617                         logger.warn( "Failed to lookup lifecycle participants: " + e.getMessage() );
618                     }
619                 }
620             }
621         }
622         finally
623         {
624             Thread.currentThread().setContextClassLoader( originalClassLoader );
625         }
626 
627         return lifecycleListeners;
628     }
629 
630     private MavenExecutionResult addExceptionToResult( MavenExecutionResult result, Throwable e )
631     {
632         if ( !result.getExceptions().contains( e ) )
633         {
634             result.addException( e );
635         }
636 
637         return result;
638     }
639 
640     private List<MavenProject> getProjectsForMavenReactor( MavenSession session )
641         throws ProjectBuildingException
642     {
643         MavenExecutionRequest request = session.getRequest();
644         
645         request.getProjectBuildingRequest().setRepositorySession( session.getRepositorySession() );
646 
647         List<MavenProject> projects = new ArrayList<MavenProject>();
648 
649         // We have no POM file.
650         //
651         if ( request.getPom() == null )
652         {
653             ModelSource modelSource = new UrlModelSource( DefaultMaven.class.getResource( "project/standalone.xml" ) );
654             MavenProject project =
655                 projectBuilder.build( modelSource, request.getProjectBuildingRequest() ).getProject();
656             project.setExecutionRoot( true );
657             projects.add( project );
658             request.setProjectPresent( false );
659             return projects;
660         }
661 
662         List<File> files = Arrays.asList( request.getPom().getAbsoluteFile() );
663         collectProjects( projects, files, request );
664         return projects;
665     }
666 
667     private void collectProjects( List<MavenProject> projects, List<File> files, MavenExecutionRequest request )
668         throws ProjectBuildingException
669     {
670         ProjectBuildingRequest projectBuildingRequest = request.getProjectBuildingRequest();
671 
672         List<ProjectBuildingResult> results =
673             projectBuilder.build( files, request.isRecursive(), projectBuildingRequest );
674 
675         boolean problems = false;
676 
677         for ( ProjectBuildingResult result : results )
678         {
679             projects.add( result.getProject() );
680 
681             if ( !result.getProblems().isEmpty() && logger.isWarnEnabled() )
682             {
683                 logger.warn( "" );
684                 logger.warn( "Some problems were encountered while building the effective model for "
685                     + result.getProject().getId() );
686 
687                 for ( ModelProblem problem : result.getProblems() )
688                 {
689                     String location = ModelProblemUtils.formatLocation( problem, result.getProjectId() );
690                     logger.warn( problem.getMessage() + ( StringUtils.isNotEmpty( location ) ? " @ " + location : "" ) );
691                 }
692 
693                 problems = true;
694             }
695         }
696 
697         if ( problems )
698         {
699             logger.warn( "" );
700             logger.warn( "It is highly recommended to fix these problems"
701                 + " because they threaten the stability of your build." );
702             logger.warn( "" );
703             logger.warn( "For this reason, future Maven versions might no"
704                 + " longer support building such malformed projects." );
705             logger.warn( "" );
706         }
707     }
708 
709     private Map<String, MavenProject> getProjectMap( Collection<MavenProject> projects )
710         throws DuplicateProjectException
711     {
712         Map<String, MavenProject> index = new LinkedHashMap<String, MavenProject>();
713         Map<String, List<File>> collisions = new LinkedHashMap<String, List<File>>();
714 
715         for ( MavenProject project : projects )
716         {
717             String projectId = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() );
718 
719             MavenProject collision = index.get( projectId );
720 
721             if ( collision == null )
722             {
723                 index.put( projectId, project );
724             }
725             else
726             {
727                 List<File> pomFiles = collisions.get( projectId );
728 
729                 if ( pomFiles == null )
730                 {
731                     pomFiles = new ArrayList<File>( Arrays.asList( collision.getFile(), project.getFile() ) );
732                     collisions.put( projectId, pomFiles );
733                 }
734                 else
735                 {
736                     pomFiles.add( project.getFile() );
737                 }
738             }
739         }
740 
741         if ( !collisions.isEmpty() )
742         {
743             throw new DuplicateProjectException( "Two or more projects in the reactor"
744                 + " have the same identifier, please make sure that <groupId>:<artifactId>:<version>"
745                 + " is unique for each project: " + collisions, collisions );
746         }
747 
748         return index;
749     }
750 
751     private void validateProjects( List<MavenProject> projects )
752     {
753         Map<String, MavenProject> projectsMap = new HashMap<String, MavenProject>();
754 
755         for ( MavenProject project : projects )
756         {
757             String projectKey = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() );
758 
759             projectsMap.put( projectKey, project );
760         }
761 
762         for ( MavenProject project : projects )
763         {
764             // MNG-1911 / MNG-5572: Building plugins with extensions cannot be part of reactor 
765             for ( Plugin plugin : project.getBuildPlugins() )
766             {
767                 if ( plugin.isExtensions() )
768                 {
769                     String pluginKey =
770                         ArtifactUtils.key( plugin.getGroupId(), plugin.getArtifactId(), plugin.getVersion() );
771 
772                     if ( projectsMap.containsKey( pluginKey ) )
773                     {
774                         logger.warn( project.getName() + " uses " + plugin.getKey()
775                             + " as extensions, which is not possible within the same reactor build. This plugin was pulled from the local repository!" );
776                     }
777                 }
778             }
779         }
780     }
781 
782     private void validateActivatedProfiles( List<MavenProject> projects, List<String> activeProfileIds )
783     {
784         Collection<String> notActivatedProfileIds = new LinkedHashSet<String>( activeProfileIds );
785 
786         for ( MavenProject project : projects )
787         {
788             for ( List<String> profileIds : project.getInjectedProfileIds().values() )
789             {
790                 notActivatedProfileIds.removeAll( profileIds );
791             }
792         }
793 
794         for ( String notActivatedProfileId : notActivatedProfileIds )
795         {
796             logger.warn( "The requested profile \"" + notActivatedProfileId
797                 + "\" could not be activated because it does not exist." );
798         }
799     }
800 
801     @Deprecated // 5 January 2014
802     protected Logger getLogger()
803     {
804         return logger;
805     }
806 
807     private ProjectDependencyGraph createProjectDependencyGraph( Collection<MavenProject> projects, MavenExecutionRequest request,
808                                                                  MavenExecutionResult result, boolean trimming )
809     {
810         ProjectDependencyGraph projectDependencyGraph = null;
811 
812         try
813         {
814             projectDependencyGraph = new DefaultProjectDependencyGraph( projects );
815 
816             if ( trimming )
817             {
818                 List<MavenProject> activeProjects = projectDependencyGraph.getSortedProjects();
819 
820                 activeProjects = trimSelectedProjects( activeProjects, projectDependencyGraph, request );
821                 activeProjects = trimExcludedProjects( activeProjects,  request );
822                 activeProjects = trimResumedProjects( activeProjects, request );
823 
824                 if ( activeProjects.size() != projectDependencyGraph.getSortedProjects().size() )
825                 {
826                     projectDependencyGraph =
827                         new FilteredProjectDependencyGraph( projectDependencyGraph, activeProjects );
828                 }
829             }
830         }
831         catch ( CycleDetectedException e )
832         {
833             String message = "The projects in the reactor contain a cyclic reference: " + e.getMessage();
834 
835             ProjectCycleException error = new ProjectCycleException( message, e );
836 
837             addExceptionToResult( result, error );
838         }
839         catch ( org.apache.maven.project.DuplicateProjectException e )
840         {
841             addExceptionToResult( result, e );
842         }
843         catch ( MavenExecutionException e )
844         {
845             addExceptionToResult( result, e );
846         }
847 
848         return projectDependencyGraph;
849     }
850 
851     private List<MavenProject> trimSelectedProjects( List<MavenProject> projects, ProjectDependencyGraph graph,
852                                                      MavenExecutionRequest request )
853         throws MavenExecutionException
854     {
855         List<MavenProject> result = projects;
856 
857         if ( !request.getSelectedProjects().isEmpty() )
858         {
859             File reactorDirectory = null;
860             if ( request.getBaseDirectory() != null )
861             {
862                 reactorDirectory = new File( request.getBaseDirectory() );
863             }
864 
865             Collection<MavenProject> selectedProjects = new LinkedHashSet<MavenProject>( projects.size() );
866 
867             for ( String selector : request.getSelectedProjects() )
868             {
869                 MavenProject selectedProject = null;
870 
871                 for ( MavenProject project : projects )
872                 {
873                     if ( isMatchingProject( project, selector, reactorDirectory ) )
874                     {
875                         selectedProject = project;
876                         break;
877                     }
878                 }
879 
880                 if ( selectedProject != null )
881                 {
882                     selectedProjects.add( selectedProject );
883                 }
884                 else
885                 {
886                     throw new MavenExecutionException( "Could not find the selected project in the reactor: "
887                         + selector, request.getPom() );
888                 }
889             }
890 
891             boolean makeUpstream = false;
892             boolean makeDownstream = false;
893 
894             if ( MavenExecutionRequest.REACTOR_MAKE_UPSTREAM.equals( request.getMakeBehavior() ) )
895             {
896                 makeUpstream = true;
897             }
898             else if ( MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM.equals( request.getMakeBehavior() ) )
899             {
900                 makeDownstream = true;
901             }
902             else if ( MavenExecutionRequest.REACTOR_MAKE_BOTH.equals( request.getMakeBehavior() ) )
903             {
904                 makeUpstream = true;
905                 makeDownstream = true;
906             }
907             else if ( StringUtils.isNotEmpty( request.getMakeBehavior() ) )
908             {
909                 throw new MavenExecutionException( "Invalid reactor make behavior: " + request.getMakeBehavior(),
910                                                    request.getPom() );
911             }
912 
913             if ( makeUpstream || makeDownstream )
914             {
915                 for ( MavenProject selectedProject : new ArrayList<MavenProject>( selectedProjects ) )
916                 {
917                     if ( makeUpstream )
918                     {
919                         selectedProjects.addAll( graph.getUpstreamProjects( selectedProject, true ) );
920                     }
921                     if ( makeDownstream )
922                     {
923                         selectedProjects.addAll( graph.getDownstreamProjects( selectedProject, true ) );
924                     }
925                 }
926             }
927 
928             result = new ArrayList<MavenProject>( selectedProjects.size() );
929 
930             for ( MavenProject project : projects )
931             {
932                 if ( selectedProjects.contains( project ) )
933                 {
934                     result.add( project );
935                 }
936             }
937         }
938 
939         return result;
940     }
941     
942     private List<MavenProject> trimExcludedProjects( List<MavenProject> projects, MavenExecutionRequest request )
943         throws MavenExecutionException
944     {
945         List<MavenProject> result = projects;
946 
947         if ( !request.getExcludedProjects().isEmpty() )
948         {
949             File reactorDirectory = null;
950 
951             if ( request.getBaseDirectory() != null )
952             {
953                 reactorDirectory = new File( request.getBaseDirectory() );
954             }
955 
956             Collection<MavenProject> excludedProjects = new LinkedHashSet<MavenProject>( projects.size() );
957 
958             for ( String selector : request.getExcludedProjects() )
959             {
960                 MavenProject excludedProject = null;
961 
962                 for ( MavenProject project : projects )
963                 {
964                     if ( isMatchingProject( project, selector, reactorDirectory ) )
965                     {
966                         excludedProject = project;
967                         break;
968                     }
969                 }
970 
971                 if ( excludedProject != null )
972                 {
973                     excludedProjects.add( excludedProject );
974                 }
975                 else
976                 {
977                     throw new MavenExecutionException( "Could not find the selected project in the reactor: "
978                         + selector, request.getPom() );
979                 }
980             }
981 
982             result = new ArrayList<MavenProject>( projects.size() );
983             for ( MavenProject project : projects )
984             {
985                 if ( !excludedProjects.contains( project ) )
986                 {
987                     result.add( project );
988                 }
989             }
990         }
991 
992         return result;
993     }
994 
995     private List<MavenProject> trimResumedProjects( List<MavenProject> projects, MavenExecutionRequest request )
996         throws MavenExecutionException
997     {
998         List<MavenProject> result = projects;
999 
1000         if ( StringUtils.isNotEmpty( request.getResumeFrom() ) )
1001         {
1002             File reactorDirectory = null;
1003             if ( request.getBaseDirectory() != null )
1004             {
1005                 reactorDirectory = new File( request.getBaseDirectory() );
1006             }
1007 
1008             String selector = request.getResumeFrom();
1009 
1010             result = new ArrayList<MavenProject>( projects.size() );
1011 
1012             boolean resumed = false;
1013 
1014             for ( MavenProject project : projects )
1015             {
1016                 if ( !resumed && isMatchingProject( project, selector, reactorDirectory ) )
1017                 {
1018                     resumed = true;
1019                 }
1020 
1021                 if ( resumed )
1022                 {
1023                     result.add( project );
1024                 }
1025             }
1026 
1027             if ( !resumed )
1028             {
1029                 throw new MavenExecutionException( "Could not find project to resume reactor build from: " + selector
1030                     + " vs " + projects, request.getPom() );
1031             }
1032         }
1033 
1034         return result;
1035     }
1036 
1037     private boolean isMatchingProject( MavenProject project, String selector, File reactorDirectory )
1038     {
1039         // [groupId]:artifactId
1040         if ( selector.indexOf( ':' ) >= 0 )
1041         {
1042             String id = ':' + project.getArtifactId();
1043 
1044             if ( id.equals( selector ) )
1045             {
1046                 return true;
1047             }
1048 
1049             id = project.getGroupId() + id;
1050 
1051             if ( id.equals( selector ) )
1052             {
1053                 return true;
1054             }
1055         }
1056 
1057         // relative path, e.g. "sub", "../sub" or "."
1058         else if ( reactorDirectory != null )
1059         {
1060             File selectedProject = new File( new File( reactorDirectory, selector ).toURI().normalize() );
1061 
1062             if ( selectedProject.isFile() )
1063             {
1064                 return selectedProject.equals( project.getFile() );
1065             }
1066             else if ( selectedProject.isDirectory() )
1067             {
1068                 return selectedProject.equals( project.getBasedir() );
1069             }
1070         }
1071 
1072         return false;
1073     }
1074 
1075 }