1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven;
20
21 import static java.util.stream.Collectors.toSet;
22
23 import java.io.File;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.Date;
29 import java.util.HashSet;
30 import java.util.LinkedHashMap;
31 import java.util.LinkedHashSet;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Set;
35 import java.util.function.Function;
36 import java.util.stream.Stream;
37 import javax.inject.Inject;
38 import javax.inject.Named;
39 import javax.inject.Singleton;
40 import org.apache.maven.api.Session;
41 import org.apache.maven.api.model.Model;
42 import org.apache.maven.api.model.Prerequisites;
43 import org.apache.maven.api.model.Profile;
44 import org.apache.maven.artifact.ArtifactUtils;
45 import org.apache.maven.execution.BuildResumptionAnalyzer;
46 import org.apache.maven.execution.BuildResumptionDataRepository;
47 import org.apache.maven.execution.BuildResumptionPersistenceException;
48 import org.apache.maven.execution.DefaultMavenExecutionResult;
49 import org.apache.maven.execution.ExecutionEvent;
50 import org.apache.maven.execution.MavenExecutionRequest;
51 import org.apache.maven.execution.MavenExecutionResult;
52 import org.apache.maven.execution.MavenSession;
53 import org.apache.maven.execution.ProfileActivation;
54 import org.apache.maven.execution.ProjectActivation;
55 import org.apache.maven.execution.ProjectDependencyGraph;
56 import org.apache.maven.graph.GraphBuilder;
57 import org.apache.maven.graph.ProjectSelector;
58 import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory;
59 import org.apache.maven.internal.aether.MavenChainedWorkspaceReader;
60 import org.apache.maven.internal.impl.DefaultSession;
61 import org.apache.maven.internal.impl.DefaultSessionFactory;
62 import org.apache.maven.lifecycle.LifecycleExecutionException;
63 import org.apache.maven.lifecycle.internal.ExecutionEventCatapult;
64 import org.apache.maven.lifecycle.internal.LifecycleStarter;
65 import org.apache.maven.model.building.ModelProblem;
66 import org.apache.maven.model.building.Result;
67 import org.apache.maven.model.superpom.SuperPomProvider;
68 import org.apache.maven.plugin.LegacySupport;
69 import org.apache.maven.project.MavenProject;
70 import org.apache.maven.project.ProjectBuilder;
71 import org.apache.maven.repository.LocalRepositoryNotAccessibleException;
72 import org.apache.maven.session.scope.internal.SessionScope;
73 import org.codehaus.plexus.PlexusContainer;
74 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
75 import org.eclipse.aether.DefaultRepositorySystemSession;
76 import org.eclipse.aether.RepositorySystemSession;
77 import org.eclipse.aether.repository.WorkspaceReader;
78 import org.slf4j.Logger;
79 import org.slf4j.LoggerFactory;
80 import org.slf4j.helpers.MessageFormatter;
81
82
83
84
85 @Named
86 @Singleton
87 public class DefaultMaven implements Maven {
88 private final Logger logger = LoggerFactory.getLogger(getClass());
89
90 protected ProjectBuilder projectBuilder;
91
92 private LifecycleStarter lifecycleStarter;
93
94 protected PlexusContainer container;
95
96 private ExecutionEventCatapult eventCatapult;
97
98 private LegacySupport legacySupport;
99
100 private SessionScope sessionScope;
101
102 private DefaultRepositorySystemSessionFactory repositorySessionFactory;
103
104 private final GraphBuilder graphBuilder;
105
106 private final BuildResumptionAnalyzer buildResumptionAnalyzer;
107
108 private final BuildResumptionDataRepository buildResumptionDataRepository;
109
110 private final SuperPomProvider superPomProvider;
111
112 private final DefaultSessionFactory defaultSessionFactory;
113
114 private final ProjectSelector projectSelector;
115
116 @Inject
117 @SuppressWarnings("checkstyle:ParameterNumber")
118 public DefaultMaven(
119 ProjectBuilder projectBuilder,
120 LifecycleStarter lifecycleStarter,
121 PlexusContainer container,
122 ExecutionEventCatapult eventCatapult,
123 LegacySupport legacySupport,
124 SessionScope sessionScope,
125 DefaultRepositorySystemSessionFactory repositorySessionFactory,
126 @Named(GraphBuilder.HINT) GraphBuilder graphBuilder,
127 BuildResumptionAnalyzer buildResumptionAnalyzer,
128 BuildResumptionDataRepository buildResumptionDataRepository,
129 SuperPomProvider superPomProvider,
130 DefaultSessionFactory defaultSessionFactory) {
131 this.projectBuilder = projectBuilder;
132 this.lifecycleStarter = lifecycleStarter;
133 this.container = container;
134 this.eventCatapult = eventCatapult;
135 this.legacySupport = legacySupport;
136 this.sessionScope = sessionScope;
137 this.repositorySessionFactory = repositorySessionFactory;
138 this.graphBuilder = graphBuilder;
139 this.buildResumptionAnalyzer = buildResumptionAnalyzer;
140 this.buildResumptionDataRepository = buildResumptionDataRepository;
141 this.superPomProvider = superPomProvider;
142 this.defaultSessionFactory = defaultSessionFactory;
143 this.projectSelector = new ProjectSelector();
144 }
145
146 @Override
147 public MavenExecutionResult execute(MavenExecutionRequest request) {
148 MavenExecutionResult result;
149
150 try {
151 result = doExecute(request);
152 } catch (OutOfMemoryError e) {
153 result = addExceptionToResult(new DefaultMavenExecutionResult(), e);
154 } catch (RuntimeException e) {
155
156 if (e.getCause() instanceof ProjectCycleException) {
157 result = addExceptionToResult(new DefaultMavenExecutionResult(), e.getCause());
158 } else {
159 result = addExceptionToResult(
160 new DefaultMavenExecutionResult(), new InternalErrorException("Internal error: " + e, e));
161 }
162 } finally {
163 legacySupport.setSession(null);
164 }
165
166 return result;
167 }
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197 @SuppressWarnings("checkstyle:methodlength")
198 private MavenExecutionResult doExecute(MavenExecutionRequest request) {
199 request.setStartTime(new Date());
200
201 MavenExecutionResult result = new DefaultMavenExecutionResult();
202
203 try {
204 validateLocalRepository(request);
205 } catch (LocalRepositoryNotAccessibleException e) {
206 return addExceptionToResult(result, e);
207 }
208
209
210
211
212
213
214 sessionScope.enter();
215 try {
216 DefaultRepositorySystemSession repoSession = (DefaultRepositorySystemSession) newRepositorySession(request);
217 MavenSession session = new MavenSession(container, repoSession, request, result);
218 session.setSession(defaultSessionFactory.getSession(session));
219
220 sessionScope.seed(MavenSession.class, session);
221 sessionScope.seed(Session.class, session.getSession());
222 sessionScope.seed(DefaultSession.class, (DefaultSession) session.getSession());
223
224 legacySupport.setSession(session);
225
226 return doExecute(request, session, result, repoSession);
227 } finally {
228 sessionScope.exit();
229 }
230 }
231
232 private MavenExecutionResult doExecute(
233 MavenExecutionRequest request,
234 MavenSession session,
235 MavenExecutionResult result,
236 DefaultRepositorySystemSession repoSession) {
237 try {
238 afterSessionStart(session);
239 } catch (MavenExecutionException e) {
240 return addExceptionToResult(result, e);
241 }
242
243 eventCatapult.fire(ExecutionEvent.Type.ProjectDiscoveryStarted, session, null);
244
245 Result<? extends ProjectDependencyGraph> graphResult = buildGraph(session);
246
247 if (graphResult.hasErrors()) {
248 return addExceptionToResult(
249 result, graphResult.getProblems().iterator().next().getException());
250 }
251
252 try {
253 session.setProjectMap(getProjectMap(session.getProjects()));
254 } catch (DuplicateProjectException e) {
255 return addExceptionToResult(result, e);
256 }
257
258 try {
259 setupWorkspaceReader(session, repoSession);
260 } catch (ComponentLookupException e) {
261 return addExceptionToResult(result, e);
262 }
263 repoSession.setReadOnly();
264 try {
265 afterProjectsRead(session);
266 } catch (MavenExecutionException e) {
267 return addExceptionToResult(result, e);
268 }
269
270
271
272
273
274
275
276
277
278
279 graphResult = buildGraph(session);
280
281 if (graphResult.hasErrors()) {
282 return addExceptionToResult(
283 result, graphResult.getProblems().iterator().next().getException());
284 }
285
286 try {
287 if (result.hasExceptions()) {
288 return result;
289 }
290
291 result.setTopologicallySortedProjects(session.getProjects());
292
293 result.setProject(session.getTopLevelProject());
294
295 validatePrerequisitesForNonMavenPluginProjects(session.getProjects());
296
297 validateRequiredProfiles(session, request.getProfileActivation());
298 if (session.getResult().hasExceptions()) {
299 return result;
300 }
301
302 validateOptionalProfiles(session, request.getProfileActivation());
303
304 lifecycleStarter.execute(session);
305
306 validateOptionalProjects(request, session);
307 validateOptionalProfiles(session, request.getProfileActivation());
308
309 if (session.getResult().hasExceptions()) {
310 addExceptionToResult(result, session.getResult().getExceptions().get(0));
311 persistResumptionData(result, session);
312 return result;
313 } else {
314 session.getAllProjects().stream()
315 .filter(MavenProject::isExecutionRoot)
316 .findFirst()
317 .ifPresent(buildResumptionDataRepository::removeResumptionData);
318 }
319 } finally {
320 try {
321 afterSessionEnd(session.getProjects(), session);
322 } catch (MavenExecutionException e) {
323 return addExceptionToResult(result, e);
324 }
325 }
326
327 return result;
328 }
329
330 private void setupWorkspaceReader(MavenSession session, DefaultRepositorySystemSession repoSession)
331 throws ComponentLookupException {
332
333 List<WorkspaceReader> workspaceReaders = new ArrayList<WorkspaceReader>();
334
335 workspaceReaders.add(container.lookup(WorkspaceReader.class, ReactorReader.HINT));
336
337 WorkspaceReader repoWorkspaceReader = repoSession.getWorkspaceReader();
338 if (repoWorkspaceReader != null) {
339 workspaceReaders.add(repoWorkspaceReader);
340 }
341
342 for (WorkspaceReader workspaceReader :
343 getProjectScopedExtensionComponents(session.getProjects(), WorkspaceReader.class)) {
344 if (workspaceReaders.contains(workspaceReader)) {
345 continue;
346 }
347 workspaceReaders.add(workspaceReader);
348 }
349 repoSession.setWorkspaceReader(MavenChainedWorkspaceReader.of(workspaceReaders));
350 }
351
352 private void afterSessionStart(MavenSession session) throws MavenExecutionException {
353
354 for (AbstractMavenLifecycleParticipant listener :
355 getExtensionComponents(Collections.emptyList(), AbstractMavenLifecycleParticipant.class))
356
357 {
358 listener.afterSessionStart(session);
359 }
360 }
361
362 private void afterProjectsRead(MavenSession session) throws MavenExecutionException {
363 ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
364 try {
365
366 for (AbstractMavenLifecycleParticipant listener :
367 getExtensionComponents(session.getProjects(), AbstractMavenLifecycleParticipant.class))
368
369 {
370 Thread.currentThread().setContextClassLoader(listener.getClass().getClassLoader());
371
372 listener.afterProjectsRead(session);
373 }
374 } finally {
375 Thread.currentThread().setContextClassLoader(originalClassLoader);
376 }
377 }
378
379 private void afterSessionEnd(Collection<MavenProject> projects, MavenSession session)
380 throws MavenExecutionException {
381 ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
382 try {
383
384 for (AbstractMavenLifecycleParticipant listener :
385 getExtensionComponents(projects, AbstractMavenLifecycleParticipant.class))
386
387 {
388 Thread.currentThread().setContextClassLoader(listener.getClass().getClassLoader());
389
390 listener.afterSessionEnd(session);
391 }
392 } finally {
393 Thread.currentThread().setContextClassLoader(originalClassLoader);
394 }
395 }
396
397 private void persistResumptionData(MavenExecutionResult result, MavenSession session) {
398 boolean hasLifecycleExecutionExceptions =
399 result.getExceptions().stream().anyMatch(LifecycleExecutionException.class::isInstance);
400
401 if (hasLifecycleExecutionExceptions) {
402 MavenProject rootProject = session.getAllProjects().stream()
403 .filter(MavenProject::isExecutionRoot)
404 .findFirst()
405 .orElseThrow(() -> new IllegalStateException("No project in the session is execution root"));
406
407 buildResumptionAnalyzer.determineBuildResumptionData(result).ifPresent(resumption -> {
408 try {
409 buildResumptionDataRepository.persistResumptionData(rootProject, resumption);
410 result.setCanResume(true);
411 } catch (BuildResumptionPersistenceException e) {
412 logger.warn("Could not persist build resumption data", e);
413 }
414 });
415 }
416 }
417
418 public RepositorySystemSession newRepositorySession(MavenExecutionRequest request) {
419 return repositorySessionFactory.newRepositorySession(request);
420 }
421
422 private void validateLocalRepository(MavenExecutionRequest request) throws LocalRepositoryNotAccessibleException {
423 File localRepoDir = request.getLocalRepositoryPath();
424
425 logger.debug("Using local repository at " + localRepoDir);
426
427 localRepoDir.mkdirs();
428
429 if (!localRepoDir.isDirectory()) {
430 throw new LocalRepositoryNotAccessibleException("Could not create local repository at " + localRepoDir);
431 }
432 }
433
434 private <T> Collection<T> getExtensionComponents(Collection<MavenProject> projects, Class<T> role) {
435 Collection<T> foundComponents = new LinkedHashSet<>();
436
437 try {
438 foundComponents.addAll(container.lookupList(role));
439 } catch (ComponentLookupException e) {
440
441 logger.warn("Failed to lookup " + role + ": " + e.getMessage());
442 }
443
444 foundComponents.addAll(getProjectScopedExtensionComponents(projects, role));
445
446 return foundComponents;
447 }
448
449 protected <T> Collection<T> getProjectScopedExtensionComponents(Collection<MavenProject> projects, Class<T> role) {
450
451 Collection<T> foundComponents = new LinkedHashSet<>();
452 Collection<ClassLoader> scannedRealms = new HashSet<>();
453
454 Thread currentThread = Thread.currentThread();
455 ClassLoader originalContextClassLoader = currentThread.getContextClassLoader();
456 try {
457 for (MavenProject project : projects) {
458 ClassLoader projectRealm = project.getClassRealm();
459
460 if (projectRealm != null && scannedRealms.add(projectRealm)) {
461 currentThread.setContextClassLoader(projectRealm);
462
463 try {
464 foundComponents.addAll(container.lookupList(role));
465 } catch (ComponentLookupException e) {
466
467 logger.warn("Failed to lookup " + role + ": " + e.getMessage());
468 }
469 }
470 }
471 return foundComponents;
472 } finally {
473 currentThread.setContextClassLoader(originalContextClassLoader);
474 }
475 }
476
477 private MavenExecutionResult addExceptionToResult(MavenExecutionResult result, Throwable e) {
478 if (!result.getExceptions().contains(e)) {
479 result.addException(e);
480 }
481
482 return result;
483 }
484
485 private void validatePrerequisitesForNonMavenPluginProjects(List<MavenProject> projects) {
486 for (MavenProject mavenProject : projects) {
487 if (!"maven-plugin".equals(mavenProject.getPackaging())) {
488 Prerequisites prerequisites =
489 mavenProject.getModel().getDelegate().getPrerequisites();
490 if (prerequisites != null && prerequisites.getMaven() != null) {
491 logger.warn("The project " + mavenProject.getId() + " uses prerequisites"
492 + " which is only intended for maven-plugin projects "
493 + "but not for non maven-plugin projects. "
494 + "For such purposes you should use the maven-enforcer-plugin. "
495 + "See https://maven.apache.org/enforcer/enforcer-rules/requireMavenVersion.html");
496 }
497 }
498 }
499 }
500
501
502
503
504
505
506 private Set<String> getAllProfiles(MavenSession session) {
507 final Model superPomModel = superPomProvider.getSuperModel("4.0.0");
508 final Set<MavenProject> projectsIncludingParents = new HashSet<>();
509 for (MavenProject project : session.getProjects()) {
510 boolean isAdded = projectsIncludingParents.add(project);
511 MavenProject parent = project.getParent();
512 while (isAdded && parent != null) {
513 isAdded = projectsIncludingParents.add(parent);
514 parent = parent.getParent();
515 }
516 }
517
518 final Stream<String> projectProfiles = projectsIncludingParents.stream()
519 .flatMap(p -> p.getModel().getDelegate().getProfiles().stream())
520 .map(Profile::getId);
521 final Stream<String> settingsProfiles =
522 session.getSettings().getProfiles().stream().map(org.apache.maven.settings.Profile::getId);
523 final Stream<String> superPomProfiles =
524 superPomModel.getProfiles().stream().map(Profile::getId);
525
526 return Stream.of(projectProfiles, settingsProfiles, superPomProfiles)
527 .flatMap(Function.identity())
528 .collect(toSet());
529 }
530
531
532
533
534
535
536 private void validateRequiredProfiles(MavenSession session, ProfileActivation profileActivation) {
537 final Set<String> allAvailableProfiles = getAllProfiles(session);
538
539 final Set<String> requiredProfiles = new HashSet<>();
540 requiredProfiles.addAll(profileActivation.getRequiredActiveProfileIds());
541 requiredProfiles.addAll(profileActivation.getRequiredInactiveProfileIds());
542
543
544 final Set<String> notFoundRequiredProfiles = requiredProfiles.stream()
545 .filter(rap -> !allAvailableProfiles.contains(rap))
546 .collect(toSet());
547
548 if (!notFoundRequiredProfiles.isEmpty()) {
549
550 final String message = MessageFormatter.format(
551 "The requested profiles {} could not be activated or deactivated because they do not"
552 + " exist.",
553 notFoundRequiredProfiles)
554 .getMessage();
555 addExceptionToResult(session.getResult(), new MissingProfilesException(message));
556 }
557 }
558
559
560
561
562
563
564 private void validateOptionalProjects(MavenExecutionRequest request, MavenSession session) {
565 final ProjectActivation projectActivation = request.getProjectActivation();
566 final Set<String> allOptionalSelectors = new HashSet<>();
567 allOptionalSelectors.addAll(projectActivation.getOptionalActiveProjectSelectors());
568 allOptionalSelectors.addAll(projectActivation.getRequiredActiveProjectSelectors());
569
570
571 projectSelector.getOptionalProjectsBySelectors(request, session.getAllProjects(), allOptionalSelectors);
572 }
573
574
575
576
577
578
579 private void validateOptionalProfiles(MavenSession session, ProfileActivation profileActivation) {
580 final Set<String> allAvailableProfiles = getAllProfiles(session);
581
582 final Set<String> optionalProfiles = new HashSet<>();
583 optionalProfiles.addAll(profileActivation.getOptionalActiveProfileIds());
584 optionalProfiles.addAll(profileActivation.getOptionalInactiveProfileIds());
585
586 final Set<String> notFoundOptionalProfiles = optionalProfiles.stream()
587 .filter(rap -> !allAvailableProfiles.contains(rap))
588 .collect(toSet());
589
590 if (!notFoundOptionalProfiles.isEmpty()) {
591 logger.info(
592 "The requested optional profiles {} could not be activated or deactivated because they do not"
593 + " exist.",
594 notFoundOptionalProfiles);
595 }
596 }
597
598 private Map<String, MavenProject> getProjectMap(Collection<MavenProject> projects)
599 throws DuplicateProjectException {
600 Map<String, MavenProject> index = new LinkedHashMap<>();
601 Map<String, List<File>> collisions = new LinkedHashMap<>();
602
603 for (MavenProject project : projects) {
604 String projectId = ArtifactUtils.key(project.getGroupId(), project.getArtifactId(), project.getVersion());
605
606 MavenProject collision = index.get(projectId);
607
608 if (collision == null) {
609 index.put(projectId, project);
610 } else {
611 List<File> pomFiles = collisions.get(projectId);
612
613 if (pomFiles == null) {
614 pomFiles = new ArrayList<>(Arrays.asList(collision.getFile(), project.getFile()));
615 collisions.put(projectId, pomFiles);
616 } else {
617 pomFiles.add(project.getFile());
618 }
619 }
620 }
621
622 if (!collisions.isEmpty()) {
623 throw new DuplicateProjectException(
624 "Two or more projects in the reactor"
625 + " have the same identifier, please make sure that <groupId>:<artifactId>:<version>"
626 + " is unique for each project: " + collisions,
627 collisions);
628 }
629
630 return index;
631 }
632
633 private Result<? extends ProjectDependencyGraph> buildGraph(MavenSession session) {
634 Result<? extends ProjectDependencyGraph> graphResult = graphBuilder.build(session);
635 for (ModelProblem problem : graphResult.getProblems()) {
636 if (problem.getSeverity() == ModelProblem.Severity.WARNING) {
637 logger.warn(problem.getMessage());
638 } else {
639 logger.error(problem.getMessage());
640 }
641 }
642
643 if (!graphResult.hasErrors()) {
644 ProjectDependencyGraph projectDependencyGraph = graphResult.get();
645 session.setProjects(projectDependencyGraph.getSortedProjects());
646 session.setAllProjects(projectDependencyGraph.getAllProjects());
647 session.setProjectDependencyGraph(projectDependencyGraph);
648 }
649
650 return graphResult;
651 }
652
653 @Deprecated
654
655 protected Logger getLogger() {
656 return logger;
657 }
658 }