1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.project;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.io.ByteArrayInputStream;
26 import java.io.File;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.nio.charset.StandardCharsets;
30 import java.nio.file.Path;
31 import java.util.AbstractMap;
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.HashMap;
36 import java.util.HashSet;
37 import java.util.LinkedHashSet;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.Properties;
41 import java.util.Set;
42 import java.util.concurrent.Callable;
43 import java.util.concurrent.ConcurrentHashMap;
44 import java.util.concurrent.ConcurrentMap;
45 import java.util.concurrent.ExecutionException;
46 import java.util.concurrent.ExecutorService;
47 import java.util.concurrent.Future;
48 import java.util.concurrent.LinkedBlockingQueue;
49 import java.util.concurrent.ThreadPoolExecutor;
50 import java.util.concurrent.TimeUnit;
51 import java.util.concurrent.atomic.AtomicInteger;
52 import java.util.concurrent.atomic.AtomicReference;
53 import java.util.function.Consumer;
54 import java.util.function.Supplier;
55 import java.util.stream.Collectors;
56 import java.util.stream.Stream;
57
58 import org.apache.maven.ProjectCycleException;
59 import org.apache.maven.RepositoryUtils;
60 import org.apache.maven.api.Constants;
61 import org.apache.maven.api.Session;
62 import org.apache.maven.api.SessionData;
63 import org.apache.maven.api.feature.Features;
64 import org.apache.maven.api.model.Build;
65 import org.apache.maven.api.model.Dependency;
66 import org.apache.maven.api.model.DependencyManagement;
67 import org.apache.maven.api.model.DeploymentRepository;
68 import org.apache.maven.api.model.Extension;
69 import org.apache.maven.api.model.Model;
70 import org.apache.maven.api.model.Parent;
71 import org.apache.maven.api.model.Plugin;
72 import org.apache.maven.api.model.Profile;
73 import org.apache.maven.api.model.ReportPlugin;
74 import org.apache.maven.api.services.MavenException;
75 import org.apache.maven.api.services.ModelBuilder;
76 import org.apache.maven.api.services.ModelBuilderException;
77 import org.apache.maven.api.services.ModelBuilderRequest;
78 import org.apache.maven.api.services.ModelBuilderResult;
79 import org.apache.maven.api.services.ModelCache;
80 import org.apache.maven.api.services.ModelProblem;
81 import org.apache.maven.api.services.ModelResolver;
82 import org.apache.maven.api.services.ModelResolverException;
83 import org.apache.maven.api.services.ModelSource;
84 import org.apache.maven.api.services.ModelTransformerContext;
85 import org.apache.maven.api.services.ModelTransformerContextBuilder;
86 import org.apache.maven.api.services.Source;
87 import org.apache.maven.api.services.model.ModelBuildingListener;
88 import org.apache.maven.api.services.model.ModelProcessor;
89 import org.apache.maven.artifact.Artifact;
90 import org.apache.maven.artifact.InvalidArtifactRTException;
91 import org.apache.maven.artifact.InvalidRepositoryException;
92 import org.apache.maven.artifact.repository.ArtifactRepository;
93 import org.apache.maven.bridge.MavenRepositorySystem;
94 import org.apache.maven.internal.impl.InternalSession;
95 import org.apache.maven.internal.impl.resolver.DefaultModelCache;
96 import org.apache.maven.internal.impl.resolver.DefaultModelRepositoryHolder;
97 import org.apache.maven.model.building.DefaultModelProblem;
98 import org.apache.maven.model.building.FileModelSource;
99 import org.apache.maven.model.building.ModelSource2;
100 import org.apache.maven.model.building.ModelSource3;
101 import org.apache.maven.model.resolution.UnresolvableModelException;
102 import org.apache.maven.model.root.RootLocator;
103 import org.apache.maven.repository.internal.ArtifactDescriptorUtils;
104 import org.apache.maven.utils.Os;
105 import org.codehaus.plexus.interpolation.AbstractValueSource;
106 import org.codehaus.plexus.interpolation.InterpolationException;
107 import org.codehaus.plexus.interpolation.StringSearchInterpolator;
108 import org.eclipse.aether.RepositorySystem;
109 import org.eclipse.aether.RepositorySystemSession;
110 import org.eclipse.aether.RequestTrace;
111 import org.eclipse.aether.impl.RemoteRepositoryManager;
112 import org.eclipse.aether.repository.LocalRepositoryManager;
113 import org.eclipse.aether.repository.RemoteRepository;
114 import org.eclipse.aether.repository.WorkspaceRepository;
115 import org.eclipse.aether.resolution.ArtifactRequest;
116 import org.eclipse.aether.resolution.ArtifactResult;
117 import org.slf4j.Logger;
118 import org.slf4j.LoggerFactory;
119
120
121
122
123 @Named
124 @Singleton
125 public class DefaultProjectBuilder implements ProjectBuilder {
126
127 public static final int DEFAULT_BUILDER_PARALLELISM = Runtime.getRuntime().availableProcessors() / 2 + 1;
128
129 private final Logger logger = LoggerFactory.getLogger(getClass());
130 private final ModelBuilder modelBuilder;
131 private final ModelProcessor modelProcessor;
132 private final ProjectBuildingHelper projectBuildingHelper;
133 private final MavenRepositorySystem repositorySystem;
134 private final org.eclipse.aether.RepositorySystem repoSystem;
135 private final RemoteRepositoryManager repositoryManager;
136 private final ProjectDependenciesResolver dependencyResolver;
137
138 private final RootLocator rootLocator;
139
140 @SuppressWarnings("checkstyle:ParameterNumber")
141 @Inject
142 public DefaultProjectBuilder(
143 ModelBuilder modelBuilder,
144 ModelProcessor modelProcessor,
145 ProjectBuildingHelper projectBuildingHelper,
146 MavenRepositorySystem repositorySystem,
147 RepositorySystem repoSystem,
148 RemoteRepositoryManager repositoryManager,
149 ProjectDependenciesResolver dependencyResolver,
150 RootLocator rootLocator) {
151 this.modelBuilder = modelBuilder;
152 this.modelProcessor = modelProcessor;
153 this.projectBuildingHelper = projectBuildingHelper;
154 this.repositorySystem = repositorySystem;
155 this.repoSystem = repoSystem;
156 this.repositoryManager = repositoryManager;
157 this.dependencyResolver = dependencyResolver;
158 this.rootLocator = rootLocator;
159 }
160
161
162
163
164 @Override
165 public ProjectBuildingResult build(File pomFile, ProjectBuildingRequest request) throws ProjectBuildingException {
166 try (BuildSession bs = new BuildSession(request, false)) {
167 Path path = pomFile.toPath();
168 return bs.build(path, ModelSource.fromPath(path));
169 }
170 }
171
172 @Deprecated
173 @Override
174 public ProjectBuildingResult build(
175 org.apache.maven.model.building.ModelSource modelSource, ProjectBuildingRequest request)
176 throws ProjectBuildingException {
177 return build(toSource(modelSource), request);
178 }
179
180 @Deprecated
181 static ModelSource toSource(org.apache.maven.model.building.ModelSource modelSource) {
182 if (modelSource instanceof FileModelSource fms) {
183 return ModelSource.fromPath(fms.getPath());
184 } else {
185 return new ModelSource() {
186 @Override
187 public ModelSource resolve(ModelLocator modelLocator, String relative) {
188 if (modelSource instanceof ModelSource3 ms) {
189 return toSource(ms.getRelatedSource(
190 new org.apache.maven.model.locator.ModelLocator() {
191 @Override
192 public File locatePom(File projectDirectory) {
193 return null;
194 }
195
196 @Override
197 public Path locatePom(Path projectDirectory) {
198 return null;
199 }
200
201 @Override
202 public Path locateExistingPom(Path project) {
203 return modelLocator.locateExistingPom(project);
204 }
205 },
206 relative));
207 }
208 return null;
209 }
210
211 @Override
212 public Path getPath() {
213 return null;
214 }
215
216 @Override
217 public InputStream openStream() throws IOException {
218 return modelSource.getInputStream();
219 }
220
221 @Override
222 public String getLocation() {
223 return modelSource.getLocation();
224 }
225
226 @Override
227 public Source resolve(String relative) {
228 if (modelSource instanceof ModelSource2 ms) {
229 return toSource(ms.getRelatedSource(relative));
230 } else {
231 return null;
232 }
233 }
234 };
235 }
236 }
237
238 @Override
239 public ProjectBuildingResult build(ModelSource modelSource, ProjectBuildingRequest request)
240 throws ProjectBuildingException {
241 try (BuildSession bs = new BuildSession(request, false)) {
242 return bs.build(null, modelSource);
243 }
244 }
245
246 @Override
247 public ProjectBuildingResult build(Artifact artifact, ProjectBuildingRequest request)
248 throws ProjectBuildingException {
249 return build(artifact, false, request);
250 }
251
252 @Override
253 public ProjectBuildingResult build(Artifact artifact, boolean allowStubModel, ProjectBuildingRequest request)
254 throws ProjectBuildingException {
255 try (BuildSession bs = new BuildSession(request, false)) {
256 return bs.build(artifact, allowStubModel);
257 }
258 }
259
260 @Override
261 public List<ProjectBuildingResult> build(List<File> pomFiles, boolean recursive, ProjectBuildingRequest request)
262 throws ProjectBuildingException {
263 try (BuildSession bs = new BuildSession(request, true)) {
264 return bs.build(pomFiles, recursive);
265 }
266 }
267
268 static class InterimResult {
269
270 File pomFile;
271
272 ModelBuilderRequest request;
273
274 ModelBuilderResult result;
275
276 MavenProject project;
277
278 boolean root;
279
280 List<InterimResult> subprojects = Collections.emptyList();
281
282 ProjectBuildingResult projectBuildingResult;
283
284 InterimResult(
285 File pomFile,
286 ModelBuilderRequest request,
287 ModelBuilderResult result,
288 MavenProject project,
289 boolean root) {
290 this.pomFile = pomFile;
291 this.request = request;
292 this.result = result;
293 this.project = project;
294 this.root = root;
295 }
296
297 InterimResult(ModelBuilderRequest request, ProjectBuildingResult projectBuildingResult) {
298 this.request = request;
299 this.projectBuildingResult = projectBuildingResult;
300 this.pomFile = projectBuildingResult.getPomFile();
301 this.project = projectBuildingResult.getProject();
302 }
303 }
304
305 class BuildSession implements AutoCloseable {
306 private final ProjectBuildingRequest request;
307 private final RepositorySystemSession session;
308 private final List<RemoteRepository> repositories;
309 private final ReactorModelPool modelPool;
310 private final ConcurrentMap<String, Object> parentCache;
311 private final ModelTransformerContextBuilder transformerContextBuilder;
312 private final ExecutorService executor;
313 private final ModelCache modelCache;
314 private final ModelResolver modelResolver;
315 private final Map<String, String> ciFriendlyVersions = new ConcurrentHashMap<>();
316
317 BuildSession(ProjectBuildingRequest request, boolean localProjects) {
318 this.request = request;
319 this.session =
320 RepositoryUtils.overlay(request.getLocalRepository(), request.getRepositorySession(), repoSystem);
321 InternalSession.from(session);
322 this.repositories = RepositoryUtils.toRepos(request.getRemoteRepositories());
323 this.executor = createExecutor(getParallelism(request));
324 if (localProjects) {
325 this.modelPool = new ReactorModelPool();
326 this.transformerContextBuilder = modelBuilder.newTransformerContextBuilder();
327 } else {
328 this.modelPool = null;
329 this.transformerContextBuilder = null;
330 }
331 this.parentCache = new ConcurrentHashMap<>();
332 this.modelCache = DefaultModelCache.newInstance(session, true);
333 this.modelResolver = new ModelResolverWrapper() {
334 @Override
335 protected org.apache.maven.model.resolution.ModelResolver getResolver(
336 List<RemoteRepository> repositories) {
337 return new ProjectModelResolver(
338 session,
339 RequestTrace.newChild(null, request),
340 repoSystem,
341 repositoryManager,
342 repositories,
343 request.getRepositoryMerging(),
344 modelPool,
345 parentCache);
346 }
347 };
348 }
349
350 ExecutorService createExecutor(int parallelism) {
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366 return new ThreadPoolExecutor(
367 parallelism, Integer.MAX_VALUE, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()) {
368 final AtomicInteger parked = new AtomicInteger();
369
370 @Override
371 public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
372 throws InterruptedException {
373 setCorePoolSize(parallelism + parked.incrementAndGet());
374 try {
375 return super.invokeAll(tasks);
376 } finally {
377 setCorePoolSize(parallelism + parked.decrementAndGet());
378 }
379 }
380 };
381 }
382
383 @Override
384 public void close() {
385 this.executor.shutdownNow();
386 }
387
388 private int getParallelism(ProjectBuildingRequest request) {
389 int parallelism = DEFAULT_BUILDER_PARALLELISM;
390 try {
391 String str = request.getUserProperties().getProperty(Constants.MAVEN_PROJECT_BUILDER_PARALLELISM);
392 if (str != null) {
393 parallelism = Integer.parseInt(str);
394 }
395 } catch (Exception e) {
396
397 }
398 return Math.max(1, Math.min(parallelism, Runtime.getRuntime().availableProcessors()));
399 }
400
401 ProjectBuildingResult build(Path pomFile, ModelSource modelSource) throws ProjectBuildingException {
402 ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
403
404 try {
405 MavenProject project = request.getProject();
406
407 List<ModelProblem> modelProblems = null;
408 Throwable error = null;
409
410 if (project == null) {
411 project = new MavenProject();
412 project.setFile(pomFile != null ? pomFile.toFile() : null);
413
414 ModelBuildingListener listener =
415 new DefaultModelBuildingListener(project, projectBuildingHelper, this.request);
416
417 ModelBuilderRequest.ModelBuilderRequestBuilder builder = getModelBuildingRequest();
418 ModelBuilderRequest request = builder.projectBuild(modelPool != null)
419 .source(modelSource)
420 .projectBuild(true)
421 .locationTracking(true)
422 .listener(listener)
423 .build();
424
425 if (pomFile != null) {
426 project.setRootDirectory(rootLocator.findRoot(pomFile.getParent()));
427 }
428
429 ModelBuilderResult result;
430 try {
431 result = modelBuilder.build(request);
432 } catch (ModelBuilderException e) {
433 result = e.getResult();
434 if (result == null || result.getEffectiveModel() == null) {
435 throw new ProjectBuildingException(
436 e.getModelId(), e.getMessage(), pomFile != null ? pomFile.toFile() : null, e);
437 }
438
439 error = e;
440 }
441
442 modelProblems = result.getProblems();
443
444 initProject(project, Collections.emptyMap(), result);
445 } else if (request.isResolveDependencies()) {
446 projectBuildingHelper.selectProjectRealm(project);
447 }
448
449 DependencyResolutionResult resolutionResult = null;
450
451 if (request.isResolveDependencies()) {
452 resolutionResult = resolveDependencies(project);
453 }
454
455 ProjectBuildingResult result =
456 new DefaultProjectBuildingResult(project, convert(modelProblems), resolutionResult);
457
458 if (error != null) {
459 ProjectBuildingException e = new ProjectBuildingException(List.of(result));
460 e.initCause(error);
461 throw e;
462 }
463
464 return result;
465 } finally {
466 Thread.currentThread().setContextClassLoader(oldContextClassLoader);
467 }
468 }
469
470 ProjectBuildingResult build(Artifact artifact, boolean allowStubModel) throws ProjectBuildingException {
471 org.eclipse.aether.artifact.Artifact pomArtifact = RepositoryUtils.toArtifact(artifact);
472 pomArtifact = ArtifactDescriptorUtils.toPomArtifact(pomArtifact);
473
474 boolean localProject;
475
476 try {
477 ArtifactRequest pomRequest = new ArtifactRequest();
478 pomRequest.setArtifact(pomArtifact);
479 pomRequest.setRepositories(RepositoryUtils.toRepos(request.getRemoteRepositories()));
480 ArtifactResult pomResult = repoSystem.resolveArtifact(session, pomRequest);
481
482 pomArtifact = pomResult.getArtifact();
483 localProject = pomResult.getRepository() instanceof WorkspaceRepository;
484 } catch (org.eclipse.aether.resolution.ArtifactResolutionException e) {
485 if (e.getResults().get(0).isMissing() && allowStubModel) {
486 return build(null, createStubModelSource(artifact));
487 }
488 throw new ProjectBuildingException(
489 artifact.getId(), "Error resolving project artifact: " + e.getMessage(), e);
490 }
491
492 Path pomFile = pomArtifact.getPath();
493
494 if (!artifact.isResolved() && "pom".equals(artifact.getType())) {
495 artifact.selectVersion(pomArtifact.getVersion());
496 artifact.setFile(pomFile.toFile());
497 artifact.setResolved(true);
498 }
499
500 if (localProject) {
501 return build(pomFile, ModelSource.fromPath(pomFile));
502 } else {
503 return build(
504 null,
505 ModelSource.fromPath(
506 pomFile,
507 artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion()));
508 }
509 }
510
511 List<ProjectBuildingResult> build(List<File> pomFiles, boolean recursive) throws ProjectBuildingException {
512 List<ProjectBuildingResult> results = doBuild(pomFiles, recursive);
513 if (results.stream()
514 .flatMap(r -> r.getProblems().stream())
515 .anyMatch(p -> p.getSeverity() != org.apache.maven.model.building.ModelProblem.Severity.WARNING)) {
516 org.apache.maven.model.building.ModelProblem cycle = results.stream()
517 .flatMap(r -> r.getProblems().stream())
518 .filter(p -> p.getException() instanceof CycleDetectedException)
519 .findAny()
520 .orElse(null);
521 if (cycle != null) {
522 throw new RuntimeException(new ProjectCycleException(
523 "The projects in the reactor contain a cyclic reference: " + cycle.getMessage(),
524 (CycleDetectedException) cycle.getException()));
525 }
526 throw new ProjectBuildingException(results);
527 }
528
529 return results;
530 }
531
532 List<ProjectBuildingResult> doBuild(List<File> pomFiles, boolean recursive) {
533 Map<File, MavenProject> projectIndex = new ConcurrentHashMap<>(256);
534
535
536 List<InterimResult> interimResults = build(projectIndex, pomFiles, new LinkedHashSet<>(), true, recursive);
537
538 ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
539
540 try {
541
542 List<ProjectBuildingResult> results = build(projectIndex, interimResults);
543
544 if (Features.buildConsumer(request.getUserProperties())) {
545 request.getRepositorySession()
546 .getData()
547 .set(ModelTransformerContext.KEY, transformerContextBuilder.build());
548 }
549
550 return results;
551 } finally {
552 Thread.currentThread().setContextClassLoader(oldContextClassLoader);
553 }
554 }
555
556 @SuppressWarnings("checkstyle:parameternumber")
557 private List<InterimResult> build(
558 Map<File, MavenProject> projectIndex,
559 List<File> pomFiles,
560 Set<File> aggregatorFiles,
561 boolean root,
562 boolean recursive) {
563 List<Callable<InterimResult>> tasks = pomFiles.stream()
564 .map(pomFile -> ((Callable<InterimResult>)
565 () -> build(projectIndex, pomFile, concat(aggregatorFiles, pomFile), root, recursive)))
566 .collect(Collectors.toList());
567 try {
568 List<Future<InterimResult>> futures = executor.invokeAll(tasks);
569 List<InterimResult> list = new ArrayList<>();
570 for (Future<InterimResult> future : futures) {
571 InterimResult interimResult = future.get();
572 list.add(interimResult);
573 }
574 return list;
575 } catch (Exception e) {
576 throw new RuntimeException(e);
577 }
578 }
579
580 private <T> Set<T> concat(Set<T> set, T elem) {
581 Set<T> newSet = new HashSet<>(set);
582 newSet.add(elem);
583 return newSet;
584 }
585
586 @SuppressWarnings("checkstyle:parameternumber")
587 private InterimResult build(
588 Map<File, MavenProject> projectIndex,
589 File pomFile,
590 Set<File> aggregatorFiles,
591 boolean isRoot,
592 boolean recursive) {
593 MavenProject project = new MavenProject();
594 project.setFile(pomFile);
595
596 project.setRootDirectory(
597 rootLocator.findRoot(pomFile.getParentFile().toPath()));
598
599 DefaultModelBuildingListener listener =
600 new DefaultModelBuildingListener(project, projectBuildingHelper, request);
601
602 ModelBuilderRequest modelBuildingRequest = getModelBuildingRequest()
603 .source(ModelSource.fromPath(pomFile.toPath()))
604 .projectBuild(true)
605 .twoPhaseBuilding(true)
606 .locationTracking(true)
607 .listener(listener)
608 .build();
609
610 ModelBuilderResult result;
611 try {
612 result = modelBuilder.build(modelBuildingRequest);
613 } catch (ModelBuilderException e) {
614 result = e.getResult();
615 if (result == null || result.getFileModel() == null) {
616 return new InterimResult(
617 modelBuildingRequest,
618 new DefaultProjectBuildingResult(e.getModelId(), pomFile, convert(e.getProblems())));
619 }
620
621
622 }
623
624 Model model = result.getActivatedFileModel();
625
626
627
628 Model modelWithVersion = getModelWithInterpolatedVersion(model, result.getProblems()::add);
629 modelPool.put(model.getPomFile(), modelWithVersion);
630
631 InterimResult interimResult = new InterimResult(pomFile, modelBuildingRequest, result, project, isRoot);
632
633 if (recursive) {
634 File basedir = pomFile.getParentFile();
635 List<String> subprojects = model.getSubprojects();
636 if (subprojects.isEmpty()) {
637 subprojects = model.getModules();
638 }
639 List<File> subprojectFiles = new ArrayList<>();
640 for (String subproject : subprojects) {
641 if (subproject == null || subproject.isEmpty()) {
642 continue;
643 }
644
645 subproject = subproject.replace('\\', File.separatorChar).replace('/', File.separatorChar);
646
647 Path subprojectPath = modelProcessor.locateExistingPom(new File(basedir, subproject).toPath());
648 File subprojectFile = subprojectPath != null ? subprojectPath.toFile() : null;
649
650 if (subprojectFile == null) {
651 ModelProblem problem = new org.apache.maven.internal.impl.model.DefaultModelProblem(
652 "Child subproject " + subprojectFile + " of " + pomFile + " does not exist",
653 ModelProblem.Severity.ERROR,
654 ModelProblem.Version.BASE,
655 model,
656 -1,
657 -1,
658 null);
659 result.getProblems().add(problem);
660 continue;
661 }
662
663 if (Os.IS_WINDOWS) {
664
665 try {
666 subprojectFile = subprojectFile.getCanonicalFile();
667 } catch (IOException e) {
668 subprojectFile = subprojectFile.getAbsoluteFile();
669 }
670 } else {
671 subprojectFile = new File(subprojectFile.toURI().normalize());
672 }
673
674 if (aggregatorFiles.contains(subprojectFile)) {
675 StringBuilder buffer = new StringBuilder(256);
676 for (File aggregatorFile : aggregatorFiles) {
677 buffer.append(aggregatorFile).append(" -> ");
678 }
679 buffer.append(subprojectFile);
680
681 ModelProblem problem = new org.apache.maven.internal.impl.model.DefaultModelProblem(
682 "Child subproject " + subprojectFile + " of " + pomFile + " forms aggregation cycle "
683 + buffer,
684 ModelProblem.Severity.ERROR,
685 ModelProblem.Version.BASE,
686 model,
687 -1,
688 -1,
689 null);
690 result.getProblems().add(problem);
691
692 continue;
693 }
694
695 subprojectFiles.add(subprojectFile);
696 }
697
698 if (!subprojectFiles.isEmpty()) {
699 interimResult.subprojects = build(projectIndex, subprojectFiles, aggregatorFiles, false, recursive);
700 }
701 }
702
703 projectIndex.put(pomFile, project);
704
705 return interimResult;
706 }
707
708 private Model getModelWithInterpolatedVersion(
709 Model model, Consumer<org.apache.maven.api.services.ModelProblem> problems) {
710 String version = model.getVersion();
711 if (version == null && model.getParent() != null) {
712 version = model.getParent().getVersion();
713 }
714 if (version != null && version.contains("${")) {
715 try {
716 StringSearchInterpolator interpolator = new StringSearchInterpolator();
717 interpolator.addValueSource(new AbstractValueSource(false) {
718 @Override
719 public String getValue(String key) {
720 String val = request.getUserProperties().getProperty(key);
721 if (val == null) {
722 val = model.getProperties().get(key);
723 if (val == null) {
724 val = request.getSystemProperties().getProperty(key);
725 if (val == null) {
726 val = ciFriendlyVersions.get(key);
727 }
728 }
729 }
730 if (val != null) {
731 String oldVal = ciFriendlyVersions.put(key, val);
732 if (oldVal != null && !val.equals(oldVal)) {
733 throw new MavenException("Non unique property value detected for key '" + key
734 + "' which is bound to '" + oldVal + "' and '" + val + "'");
735 }
736 }
737 return val;
738 }
739 });
740 version = interpolator.interpolate(version);
741 } catch (InterpolationException | MavenException e) {
742 ModelProblem problem = new org.apache.maven.internal.impl.model.DefaultModelProblem(
743 "Unable to interpolate ",
744 ModelProblem.Severity.ERROR,
745 ModelProblem.Version.BASE,
746 model,
747 -1,
748 -1,
749 null);
750 problems.accept(problem);
751 }
752 if (model.getVersion() != null) {
753 return model.withVersion(version);
754 } else {
755 return model.withParent(model.getParent().withVersion(version));
756 }
757 }
758 return model;
759 }
760
761 private List<ProjectBuildingResult> build(
762 Map<File, MavenProject> projectIndex, List<InterimResult> interimResults) {
763
764
765
766
767 List<ProjectBuildingResult> results = new ArrayList<>();
768 boolean failure = false;
769 for (InterimResult r : interimResults) {
770 DefaultProjectBuildingResult res;
771 try {
772 Model model = modelBuilder.buildRawModel(r.request);
773 res = new DefaultProjectBuildingResult(
774 model.getId(),
775 model.getPomFile() != null ? model.getPomFile().toFile() : null,
776 null);
777 } catch (ModelBuilderException e) {
778 failure = true;
779 res = new DefaultProjectBuildingResult(
780 e.getModelId(),
781 r.request.getSource().getPath() != null
782 ? r.request.getSource().getPath().toFile()
783 : null,
784 convert(e.getProblems()));
785 }
786 results.add(res);
787 }
788 if (failure) {
789 return results;
790 }
791
792 List<Callable<List<ProjectBuildingResult>>> callables = interimResults.stream()
793 .map(interimResult ->
794 (Callable<List<ProjectBuildingResult>>) () -> doBuild(projectIndex, interimResult))
795 .collect(Collectors.toList());
796
797 try {
798 List<Future<List<ProjectBuildingResult>>> futures = executor.invokeAll(callables);
799 return futures.stream()
800 .map(listFuture -> {
801 try {
802 return listFuture.get();
803 } catch (InterruptedException e) {
804 uncheckedThrow(e);
805 return null;
806 } catch (ExecutionException e) {
807 uncheckedThrow(e.getCause());
808 return null;
809 }
810 })
811 .flatMap(List::stream)
812 .collect(Collectors.toList());
813 } catch (InterruptedException e) {
814 uncheckedThrow(e);
815 return null;
816 }
817 }
818
819 private List<ProjectBuildingResult> doBuild(Map<File, MavenProject> projectIndex, InterimResult interimResult) {
820 if (interimResult.projectBuildingResult != null) {
821 return Collections.singletonList(interimResult.projectBuildingResult);
822 }
823 MavenProject project = interimResult.project;
824 try {
825 ModelBuilderResult result = modelBuilder.build(ModelBuilderRequest.builder(interimResult.request)
826 .interimResult(interimResult.result)
827 .build());
828
829
830 List<org.apache.maven.model.building.ModelProblem> problems = convert(result.getProblems());
831 try {
832 initProject(project, projectIndex, result);
833 } catch (InvalidArtifactRTException iarte) {
834 problems.add(new DefaultModelProblem(
835 null,
836 org.apache.maven.model.building.ModelProblem.Severity.ERROR,
837 null,
838 new org.apache.maven.model.Model(result.getEffectiveModel()),
839 -1,
840 -1,
841 iarte));
842 }
843
844 List<ProjectBuildingResult> results = build(projectIndex, interimResult.subprojects);
845
846 project.setExecutionRoot(interimResult.root);
847 project.setCollectedProjects(
848 results.stream().map(ProjectBuildingResult::getProject).collect(Collectors.toList()));
849 DependencyResolutionResult resolutionResult = null;
850 if (request.isResolveDependencies()) {
851 resolutionResult = resolveDependencies(project);
852 }
853
854 results.add(new DefaultProjectBuildingResult(project, problems, resolutionResult));
855
856 return results;
857 } catch (ModelBuilderException e) {
858 DefaultProjectBuildingResult result;
859 if (project == null || interimResult.result.getEffectiveModel() == null) {
860 result = new DefaultProjectBuildingResult(
861 e.getModelId(), interimResult.pomFile, convert(e.getProblems()));
862 } else {
863 project.setModel(new org.apache.maven.model.Model(interimResult.result.getEffectiveModel()));
864 result = new DefaultProjectBuildingResult(project, convert(e.getProblems()), null);
865 }
866 return Collections.singletonList(result);
867 }
868 }
869
870 private List<org.apache.maven.model.building.ModelProblem> convert(List<ModelProblem> problems) {
871 if (problems == null) {
872 return null;
873 }
874 return problems.stream()
875 .map(p -> (org.apache.maven.model.building.ModelProblem) new DefaultModelProblem(
876 p.getMessage(),
877 org.apache.maven.model.building.ModelProblem.Severity.valueOf(
878 p.getSeverity().name()),
879 org.apache.maven.model.building.ModelProblem.Version.valueOf(
880 p.getVersion().name()),
881 p.getSource(),
882 p.getLineNumber(),
883 p.getColumnNumber(),
884 p.getModelId(),
885 p.getException()))
886 .toList();
887 }
888
889 @SuppressWarnings({"checkstyle:methodlength", "deprecation"})
890 private void initProject(MavenProject project, Map<File, MavenProject> projects, ModelBuilderResult result) {
891 project.setModel(new org.apache.maven.model.Model(result.getEffectiveModel()));
892 project.setOriginalModel(new org.apache.maven.model.Model(result.getFileModel()));
893
894 initParent(project, projects, result);
895
896 Artifact projectArtifact = repositorySystem.createArtifact(
897 project.getGroupId(), project.getArtifactId(), project.getVersion(), null, project.getPackaging());
898 project.setArtifact(projectArtifact);
899
900
901 if (project.getFile() != null) {
902 Build build = project.getBuild().getDelegate();
903 project.addScriptSourceRoot(build.getScriptSourceDirectory());
904 project.addCompileSourceRoot(build.getSourceDirectory());
905 project.addTestCompileSourceRoot(build.getTestSourceDirectory());
906 }
907
908 project.setActiveProfiles(Stream.concat(
909 result.getActivePomProfiles(result.getModelIds().get(0)).stream(),
910 result.getActiveExternalProfiles().stream())
911 .map(org.apache.maven.model.Profile::new)
912 .toList());
913
914 project.setInjectedProfileIds("external", getProfileIds(result.getActiveExternalProfiles()));
915 for (String modelId : result.getModelIds()) {
916 project.setInjectedProfileIds(modelId, getProfileIds(result.getActivePomProfiles(modelId)));
917 }
918
919
920
921
922
923 project.setProjectBuildingRequest(request);
924
925
926 Set<Artifact> pluginArtifacts = new HashSet<>();
927 for (Plugin plugin : project.getModel().getDelegate().getBuild().getPlugins()) {
928 Artifact artifact = repositorySystem.createPluginArtifact(new org.apache.maven.model.Plugin(plugin));
929
930 if (artifact != null) {
931 pluginArtifacts.add(artifact);
932 }
933 }
934 project.setPluginArtifacts(pluginArtifacts);
935
936
937 Set<Artifact> reportArtifacts = new HashSet<>();
938 for (ReportPlugin report :
939 project.getModel().getDelegate().getReporting().getPlugins()) {
940 Plugin pp = Plugin.newBuilder()
941 .groupId(report.getGroupId())
942 .artifactId(report.getArtifactId())
943 .version(report.getVersion())
944 .build();
945
946 Artifact artifact = repositorySystem.createPluginArtifact(new org.apache.maven.model.Plugin(pp));
947
948 if (artifact != null) {
949 reportArtifacts.add(artifact);
950 }
951 }
952 project.setReportArtifacts(reportArtifacts);
953
954
955 Set<Artifact> extensionArtifacts = new HashSet<>();
956 List<Extension> extensions =
957 project.getModel().getDelegate().getBuild().getExtensions();
958 if (extensions != null) {
959 for (Extension ext : extensions) {
960 String version;
961 if (ext.getVersion() == null || ext.getVersion().isEmpty()) {
962 version = "RELEASE";
963 } else {
964 version = ext.getVersion();
965 }
966
967 Artifact artifact = repositorySystem.createArtifact(
968 ext.getGroupId(), ext.getArtifactId(), version, null, "jar");
969
970 if (artifact != null) {
971 extensionArtifacts.add(artifact);
972 }
973 }
974 }
975 project.setExtensionArtifacts(extensionArtifacts);
976
977
978 Map<String, Artifact> map = Collections.emptyMap();
979 final DependencyManagement dependencyManagement =
980 project.getModel().getDelegate().getDependencyManagement();
981 if (dependencyManagement != null
982 && dependencyManagement.getDependencies() != null
983 && !dependencyManagement.getDependencies().isEmpty()) {
984 map = new LazyMap<>(() -> {
985 Map<String, Artifact> tmp = new HashMap<>();
986 for (Dependency d : dependencyManagement.getDependencies()) {
987 Artifact artifact =
988 repositorySystem.createDependencyArtifact(new org.apache.maven.model.Dependency(d));
989 if (artifact != null) {
990 tmp.put(d.getManagementKey(), artifact);
991 }
992 }
993 return Collections.unmodifiableMap(tmp);
994 });
995 }
996 project.setManagedVersionMap(map);
997
998
999 if (project.getDistributionManagement() != null
1000 && project.getDistributionManagement().getRepository() != null) {
1001 try {
1002 DeploymentRepository r = project.getModel()
1003 .getDelegate()
1004 .getDistributionManagement()
1005 .getRepository();
1006 if (r.getId() != null
1007 && !r.getId().isEmpty()
1008 && r.getUrl() != null
1009 && !r.getUrl().isEmpty()) {
1010 ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository(
1011 new org.apache.maven.model.DeploymentRepository(r));
1012 repositorySystem.injectProxy(request.getRepositorySession(), List.of(repo));
1013 repositorySystem.injectAuthentication(request.getRepositorySession(), List.of(repo));
1014 project.setReleaseArtifactRepository(repo);
1015 }
1016 } catch (InvalidRepositoryException e) {
1017 throw new IllegalStateException(
1018 "Failed to create release distribution repository for " + project.getId(), e);
1019 }
1020 }
1021
1022
1023 if (project.getDistributionManagement() != null
1024 && project.getDistributionManagement().getSnapshotRepository() != null) {
1025 try {
1026 DeploymentRepository r = project.getModel()
1027 .getDelegate()
1028 .getDistributionManagement()
1029 .getSnapshotRepository();
1030 if (r.getId() != null
1031 && !r.getId().isEmpty()
1032 && r.getUrl() != null
1033 && !r.getUrl().isEmpty()) {
1034 ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository(
1035 new org.apache.maven.model.DeploymentRepository(r));
1036 repositorySystem.injectProxy(request.getRepositorySession(), List.of(repo));
1037 repositorySystem.injectAuthentication(request.getRepositorySession(), List.of(repo));
1038 project.setSnapshotArtifactRepository(repo);
1039 }
1040 } catch (InvalidRepositoryException e) {
1041 throw new IllegalStateException(
1042 "Failed to create snapshot distribution repository for " + project.getId(), e);
1043 }
1044 }
1045 }
1046
1047 private void initParent(MavenProject project, Map<File, MavenProject> projects, ModelBuilderResult result) {
1048 Model parentModel = result.getModelIds().size() > 1
1049 && !result.getModelIds().get(1).isEmpty()
1050 ? result.getRawModel(result.getModelIds().get(1)).orElse(null)
1051 : null;
1052
1053 if (parentModel != null) {
1054 final String parentGroupId = inheritedGroupId(result, 1);
1055 final String parentVersion = inheritedVersion(result, 1);
1056
1057 project.setParentArtifact(repositorySystem.createProjectArtifact(
1058 parentGroupId, parentModel.getArtifactId(), parentVersion));
1059
1060
1061 String parentModelId = result.getModelIds().get(1);
1062 Path parentPomFile =
1063 result.getRawModel(parentModelId).map(Model::getPomFile).orElse(null);
1064 MavenProject parent = parentPomFile != null ? projects.get(parentPomFile.toFile()) : null;
1065 if (parent == null) {
1066
1067
1068
1069
1070
1071 request.setRemoteRepositories(project.getRemoteArtifactRepositories());
1072 if (parentPomFile != null) {
1073 project.setParentFile(parentPomFile.toFile());
1074 try {
1075 parent = build(parentPomFile, ModelSource.fromPath(parentPomFile))
1076 .getProject();
1077 } catch (ProjectBuildingException e) {
1078
1079 if (logger.isDebugEnabled()) {
1080
1081 logger.warn("Failed to build parent project for " + project.getId(), e);
1082 } else {
1083
1084 logger.warn("Failed to build parent project for " + project.getId());
1085 }
1086 }
1087 } else {
1088 Artifact parentArtifact = project.getParentArtifact();
1089 try {
1090 parent = build(parentArtifact, false).getProject();
1091 } catch (ProjectBuildingException e) {
1092
1093 if (logger.isDebugEnabled()) {
1094
1095 logger.warn("Failed to build parent project for " + project.getId(), e);
1096 } else {
1097
1098 logger.warn("Failed to build parent project for " + project.getId());
1099 }
1100 }
1101 }
1102 }
1103 project.setParent(parent);
1104 if (project.getParentFile() == null && parent != null) {
1105 project.setParentFile(parent.getFile());
1106 }
1107 }
1108 }
1109
1110 private ModelBuilderRequest.ModelBuilderRequestBuilder getModelBuildingRequest() {
1111 ModelBuilderRequest.ModelBuilderRequestBuilder modelBuildingRequest = ModelBuilderRequest.builder();
1112
1113 InternalSession internalSession = InternalSession.from(session);
1114 modelBuildingRequest.session(internalSession.withRemoteRepositories(request.getRemoteRepositories().stream()
1115 .map(r -> internalSession.getRemoteRepository(RepositoryUtils.toRepo(r)))
1116 .toList()));
1117 modelBuildingRequest.validationLevel(request.getValidationLevel());
1118 modelBuildingRequest.processPlugins(request.isProcessPlugins());
1119 modelBuildingRequest.profiles(
1120 request.getProfiles() != null
1121 ? request.getProfiles().stream()
1122 .map(org.apache.maven.model.Profile::getDelegate)
1123 .toList()
1124 : null);
1125 modelBuildingRequest.activeProfileIds(request.getActiveProfileIds());
1126 modelBuildingRequest.inactiveProfileIds(request.getInactiveProfileIds());
1127 modelBuildingRequest.systemProperties(toMap(request.getSystemProperties()));
1128 modelBuildingRequest.userProperties(toMap(request.getUserProperties()));
1129
1130 modelBuildingRequest.modelResolver(modelResolver);
1131 DefaultModelRepositoryHolder holder = new DefaultModelRepositoryHolder(
1132 internalSession,
1133 DefaultModelRepositoryHolder.RepositoryMerging.valueOf(
1134 request.getRepositoryMerging().name()),
1135 repositories.stream()
1136 .map(internalSession::getRemoteRepository)
1137 .toList());
1138 modelBuildingRequest.modelRepositoryHolder(holder);
1139 modelBuildingRequest.modelCache(modelCache);
1140 modelBuildingRequest.transformerContextBuilder(transformerContextBuilder);
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152 internalSession.getData().set(SessionData.key(ModelResolver.class), modelResolver);
1153
1154 return modelBuildingRequest;
1155 }
1156
1157 private DependencyResolutionResult resolveDependencies(MavenProject project) {
1158 DependencyResolutionResult resolutionResult;
1159
1160 try {
1161 DefaultDependencyResolutionRequest resolution =
1162 new DefaultDependencyResolutionRequest(project, session);
1163 resolutionResult = dependencyResolver.resolve(resolution);
1164 } catch (DependencyResolutionException e) {
1165 resolutionResult = e.getResult();
1166 }
1167
1168 Set<Artifact> artifacts = new LinkedHashSet<>();
1169 if (resolutionResult.getDependencyGraph() != null) {
1170 RepositoryUtils.toArtifacts(
1171 artifacts,
1172 resolutionResult.getDependencyGraph().getChildren(),
1173 Collections.singletonList(project.getArtifact().getId()),
1174 null);
1175
1176
1177 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
1178 for (Artifact artifact : artifacts) {
1179 if (!artifact.isResolved()) {
1180 String path = lrm.getPathForLocalArtifact(RepositoryUtils.toArtifact(artifact));
1181 artifact.setFile(
1182 lrm.getRepository().getBasePath().resolve(path).toFile());
1183 }
1184 }
1185 }
1186 project.setResolvedArtifacts(artifacts);
1187 project.setArtifacts(artifacts);
1188
1189 return resolutionResult;
1190 }
1191 }
1192
1193 private List<String> getProfileIds(List<Profile> profiles) {
1194 return profiles.stream().map(Profile::getId).collect(Collectors.toList());
1195 }
1196
1197 private static ModelSource createStubModelSource(Artifact artifact) {
1198 StringBuilder buffer = new StringBuilder(1024);
1199
1200 buffer.append("<?xml version='1.0'?>");
1201 buffer.append("<project>");
1202 buffer.append("<modelVersion>4.0.0</modelVersion>");
1203 buffer.append("<groupId>").append(artifact.getGroupId()).append("</groupId>");
1204 buffer.append("<artifactId>").append(artifact.getArtifactId()).append("</artifactId>");
1205 buffer.append("<version>").append(artifact.getBaseVersion()).append("</version>");
1206 buffer.append("<packaging>").append(artifact.getType()).append("</packaging>");
1207 buffer.append("</project>");
1208
1209 return new ModelSource() {
1210 @Override
1211 public ModelSource resolve(ModelLocator modelLocator, String relative) {
1212 return null;
1213 }
1214
1215 @Override
1216 public Path getPath() {
1217 return null;
1218 }
1219
1220 @Override
1221 public InputStream openStream() throws IOException {
1222 return new ByteArrayInputStream(buffer.toString().getBytes(StandardCharsets.UTF_8));
1223 }
1224
1225 @Override
1226 public String getLocation() {
1227 return artifact.getId();
1228 }
1229
1230 @Override
1231 public Source resolve(String relative) {
1232 return null;
1233 }
1234 };
1235 }
1236
1237 private static String inheritedGroupId(final ModelBuilderResult result, final int modelIndex) {
1238 String groupId = null;
1239 final String modelId = result.getModelIds().get(modelIndex);
1240
1241 if (!modelId.isEmpty()) {
1242 final Model model = result.getRawModel(modelId).orElseThrow();
1243 groupId = model.getGroupId() != null ? model.getGroupId() : inheritedGroupId(result, modelIndex + 1);
1244 }
1245
1246 return groupId;
1247 }
1248
1249 private static String inheritedVersion(final ModelBuilderResult result, final int modelIndex) {
1250 String version = null;
1251 final String modelId = result.getModelIds().get(modelIndex);
1252
1253 if (!modelId.isEmpty()) {
1254 version = result.getRawModel(modelId).map(Model::getVersion).orElse(null);
1255 if (version == null) {
1256 version = inheritedVersion(result, modelIndex + 1);
1257 }
1258 }
1259
1260 return version;
1261 }
1262
1263 private static Map<String, String> toMap(Properties properties) {
1264 if (properties != null && !properties.isEmpty()) {
1265 return properties.entrySet().stream()
1266 .collect(Collectors.toMap(e -> String.valueOf(e.getKey()), e -> String.valueOf(e.getValue())));
1267
1268 } else {
1269 return null;
1270 }
1271 }
1272
1273 @SuppressWarnings("unchecked")
1274 static <T extends Throwable> void uncheckedThrow(Throwable t) throws T {
1275 throw (T) t;
1276 }
1277
1278 static class LazyMap<K, V> extends AbstractMap<K, V> {
1279 private final Supplier<Map<K, V>> supplier;
1280 private volatile Map<K, V> delegate;
1281
1282 LazyMap(Supplier<Map<K, V>> supplier) {
1283 this.supplier = supplier;
1284 }
1285
1286 @Override
1287 public Set<Entry<K, V>> entrySet() {
1288 if (delegate == null) {
1289 synchronized (this) {
1290 if (delegate == null) {
1291 delegate = supplier.get();
1292 }
1293 }
1294 }
1295 return delegate.entrySet();
1296 }
1297 }
1298
1299 protected abstract class ModelResolverWrapper implements ModelResolver {
1300
1301 protected abstract org.apache.maven.model.resolution.ModelResolver getResolver(
1302 List<RemoteRepository> repositories);
1303
1304 @Override
1305 public ModelSource resolveModel(
1306 Session session, String groupId, String artifactId, String version, Consumer<String> resolved)
1307 throws ModelResolverException {
1308 try {
1309 InternalSession internalSession = InternalSession.from(session);
1310 org.apache.maven.model.resolution.ModelResolver resolver =
1311 getResolver(internalSession.toRepositories(internalSession.getRemoteRepositories()));
1312 org.apache.maven.model.Parent p = new org.apache.maven.model.Parent(Parent.newBuilder()
1313 .groupId(groupId)
1314 .artifactId(artifactId)
1315 .version(version)
1316 .build());
1317 org.apache.maven.model.building.ModelSource modelSource = resolver.resolveModel(p);
1318 if (!p.getVersion().equals(version)) {
1319 resolved.accept(p.getVersion());
1320 }
1321 return toSource(modelSource);
1322 } catch (UnresolvableModelException e) {
1323 throw new ModelResolverException(e.getMessage(), e.getGroupId(), e.getArtifactId(), e.getVersion(), e);
1324 }
1325 }
1326
1327 @Override
1328 public ModelSource resolveModel(Session session, Parent parent, AtomicReference<Parent> modified)
1329 throws ModelResolverException {
1330 try {
1331 org.apache.maven.model.Parent p = new org.apache.maven.model.Parent(parent);
1332 InternalSession internalSession = InternalSession.from(session);
1333 org.apache.maven.model.resolution.ModelResolver resolver =
1334 getResolver(internalSession.toRepositories(internalSession.getRemoteRepositories()));
1335 ModelSource source = toSource(resolver.resolveModel(p));
1336 if (p.getDelegate() != parent) {
1337 modified.set(p.getDelegate());
1338 }
1339 return source;
1340 } catch (UnresolvableModelException e) {
1341 throw new ModelResolverException(e.getMessage(), e.getGroupId(), e.getArtifactId(), e.getVersion(), e);
1342 }
1343 }
1344
1345 @Override
1346 public ModelSource resolveModel(Session session, Dependency dependency, AtomicReference<Dependency> modified)
1347 throws ModelResolverException {
1348 try {
1349 org.apache.maven.model.Dependency d = new org.apache.maven.model.Dependency(dependency);
1350 InternalSession internalSession = InternalSession.from(session);
1351 org.apache.maven.model.resolution.ModelResolver resolver =
1352 getResolver(internalSession.toRepositories(internalSession.getRemoteRepositories()));
1353 ModelSource source = toSource(resolver.resolveModel(d));
1354 if (d.getDelegate() != dependency) {
1355 modified.set(d.getDelegate());
1356 }
1357 return source;
1358 } catch (UnresolvableModelException e) {
1359 throw new ModelResolverException(e.getMessage(), e.getGroupId(), e.getArtifactId(), e.getVersion(), e);
1360 }
1361 }
1362 }
1363 }