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