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