001package org.apache.maven;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.File;
023import java.io.IOException;
024import java.io.InputStream;
025import java.text.SimpleDateFormat;
026import java.util.ArrayList;
027import java.util.Arrays;
028import java.util.Collection;
029import java.util.Collections;
030import java.util.Date;
031import java.util.HashSet;
032import java.util.LinkedHashMap;
033import java.util.LinkedHashSet;
034import java.util.List;
035import java.util.Map;
036import java.util.Properties;
037
038import org.apache.maven.artifact.ArtifactUtils;
039import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
040import org.apache.maven.eventspy.internal.EventSpyDispatcher;
041import org.apache.maven.execution.DefaultMavenExecutionResult;
042import org.apache.maven.execution.ExecutionEvent;
043import org.apache.maven.execution.MavenExecutionRequest;
044import org.apache.maven.execution.MavenExecutionRequestPopulationException;
045import org.apache.maven.execution.MavenExecutionRequestPopulator;
046import org.apache.maven.execution.MavenExecutionResult;
047import org.apache.maven.execution.MavenSession;
048import org.apache.maven.execution.ProjectDependencyGraph;
049import org.apache.maven.lifecycle.internal.ExecutionEventCatapult;
050import org.apache.maven.lifecycle.internal.LifecycleStarter;
051import org.apache.maven.model.building.ModelProblem;
052import org.apache.maven.model.building.ModelProblemUtils;
053import org.apache.maven.model.building.ModelSource;
054import org.apache.maven.model.building.UrlModelSource;
055import org.apache.maven.plugin.LegacySupport;
056import org.apache.maven.project.MavenProject;
057import org.apache.maven.project.ProjectBuilder;
058import org.apache.maven.project.ProjectBuildingException;
059import org.apache.maven.project.ProjectBuildingRequest;
060import org.apache.maven.project.ProjectBuildingResult;
061import org.apache.maven.project.ProjectSorter;
062import org.apache.maven.repository.DelegatingLocalArtifactRepository;
063import org.apache.maven.repository.LocalRepositoryNotAccessibleException;
064import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
065import org.apache.maven.settings.Mirror;
066import org.apache.maven.settings.Proxy;
067import org.apache.maven.settings.Server;
068import org.apache.maven.settings.building.SettingsProblem;
069import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
070import org.apache.maven.settings.crypto.SettingsDecrypter;
071import org.apache.maven.settings.crypto.SettingsDecryptionResult;
072import org.codehaus.plexus.PlexusContainer;
073import org.codehaus.plexus.component.annotations.Component;
074import org.codehaus.plexus.component.annotations.Requirement;
075import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
076import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
077import org.codehaus.plexus.logging.Logger;
078import org.codehaus.plexus.util.IOUtil;
079import org.codehaus.plexus.util.StringUtils;
080import org.codehaus.plexus.util.dag.CycleDetectedException;
081import org.codehaus.plexus.util.xml.Xpp3Dom;
082import org.eclipse.aether.ConfigurationProperties;
083import org.eclipse.aether.DefaultRepositorySystemSession;
084import org.eclipse.aether.RepositorySystem;
085import org.eclipse.aether.RepositorySystemSession;
086import org.eclipse.aether.repository.LocalRepository;
087import org.eclipse.aether.repository.NoLocalRepositoryManagerException;
088import org.eclipse.aether.repository.RepositoryPolicy;
089import org.eclipse.aether.repository.WorkspaceReader;
090import org.eclipse.aether.resolution.ResolutionErrorPolicy;
091import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory;
092import org.eclipse.aether.util.repository.AuthenticationBuilder;
093import org.eclipse.aether.util.repository.ChainedWorkspaceReader;
094import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
095import org.eclipse.aether.util.repository.DefaultMirrorSelector;
096import org.eclipse.aether.util.repository.DefaultProxySelector;
097import org.eclipse.aether.util.repository.SimpleResolutionErrorPolicy;
098
099/**
100 * @author Jason van Zyl
101 */
102@Component( role = Maven.class )
103public 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}