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.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.building.ModelProblem;
52  import org.apache.maven.model.building.ModelProblemUtils;
53  import org.apache.maven.model.building.ModelSource;
54  import org.apache.maven.model.building.UrlModelSource;
55  import org.apache.maven.plugin.LegacySupport;
56  import org.apache.maven.project.DuplicateProjectException;
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.project.ProjectSorter;
63  import org.apache.maven.repository.DelegatingLocalArtifactRepository;
64  import org.apache.maven.repository.LocalRepositoryNotAccessibleException;
65  import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
66  import org.apache.maven.settings.Mirror;
67  import org.apache.maven.settings.Proxy;
68  import org.apache.maven.settings.Server;
69  import org.apache.maven.settings.building.SettingsProblem;
70  import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
71  import org.apache.maven.settings.crypto.SettingsDecrypter;
72  import org.apache.maven.settings.crypto.SettingsDecryptionResult;
73  import org.codehaus.plexus.PlexusContainer;
74  import org.codehaus.plexus.component.annotations.Component;
75  import org.codehaus.plexus.component.annotations.Requirement;
76  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
77  import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
78  import org.codehaus.plexus.logging.Logger;
79  import org.codehaus.plexus.util.IOUtil;
80  import org.codehaus.plexus.util.StringUtils;
81  import org.codehaus.plexus.util.dag.CycleDetectedException;
82  import org.codehaus.plexus.util.xml.Xpp3Dom;
83  import org.eclipse.aether.ConfigurationProperties;
84  import org.eclipse.aether.DefaultRepositorySystemSession;
85  import org.eclipse.aether.RepositorySystem;
86  import org.eclipse.aether.RepositorySystemSession;
87  import org.eclipse.aether.repository.LocalRepository;
88  import org.eclipse.aether.repository.NoLocalRepositoryManagerException;
89  import org.eclipse.aether.repository.RepositoryPolicy;
90  import org.eclipse.aether.repository.WorkspaceReader;
91  import org.eclipse.aether.resolution.ResolutionErrorPolicy;
92  import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory;
93  import org.eclipse.aether.util.repository.AuthenticationBuilder;
94  import org.eclipse.aether.util.repository.ChainedWorkspaceReader;
95  import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
96  import org.eclipse.aether.util.repository.DefaultMirrorSelector;
97  import org.eclipse.aether.util.repository.DefaultProxySelector;
98  import org.eclipse.aether.util.repository.SimpleResolutionErrorPolicy;
99  
100 /**
101  * @author Jason van Zyl
102  */
103 @Component( role = Maven.class )
104 public class DefaultMaven
105     implements Maven
106 {
107 
108     @Requirement
109     private Logger logger;
110 
111     @Requirement
112     protected ProjectBuilder projectBuilder;
113 
114     @Requirement
115     private LifecycleStarter lifecycleStarter;
116 
117     @Requirement
118     protected PlexusContainer container;
119 
120     @Requirement
121     MavenExecutionRequestPopulator populator;
122 
123     @Requirement
124     private ExecutionEventCatapult eventCatapult;
125 
126     @Requirement
127     private ArtifactHandlerManager artifactHandlerManager;
128 
129     @Requirement( optional = true, hint = "ide" )
130     private WorkspaceReader workspaceRepository;
131 
132     @Requirement
133     private RepositorySystem repoSystem;
134 
135     @Requirement( optional = true, hint = "simple" )
136     private LocalRepositoryManagerFactory simpleLocalRepositoryManagerFactory;
137 
138     @Requirement
139     private SettingsDecrypter settingsDecrypter;
140 
141     @Requirement
142     private LegacySupport legacySupport;
143 
144     @Requirement
145     private EventSpyDispatcher eventSpyDispatcher;
146 
147     public MavenExecutionResult execute( MavenExecutionRequest request )
148     {
149         MavenExecutionResult result;
150 
151         try
152         {
153             result = doExecute( populator.populateDefaults( request ) );
154         }
155         catch ( OutOfMemoryError e )
156         {
157             result = addExceptionToResult( new DefaultMavenExecutionResult(), e );
158         }
159         catch ( MavenExecutionRequestPopulationException e )
160         {
161             result = addExceptionToResult( new DefaultMavenExecutionResult(), e );
162         }
163         catch ( RuntimeException e )
164         {
165             result =
166                 addExceptionToResult( new DefaultMavenExecutionResult(),
167                                       new InternalErrorException( "Internal error: " + e, e ) );
168         }
169         finally
170         {
171             legacySupport.setSession( null );
172         }
173 
174         return result;
175     }
176 
177     private MavenExecutionResult doExecute( MavenExecutionRequest request )
178     {
179         //TODO: Need a general way to inject standard properties
180         if ( request.getStartTime() != null )
181         {
182             request.getSystemProperties().put( "${build.timestamp}",
183                                                new SimpleDateFormat( "yyyyMMdd-hhmm" ).format( request.getStartTime() ) );
184         }        
185         
186         request.setStartTime( new Date() );
187         
188         MavenExecutionResult result = new DefaultMavenExecutionResult();
189 
190         try
191         {
192             validateLocalRepository( request );
193         }
194         catch ( LocalRepositoryNotAccessibleException e )
195         {
196             return addExceptionToResult( result, e );
197         }
198 
199         DelegatingLocalArtifactRepository delegatingLocalArtifactRepository =
200             new DelegatingLocalArtifactRepository( request.getLocalRepository() );
201         
202         request.setLocalRepository( delegatingLocalArtifactRepository );        
203 
204         DefaultRepositorySystemSession repoSession = (DefaultRepositorySystemSession) newRepositorySession( request );
205 
206         MavenSession session = new MavenSession( container, repoSession, request, result );
207         legacySupport.setSession( session );
208 
209         try
210         {
211             for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( Collections.<MavenProject> emptyList() ) )
212             {
213                 listener.afterSessionStart( session );
214             }
215         }
216         catch ( MavenExecutionException e )
217         {
218             return addExceptionToResult( result, e );
219         }
220 
221         eventCatapult.fire( ExecutionEvent.Type.ProjectDiscoveryStarted, session, null );
222 
223         request.getProjectBuildingRequest().setRepositorySession( session.getRepositorySession() );
224 
225         //TODO: optimize for the single project or no project
226         
227         List<MavenProject> projects;
228         try
229         {
230             projects = getProjectsForMavenReactor( request );                                                
231         }
232         catch ( ProjectBuildingException e )
233         {
234             return addExceptionToResult( result, e );
235         }
236 
237         session.setProjects( projects );
238 
239         result.setTopologicallySortedProjects( session.getProjects() );
240         
241         result.setProject( session.getTopLevelProject() );
242 
243         try
244         {
245             Map<String, MavenProject> projectMap;
246             projectMap = getProjectMap( session.getProjects() );
247     
248             // Desired order of precedence for local artifact repositories
249             //
250             // Reactor
251             // Workspace
252             // User Local Repository
253             ReactorReader reactorRepository = new ReactorReader( projectMap );
254 
255             repoSession.setWorkspaceReader( ChainedWorkspaceReader.newInstance( reactorRepository,
256                                                                                 repoSession.getWorkspaceReader() ) );
257         }
258         catch ( org.apache.maven.DuplicateProjectException e )
259         {
260             return addExceptionToResult( result, e );
261         }
262 
263         repoSession.setReadOnly();
264 
265         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
266         try
267         {
268             for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( projects ) )
269             {
270                 Thread.currentThread().setContextClassLoader( listener.getClass().getClassLoader() );
271 
272                 listener.afterProjectsRead( session );
273             }
274         }
275         catch ( MavenExecutionException e )
276         {
277             return addExceptionToResult( result, e );
278         }
279         finally
280         {
281             Thread.currentThread().setContextClassLoader( originalClassLoader );
282         }
283 
284         try
285         {
286             ProjectSorter projectSorter = new ProjectSorter( session.getProjects() );
287 
288             ProjectDependencyGraph projectDependencyGraph = createDependencyGraph( projectSorter, request );
289 
290             session.setProjects( projectDependencyGraph.getSortedProjects() );
291 
292             session.setProjectDependencyGraph( projectDependencyGraph );
293         }
294         catch ( CycleDetectedException e )
295         {            
296             String message = "The projects in the reactor contain a cyclic reference: " + e.getMessage();
297 
298             ProjectCycleException error = new ProjectCycleException( message, e );
299 
300             return addExceptionToResult( result, error );
301         }
302         catch ( DuplicateProjectException e )
303         {
304             return addExceptionToResult( result, e );
305         }
306         catch ( MavenExecutionException e )
307         {
308             return addExceptionToResult( result, e );
309         }
310 
311         result.setTopologicallySortedProjects( session.getProjects() );
312 
313         if ( result.hasExceptions() )
314         {
315             return result;
316         }
317 
318         lifecycleStarter.execute( session );
319 
320         validateActivatedProfiles( session.getProjects(), request.getActiveProfiles() );
321 
322         if ( session.getResult().hasExceptions() )
323         {
324             return addExceptionToResult( result, session.getResult().getExceptions().get( 0 ) );
325         }
326 
327         return result;
328     }
329 
330     public RepositorySystemSession newRepositorySession( MavenExecutionRequest request )
331     {
332         DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
333 
334         session.setCache( request.getRepositoryCache() );
335 
336         Map<Object, Object> configProps = new LinkedHashMap<Object, Object>();
337         configProps.put( ConfigurationProperties.USER_AGENT, getUserAgent() );
338         configProps.put( ConfigurationProperties.INTERACTIVE, request.isInteractiveMode() );
339         configProps.putAll( request.getSystemProperties() );
340         configProps.putAll( request.getUserProperties() );
341 
342         session.setOffline( request.isOffline() );
343         session.setChecksumPolicy( request.getGlobalChecksumPolicy() );
344         if ( request.isNoSnapshotUpdates() )
345         {
346             session.setUpdatePolicy( RepositoryPolicy.UPDATE_POLICY_NEVER );
347         }
348         else if ( request.isUpdateSnapshots() )
349         {
350             session.setUpdatePolicy( RepositoryPolicy.UPDATE_POLICY_ALWAYS );
351         }
352         else
353         {
354             session.setUpdatePolicy( null );
355         }
356 
357         int errorPolicy = 0;
358         errorPolicy |= request.isCacheNotFound() ? ResolutionErrorPolicy.CACHE_NOT_FOUND : 0;
359         errorPolicy |= request.isCacheTransferError() ? ResolutionErrorPolicy.CACHE_TRANSFER_ERROR : 0;
360         session.setResolutionErrorPolicy( new SimpleResolutionErrorPolicy( errorPolicy, errorPolicy
361             | ResolutionErrorPolicy.CACHE_NOT_FOUND ) );
362 
363         session.setArtifactTypeRegistry( RepositoryUtils.newArtifactTypeRegistry( artifactHandlerManager ) );
364 
365         LocalRepository localRepo = new LocalRepository( request.getLocalRepository().getBasedir() );
366 
367         if ( request.isUseLegacyLocalRepository() )
368         {
369             logger.warn( "Disabling enhanced local repository: using legacy is strongly discouraged to ensure build reproducibility." );
370             try
371             {
372                 session.setLocalRepositoryManager( simpleLocalRepositoryManagerFactory.newInstance( session, localRepo ) );
373             }
374             catch ( NoLocalRepositoryManagerException e )
375             {
376 
377                 logger.warn( "Failed to configure legacy local repository: back to default" );
378                 session.setLocalRepositoryManager( repoSystem.newLocalRepositoryManager( session, localRepo ) );
379             }
380         }
381         else
382         {
383             session.setLocalRepositoryManager( repoSystem.newLocalRepositoryManager( session, localRepo ) );
384         }
385 
386         if ( request.getWorkspaceReader() != null )
387         {
388             session.setWorkspaceReader( request.getWorkspaceReader() );
389         }
390         else
391         {
392             session.setWorkspaceReader( workspaceRepository );
393         }
394 
395         DefaultSettingsDecryptionRequest decrypt = new DefaultSettingsDecryptionRequest();
396         decrypt.setProxies( request.getProxies() );
397         decrypt.setServers( request.getServers() );
398         SettingsDecryptionResult decrypted = settingsDecrypter.decrypt( decrypt );
399 
400         if ( logger.isDebugEnabled() )
401         {
402             for ( SettingsProblem problem : decrypted.getProblems() )
403             {
404                 logger.debug( problem.getMessage(), problem.getException() );
405             }
406         }
407 
408         DefaultMirrorSelector mirrorSelector = new DefaultMirrorSelector();
409         for ( Mirror mirror : request.getMirrors() )
410         {
411             mirrorSelector.add( mirror.getId(), mirror.getUrl(), mirror.getLayout(), false, mirror.getMirrorOf(),
412                                 mirror.getMirrorOfLayouts() );
413         }
414         session.setMirrorSelector( mirrorSelector );
415 
416         DefaultProxySelector proxySelector = new DefaultProxySelector();
417         for ( Proxy proxy : decrypted.getProxies() )
418         {
419             AuthenticationBuilder authBuilder = new AuthenticationBuilder();
420             authBuilder.addUsername( proxy.getUsername() ).addPassword( proxy.getPassword() );
421             proxySelector.add( new org.eclipse.aether.repository.Proxy( proxy.getProtocol(), proxy.getHost(),
422                                                                         proxy.getPort(), authBuilder.build() ),
423                                proxy.getNonProxyHosts() );
424         }
425         session.setProxySelector( proxySelector );
426 
427         DefaultAuthenticationSelector authSelector = new DefaultAuthenticationSelector();
428         for ( Server server : decrypted.getServers() )
429         {
430             AuthenticationBuilder authBuilder = new AuthenticationBuilder();
431             authBuilder.addUsername( server.getUsername() ).addPassword( server.getPassword() );
432             authBuilder.addPrivateKey( server.getPrivateKey(), server.getPassphrase() );
433             authSelector.add( server.getId(), authBuilder.build() );
434 
435             if ( server.getConfiguration() != null )
436             {
437                 Xpp3Dom dom = (Xpp3Dom) server.getConfiguration();
438                 for ( int i = dom.getChildCount() - 1; i >= 0; i-- )
439                 {
440                     Xpp3Dom child = dom.getChild( i );
441                     if ( "wagonProvider".equals( child.getName() ) )
442                     {
443                         dom.removeChild( i );
444                     }
445                 }
446 
447                 XmlPlexusConfiguration config = new XmlPlexusConfiguration( dom );
448                 configProps.put( "aether.connector.wagon.config." + server.getId(), config );
449             }
450 
451             configProps.put( "aether.connector.perms.fileMode." + server.getId(), server.getFilePermissions() );
452             configProps.put( "aether.connector.perms.dirMode." + server.getId(), server.getDirectoryPermissions() );
453         }
454         session.setAuthenticationSelector( authSelector );
455 
456         session.setTransferListener( request.getTransferListener() );
457 
458         session.setRepositoryListener( eventSpyDispatcher.chainListener( new LoggingRepositoryListener( logger ) ) );
459 
460         session.setUserProperties( request.getUserProperties() );
461         session.setSystemProperties( request.getSystemProperties() );
462         session.setConfigProperties( configProps );
463 
464         return session;
465     }
466 
467     private String getUserAgent()
468     {
469         return "Apache-Maven/" + getMavenVersion()
470             + " (Java " + System.getProperty( "java.version" ) + "; "
471             + System.getProperty( "os.name" ) + " " + System.getProperty( "os.version" ) + ")";
472     }
473 
474     private String getMavenVersion()
475     {
476         Properties props = new Properties();
477 
478         InputStream is = getClass().getResourceAsStream( "/META-INF/maven/org.apache.maven/maven-core/pom.properties" );
479         if ( is != null )
480         {
481             try
482             {
483                 props.load( is );
484             }
485             catch ( IOException e )
486             {
487                 logger.debug( "Failed to read Maven version", e );
488             }
489             IOUtil.close( is );
490         }
491 
492         return props.getProperty( "version", "unknown-version" );
493     }
494 
495     private void validateLocalRepository( MavenExecutionRequest request )
496         throws LocalRepositoryNotAccessibleException
497     {
498         File localRepoDir = request.getLocalRepositoryPath();
499 
500         logger.debug( "Using local repository at " + localRepoDir );
501 
502         localRepoDir.mkdirs();
503 
504         if ( !localRepoDir.isDirectory() )
505         {
506             throw new LocalRepositoryNotAccessibleException( "Could not create local repository at " + localRepoDir );
507         }
508     }
509 
510     private Collection<AbstractMavenLifecycleParticipant> getLifecycleParticipants( Collection<MavenProject> projects )
511     {
512         Collection<AbstractMavenLifecycleParticipant> lifecycleListeners =
513             new LinkedHashSet<AbstractMavenLifecycleParticipant>();
514 
515         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
516         try
517         {
518             try
519             {
520                 lifecycleListeners.addAll( container.lookupList( AbstractMavenLifecycleParticipant.class ) );
521             }
522             catch ( ComponentLookupException e )
523             {
524                 // this is just silly, lookupList should return an empty list!
525                 logger.warn( "Failed to lookup lifecycle participants: " + e.getMessage() );
526             }
527 
528             Collection<ClassLoader> scannedRealms = new HashSet<ClassLoader>();
529 
530             for ( MavenProject project : projects )
531             {
532                 ClassLoader projectRealm = project.getClassRealm();
533 
534                 if ( projectRealm != null && scannedRealms.add( projectRealm ) )
535                 {
536                     Thread.currentThread().setContextClassLoader( projectRealm );
537 
538                     try
539                     {
540                         lifecycleListeners.addAll( container.lookupList( AbstractMavenLifecycleParticipant.class ) );
541                     }
542                     catch ( ComponentLookupException e )
543                     {
544                         // this is just silly, lookupList should return an empty list!
545                         logger.warn( "Failed to lookup lifecycle participants: " + e.getMessage() );
546                     }
547                 }
548             }
549         }
550         finally
551         {
552             Thread.currentThread().setContextClassLoader( originalClassLoader );
553         }
554 
555         return lifecycleListeners;
556     }
557 
558     private MavenExecutionResult addExceptionToResult( MavenExecutionResult result, Throwable e )
559     {
560         if ( !result.getExceptions().contains( e ) )
561         {
562             result.addException( e );
563         }
564 
565         return result;
566     }
567     
568     private List<MavenProject> getProjectsForMavenReactor( MavenExecutionRequest request )
569         throws ProjectBuildingException
570     {
571         List<MavenProject> projects =  new ArrayList<MavenProject>();
572 
573         // We have no POM file.
574         //
575         if ( request.getPom() == null )
576         {
577             ModelSource modelSource = new UrlModelSource( DefaultMaven.class.getResource( "project/standalone.xml" ) );
578             MavenProject project =
579                 projectBuilder.build( modelSource, request.getProjectBuildingRequest() ).getProject();
580             project.setExecutionRoot( true );
581             projects.add( project );
582             request.setProjectPresent( false );
583             return projects;
584         }
585 
586         List<File> files = Arrays.asList( request.getPom().getAbsoluteFile() );        
587         collectProjects( projects, files, request );
588         return projects;
589     }
590 
591     private Map<String, MavenProject> getProjectMap( List<MavenProject> projects )
592         throws org.apache.maven.DuplicateProjectException
593     {
594         Map<String, MavenProject> index = new LinkedHashMap<String, MavenProject>();
595         Map<String, List<File>> collisions = new LinkedHashMap<String, List<File>>();
596 
597         for ( MavenProject project : projects )
598         {
599             String projectId = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() );
600 
601             MavenProject collision = index.get( projectId );
602 
603             if ( collision == null )
604             {
605                 index.put( projectId, project );
606             }
607             else
608             {
609                 List<File> pomFiles = collisions.get( projectId );
610 
611                 if ( pomFiles == null )
612                 {
613                     pomFiles = new ArrayList<File>( Arrays.asList( collision.getFile(), project.getFile() ) );
614                     collisions.put( projectId, pomFiles );
615                 }
616                 else
617                 {
618                     pomFiles.add( project.getFile() );
619                 }
620             }
621         }
622 
623         if ( !collisions.isEmpty() )
624         {
625             throw new org.apache.maven.DuplicateProjectException( "Two or more projects in the reactor"
626                 + " have the same identifier, please make sure that <groupId>:<artifactId>:<version>"
627                 + " is unique for each project: " + collisions, collisions );
628         }
629 
630         return index;
631     }
632 
633     private void collectProjects( List<MavenProject> projects, List<File> files, MavenExecutionRequest request )
634         throws ProjectBuildingException
635     {
636         ProjectBuildingRequest projectBuildingRequest = request.getProjectBuildingRequest();
637 
638         List<ProjectBuildingResult> results = projectBuilder.build( files, request.isRecursive(), projectBuildingRequest );
639 
640         boolean problems = false;
641 
642         for ( ProjectBuildingResult result : results )
643         {
644             projects.add( result.getProject() );
645 
646             if ( !result.getProblems().isEmpty() && logger.isWarnEnabled() )
647             {
648                 logger.warn( "" );
649                 logger.warn( "Some problems were encountered while building the effective model for "
650                     + result.getProject().getId() );
651 
652                 for ( ModelProblem problem : result.getProblems() )
653                 {
654                     String location = ModelProblemUtils.formatLocation( problem, result.getProjectId() );
655                     logger.warn( problem.getMessage() + ( StringUtils.isNotEmpty( location ) ? " @ " + location : "" ) );
656                 }
657 
658                 problems = true;
659             }
660         }
661 
662         if ( problems )
663         {
664             logger.warn( "" );
665             logger.warn( "It is highly recommended to fix these problems"
666                 + " because they threaten the stability of your build." );
667             logger.warn( "" );
668             logger.warn( "For this reason, future Maven versions might no"
669                 + " longer support building such malformed projects." );
670             logger.warn( "" );
671         }
672     }
673 
674     private void validateActivatedProfiles( List<MavenProject> projects, List<String> activeProfileIds )
675     {
676         Collection<String> notActivatedProfileIds = new LinkedHashSet<String>( activeProfileIds );
677 
678         for ( MavenProject project : projects )
679         {
680             for ( List<String> profileIds : project.getInjectedProfileIds().values() )
681             {
682                 notActivatedProfileIds.removeAll( profileIds );
683             }
684         }
685 
686         for ( String notActivatedProfileId : notActivatedProfileIds )
687         {
688             logger.warn( "The requested profile \"" + notActivatedProfileId
689                 + "\" could not be activated because it does not exist." );
690         }
691     }
692 
693     protected Logger getLogger()
694     {
695         return logger;
696     }
697 
698     private ProjectDependencyGraph createDependencyGraph( ProjectSorter sorter, MavenExecutionRequest request )
699         throws MavenExecutionException
700     {
701         ProjectDependencyGraph graph = new DefaultProjectDependencyGraph( sorter );
702 
703         List<MavenProject> activeProjects = sorter.getSortedProjects();
704 
705         activeProjects = trimSelectedProjects( activeProjects, graph, request );
706         activeProjects = trimResumedProjects( activeProjects, request );
707 
708         if ( activeProjects.size() != sorter.getSortedProjects().size() )
709         {
710             graph = new FilteredProjectDependencyGraph( graph, activeProjects );
711         }
712 
713         return graph;
714     }
715 
716     private List<MavenProject> trimSelectedProjects( List<MavenProject> projects, ProjectDependencyGraph graph,
717                                                      MavenExecutionRequest request )
718         throws MavenExecutionException
719     {
720         List<MavenProject> result = projects;
721 
722         if ( !request.getSelectedProjects().isEmpty() )
723         {
724             File reactorDirectory = null;
725             if ( request.getBaseDirectory() != null )
726             {
727                 reactorDirectory = new File( request.getBaseDirectory() );
728             }
729 
730             Collection<MavenProject> selectedProjects = new LinkedHashSet<MavenProject>( projects.size() );
731 
732             for ( String selector : request.getSelectedProjects() )
733             {
734                 MavenProject selectedProject = null;
735 
736                 for ( MavenProject project : projects )
737                 {
738                     if ( isMatchingProject( project, selector, reactorDirectory ) )
739                     {
740                         selectedProject = project;
741                         break;
742                     }
743                 }
744 
745                 if ( selectedProject != null )
746                 {
747                     selectedProjects.add( selectedProject );
748                 }
749                 else
750                 {
751                     throw new MavenExecutionException( "Could not find the selected project in the reactor: "
752                         + selector, request.getPom() );
753                 }
754             }
755 
756             boolean makeUpstream = false;
757             boolean makeDownstream = false;
758 
759             if ( MavenExecutionRequest.REACTOR_MAKE_UPSTREAM.equals( request.getMakeBehavior() ) )
760             {
761                 makeUpstream = true;
762             }
763             else if ( MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM.equals( request.getMakeBehavior() ) )
764             {
765                 makeDownstream = true;
766             }
767             else if ( MavenExecutionRequest.REACTOR_MAKE_BOTH.equals( request.getMakeBehavior() ) )
768             {
769                 makeUpstream = true;
770                 makeDownstream = true;
771             }
772             else if ( StringUtils.isNotEmpty( request.getMakeBehavior() ) )
773             {
774                 throw new MavenExecutionException( "Invalid reactor make behavior: " + request.getMakeBehavior(),
775                                                    request.getPom() );
776             }
777 
778             if ( makeUpstream || makeDownstream )
779             {
780                 for ( MavenProject selectedProject : new ArrayList<MavenProject>( selectedProjects ) )
781                 {
782                     if ( makeUpstream )
783                     {
784                         selectedProjects.addAll( graph.getUpstreamProjects( selectedProject, true ) );
785                     }
786                     if ( makeDownstream )
787                     {
788                         selectedProjects.addAll( graph.getDownstreamProjects( selectedProject, true ) );
789                     }
790                 }
791             }
792 
793             result = new ArrayList<MavenProject>( selectedProjects.size() );
794 
795             for ( MavenProject project : projects )
796             {
797                 if ( selectedProjects.contains( project ) )
798                 {
799                     result.add( project );
800                 }
801             }
802         }
803 
804         return result;
805     }
806 
807     private List<MavenProject> trimResumedProjects( List<MavenProject> projects, MavenExecutionRequest request )
808         throws MavenExecutionException
809     {
810         List<MavenProject> result = projects;
811 
812         if ( StringUtils.isNotEmpty( request.getResumeFrom() ) )
813         {
814             File reactorDirectory = null;
815             if ( request.getBaseDirectory() != null )
816             {
817                 reactorDirectory = new File( request.getBaseDirectory() );
818             }
819 
820             String selector = request.getResumeFrom();
821 
822             result = new ArrayList<MavenProject>( projects.size() );
823 
824             boolean resumed = false;
825 
826             for ( MavenProject project : projects )
827             {
828                 if ( !resumed && isMatchingProject( project, selector, reactorDirectory ) )
829                 {
830                     resumed = true;
831                 }
832 
833                 if ( resumed )
834                 {
835                     result.add( project );
836                 }
837             }
838 
839             if ( !resumed )
840             {
841                 throw new MavenExecutionException( "Could not find project to resume reactor build from: " + selector
842                     + " vs " + projects, request.getPom() );
843             }
844         }
845 
846         return result;
847     }
848 
849     private boolean isMatchingProject( MavenProject project, String selector, File reactorDirectory )
850     {
851         // [groupId]:artifactId
852         if ( selector.indexOf( ':' ) >= 0 )
853         {
854             String id = ':' + project.getArtifactId();
855 
856             if ( id.equals( selector ) )
857             {
858                 return true;
859             }
860 
861             id = project.getGroupId() + id;
862 
863             if ( id.equals( selector ) )
864             {
865                 return true;
866             }
867         }
868 
869         // relative path, e.g. "sub", "../sub" or "."
870         else if ( reactorDirectory != null )
871         {
872             File selectedProject = new File( new File( reactorDirectory, selector ).toURI().normalize() );
873 
874             if ( selectedProject.isFile() )
875             {
876                 return selectedProject.equals( project.getFile() );
877             }
878             else if ( selectedProject.isDirectory() )
879             {
880                 return selectedProject.equals( project.getBasedir() );
881             }
882         }
883 
884         return false;
885     }
886 
887 }