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.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.Collection;
28  import java.util.Collections;
29  import java.util.Date;
30  import java.util.HashMap;
31  import java.util.HashSet;
32  import java.util.LinkedHashMap;
33  import java.util.LinkedHashSet;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.Properties;
37  
38  import org.apache.maven.artifact.ArtifactUtils;
39  import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
40  import org.apache.maven.eventspy.internal.EventSpyDispatcher;
41  import org.apache.maven.execution.DefaultMavenExecutionResult;
42  import org.apache.maven.execution.ExecutionEvent;
43  import org.apache.maven.execution.MavenExecutionRequest;
44  import org.apache.maven.execution.MavenExecutionRequestPopulationException;
45  import org.apache.maven.execution.MavenExecutionRequestPopulator;
46  import org.apache.maven.execution.MavenExecutionResult;
47  import org.apache.maven.execution.MavenSession;
48  import org.apache.maven.execution.ProjectDependencyGraph;
49  import org.apache.maven.lifecycle.internal.ExecutionEventCatapult;
50  import org.apache.maven.lifecycle.internal.LifecycleStarter;
51  import org.apache.maven.model.Plugin;
52  import org.apache.maven.model.building.ModelProblem;
53  import org.apache.maven.model.building.ModelProblemUtils;
54  import org.apache.maven.model.building.ModelSource;
55  import org.apache.maven.model.building.UrlModelSource;
56  import org.apache.maven.plugin.LegacySupport;
57  import org.apache.maven.project.MavenProject;
58  import org.apache.maven.project.ProjectBuilder;
59  import org.apache.maven.project.ProjectBuildingException;
60  import org.apache.maven.project.ProjectBuildingRequest;
61  import org.apache.maven.project.ProjectBuildingResult;
62  import org.apache.maven.repository.LocalRepositoryNotAccessibleException;
63  import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
64  import org.apache.maven.session.scope.internal.SessionScope;
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 simpleLocalRepoMgrFactory;
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.
193     //    This ensures 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
197     //    part of 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
202     //     required after the execution of 9) as the AbstractLifecycleParticipants are free to mutate the MavenProject
203     //     instances, which may change dependencies which can, in turn, affect the build order.
204     //
205     // 11) Execute LifecycleStarter.start()
206     //
207     @SuppressWarnings( "checkstyle:methodlength" )
208     private MavenExecutionResult doExecute( MavenExecutionRequest request )
209     {
210         request.setStartTime( new Date() );
211 
212         MavenExecutionResult result = new DefaultMavenExecutionResult();
213 
214         try
215         {
216             validateLocalRepository( request );
217         }
218         catch ( LocalRepositoryNotAccessibleException e )
219         {
220             return addExceptionToResult( result, e );
221         }
222 
223         DefaultRepositorySystemSession repoSession = (DefaultRepositorySystemSession) newRepositorySession( request );
224 
225         MavenSession session = new MavenSession( container, repoSession, request, result );
226 
227         //
228         // We enter the session scope right after the MavenSession creation and before any of the AbstractLifecycleParticipant lookups
229         // so that @SessionScoped components can be @Injected into AbstractLifecycleParticipants.
230         //
231         sessionScope.enter();
232         sessionScope.seed( MavenSession.class, session );
233 
234         legacySupport.setSession( session );
235 
236         try
237         {
238             for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( Collections.<MavenProject>emptyList() ) )
239             {
240                 listener.afterSessionStart( session );
241             }
242         }
243         catch ( MavenExecutionException e )
244         {
245             return addExceptionToResult( result, e );
246         }
247 
248         eventCatapult.fire( ExecutionEvent.Type.ProjectDiscoveryStarted, session, null );
249 
250         List<MavenProject> projects;
251         try
252         {
253             projects = getProjectsForMavenReactor( session );
254             //
255             // Capture the full set of projects before any potential constraining is performed by --projects
256             //
257             session.setAllProjects( projects );
258         }
259         catch ( ProjectBuildingException e )
260         {
261             return addExceptionToResult( result, e );
262         }
263 
264         validateProjects( projects );
265 
266         //
267         // This creates the graph and trims the projects down based on the user request using something like:
268         //
269         // -pl project0,project2 eclipse:eclipse
270         //
271         ProjectDependencyGraph projectDependencyGraph = createProjectDependencyGraph( projects, request, result, true );
272 
273         if ( result.hasExceptions() )
274         {
275             return result;
276         }
277 
278         session.setProjects( projectDependencyGraph.getSortedProjects() );
279 
280         try
281         {
282             session.setProjectMap( getProjectMap( session.getProjects() ) );
283         }
284         catch ( DuplicateProjectException e )
285         {
286             return addExceptionToResult( result, e );
287         }
288 
289         WorkspaceReader reactorWorkspace;
290         try
291         {
292             reactorWorkspace = container.lookup( WorkspaceReader.class, ReactorReader.HINT );
293         }
294         catch ( ComponentLookupException e )
295         {
296             return addExceptionToResult( result, e );
297         }
298 
299         //
300         // Desired order of precedence for local artifact repositories
301         //
302         // Reactor
303         // Workspace
304         // User Local Repository
305         //
306         repoSession.setWorkspaceReader( ChainedWorkspaceReader.newInstance( reactorWorkspace,
307                                                                             repoSession.getWorkspaceReader() ) );
308 
309         repoSession.setReadOnly();
310 
311         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
312         try
313         {
314             for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( projects ) )
315             {
316                 Thread.currentThread().setContextClassLoader( listener.getClass().getClassLoader() );
317 
318                 listener.afterProjectsRead( session );
319             }
320         }
321         catch ( MavenExecutionException e )
322         {
323             return addExceptionToResult( result, e );
324         }
325         finally
326         {
327             Thread.currentThread().setContextClassLoader( originalClassLoader );
328         }
329 
330         //
331         // The projects need to be topologically after the participants have run their afterProjectsRead(session)
332         // because the participant is free to change the dependencies of a project which can potentially change the
333         // topological order of the projects, and therefore can potentially change the build order.
334         //
335         // Note that participants may affect the topological order of the projects but it is
336         // not expected that a participant will add or remove projects from the session.
337         //
338         projectDependencyGraph = createProjectDependencyGraph( session.getProjects(), request, result, false );
339 
340         try
341         {
342             if ( result.hasExceptions() )
343             {
344                 return result;
345             }
346 
347             session.setProjects( projectDependencyGraph.getSortedProjects() );
348 
349             session.setProjectDependencyGraph( projectDependencyGraph );
350 
351             result.setTopologicallySortedProjects( session.getProjects() );
352 
353             result.setProject( session.getTopLevelProject() );
354 
355             lifecycleStarter.execute( session );
356 
357             validateActivatedProfiles( session.getProjects(), request.getActiveProfiles() );
358 
359             if ( session.getResult().hasExceptions() )
360             {
361                 return addExceptionToResult( result, session.getResult().getExceptions().get( 0 ) );
362             }
363         }
364         finally
365         {
366             try
367             {
368                 afterSessionEnd( projects, session );
369             }
370             catch ( MavenExecutionException e )
371             {
372                 return addExceptionToResult( result, e );
373             }
374             finally
375             {
376                 sessionScope.exit();
377             }
378         }
379 
380         return result;
381     }
382 
383     private void afterSessionEnd( Collection<MavenProject> projects, MavenSession session )
384         throws MavenExecutionException
385     {
386         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
387         try
388         {
389             for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( projects ) )
390             {
391                 Thread.currentThread().setContextClassLoader( listener.getClass().getClassLoader() );
392 
393                 listener.afterSessionEnd( session );
394             }
395         }
396         finally
397         {
398             Thread.currentThread().setContextClassLoader( originalClassLoader );
399         }
400     }
401 
402     public RepositorySystemSession newRepositorySession( MavenExecutionRequest request )
403     {
404         DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
405 
406         session.setCache( request.getRepositoryCache() );
407 
408         Map<Object, Object> configProps = new LinkedHashMap<Object, Object>();
409         configProps.put( ConfigurationProperties.USER_AGENT, getUserAgent() );
410         configProps.put( ConfigurationProperties.INTERACTIVE, request.isInteractiveMode() );
411         configProps.putAll( request.getSystemProperties() );
412         configProps.putAll( request.getUserProperties() );
413 
414         session.setOffline( request.isOffline() );
415         session.setChecksumPolicy( request.getGlobalChecksumPolicy() );
416         if ( request.isNoSnapshotUpdates() )
417         {
418             session.setUpdatePolicy( RepositoryPolicy.UPDATE_POLICY_NEVER );
419         }
420         else if ( request.isUpdateSnapshots() )
421         {
422             session.setUpdatePolicy( RepositoryPolicy.UPDATE_POLICY_ALWAYS );
423         }
424         else
425         {
426             session.setUpdatePolicy( null );
427         }
428 
429         int errorPolicy = 0;
430         errorPolicy |= request.isCacheNotFound() ? ResolutionErrorPolicy.CACHE_NOT_FOUND : 0;
431         errorPolicy |= request.isCacheTransferError() ? ResolutionErrorPolicy.CACHE_TRANSFER_ERROR : 0;
432         session.setResolutionErrorPolicy( new SimpleResolutionErrorPolicy( errorPolicy, errorPolicy
433             | ResolutionErrorPolicy.CACHE_NOT_FOUND ) );
434 
435         session.setArtifactTypeRegistry( RepositoryUtils.newArtifactTypeRegistry( artifactHandlerManager ) );
436 
437         LocalRepository localRepo = new LocalRepository( request.getLocalRepository().getBasedir() );
438 
439         if ( request.isUseLegacyLocalRepository() )
440         {
441             logger.warn( "Disabling enhanced local repository: using legacy is strongly discouraged to ensure"
442                 + " build reproducibility." );
443             try
444             {
445                 session.setLocalRepositoryManager( simpleLocalRepoMgrFactory.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 loc = ModelProblemUtils.formatLocation( problem, result.getProjectId() );
690                     logger.warn( problem.getMessage() + ( StringUtils.isNotEmpty( loc ) ? " @ " + loc : "" ) );
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 p : projects )
756         {
757             String projectKey = ArtifactUtils.key( p.getGroupId(), p.getArtifactId(), p.getVersion() );
758 
759             projectsMap.put( projectKey, p );
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. "
776                             + "This plugin was pulled from the local repository!" );
777                     }
778                 }
779             }
780         }
781     }
782 
783     private void validateActivatedProfiles( List<MavenProject> projects, List<String> activeProfileIds )
784     {
785         Collection<String> notActivatedProfileIds = new LinkedHashSet<String>( activeProfileIds );
786 
787         for ( MavenProject project : projects )
788         {
789             for ( List<String> profileIds : project.getInjectedProfileIds().values() )
790             {
791                 notActivatedProfileIds.removeAll( profileIds );
792             }
793         }
794 
795         for ( String notActivatedProfileId : notActivatedProfileIds )
796         {
797             logger.warn( "The requested profile \"" + notActivatedProfileId
798                 + "\" could not be activated because it does not exist." );
799         }
800     }
801 
802     @Deprecated // 5 January 2014
803     protected Logger getLogger()
804     {
805         return logger;
806     }
807 
808     private ProjectDependencyGraph createProjectDependencyGraph( Collection<MavenProject> projects,
809                                                                  MavenExecutionRequest request,
810                                                                  MavenExecutionResult result, boolean trimming )
811     {
812         ProjectDependencyGraph projectDependencyGraph = null;
813 
814         try
815         {
816             projectDependencyGraph = new DefaultProjectDependencyGraph( projects );
817 
818             if ( trimming )
819             {
820                 List<MavenProject> activeProjects = projectDependencyGraph.getSortedProjects();
821 
822                 activeProjects = trimSelectedProjects( activeProjects, projectDependencyGraph, request );
823                 activeProjects = trimExcludedProjects( activeProjects,  request );
824                 activeProjects = trimResumedProjects( activeProjects, request );
825 
826                 if ( activeProjects.size() != projectDependencyGraph.getSortedProjects().size() )
827                 {
828                     projectDependencyGraph =
829                         new FilteredProjectDependencyGraph( projectDependencyGraph, activeProjects );
830                 }
831             }
832         }
833         catch ( CycleDetectedException e )
834         {
835             String message = "The projects in the reactor contain a cyclic reference: " + e.getMessage();
836 
837             ProjectCycleException error = new ProjectCycleException( message, e );
838 
839             addExceptionToResult( result, error );
840         }
841         catch ( org.apache.maven.project.DuplicateProjectException e )
842         {
843             addExceptionToResult( result, e );
844         }
845         catch ( MavenExecutionException e )
846         {
847             addExceptionToResult( result, e );
848         }
849 
850         return projectDependencyGraph;
851     }
852 
853     private List<MavenProject> trimSelectedProjects( List<MavenProject> projects, ProjectDependencyGraph graph,
854                                                      MavenExecutionRequest request )
855         throws MavenExecutionException
856     {
857         List<MavenProject> result = projects;
858 
859         if ( !request.getSelectedProjects().isEmpty() )
860         {
861             File reactorDirectory = null;
862             if ( request.getBaseDirectory() != null )
863             {
864                 reactorDirectory = new File( request.getBaseDirectory() );
865             }
866 
867             Collection<MavenProject> selectedProjects = new LinkedHashSet<MavenProject>( projects.size() );
868 
869             for ( String selector : request.getSelectedProjects() )
870             {
871                 MavenProject selectedProject = null;
872 
873                 for ( MavenProject project : projects )
874                 {
875                     if ( isMatchingProject( project, selector, reactorDirectory ) )
876                     {
877                         selectedProject = project;
878                         break;
879                     }
880                 }
881 
882                 if ( selectedProject != null )
883                 {
884                     selectedProjects.add( selectedProject );
885                 }
886                 else
887                 {
888                     throw new MavenExecutionException( "Could not find the selected project in the reactor: "
889                         + selector, request.getPom() );
890                 }
891             }
892 
893             boolean makeUpstream = false;
894             boolean makeDownstream = false;
895 
896             if ( MavenExecutionRequest.REACTOR_MAKE_UPSTREAM.equals( request.getMakeBehavior() ) )
897             {
898                 makeUpstream = true;
899             }
900             else if ( MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM.equals( request.getMakeBehavior() ) )
901             {
902                 makeDownstream = true;
903             }
904             else if ( MavenExecutionRequest.REACTOR_MAKE_BOTH.equals( request.getMakeBehavior() ) )
905             {
906                 makeUpstream = true;
907                 makeDownstream = true;
908             }
909             else if ( StringUtils.isNotEmpty( request.getMakeBehavior() ) )
910             {
911                 throw new MavenExecutionException( "Invalid reactor make behavior: " + request.getMakeBehavior(),
912                                                    request.getPom() );
913             }
914 
915             if ( makeUpstream || makeDownstream )
916             {
917                 for ( MavenProject selectedProject : new ArrayList<MavenProject>( selectedProjects ) )
918                 {
919                     if ( makeUpstream )
920                     {
921                         selectedProjects.addAll( graph.getUpstreamProjects( selectedProject, true ) );
922                     }
923                     if ( makeDownstream )
924                     {
925                         selectedProjects.addAll( graph.getDownstreamProjects( selectedProject, true ) );
926                     }
927                 }
928             }
929 
930             result = new ArrayList<MavenProject>( selectedProjects.size() );
931 
932             for ( MavenProject project : projects )
933             {
934                 if ( selectedProjects.contains( project ) )
935                 {
936                     result.add( project );
937                 }
938             }
939         }
940 
941         return result;
942     }
943 
944     private List<MavenProject> trimExcludedProjects( List<MavenProject> projects, MavenExecutionRequest request )
945         throws MavenExecutionException
946     {
947         List<MavenProject> result = projects;
948 
949         if ( !request.getExcludedProjects().isEmpty() )
950         {
951             File reactorDirectory = null;
952 
953             if ( request.getBaseDirectory() != null )
954             {
955                 reactorDirectory = new File( request.getBaseDirectory() );
956             }
957 
958             Collection<MavenProject> excludedProjects = new LinkedHashSet<MavenProject>( projects.size() );
959 
960             for ( String selector : request.getExcludedProjects() )
961             {
962                 MavenProject excludedProject = null;
963 
964                 for ( MavenProject project : projects )
965                 {
966                     if ( isMatchingProject( project, selector, reactorDirectory ) )
967                     {
968                         excludedProject = project;
969                         break;
970                     }
971                 }
972 
973                 if ( excludedProject != null )
974                 {
975                     excludedProjects.add( excludedProject );
976                 }
977                 else
978                 {
979                     throw new MavenExecutionException( "Could not find the selected project in the reactor: "
980                         + selector, request.getPom() );
981                 }
982             }
983 
984             result = new ArrayList<MavenProject>( projects.size() );
985             for ( MavenProject project : projects )
986             {
987                 if ( !excludedProjects.contains( project ) )
988                 {
989                     result.add( project );
990                 }
991             }
992         }
993 
994         return result;
995     }
996 
997     private List<MavenProject> trimResumedProjects( List<MavenProject> projects, MavenExecutionRequest request )
998         throws MavenExecutionException
999     {
1000         List<MavenProject> result = projects;
1001 
1002         if ( StringUtils.isNotEmpty( request.getResumeFrom() ) )
1003         {
1004             File reactorDirectory = null;
1005             if ( request.getBaseDirectory() != null )
1006             {
1007                 reactorDirectory = new File( request.getBaseDirectory() );
1008             }
1009 
1010             String selector = request.getResumeFrom();
1011 
1012             result = new ArrayList<MavenProject>( projects.size() );
1013 
1014             boolean resumed = false;
1015 
1016             for ( MavenProject project : projects )
1017             {
1018                 if ( !resumed && isMatchingProject( project, selector, reactorDirectory ) )
1019                 {
1020                     resumed = true;
1021                 }
1022 
1023                 if ( resumed )
1024                 {
1025                     result.add( project );
1026                 }
1027             }
1028 
1029             if ( !resumed )
1030             {
1031                 throw new MavenExecutionException( "Could not find project to resume reactor build from: " + selector
1032                     + " vs " + projects, request.getPom() );
1033             }
1034         }
1035 
1036         return result;
1037     }
1038 
1039     private boolean isMatchingProject( MavenProject project, String selector, File reactorDirectory )
1040     {
1041         // [groupId]:artifactId
1042         if ( selector.indexOf( ':' ) >= 0 )
1043         {
1044             String id = ':' + project.getArtifactId();
1045 
1046             if ( id.equals( selector ) )
1047             {
1048                 return true;
1049             }
1050 
1051             id = project.getGroupId() + id;
1052 
1053             if ( id.equals( selector ) )
1054             {
1055                 return true;
1056             }
1057         }
1058 
1059         // relative path, e.g. "sub", "../sub" or "."
1060         else if ( reactorDirectory != null )
1061         {
1062             File selectedProject = new File( new File( reactorDirectory, selector ).toURI().normalize() );
1063 
1064             if ( selectedProject.isFile() )
1065             {
1066                 return selectedProject.equals( project.getFile() );
1067             }
1068             else if ( selectedProject.isDirectory() )
1069             {
1070                 return selectedProject.equals( project.getBasedir() );
1071             }
1072         }
1073 
1074         return false;
1075     }
1076 
1077 }