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