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