View Javadoc

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