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