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 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
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();
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
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
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
231
232
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
306
307
308
309
310
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
379 List<WorkspaceReader> workspaceReaders = new ArrayList<WorkspaceReader>();
380
381 workspaceReaders.add( container.lookup( WorkspaceReader.class, ReactorReader.HINT ) );
382
383 WorkspaceReader repoWorkspaceReader = repoSession.getWorkspaceReader();
384 if ( repoWorkspaceReader != null )
385 {
386 workspaceReaders.add( repoWorkspaceReader );
387 }
388
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
405 for ( AbstractMavenLifecycleParticipant listener : getExtensionComponents( Collections.emptyList(),
406 AbstractMavenLifecycleParticipant.class ) )
407
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
420 for ( AbstractMavenLifecycleParticipant listener : getExtensionComponents( session.getProjects(),
421 AbstractMavenLifecycleParticipant.class ) )
422
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
442 for ( AbstractMavenLifecycleParticipant listener : getExtensionComponents( projects,
443 AbstractMavenLifecycleParticipant.class ) )
444
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
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
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
591
592
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
624
625
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
636 final Set<String> notFoundRequiredProfiles = requiredProfiles.stream()
637 .filter( rap -> !allAvailableProfiles.contains( rap ) )
638 .collect( toSet() );
639
640 if ( !notFoundRequiredProfiles.isEmpty() )
641 {
642
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
652
653
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
662
663 projectSelector.getOptionalProjectsBySelectors( request, session.getAllProjects(), allOptionalSelectors );
664 }
665
666
667
668
669
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
760 protected Logger getLogger()
761 {
762 return logger;
763 }
764 }