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