View Javadoc
1   package org.apache.maven;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.api.Session;
23  import org.apache.maven.artifact.ArtifactUtils;
24  import org.apache.maven.execution.BuildResumptionAnalyzer;
25  import org.apache.maven.execution.BuildResumptionDataRepository;
26  import org.apache.maven.execution.BuildResumptionPersistenceException;
27  import org.apache.maven.execution.DefaultMavenExecutionResult;
28  import org.apache.maven.execution.ExecutionEvent;
29  import org.apache.maven.execution.MavenExecutionRequest;
30  import org.apache.maven.execution.MavenExecutionResult;
31  import org.apache.maven.execution.MavenSession;
32  import org.apache.maven.execution.ProfileActivation;
33  import org.apache.maven.execution.ProjectActivation;
34  import org.apache.maven.execution.ProjectDependencyGraph;
35  import org.apache.maven.graph.GraphBuilder;
36  import org.apache.maven.graph.ProjectSelector;
37  import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory;
38  import org.apache.maven.internal.impl.DefaultSession;
39  import org.apache.maven.internal.impl.DefaultSessionFactory;
40  import org.apache.maven.lifecycle.LifecycleExecutionException;
41  import org.apache.maven.internal.aether.MavenChainedWorkspaceReader;
42  import org.apache.maven.lifecycle.internal.ExecutionEventCatapult;
43  import org.apache.maven.lifecycle.internal.LifecycleStarter;
44  import org.apache.maven.api.model.Model;
45  import org.apache.maven.api.model.Prerequisites;
46  import org.apache.maven.api.model.Profile;
47  import org.apache.maven.model.building.ModelProblem;
48  import org.apache.maven.model.building.Result;
49  import org.apache.maven.model.superpom.SuperPomProvider;
50  import org.apache.maven.plugin.LegacySupport;
51  import org.apache.maven.project.MavenProject;
52  import org.apache.maven.project.ProjectBuilder;
53  import org.apache.maven.repository.LocalRepositoryNotAccessibleException;
54  import org.apache.maven.session.scope.internal.SessionScope;
55  import org.codehaus.plexus.PlexusContainer;
56  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
57  import org.eclipse.aether.DefaultRepositorySystemSession;
58  import org.eclipse.aether.RepositorySystemSession;
59  import org.eclipse.aether.repository.WorkspaceReader;
60  import org.slf4j.Logger;
61  import org.slf4j.LoggerFactory;
62  import org.slf4j.helpers.MessageFormatter;
63  
64  import javax.inject.Inject;
65  import javax.inject.Named;
66  import javax.inject.Singleton;
67  import java.io.File;
68  import java.util.ArrayList;
69  import java.util.Arrays;
70  import java.util.Collection;
71  import java.util.Collections;
72  import java.util.Date;
73  import java.util.HashSet;
74  import java.util.LinkedHashMap;
75  import java.util.LinkedHashSet;
76  import java.util.List;
77  import java.util.Map;
78  import java.util.Set;
79  import java.util.function.Function;
80  import java.util.stream.Stream;
81  
82  import static java.util.stream.Collectors.toSet;
83  
84  /**
85   * @author Jason van Zyl
86   */
87  @Named
88  @Singleton
89  public class DefaultMaven
90      implements Maven
91  {
92      private final Logger logger = LoggerFactory.getLogger( getClass() );
93  
94      protected ProjectBuilder projectBuilder;
95  
96      private LifecycleStarter lifecycleStarter;
97  
98      protected PlexusContainer container;
99  
100     private ExecutionEventCatapult eventCatapult;
101 
102     private LegacySupport legacySupport;
103 
104     private SessionScope sessionScope;
105 
106     private DefaultRepositorySystemSessionFactory repositorySessionFactory;
107 
108     private final GraphBuilder graphBuilder;
109 
110     private final BuildResumptionAnalyzer buildResumptionAnalyzer;
111 
112     private final BuildResumptionDataRepository buildResumptionDataRepository;
113 
114     private final SuperPomProvider superPomProvider;
115 
116     private final DefaultSessionFactory defaultSessionFactory;
117 
118     private final ProjectSelector projectSelector;
119 
120     @Inject
121     @SuppressWarnings( "checkstyle:ParameterNumber" )
122     public DefaultMaven(
123             ProjectBuilder projectBuilder,
124             LifecycleStarter lifecycleStarter,
125             PlexusContainer container,
126             ExecutionEventCatapult eventCatapult,
127             LegacySupport legacySupport,
128             SessionScope sessionScope,
129             DefaultRepositorySystemSessionFactory repositorySessionFactory,
130             @Named( GraphBuilder.HINT ) GraphBuilder graphBuilder,
131             BuildResumptionAnalyzer buildResumptionAnalyzer,
132             BuildResumptionDataRepository buildResumptionDataRepository,
133             SuperPomProvider superPomProvider,
134             DefaultSessionFactory defaultSessionFactory )
135     {
136         this.projectBuilder = projectBuilder;
137         this.lifecycleStarter = lifecycleStarter;
138         this.container = container;
139         this.eventCatapult = eventCatapult;
140         this.legacySupport = legacySupport;
141         this.sessionScope = sessionScope;
142         this.repositorySessionFactory = repositorySessionFactory;
143         this.graphBuilder = graphBuilder;
144         this.buildResumptionAnalyzer = buildResumptionAnalyzer;
145         this.buildResumptionDataRepository = buildResumptionDataRepository;
146         this.superPomProvider = superPomProvider;
147         this.defaultSessionFactory = defaultSessionFactory;
148         this.projectSelector = new ProjectSelector(); // if necessary switch to DI
149     }
150 
151     @Override
152     public MavenExecutionResult execute( MavenExecutionRequest request )
153     {
154         MavenExecutionResult result;
155 
156         try
157         {
158             result = doExecute( request );
159         }
160         catch ( OutOfMemoryError e )
161         {
162             result = addExceptionToResult( new DefaultMavenExecutionResult(), e );
163         }
164         catch ( RuntimeException e )
165         {
166             // TODO Hack to make the cycle detection the same for the new graph builder
167             if ( e.getCause() instanceof ProjectCycleException )
168             {
169                 result = addExceptionToResult( new DefaultMavenExecutionResult(), e.getCause() );
170             }
171             else
172             {
173                 result = addExceptionToResult( new DefaultMavenExecutionResult(),
174                                                new InternalErrorException( "Internal error: " + e, e ) );
175             }
176         }
177         finally
178         {
179             legacySupport.setSession( null );
180         }
181 
182         return result;
183     }
184 
185     //
186     // 1) Setup initial properties.
187     //
188     // 2) Validate local repository directory is accessible.
189     //
190     // 3) Create RepositorySystemSession.
191     //
192     // 4) Create MavenSession.
193     //
194     // 5) Execute AbstractLifecycleParticipant.afterSessionStart(session)
195     //
196     // 6) Get reactor projects looking for general POM errors
197     //
198     // 7) Create ProjectDependencyGraph using trimming which takes into account --projects and reactor mode.
199     // This ensures that the projects passed into the ReactorReader are only those specified.
200     //
201     // 8) Create ReactorReader with the getProjectMap( projects ). NOTE that getProjectMap(projects) is the code that
202     // checks for duplicate projects definitions in the build. Ideally this type of duplicate checking should be
203     // part of getting the reactor projects in 6). The duplicate checking is conflated with getProjectMap(projects).
204     //
205     // 9) Execute AbstractLifecycleParticipant.afterProjectsRead(session)
206     //
207     // 10) Create ProjectDependencyGraph without trimming (as trimming was done in 7). A new topological sort is
208     // required after the execution of 9) as the AbstractLifecycleParticipants are free to mutate the MavenProject
209     // instances, which may change dependencies which can, in turn, affect the build order.
210     //
211     // 11) Execute LifecycleStarter.start()
212     //
213     @SuppressWarnings( "checkstyle:methodlength" )
214     private MavenExecutionResult doExecute( MavenExecutionRequest request )
215     {
216         request.setStartTime( new Date() );
217 
218         MavenExecutionResult result = new DefaultMavenExecutionResult();
219 
220         try
221         {
222             validateLocalRepository( request );
223         }
224         catch ( LocalRepositoryNotAccessibleException e )
225         {
226             return addExceptionToResult( result, e );
227         }
228 
229         //
230         // We enter the session scope right after the MavenSession creation and before any of the
231         // AbstractLifecycleParticipant lookups
232         // so that @SessionScoped components can be @Injected into AbstractLifecycleParticipants.
233         //
234         sessionScope.enter();
235         try
236         {
237             DefaultRepositorySystemSession repoSession =
238                 (DefaultRepositorySystemSession) newRepositorySession( request );
239             MavenSession session = new MavenSession( container, repoSession, request, result );
240             session.setSession( defaultSessionFactory.getSession( session ) );
241 
242             sessionScope.seed( MavenSession.class, session );
243             sessionScope.seed( Session.class, session.getSession() );
244             sessionScope.seed( DefaultSession.class, (DefaultSession) session.getSession() );
245 
246             legacySupport.setSession( session );
247 
248             return doExecute( request, session, result, repoSession );
249         }
250         finally
251         {
252             sessionScope.exit();
253         }
254     }
255 
256     private MavenExecutionResult doExecute( MavenExecutionRequest request, MavenSession session,
257                                             MavenExecutionResult result, DefaultRepositorySystemSession repoSession )
258     {
259         try
260         {
261             afterSessionStart( session );
262         }
263         catch ( MavenExecutionException e )
264         {
265             return addExceptionToResult( result, e );
266         }
267 
268         eventCatapult.fire( ExecutionEvent.Type.ProjectDiscoveryStarted, session, null );
269 
270         Result<? extends ProjectDependencyGraph> graphResult = buildGraph( session );
271 
272         if ( graphResult.hasErrors() )
273         {
274             return addExceptionToResult( result, graphResult.getProblems().iterator().next().getException() );
275         }
276 
277         try
278         {
279             session.setProjectMap( getProjectMap( session.getProjects() ) );
280         }
281         catch ( DuplicateProjectException e )
282         {
283             return addExceptionToResult( result, e );
284         }
285 
286         try
287         {
288             setupWorkspaceReader( session, repoSession );
289         }
290         catch ( ComponentLookupException e )
291         {
292             return addExceptionToResult( result, e );
293         }
294         repoSession.setReadOnly();
295         try
296         {
297             afterProjectsRead( session );
298         }
299         catch ( MavenExecutionException e )
300         {
301             return addExceptionToResult( result, e );
302         }
303 
304         //
305         // The projects need to be topologically after the participants have run their afterProjectsRead(session)
306         // because the participant is free to change the dependencies of a project which can potentially change the
307         // topological order of the projects, and therefore can potentially change the build order.
308         //
309         // Note that participants may affect the topological order of the projects but it is
310         // not expected that a participant will add or remove projects from the session.
311         //
312 
313         graphResult = buildGraph( session );
314 
315         if ( graphResult.hasErrors() )
316         {
317             return addExceptionToResult( result, graphResult.getProblems().iterator().next().getException() );
318         }
319 
320         try
321         {
322             if ( result.hasExceptions() )
323             {
324                 return result;
325             }
326 
327             result.setTopologicallySortedProjects( session.getProjects() );
328 
329             result.setProject( session.getTopLevelProject() );
330 
331             validatePrerequisitesForNonMavenPluginProjects( session.getProjects() );
332 
333             validateRequiredProfiles( session, request.getProfileActivation() );
334             if ( session.getResult().hasExceptions() )
335             {
336                 return result;
337             }
338 
339             validateOptionalProfiles( session, request.getProfileActivation() );
340 
341             lifecycleStarter.execute( session );
342 
343             validateOptionalProjects( request, session );
344             validateOptionalProfiles( session, request.getProfileActivation() );
345 
346             if ( session.getResult().hasExceptions() )
347             {
348                 addExceptionToResult( result, session.getResult().getExceptions().get( 0 ) );
349                 persistResumptionData( result, session );
350                 return result;
351             }
352             else
353             {
354                 session.getAllProjects().stream()
355                         .filter( MavenProject::isExecutionRoot )
356                         .findFirst()
357                         .ifPresent( buildResumptionDataRepository::removeResumptionData );
358             }
359         }
360         finally
361         {
362             try
363             {
364                 afterSessionEnd( session.getProjects(), session );
365             }
366             catch ( MavenExecutionException e )
367             {
368                 return addExceptionToResult( result, e );
369             }
370         }
371 
372         return result;
373     }
374 
375     private void setupWorkspaceReader( MavenSession session, DefaultRepositorySystemSession repoSession )
376         throws ComponentLookupException
377     {
378         // Desired order of precedence for workspace readers before querying the local artifact repositories
379         List<WorkspaceReader> workspaceReaders = new ArrayList<WorkspaceReader>();
380         // 1) Reactor workspace reader
381         workspaceReaders.add( container.lookup( WorkspaceReader.class, ReactorReader.HINT ) );
382         // 2) Repository system session-scoped workspace reader
383         WorkspaceReader repoWorkspaceReader = repoSession.getWorkspaceReader();
384         if ( repoWorkspaceReader != null )
385         {
386             workspaceReaders.add( repoWorkspaceReader );
387         }
388         // 3) .. n) Project-scoped workspace readers
389         for ( WorkspaceReader workspaceReader : getProjectScopedExtensionComponents( session.getProjects(),
390                                                                                      WorkspaceReader.class ) )
391         {
392             if ( workspaceReaders.contains( workspaceReader ) )
393             {
394                 continue;
395             }
396             workspaceReaders.add( workspaceReader );
397         }
398         repoSession.setWorkspaceReader( MavenChainedWorkspaceReader.of( workspaceReaders ) );
399     }
400 
401     private void afterSessionStart( MavenSession session )
402         throws MavenExecutionException
403     {
404         // CHECKSTYLE_OFF: LineLength
405         for ( AbstractMavenLifecycleParticipant listener : getExtensionComponents( Collections.emptyList(),
406                                                                                    AbstractMavenLifecycleParticipant.class ) )
407         // CHECKSTYLE_ON: LineLength
408         {
409             listener.afterSessionStart( session );
410         }
411     }
412 
413     private void afterProjectsRead( MavenSession session )
414         throws MavenExecutionException
415     {
416         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
417         try
418         {
419             // CHECKSTYLE_OFF: LineLength
420             for ( AbstractMavenLifecycleParticipant listener : getExtensionComponents( session.getProjects(),
421                                                                                        AbstractMavenLifecycleParticipant.class ) )
422             // CHECKSTYLE_ON: LineLength
423             {
424                 Thread.currentThread().setContextClassLoader( listener.getClass().getClassLoader() );
425 
426                 listener.afterProjectsRead( session );
427             }
428         }
429         finally
430         {
431             Thread.currentThread().setContextClassLoader( originalClassLoader );
432         }
433     }
434 
435     private void afterSessionEnd( Collection<MavenProject> projects, MavenSession session )
436         throws MavenExecutionException
437     {
438         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
439         try
440         {
441             // CHECKSTYLE_OFF: LineLength
442             for ( AbstractMavenLifecycleParticipant listener : getExtensionComponents( projects,
443                                                                                        AbstractMavenLifecycleParticipant.class ) )
444             // CHECKSTYLE_ON: LineLength
445             {
446                 Thread.currentThread().setContextClassLoader( listener.getClass().getClassLoader() );
447 
448                 listener.afterSessionEnd( session );
449             }
450         }
451         finally
452         {
453             Thread.currentThread().setContextClassLoader( originalClassLoader );
454         }
455     }
456 
457     private void persistResumptionData( MavenExecutionResult result, MavenSession session )
458     {
459         boolean hasLifecycleExecutionExceptions = result.getExceptions().stream()
460                 .anyMatch( LifecycleExecutionException.class::isInstance );
461 
462         if ( hasLifecycleExecutionExceptions )
463         {
464             MavenProject rootProject = session.getAllProjects().stream()
465                     .filter( MavenProject::isExecutionRoot )
466                     .findFirst()
467                     .orElseThrow( () -> new IllegalStateException( "No project in the session is execution root" ) );
468 
469             buildResumptionAnalyzer.determineBuildResumptionData( result ).ifPresent( resumption ->
470             {
471                 try
472                 {
473                     buildResumptionDataRepository.persistResumptionData( rootProject, resumption );
474                     result.setCanResume( true );
475                 }
476                 catch ( BuildResumptionPersistenceException e )
477                 {
478                     logger.warn( "Could not persist build resumption data", e );
479                 }
480             } );
481         }
482     }
483 
484     public RepositorySystemSession newRepositorySession( MavenExecutionRequest request )
485     {
486         return repositorySessionFactory.newRepositorySession( request );
487     }
488 
489     private void validateLocalRepository( MavenExecutionRequest request )
490         throws LocalRepositoryNotAccessibleException
491     {
492         File localRepoDir = request.getLocalRepositoryPath();
493 
494         logger.debug( "Using local repository at " + localRepoDir );
495 
496         localRepoDir.mkdirs();
497 
498         if ( !localRepoDir.isDirectory() )
499         {
500             throw new LocalRepositoryNotAccessibleException( "Could not create local repository at " + localRepoDir );
501         }
502     }
503 
504     private <T> Collection<T> getExtensionComponents( Collection<MavenProject> projects, Class<T> role )
505     {
506         Collection<T> foundComponents = new LinkedHashSet<>();
507 
508         try
509         {
510             foundComponents.addAll( container.lookupList( role ) );
511         }
512         catch ( ComponentLookupException e )
513         {
514             // this is just silly, lookupList should return an empty list!
515             logger.warn( "Failed to lookup " + role + ": " + e.getMessage() );
516         }
517 
518         foundComponents.addAll( getProjectScopedExtensionComponents( projects, role ) );
519 
520         return foundComponents;
521     }
522 
523     protected <T> Collection<T> getProjectScopedExtensionComponents( Collection<MavenProject> projects, Class<T> role )
524     {
525 
526         Collection<T> foundComponents = new LinkedHashSet<>();
527         Collection<ClassLoader> scannedRealms = new HashSet<>();
528 
529         Thread currentThread = Thread.currentThread();
530         ClassLoader originalContextClassLoader = currentThread.getContextClassLoader();
531         try
532         {
533             for ( MavenProject project : projects )
534             {
535                 ClassLoader projectRealm = project.getClassRealm();
536 
537                 if ( projectRealm != null && scannedRealms.add( projectRealm ) )
538                 {
539                     currentThread.setContextClassLoader( projectRealm );
540 
541                     try
542                     {
543                         foundComponents.addAll( container.lookupList( role ) );
544                     }
545                     catch ( ComponentLookupException e )
546                     {
547                         // this is just silly, lookupList should return an empty list!
548                         logger.warn( "Failed to lookup " + role + ": " + e.getMessage() );
549                     }
550                 }
551             }
552             return foundComponents;
553         }
554         finally
555         {
556             currentThread.setContextClassLoader( originalContextClassLoader );
557         }
558     }
559 
560     private MavenExecutionResult addExceptionToResult( MavenExecutionResult result, Throwable e )
561     {
562         if ( !result.getExceptions().contains( e ) )
563         {
564             result.addException( e );
565         }
566 
567         return result;
568     }
569 
570     private void validatePrerequisitesForNonMavenPluginProjects( List<MavenProject> projects )
571     {
572         for ( MavenProject mavenProject : projects )
573         {
574             if ( !"maven-plugin".equals( mavenProject.getPackaging() ) )
575             {
576                 Prerequisites prerequisites = mavenProject.getModel().getDelegate().getPrerequisites();
577                 if ( prerequisites != null && prerequisites.getMaven() != null )
578                 {
579                     logger.warn( "The project " + mavenProject.getId() + " uses prerequisites"
580                         + " which is only intended for maven-plugin projects "
581                         + "but not for non maven-plugin projects. "
582                         + "For such purposes you should use the maven-enforcer-plugin. "
583                         + "See https://maven.apache.org/enforcer/enforcer-rules/requireMavenVersion.html" );
584                 }
585             }
586         }
587     }
588 
589     /**
590      * Get all profiles that are detected in the projects, any parent of the projects, or the settings.
591      * @param session The Maven session
592      * @return A {@link Set} of profile identifiers, never {@code null}.
593      */
594     private Set<String> getAllProfiles( MavenSession session )
595     {
596         final Model superPomModel = superPomProvider.getSuperModel( "4.0.0" );
597         final Set<MavenProject> projectsIncludingParents = new HashSet<>();
598         for ( MavenProject project : session.getProjects() )
599         {
600             boolean isAdded = projectsIncludingParents.add( project );
601             MavenProject parent = project.getParent();
602             while ( isAdded && parent != null )
603             {
604                 isAdded = projectsIncludingParents.add( parent );
605                 parent = parent.getParent();
606             }
607         }
608 
609         final Stream<String> projectProfiles = projectsIncludingParents.stream()
610                 .flatMap( p -> p.getModel().getDelegate().getProfiles().stream() )
611                 .map( Profile::getId );
612         final Stream<String> settingsProfiles = session.getSettings().getProfiles().stream()
613                 .map( org.apache.maven.settings.Profile::getId );
614         final Stream<String> superPomProfiles = superPomModel.getProfiles().stream()
615                 .map( Profile::getId );
616 
617         return Stream.of( projectProfiles, settingsProfiles, superPomProfiles )
618                 .flatMap( Function.identity() )
619                 .collect( toSet() );
620     }
621 
622     /**
623      * Check whether the required profiles were found in any of the projects we're building or the settings.
624      * @param session the Maven session.
625      * @param profileActivation the requested optional and required profiles.
626      */
627     private void validateRequiredProfiles( MavenSession session, ProfileActivation profileActivation )
628     {
629         final Set<String> allAvailableProfiles = getAllProfiles( session );
630 
631         final Set<String> requiredProfiles = new HashSet<>( );
632         requiredProfiles.addAll( profileActivation.getRequiredActiveProfileIds() );
633         requiredProfiles.addAll( profileActivation.getRequiredInactiveProfileIds() );
634 
635         // Check whether the required profiles were found in any of the projects we're building.
636         final Set<String> notFoundRequiredProfiles = requiredProfiles.stream()
637                 .filter( rap -> !allAvailableProfiles.contains( rap ) )
638                 .collect( toSet() );
639 
640         if ( !notFoundRequiredProfiles.isEmpty() )
641         {
642             // Use SLF4J formatter for consistency with warnings reported by logger
643             final String message = MessageFormatter.format(
644                     "The requested profiles {} could not be activated or deactivated because they do not"
645                             + " exist.", notFoundRequiredProfiles ).getMessage();
646             addExceptionToResult( session.getResult(), new MissingProfilesException( message ) );
647         }
648     }
649 
650     /**
651      * Check whether any of the requested optional projects were not activated or deactivated.
652      * @param request the {@link MavenExecutionRequest}.
653      * @param session the {@link MavenSession}.
654      */
655     private void validateOptionalProjects( MavenExecutionRequest request, MavenSession session )
656     {
657         final ProjectActivation projectActivation = request.getProjectActivation();
658         final Set<String> allOptionalSelectors = new HashSet<>();
659         allOptionalSelectors.addAll( projectActivation.getOptionalActiveProjectSelectors() );
660         allOptionalSelectors.addAll( projectActivation.getRequiredActiveProjectSelectors() );
661         // We intentionally ignore the results of this method.
662         // As a side effect it will log the optional projects that could not be resolved.
663         projectSelector.getOptionalProjectsBySelectors( request, session.getAllProjects(), allOptionalSelectors );
664     }
665 
666     /**
667      * Check whether any of the requested optional profiles were not activated or deactivated.
668      * @param session the Maven session.
669      * @param profileActivation the requested optional and required profiles.
670      */
671     private void validateOptionalProfiles( MavenSession session, ProfileActivation profileActivation )
672     {
673         final Set<String> allAvailableProfiles = getAllProfiles( session );
674 
675         final Set<String> optionalProfiles = new HashSet<>( );
676         optionalProfiles.addAll( profileActivation.getOptionalActiveProfileIds() );
677         optionalProfiles.addAll( profileActivation.getOptionalInactiveProfileIds() );
678 
679         final Set<String> notFoundOptionalProfiles = optionalProfiles.stream()
680                 .filter( rap -> !allAvailableProfiles.contains( rap ) )
681                 .collect( toSet() );
682 
683         if ( !notFoundOptionalProfiles.isEmpty() )
684         {
685             logger.info( "The requested optional profiles {} could not be activated or deactivated because they do not"
686                             + " exist.", notFoundOptionalProfiles );
687         }
688     }
689 
690     private Map<String, MavenProject> getProjectMap( Collection<MavenProject> projects )
691         throws DuplicateProjectException
692     {
693         Map<String, MavenProject> index = new LinkedHashMap<>();
694         Map<String, List<File>> collisions = new LinkedHashMap<>();
695 
696         for ( MavenProject project : projects )
697         {
698             String projectId = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() );
699 
700             MavenProject collision = index.get( projectId );
701 
702             if ( collision == null )
703             {
704                 index.put( projectId, project );
705             }
706             else
707             {
708                 List<File> pomFiles = collisions.get( projectId );
709 
710                 if ( pomFiles == null )
711                 {
712                     pomFiles = new ArrayList<>( Arrays.asList( collision.getFile(), project.getFile() ) );
713                     collisions.put( projectId, pomFiles );
714                 }
715                 else
716                 {
717                     pomFiles.add( project.getFile() );
718                 }
719             }
720         }
721 
722         if ( !collisions.isEmpty() )
723         {
724             throw new DuplicateProjectException( "Two or more projects in the reactor"
725                 + " have the same identifier, please make sure that <groupId>:<artifactId>:<version>"
726                 + " is unique for each project: " + collisions, collisions );
727         }
728 
729         return index;
730     }
731 
732     private Result<? extends ProjectDependencyGraph> buildGraph( MavenSession session )
733     {
734         Result<? extends ProjectDependencyGraph> graphResult = graphBuilder.build( session );
735         for ( ModelProblem problem : graphResult.getProblems() )
736         {
737             if ( problem.getSeverity() == ModelProblem.Severity.WARNING )
738             {
739                 logger.warn( problem.getMessage() );
740             }
741             else
742             {
743                 logger.error( problem.getMessage() );
744             }
745         }
746 
747         if ( !graphResult.hasErrors() )
748         {
749             ProjectDependencyGraph projectDependencyGraph = graphResult.get();
750             session.setProjects( projectDependencyGraph.getSortedProjects() );
751             session.setAllProjects( projectDependencyGraph.getAllProjects() );
752             session.setProjectDependencyGraph( projectDependencyGraph );
753         }
754 
755         return graphResult;
756     }
757 
758     @Deprecated
759     // 5 January 2014
760     protected Logger getLogger()
761     {
762         return logger;
763     }
764 }