001    package 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    
022    import java.io.File;
023    import java.io.IOException;
024    import java.io.InputStream;
025    import java.text.SimpleDateFormat;
026    import java.util.ArrayList;
027    import java.util.Arrays;
028    import java.util.Collection;
029    import java.util.Collections;
030    import java.util.Date;
031    import java.util.HashSet;
032    import java.util.LinkedHashMap;
033    import java.util.LinkedHashSet;
034    import java.util.List;
035    import java.util.Map;
036    import java.util.Properties;
037    
038    import org.apache.maven.artifact.ArtifactUtils;
039    import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
040    import org.apache.maven.eventspy.internal.EventSpyDispatcher;
041    import org.apache.maven.execution.DefaultMavenExecutionResult;
042    import org.apache.maven.execution.ExecutionEvent;
043    import org.apache.maven.execution.MavenExecutionRequest;
044    import org.apache.maven.execution.MavenExecutionRequestPopulationException;
045    import org.apache.maven.execution.MavenExecutionRequestPopulator;
046    import org.apache.maven.execution.MavenExecutionResult;
047    import org.apache.maven.execution.MavenSession;
048    import org.apache.maven.execution.ProjectDependencyGraph;
049    import org.apache.maven.lifecycle.internal.ExecutionEventCatapult;
050    import org.apache.maven.lifecycle.internal.LifecycleStarter;
051    import org.apache.maven.model.building.ModelProblem;
052    import org.apache.maven.model.building.ModelProblemUtils;
053    import org.apache.maven.model.building.ModelSource;
054    import org.apache.maven.model.building.UrlModelSource;
055    import org.apache.maven.plugin.LegacySupport;
056    import org.apache.maven.project.DuplicateProjectException;
057    import org.apache.maven.project.MavenProject;
058    import org.apache.maven.project.ProjectBuilder;
059    import org.apache.maven.project.ProjectBuildingException;
060    import org.apache.maven.project.ProjectBuildingRequest;
061    import org.apache.maven.project.ProjectBuildingResult;
062    import org.apache.maven.project.ProjectSorter;
063    import org.apache.maven.repository.DelegatingLocalArtifactRepository;
064    import org.apache.maven.repository.LocalRepositoryNotAccessibleException;
065    import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
066    import org.apache.maven.settings.Mirror;
067    import org.apache.maven.settings.Proxy;
068    import org.apache.maven.settings.Server;
069    import org.apache.maven.settings.building.SettingsProblem;
070    import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
071    import org.apache.maven.settings.crypto.SettingsDecrypter;
072    import org.apache.maven.settings.crypto.SettingsDecryptionResult;
073    import org.codehaus.plexus.PlexusContainer;
074    import org.codehaus.plexus.component.annotations.Component;
075    import org.codehaus.plexus.component.annotations.Requirement;
076    import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
077    import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
078    import org.codehaus.plexus.logging.Logger;
079    import org.codehaus.plexus.util.IOUtil;
080    import org.codehaus.plexus.util.StringUtils;
081    import org.codehaus.plexus.util.dag.CycleDetectedException;
082    import org.codehaus.plexus.util.xml.Xpp3Dom;
083    import org.eclipse.aether.ConfigurationProperties;
084    import org.eclipse.aether.DefaultRepositorySystemSession;
085    import org.eclipse.aether.RepositorySystem;
086    import org.eclipse.aether.RepositorySystemSession;
087    import org.eclipse.aether.repository.LocalRepository;
088    import org.eclipse.aether.repository.NoLocalRepositoryManagerException;
089    import org.eclipse.aether.repository.RepositoryPolicy;
090    import org.eclipse.aether.repository.WorkspaceReader;
091    import org.eclipse.aether.resolution.ResolutionErrorPolicy;
092    import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory;
093    import org.eclipse.aether.util.repository.AuthenticationBuilder;
094    import org.eclipse.aether.util.repository.ChainedWorkspaceReader;
095    import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
096    import org.eclipse.aether.util.repository.DefaultMirrorSelector;
097    import org.eclipse.aether.util.repository.DefaultProxySelector;
098    import org.eclipse.aether.util.repository.SimpleResolutionErrorPolicy;
099    
100    /**
101     * @author Jason van Zyl
102     */
103    @Component( role = Maven.class )
104    public class DefaultMaven
105        implements Maven
106    {
107    
108        @Requirement
109        private Logger logger;
110    
111        @Requirement
112        protected ProjectBuilder projectBuilder;
113    
114        @Requirement
115        private LifecycleStarter lifecycleStarter;
116    
117        @Requirement
118        protected PlexusContainer container;
119    
120        @Requirement
121        MavenExecutionRequestPopulator populator;
122    
123        @Requirement
124        private ExecutionEventCatapult eventCatapult;
125    
126        @Requirement
127        private ArtifactHandlerManager artifactHandlerManager;
128    
129        @Requirement( optional = true, hint = "ide" )
130        private WorkspaceReader workspaceRepository;
131    
132        @Requirement
133        private RepositorySystem repoSystem;
134    
135        @Requirement( optional = true, hint = "simple" )
136        private LocalRepositoryManagerFactory simpleLocalRepositoryManagerFactory;
137    
138        @Requirement
139        private SettingsDecrypter settingsDecrypter;
140    
141        @Requirement
142        private LegacySupport legacySupport;
143    
144        @Requirement
145        private EventSpyDispatcher eventSpyDispatcher;
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 = addExceptionToResult( new DefaultMavenExecutionResult(), e );
158            }
159            catch ( MavenExecutionRequestPopulationException e )
160            {
161                result = addExceptionToResult( new DefaultMavenExecutionResult(), e );
162            }
163            catch ( RuntimeException e )
164            {
165                result =
166                    addExceptionToResult( 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        private MavenExecutionResult doExecute( MavenExecutionRequest request )
178        {
179            //TODO: Need a general way to inject standard properties
180            if ( request.getStartTime() != null )
181            {
182                request.getSystemProperties().put( "${build.timestamp}",
183                                                   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 addExceptionToResult( 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 addExceptionToResult( 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 addExceptionToResult( 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 addExceptionToResult( result, e );
261            }
262    
263            repoSession.setReadOnly();
264    
265            ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
266            try
267            {
268                for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( projects ) )
269                {
270                    Thread.currentThread().setContextClassLoader( listener.getClass().getClassLoader() );
271    
272                    listener.afterProjectsRead( session );
273                }
274            }
275            catch ( MavenExecutionException e )
276            {
277                return addExceptionToResult( result, e );
278            }
279            finally
280            {
281                Thread.currentThread().setContextClassLoader( originalClassLoader );
282            }
283    
284            try
285            {
286                ProjectSorter projectSorter = new ProjectSorter( session.getProjects() );
287    
288                ProjectDependencyGraph projectDependencyGraph = createDependencyGraph( projectSorter, request );
289    
290                session.setProjects( projectDependencyGraph.getSortedProjects() );
291    
292                session.setProjectDependencyGraph( projectDependencyGraph );
293            }
294            catch ( CycleDetectedException e )
295            {            
296                String message = "The projects in the reactor contain a cyclic reference: " + e.getMessage();
297    
298                ProjectCycleException error = new ProjectCycleException( message, e );
299    
300                return addExceptionToResult( result, error );
301            }
302            catch ( DuplicateProjectException e )
303            {
304                return addExceptionToResult( result, e );
305            }
306            catch ( MavenExecutionException e )
307            {
308                return addExceptionToResult( result, e );
309            }
310    
311            result.setTopologicallySortedProjects( session.getProjects() );
312    
313            if ( result.hasExceptions() )
314            {
315                return result;
316            }
317    
318            lifecycleStarter.execute( session );
319    
320            validateActivatedProfiles( session.getProjects(), request.getActiveProfiles() );
321    
322            if ( session.getResult().hasExceptions() )
323            {
324                return addExceptionToResult( result, session.getResult().getExceptions().get( 0 ) );
325            }
326    
327            return result;
328        }
329    
330        public RepositorySystemSession newRepositorySession( MavenExecutionRequest request )
331        {
332            DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
333    
334            session.setCache( request.getRepositoryCache() );
335    
336            Map<Object, Object> configProps = new LinkedHashMap<Object, Object>();
337            configProps.put( ConfigurationProperties.USER_AGENT, getUserAgent() );
338            configProps.put( ConfigurationProperties.INTERACTIVE, request.isInteractiveMode() );
339            configProps.putAll( request.getSystemProperties() );
340            configProps.putAll( request.getUserProperties() );
341    
342            session.setOffline( request.isOffline() );
343            session.setChecksumPolicy( request.getGlobalChecksumPolicy() );
344            if ( request.isNoSnapshotUpdates() )
345            {
346                session.setUpdatePolicy( RepositoryPolicy.UPDATE_POLICY_NEVER );
347            }
348            else if ( request.isUpdateSnapshots() )
349            {
350                session.setUpdatePolicy( RepositoryPolicy.UPDATE_POLICY_ALWAYS );
351            }
352            else
353            {
354                session.setUpdatePolicy( null );
355            }
356    
357            int errorPolicy = 0;
358            errorPolicy |= request.isCacheNotFound() ? ResolutionErrorPolicy.CACHE_NOT_FOUND : 0;
359            errorPolicy |= request.isCacheTransferError() ? ResolutionErrorPolicy.CACHE_TRANSFER_ERROR : 0;
360            session.setResolutionErrorPolicy( new SimpleResolutionErrorPolicy( errorPolicy, errorPolicy
361                | ResolutionErrorPolicy.CACHE_NOT_FOUND ) );
362    
363            session.setArtifactTypeRegistry( RepositoryUtils.newArtifactTypeRegistry( artifactHandlerManager ) );
364    
365            LocalRepository localRepo = new LocalRepository( request.getLocalRepository().getBasedir() );
366    
367            if ( request.isUseLegacyLocalRepository() )
368            {
369                logger.warn( "Disabling enhanced local repository: using legacy is strongly discouraged to ensure build reproducibility." );
370                try
371                {
372                    session.setLocalRepositoryManager( simpleLocalRepositoryManagerFactory.newInstance( session, localRepo ) );
373                }
374                catch ( NoLocalRepositoryManagerException e )
375                {
376    
377                    logger.warn( "Failed to configure legacy local repository: back to default" );
378                    session.setLocalRepositoryManager( repoSystem.newLocalRepositoryManager( session, localRepo ) );
379                }
380            }
381            else
382            {
383                session.setLocalRepositoryManager( repoSystem.newLocalRepositoryManager( session, localRepo ) );
384            }
385    
386            if ( request.getWorkspaceReader() != null )
387            {
388                session.setWorkspaceReader( request.getWorkspaceReader() );
389            }
390            else
391            {
392                session.setWorkspaceReader( workspaceRepository );
393            }
394    
395            DefaultSettingsDecryptionRequest decrypt = new DefaultSettingsDecryptionRequest();
396            decrypt.setProxies( request.getProxies() );
397            decrypt.setServers( request.getServers() );
398            SettingsDecryptionResult decrypted = settingsDecrypter.decrypt( decrypt );
399    
400            if ( logger.isDebugEnabled() )
401            {
402                for ( SettingsProblem problem : decrypted.getProblems() )
403                {
404                    logger.debug( problem.getMessage(), problem.getException() );
405                }
406            }
407    
408            DefaultMirrorSelector mirrorSelector = new DefaultMirrorSelector();
409            for ( Mirror mirror : request.getMirrors() )
410            {
411                mirrorSelector.add( mirror.getId(), mirror.getUrl(), mirror.getLayout(), false, mirror.getMirrorOf(),
412                                    mirror.getMirrorOfLayouts() );
413            }
414            session.setMirrorSelector( mirrorSelector );
415    
416            DefaultProxySelector proxySelector = new DefaultProxySelector();
417            for ( Proxy proxy : decrypted.getProxies() )
418            {
419                AuthenticationBuilder authBuilder = new AuthenticationBuilder();
420                authBuilder.addUsername( proxy.getUsername() ).addPassword( proxy.getPassword() );
421                proxySelector.add( new org.eclipse.aether.repository.Proxy( proxy.getProtocol(), proxy.getHost(),
422                                                                            proxy.getPort(), authBuilder.build() ),
423                                   proxy.getNonProxyHosts() );
424            }
425            session.setProxySelector( proxySelector );
426    
427            DefaultAuthenticationSelector authSelector = new DefaultAuthenticationSelector();
428            for ( Server server : decrypted.getServers() )
429            {
430                AuthenticationBuilder authBuilder = new AuthenticationBuilder();
431                authBuilder.addUsername( server.getUsername() ).addPassword( server.getPassword() );
432                authBuilder.addPrivateKey( server.getPrivateKey(), server.getPassphrase() );
433                authSelector.add( server.getId(), authBuilder.build() );
434    
435                if ( server.getConfiguration() != null )
436                {
437                    Xpp3Dom dom = (Xpp3Dom) server.getConfiguration();
438                    for ( int i = dom.getChildCount() - 1; i >= 0; i-- )
439                    {
440                        Xpp3Dom child = dom.getChild( i );
441                        if ( "wagonProvider".equals( child.getName() ) )
442                        {
443                            dom.removeChild( i );
444                        }
445                    }
446    
447                    XmlPlexusConfiguration config = new XmlPlexusConfiguration( dom );
448                    configProps.put( "aether.connector.wagon.config." + server.getId(), config );
449                }
450    
451                configProps.put( "aether.connector.perms.fileMode." + server.getId(), server.getFilePermissions() );
452                configProps.put( "aether.connector.perms.dirMode." + server.getId(), server.getDirectoryPermissions() );
453            }
454            session.setAuthenticationSelector( authSelector );
455    
456            session.setTransferListener( request.getTransferListener() );
457    
458            session.setRepositoryListener( eventSpyDispatcher.chainListener( new LoggingRepositoryListener( logger ) ) );
459    
460            session.setUserProperties( request.getUserProperties() );
461            session.setSystemProperties( request.getSystemProperties() );
462            session.setConfigProperties( configProps );
463    
464            return session;
465        }
466    
467        private String getUserAgent()
468        {
469            return "Apache-Maven/" + getMavenVersion()
470                + " (Java " + System.getProperty( "java.version" ) + "; "
471                + System.getProperty( "os.name" ) + " " + System.getProperty( "os.version" ) + ")";
472        }
473    
474        private String getMavenVersion()
475        {
476            Properties props = new Properties();
477    
478            InputStream is = getClass().getResourceAsStream( "/META-INF/maven/org.apache.maven/maven-core/pom.properties" );
479            if ( is != null )
480            {
481                try
482                {
483                    props.load( is );
484                }
485                catch ( IOException e )
486                {
487                    logger.debug( "Failed to read Maven version", e );
488                }
489                IOUtil.close( is );
490            }
491    
492            return props.getProperty( "version", "unknown-version" );
493        }
494    
495        private void validateLocalRepository( MavenExecutionRequest request )
496            throws LocalRepositoryNotAccessibleException
497        {
498            File localRepoDir = request.getLocalRepositoryPath();
499    
500            logger.debug( "Using local repository at " + localRepoDir );
501    
502            localRepoDir.mkdirs();
503    
504            if ( !localRepoDir.isDirectory() )
505            {
506                throw new LocalRepositoryNotAccessibleException( "Could not create local repository at " + localRepoDir );
507            }
508        }
509    
510        private Collection<AbstractMavenLifecycleParticipant> getLifecycleParticipants( Collection<MavenProject> projects )
511        {
512            Collection<AbstractMavenLifecycleParticipant> lifecycleListeners =
513                new LinkedHashSet<AbstractMavenLifecycleParticipant>();
514    
515            ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
516            try
517            {
518                try
519                {
520                    lifecycleListeners.addAll( container.lookupList( AbstractMavenLifecycleParticipant.class ) );
521                }
522                catch ( ComponentLookupException e )
523                {
524                    // this is just silly, lookupList should return an empty list!
525                    logger.warn( "Failed to lookup lifecycle participants: " + e.getMessage() );
526                }
527    
528                Collection<ClassLoader> scannedRealms = new HashSet<ClassLoader>();
529    
530                for ( MavenProject project : projects )
531                {
532                    ClassLoader projectRealm = project.getClassRealm();
533    
534                    if ( projectRealm != null && scannedRealms.add( projectRealm ) )
535                    {
536                        Thread.currentThread().setContextClassLoader( projectRealm );
537    
538                        try
539                        {
540                            lifecycleListeners.addAll( container.lookupList( AbstractMavenLifecycleParticipant.class ) );
541                        }
542                        catch ( ComponentLookupException e )
543                        {
544                            // this is just silly, lookupList should return an empty list!
545                            logger.warn( "Failed to lookup lifecycle participants: " + e.getMessage() );
546                        }
547                    }
548                }
549            }
550            finally
551            {
552                Thread.currentThread().setContextClassLoader( originalClassLoader );
553            }
554    
555            return lifecycleListeners;
556        }
557    
558        private MavenExecutionResult addExceptionToResult( MavenExecutionResult result, Throwable e )
559        {
560            if ( !result.getExceptions().contains( e ) )
561            {
562                result.addException( e );
563            }
564    
565            return result;
566        }
567        
568        private List<MavenProject> getProjectsForMavenReactor( MavenExecutionRequest request )
569            throws ProjectBuildingException
570        {
571            List<MavenProject> projects =  new ArrayList<MavenProject>();
572    
573            // We have no POM file.
574            //
575            if ( request.getPom() == null )
576            {
577                ModelSource modelSource = new UrlModelSource( DefaultMaven.class.getResource( "project/standalone.xml" ) );
578                MavenProject project =
579                    projectBuilder.build( modelSource, request.getProjectBuildingRequest() ).getProject();
580                project.setExecutionRoot( true );
581                projects.add( project );
582                request.setProjectPresent( false );
583                return projects;
584            }
585    
586            List<File> files = Arrays.asList( request.getPom().getAbsoluteFile() );        
587            collectProjects( projects, files, request );
588            return projects;
589        }
590    
591        private Map<String, MavenProject> getProjectMap( List<MavenProject> projects )
592            throws org.apache.maven.DuplicateProjectException
593        {
594            Map<String, MavenProject> index = new LinkedHashMap<String, MavenProject>();
595            Map<String, List<File>> collisions = new LinkedHashMap<String, List<File>>();
596    
597            for ( MavenProject project : projects )
598            {
599                String projectId = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() );
600    
601                MavenProject collision = index.get( projectId );
602    
603                if ( collision == null )
604                {
605                    index.put( projectId, project );
606                }
607                else
608                {
609                    List<File> pomFiles = collisions.get( projectId );
610    
611                    if ( pomFiles == null )
612                    {
613                        pomFiles = new ArrayList<File>( Arrays.asList( collision.getFile(), project.getFile() ) );
614                        collisions.put( projectId, pomFiles );
615                    }
616                    else
617                    {
618                        pomFiles.add( project.getFile() );
619                    }
620                }
621            }
622    
623            if ( !collisions.isEmpty() )
624            {
625                throw new org.apache.maven.DuplicateProjectException( "Two or more projects in the reactor"
626                    + " have the same identifier, please make sure that <groupId>:<artifactId>:<version>"
627                    + " is unique for each project: " + collisions, collisions );
628            }
629    
630            return index;
631        }
632    
633        private void collectProjects( List<MavenProject> projects, List<File> files, MavenExecutionRequest request )
634            throws ProjectBuildingException
635        {
636            ProjectBuildingRequest projectBuildingRequest = request.getProjectBuildingRequest();
637    
638            List<ProjectBuildingResult> results = projectBuilder.build( files, request.isRecursive(), projectBuildingRequest );
639    
640            boolean problems = false;
641    
642            for ( ProjectBuildingResult result : results )
643            {
644                projects.add( result.getProject() );
645    
646                if ( !result.getProblems().isEmpty() && logger.isWarnEnabled() )
647                {
648                    logger.warn( "" );
649                    logger.warn( "Some problems were encountered while building the effective model for "
650                        + result.getProject().getId() );
651    
652                    for ( ModelProblem problem : result.getProblems() )
653                    {
654                        String location = ModelProblemUtils.formatLocation( problem, result.getProjectId() );
655                        logger.warn( problem.getMessage() + ( StringUtils.isNotEmpty( location ) ? " @ " + location : "" ) );
656                    }
657    
658                    problems = true;
659                }
660            }
661    
662            if ( problems )
663            {
664                logger.warn( "" );
665                logger.warn( "It is highly recommended to fix these problems"
666                    + " because they threaten the stability of your build." );
667                logger.warn( "" );
668                logger.warn( "For this reason, future Maven versions might no"
669                    + " longer support building such malformed projects." );
670                logger.warn( "" );
671            }
672        }
673    
674        private void validateActivatedProfiles( List<MavenProject> projects, List<String> activeProfileIds )
675        {
676            Collection<String> notActivatedProfileIds = new LinkedHashSet<String>( activeProfileIds );
677    
678            for ( MavenProject project : projects )
679            {
680                for ( List<String> profileIds : project.getInjectedProfileIds().values() )
681                {
682                    notActivatedProfileIds.removeAll( profileIds );
683                }
684            }
685    
686            for ( String notActivatedProfileId : notActivatedProfileIds )
687            {
688                logger.warn( "The requested profile \"" + notActivatedProfileId
689                    + "\" could not be activated because it does not exist." );
690            }
691        }
692    
693        protected Logger getLogger()
694        {
695            return logger;
696        }
697    
698        private ProjectDependencyGraph createDependencyGraph( ProjectSorter sorter, MavenExecutionRequest request )
699            throws MavenExecutionException
700        {
701            ProjectDependencyGraph graph = new DefaultProjectDependencyGraph( sorter );
702    
703            List<MavenProject> activeProjects = sorter.getSortedProjects();
704    
705            activeProjects = trimSelectedProjects( activeProjects, graph, request );
706            activeProjects = trimResumedProjects( activeProjects, request );
707    
708            if ( activeProjects.size() != sorter.getSortedProjects().size() )
709            {
710                graph = new FilteredProjectDependencyGraph( graph, activeProjects );
711            }
712    
713            return graph;
714        }
715    
716        private List<MavenProject> trimSelectedProjects( List<MavenProject> projects, ProjectDependencyGraph graph,
717                                                         MavenExecutionRequest request )
718            throws MavenExecutionException
719        {
720            List<MavenProject> result = projects;
721    
722            if ( !request.getSelectedProjects().isEmpty() )
723            {
724                File reactorDirectory = null;
725                if ( request.getBaseDirectory() != null )
726                {
727                    reactorDirectory = new File( request.getBaseDirectory() );
728                }
729    
730                Collection<MavenProject> selectedProjects = new LinkedHashSet<MavenProject>( projects.size() );
731    
732                for ( String selector : request.getSelectedProjects() )
733                {
734                    MavenProject selectedProject = null;
735    
736                    for ( MavenProject project : projects )
737                    {
738                        if ( isMatchingProject( project, selector, reactorDirectory ) )
739                        {
740                            selectedProject = project;
741                            break;
742                        }
743                    }
744    
745                    if ( selectedProject != null )
746                    {
747                        selectedProjects.add( selectedProject );
748                    }
749                    else
750                    {
751                        throw new MavenExecutionException( "Could not find the selected project in the reactor: "
752                            + selector, request.getPom() );
753                    }
754                }
755    
756                boolean makeUpstream = false;
757                boolean makeDownstream = false;
758    
759                if ( MavenExecutionRequest.REACTOR_MAKE_UPSTREAM.equals( request.getMakeBehavior() ) )
760                {
761                    makeUpstream = true;
762                }
763                else if ( MavenExecutionRequest.REACTOR_MAKE_DOWNSTREAM.equals( request.getMakeBehavior() ) )
764                {
765                    makeDownstream = true;
766                }
767                else if ( MavenExecutionRequest.REACTOR_MAKE_BOTH.equals( request.getMakeBehavior() ) )
768                {
769                    makeUpstream = true;
770                    makeDownstream = true;
771                }
772                else if ( StringUtils.isNotEmpty( request.getMakeBehavior() ) )
773                {
774                    throw new MavenExecutionException( "Invalid reactor make behavior: " + request.getMakeBehavior(),
775                                                       request.getPom() );
776                }
777    
778                if ( makeUpstream || makeDownstream )
779                {
780                    for ( MavenProject selectedProject : new ArrayList<MavenProject>( selectedProjects ) )
781                    {
782                        if ( makeUpstream )
783                        {
784                            selectedProjects.addAll( graph.getUpstreamProjects( selectedProject, true ) );
785                        }
786                        if ( makeDownstream )
787                        {
788                            selectedProjects.addAll( graph.getDownstreamProjects( selectedProject, true ) );
789                        }
790                    }
791                }
792    
793                result = new ArrayList<MavenProject>( selectedProjects.size() );
794    
795                for ( MavenProject project : projects )
796                {
797                    if ( selectedProjects.contains( project ) )
798                    {
799                        result.add( project );
800                    }
801                }
802            }
803    
804            return result;
805        }
806    
807        private List<MavenProject> trimResumedProjects( List<MavenProject> projects, MavenExecutionRequest request )
808            throws MavenExecutionException
809        {
810            List<MavenProject> result = projects;
811    
812            if ( StringUtils.isNotEmpty( request.getResumeFrom() ) )
813            {
814                File reactorDirectory = null;
815                if ( request.getBaseDirectory() != null )
816                {
817                    reactorDirectory = new File( request.getBaseDirectory() );
818                }
819    
820                String selector = request.getResumeFrom();
821    
822                result = new ArrayList<MavenProject>( projects.size() );
823    
824                boolean resumed = false;
825    
826                for ( MavenProject project : projects )
827                {
828                    if ( !resumed && isMatchingProject( project, selector, reactorDirectory ) )
829                    {
830                        resumed = true;
831                    }
832    
833                    if ( resumed )
834                    {
835                        result.add( project );
836                    }
837                }
838    
839                if ( !resumed )
840                {
841                    throw new MavenExecutionException( "Could not find project to resume reactor build from: " + selector
842                        + " vs " + projects, request.getPom() );
843                }
844            }
845    
846            return result;
847        }
848    
849        private boolean isMatchingProject( MavenProject project, String selector, File reactorDirectory )
850        {
851            // [groupId]:artifactId
852            if ( selector.indexOf( ':' ) >= 0 )
853            {
854                String id = ':' + project.getArtifactId();
855    
856                if ( id.equals( selector ) )
857                {
858                    return true;
859                }
860    
861                id = project.getGroupId() + id;
862    
863                if ( id.equals( selector ) )
864                {
865                    return true;
866                }
867            }
868    
869            // relative path, e.g. "sub", "../sub" or "."
870            else if ( reactorDirectory != null )
871            {
872                File selectedProject = new File( new File( reactorDirectory, selector ).toURI().normalize() );
873    
874                if ( selectedProject.isFile() )
875                {
876                    return selectedProject.equals( project.getFile() );
877                }
878                else if ( selectedProject.isDirectory() )
879                {
880                    return selectedProject.equals( project.getBasedir() );
881                }
882            }
883    
884            return false;
885        }
886    
887    }