1   package org.apache.maven;
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  import java.io.File;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.Date;
28  import java.util.HashSet;
29  import java.util.LinkedHashMap;
30  import java.util.LinkedHashSet;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Set;
34  
35  import org.apache.maven.artifact.ArtifactUtils;
36  import org.apache.maven.execution.DefaultMavenExecutionResult;
37  import org.apache.maven.execution.ExecutionEvent;
38  import org.apache.maven.execution.MavenExecutionRequest;
39  import org.apache.maven.execution.MavenExecutionResult;
40  import org.apache.maven.execution.MavenSession;
41  import org.apache.maven.execution.ProjectDependencyGraph;
42  import org.apache.maven.graph.GraphBuilder;
43  import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory;
44  import org.apache.maven.internal.aether.MavenChainedWorkspaceReader;
45  import org.apache.maven.lifecycle.internal.ExecutionEventCatapult;
46  import org.apache.maven.lifecycle.internal.LifecycleStarter;
47  import org.apache.maven.model.Prerequisites;
48  import org.apache.maven.model.building.ModelProblem;
49  import org.apache.maven.model.building.Result;
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.annotations.Component;
57  import org.codehaus.plexus.component.annotations.Requirement;
58  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
59  import org.codehaus.plexus.logging.Logger;
60  import org.eclipse.aether.DefaultRepositorySystemSession;
61  import org.eclipse.aether.RepositorySystemSession;
62  import org.eclipse.aether.repository.WorkspaceReader;
63  
64  
65  
66  
67  @Component( role = Maven.class )
68  public class DefaultMaven
69      implements Maven
70  {
71  
72      @Requirement
73      private Logger logger;
74  
75      @Requirement
76      protected ProjectBuilder projectBuilder;
77  
78      @Requirement
79      private LifecycleStarter lifecycleStarter;
80  
81      @Requirement
82      protected PlexusContainer container;
83  
84      @Requirement
85      private ExecutionEventCatapult eventCatapult;
86  
87      @Requirement
88      private LegacySupport legacySupport;
89  
90      @Requirement
91      private SessionScope sessionScope;
92  
93      @Requirement
94      private DefaultRepositorySystemSessionFactory repositorySessionFactory;
95  
96      @Requirement( hint = GraphBuilder.HINT )
97      private GraphBuilder graphBuilder;
98  
99      @Override
100     public MavenExecutionResult execute( MavenExecutionRequest request )
101     {
102         MavenExecutionResult result;
103 
104         try
105         {
106             result = doExecute( request );
107         }
108         catch ( OutOfMemoryError e )
109         {
110             result = addExceptionToResult( new DefaultMavenExecutionResult(), e );
111         }
112         catch ( RuntimeException e )
113         {
114             
115             if ( e.getCause() instanceof ProjectCycleException )
116             {
117                 result = addExceptionToResult( new DefaultMavenExecutionResult(), e.getCause() );
118             }
119             else
120             {
121                 result = addExceptionToResult( new DefaultMavenExecutionResult(),
122                                                new InternalErrorException( "Internal error: " + e, e ) );
123             }
124         }
125         finally
126         {
127             legacySupport.setSession( null );
128         }
129 
130         return result;
131     }
132 
133     
134     
135     
136     
137     
138     
139     
140     
141     
142     
143     
144     
145     
146     
147     
148     
149     
150     
151     
152     
153     
154     
155     
156     
157     
158     
159     
160     
161     @SuppressWarnings( "checkstyle:methodlength" )
162     private MavenExecutionResult doExecute( MavenExecutionRequest request )
163     {
164         request.setStartTime( new Date() );
165 
166         MavenExecutionResult result = new DefaultMavenExecutionResult();
167 
168         try
169         {
170             validateLocalRepository( request );
171         }
172         catch ( LocalRepositoryNotAccessibleException e )
173         {
174             return addExceptionToResult( result, e );
175         }
176 
177         
178         
179         
180         
181         
182         sessionScope.enter();
183         try
184         {
185             DefaultRepositorySystemSession repoSession =
186                 (DefaultRepositorySystemSession) newRepositorySession( request );
187             MavenSession session = new MavenSession( container, repoSession, request, result );
188 
189             sessionScope.seed( MavenSession.class, session );
190 
191             legacySupport.setSession( session );
192 
193             return doExecute( request, session, result, repoSession );
194         }
195         finally
196         {
197             sessionScope.exit();
198         }
199     }
200 
201     private MavenExecutionResult doExecute( MavenExecutionRequest request, MavenSession session,
202                                             MavenExecutionResult result, DefaultRepositorySystemSession repoSession )
203     {
204         try
205         {
206             
207             for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( Collections.<MavenProject>emptyList() ) )
208             {
209                 listener.afterSessionStart( session );
210             }
211             
212         }
213         catch ( MavenExecutionException e )
214         {
215             return addExceptionToResult( result, e );
216         }
217 
218         eventCatapult.fire( ExecutionEvent.Type.ProjectDiscoveryStarted, session, null );
219 
220         Result<? extends ProjectDependencyGraph> graphResult = buildGraph( session, result );
221 
222         if ( graphResult.hasErrors() )
223         {
224             return addExceptionToResult( result, graphResult.getProblems().iterator().next().getException() );
225         }
226 
227         try
228         {
229             session.setProjectMap( getProjectMap( session.getProjects() ) );
230         }
231         catch ( DuplicateProjectException e )
232         {
233             return addExceptionToResult( result, e );
234         }
235 
236         try
237         {
238             setupWorkspaceReader( session, repoSession );
239         }
240         catch ( ComponentLookupException e )
241         {
242             return addExceptionToResult( result, e );
243         }
244 
245         repoSession.setReadOnly();
246 
247         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
248         try
249         {
250             for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( session.getProjects() ) )
251             {
252                 Thread.currentThread().setContextClassLoader( listener.getClass().getClassLoader() );
253 
254                 listener.afterProjectsRead( session );
255             }
256         }
257         catch ( MavenExecutionException e )
258         {
259             return addExceptionToResult( result, e );
260         }
261         finally
262         {
263             Thread.currentThread().setContextClassLoader( originalClassLoader );
264         }
265 
266         
267         
268         
269         
270         
271         
272         
273         
274 
275         graphResult = buildGraph( session, result );
276 
277         if ( graphResult.hasErrors() )
278         {
279             return addExceptionToResult( result, graphResult.getProblems().iterator().next().getException() );
280         }
281 
282         try
283         {
284             if ( result.hasExceptions() )
285             {
286                 return result;
287             }
288 
289             result.setTopologicallySortedProjects( session.getProjects() );
290 
291             result.setProject( session.getTopLevelProject() );
292 
293             validatePrerequisitesForNonMavenPluginProjects( session.getProjects() );
294 
295             validateActivatedProfiles( session.getProjects(),
296                     request.getActiveProfiles(),
297                     request.getInactiveProfiles() );
298 
299             lifecycleStarter.execute( session );
300 
301             validateActivatedProfiles( session.getProjects(),
302                     request.getActiveProfiles(),
303                     request.getInactiveProfiles() );
304 
305             if ( session.getResult().hasExceptions() )
306             {
307                 return addExceptionToResult( result, session.getResult().getExceptions().get( 0 ) );
308             }
309         }
310         finally
311         {
312             try
313             {
314                 afterSessionEnd( session.getProjects(), session );
315             }
316             catch ( MavenExecutionException e )
317             {
318                 return addExceptionToResult( result, e );
319             }
320         }
321 
322         return result;
323     }
324 
325     private void setupWorkspaceReader( MavenSession session, DefaultRepositorySystemSession repoSession )
326         throws ComponentLookupException
327     {
328         
329         Set<WorkspaceReader> workspaceReaders = new LinkedHashSet<>();
330         
331         workspaceReaders.add( container.lookup( WorkspaceReader.class, ReactorReader.HINT ) );
332         
333         WorkspaceReader repoWorkspaceReader = repoSession.getWorkspaceReader();
334         if ( repoWorkspaceReader != null )
335         {
336             workspaceReaders.add( repoWorkspaceReader );
337         }
338         
339         workspaceReaders.addAll( getProjectScopedExtensionComponents( session.getProjects(), WorkspaceReader.class ) );
340         repoSession.setWorkspaceReader( MavenChainedWorkspaceReader.of( workspaceReaders ) );
341     }
342 
343     private void afterSessionEnd( Collection<MavenProject> projects, MavenSession session )
344         throws MavenExecutionException
345     {
346         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
347         try
348         {
349             for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( projects ) )
350             {
351                 Thread.currentThread().setContextClassLoader( listener.getClass().getClassLoader() );
352 
353                 listener.afterSessionEnd( session );
354             }
355         }
356         finally
357         {
358             Thread.currentThread().setContextClassLoader( originalClassLoader );
359         }
360     }
361 
362     public RepositorySystemSession newRepositorySession( MavenExecutionRequest request )
363     {
364         return repositorySessionFactory.newRepositorySession( request );
365     }
366 
367     private void validateLocalRepository( MavenExecutionRequest request )
368         throws LocalRepositoryNotAccessibleException
369     {
370         File localRepoDir = request.getLocalRepositoryPath();
371 
372         logger.debug( "Using local repository at " + localRepoDir );
373 
374         localRepoDir.mkdirs();
375 
376         if ( !localRepoDir.isDirectory() )
377         {
378             throw new LocalRepositoryNotAccessibleException( "Could not create local repository at " + localRepoDir );
379         }
380     }
381 
382     private Collection<AbstractMavenLifecycleParticipant> getLifecycleParticipants( Collection<MavenProject> projects )
383     {
384         Collection<AbstractMavenLifecycleParticipant> lifecycleListeners = new LinkedHashSet<>();
385 
386         try
387         {
388             lifecycleListeners.addAll( container.lookupList( AbstractMavenLifecycleParticipant.class ) );
389         }
390         catch ( ComponentLookupException e )
391         {
392             
393             logger.warn( "Failed to lookup lifecycle participants: " + e.getMessage() );
394         }
395 
396         lifecycleListeners.addAll( getProjectScopedExtensionComponents( projects,
397                                                                         AbstractMavenLifecycleParticipant.class ) );
398 
399         return lifecycleListeners;
400     }
401 
402     protected <T> Collection<T> getProjectScopedExtensionComponents( Collection<MavenProject> projects, Class<T> role )
403     {
404 
405         Collection<T> foundComponents = new LinkedHashSet<>();
406         Collection<ClassLoader> scannedRealms = new HashSet<>();
407 
408         Thread currentThread = Thread.currentThread();
409         ClassLoader originalContextClassLoader = currentThread.getContextClassLoader();
410         try
411         {
412             for ( MavenProject project : projects )
413             {
414                 ClassLoader projectRealm = project.getClassRealm();
415 
416                 if ( projectRealm != null && scannedRealms.add( projectRealm ) )
417                 {
418                     currentThread.setContextClassLoader( projectRealm );
419 
420                     try
421                     {
422                         foundComponents.addAll( container.lookupList( role ) );
423                     }
424                     catch ( ComponentLookupException e )
425                     {
426                         
427                         logger.warn( "Failed to lookup " + role + ": " + e.getMessage() );
428                     }
429                 }
430             }
431             return foundComponents;
432         }
433         finally
434         {
435             currentThread.setContextClassLoader( originalContextClassLoader );
436         }
437     }
438 
439     private MavenExecutionResult addExceptionToResult( MavenExecutionResult result, Throwable e )
440     {
441         if ( !result.getExceptions().contains( e ) )
442         {
443             result.addException( e );
444         }
445 
446         return result;
447     }
448 
449     private void validatePrerequisitesForNonMavenPluginProjects( List<MavenProject> projects )
450     {
451         for ( MavenProject mavenProject : projects )
452         {
453             if ( !"maven-plugin".equals( mavenProject.getPackaging() ) )
454             {
455                 Prerequisites prerequisites = mavenProject.getPrerequisites();
456                 if ( prerequisites != null && prerequisites.getMaven() != null )
457                 {
458                     logger.warn( "The project " + mavenProject.getId() + " uses prerequisites"
459                         + " which is only intended for maven-plugin projects "
460                         + "but not for non maven-plugin projects. "
461                         + "For such purposes you should use the maven-enforcer-plugin. "
462                         + "See https://maven.apache.org/enforcer/enforcer-rules/requireMavenVersion.html" );
463                 }
464             }
465         }
466     }
467 
468     private void validateActivatedProfiles( List<MavenProject> projects,
469                                             List<String> activeProfileIds,
470                                             List<String> inactiveProfileIds )
471     {
472         Collection<String> notActivatedProfileIds = new LinkedHashSet<>( activeProfileIds );
473 
474         for ( MavenProject project : projects )
475         {
476             for ( List<String> profileIds : project.getInjectedProfileIds().values() )
477             {
478                 notActivatedProfileIds.removeAll( profileIds );
479             }
480         }
481 
482         notActivatedProfileIds.removeAll( inactiveProfileIds );
483 
484         for ( String notActivatedProfileId : notActivatedProfileIds )
485         {
486             logger.warn( "The requested profile \"" + notActivatedProfileId
487                 + "\" could not be activated because it does not exist." );
488         }
489     }
490 
491     private Map<String, MavenProject> getProjectMap( Collection<MavenProject> projects )
492         throws DuplicateProjectException
493     {
494         Map<String, MavenProject> index = new LinkedHashMap<>();
495         Map<String, List<File>> collisions = new LinkedHashMap<>();
496 
497         for ( MavenProject project : projects )
498         {
499             String projectId = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() );
500 
501             MavenProject collision = index.get( projectId );
502 
503             if ( collision == null )
504             {
505                 index.put( projectId, project );
506             }
507             else
508             {
509                 List<File> pomFiles = collisions.get( projectId );
510 
511                 if ( pomFiles == null )
512                 {
513                     pomFiles = new ArrayList<>( Arrays.asList( collision.getFile(), project.getFile() ) );
514                     collisions.put( projectId, pomFiles );
515                 }
516                 else
517                 {
518                     pomFiles.add( project.getFile() );
519                 }
520             }
521         }
522 
523         if ( !collisions.isEmpty() )
524         {
525             throw new DuplicateProjectException( "Two or more projects in the reactor"
526                 + " have the same identifier, please make sure that <groupId>:<artifactId>:<version>"
527                 + " is unique for each project: " + collisions, collisions );
528         }
529 
530         return index;
531     }
532 
533     private Result<? extends ProjectDependencyGraph> buildGraph( MavenSession session, MavenExecutionResult result )
534     {
535         Result<? extends ProjectDependencyGraph> graphResult = graphBuilder.build( session );
536         for ( ModelProblem problem : graphResult.getProblems() )
537         {
538             if ( problem.getSeverity() == ModelProblem.Severity.WARNING )
539             {
540                 logger.warn( problem.toString() );
541             }
542             else
543             {
544                 logger.error( problem.toString() );
545             }
546         }
547 
548         if ( !graphResult.hasErrors() )
549         {
550             ProjectDependencyGraph projectDependencyGraph = graphResult.get();
551             session.setProjects( projectDependencyGraph.getSortedProjects() );
552             session.setAllProjects( projectDependencyGraph.getAllProjects() );
553             session.setProjectDependencyGraph( projectDependencyGraph );
554         }
555 
556         return graphResult;
557     }
558 
559     @Deprecated
560     
561     protected Logger getLogger()
562     {
563         return logger;
564     }
565 }