View Javadoc

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