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.AbstractMap;
32  import java.util.ArrayList;
33  import java.util.Collections;
34  import java.util.HashMap;
35  import java.util.HashSet;
36  import java.util.LinkedHashSet;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.Objects;
40  import java.util.Properties;
41  import java.util.Set;
42  import java.util.concurrent.ConcurrentHashMap;
43  import java.util.function.Function;
44  import java.util.function.Supplier;
45  import java.util.stream.Collectors;
46  import java.util.stream.Stream;
47  
48  import org.apache.maven.RepositoryUtils;
49  import org.apache.maven.api.ArtifactCoordinates;
50  import org.apache.maven.api.Language;
51  import org.apache.maven.api.LocalRepository;
52  import org.apache.maven.api.ProjectScope;
53  import org.apache.maven.api.SessionData;
54  import org.apache.maven.api.annotations.Nonnull;
55  import org.apache.maven.api.annotations.Nullable;
56  import org.apache.maven.api.model.Build;
57  import org.apache.maven.api.model.Dependency;
58  import org.apache.maven.api.model.DependencyManagement;
59  import org.apache.maven.api.model.DeploymentRepository;
60  import org.apache.maven.api.model.Extension;
61  import org.apache.maven.api.model.Model;
62  import org.apache.maven.api.model.Plugin;
63  import org.apache.maven.api.model.Profile;
64  import org.apache.maven.api.model.ReportPlugin;
65  import org.apache.maven.api.model.Resource;
66  import org.apache.maven.api.services.ArtifactResolver;
67  import org.apache.maven.api.services.ArtifactResolverException;
68  import org.apache.maven.api.services.ArtifactResolverRequest;
69  import org.apache.maven.api.services.ArtifactResolverResult;
70  import org.apache.maven.api.services.BuilderProblem.Severity;
71  import org.apache.maven.api.services.ModelBuilder;
72  import org.apache.maven.api.services.ModelBuilderException;
73  import org.apache.maven.api.services.ModelBuilderRequest;
74  import org.apache.maven.api.services.ModelBuilderResult;
75  import org.apache.maven.api.services.ModelProblem;
76  import org.apache.maven.api.services.ModelProblem.Version;
77  import org.apache.maven.api.services.ModelProblemCollector;
78  import org.apache.maven.api.services.ModelSource;
79  import org.apache.maven.api.services.ModelTransformer;
80  import org.apache.maven.api.services.ProblemCollector;
81  import org.apache.maven.api.services.Source;
82  import org.apache.maven.api.services.Sources;
83  import org.apache.maven.api.services.model.LifecycleBindingsInjector;
84  import org.apache.maven.artifact.Artifact;
85  import org.apache.maven.artifact.InvalidRepositoryException;
86  import org.apache.maven.artifact.repository.ArtifactRepository;
87  import org.apache.maven.bridge.MavenRepositorySystem;
88  import org.apache.maven.impl.DefaultSourceRoot;
89  import org.apache.maven.impl.InternalSession;
90  import org.apache.maven.impl.resolver.ArtifactDescriptorUtils;
91  import org.apache.maven.internal.impl.InternalMavenSession;
92  import org.apache.maven.model.building.DefaultModelProblem;
93  import org.apache.maven.model.building.FileModelSource;
94  import org.apache.maven.model.building.ModelBuildingRequest;
95  import org.apache.maven.model.building.ModelSource2;
96  import org.apache.maven.model.root.RootLocator;
97  import org.apache.maven.plugin.PluginManagerException;
98  import org.apache.maven.plugin.PluginResolutionException;
99  import org.apache.maven.plugin.version.PluginVersionResolutionException;
100 import org.eclipse.aether.RepositorySystem;
101 import org.eclipse.aether.RepositorySystemSession;
102 import org.eclipse.aether.repository.LocalRepositoryManager;
103 import org.slf4j.Logger;
104 import org.slf4j.LoggerFactory;
105 
106 /**
107  * DefaultProjectBuilder
108  *
109  * @deprecated use {@code org.apache.maven.api.services.ProjectBuilder} instead
110  */
111 @Deprecated(since = "4.0.0")
112 @Named
113 @Singleton
114 public class DefaultProjectBuilder implements ProjectBuilder {
115 
116     private final Logger logger = LoggerFactory.getLogger(getClass());
117     private final ModelBuilder modelBuilder;
118     private final ProjectBuildingHelper projectBuildingHelper;
119     private final MavenRepositorySystem repositorySystem;
120     private final ProjectDependenciesResolver dependencyResolver;
121     private final RootLocator rootLocator;
122     private final LifecycleBindingsInjector lifecycleBindingsInjector;
123 
124     @SuppressWarnings("checkstyle:ParameterNumber")
125     @Inject
126     public DefaultProjectBuilder(
127             ModelBuilder modelBuilder,
128             ProjectBuildingHelper projectBuildingHelper,
129             MavenRepositorySystem repositorySystem,
130             RepositorySystem repoSystem,
131             ProjectDependenciesResolver dependencyResolver,
132             RootLocator rootLocator,
133             LifecycleBindingsInjector lifecycleBindingsInjector) {
134         this.modelBuilder = modelBuilder;
135         this.projectBuildingHelper = projectBuildingHelper;
136         this.repositorySystem = repositorySystem;
137         this.dependencyResolver = dependencyResolver;
138         this.rootLocator = rootLocator;
139         this.lifecycleBindingsInjector = lifecycleBindingsInjector;
140     }
141     // ----------------------------------------------------------------------
142     // MavenProjectBuilder Implementation
143     // ----------------------------------------------------------------------
144 
145     @Override
146     public ProjectBuildingResult build(File pomFile, ProjectBuildingRequest request) throws ProjectBuildingException {
147         try (BuildSession bs = new BuildSession(request)) {
148             Path path = pomFile.toPath();
149             return bs.build(false, path, Sources.buildSource(path));
150         }
151     }
152 
153     @Deprecated
154     @Override
155     public ProjectBuildingResult build(
156             org.apache.maven.model.building.ModelSource modelSource, ProjectBuildingRequest request)
157             throws ProjectBuildingException {
158         return build(toSource(modelSource), request);
159     }
160 
161     @Deprecated
162     static ModelSource toSource(org.apache.maven.model.building.ModelSource modelSource) {
163         if (modelSource instanceof FileModelSource fms) {
164             return Sources.buildSource(fms.getPath());
165         } else {
166             return new WrapModelSource(modelSource);
167         }
168     }
169 
170     @Override
171     public ProjectBuildingResult build(ModelSource modelSource, ProjectBuildingRequest request)
172             throws ProjectBuildingException {
173         try (BuildSession bs = new BuildSession(request)) {
174             return bs.build(false, null, modelSource);
175         }
176     }
177 
178     @Override
179     public ProjectBuildingResult build(Artifact artifact, ProjectBuildingRequest request)
180             throws ProjectBuildingException {
181         return build(artifact, false, request);
182     }
183 
184     @Override
185     public ProjectBuildingResult build(Artifact artifact, boolean allowStubModel, ProjectBuildingRequest request)
186             throws ProjectBuildingException {
187         try (BuildSession bs = new BuildSession(request)) {
188             return bs.build(false, artifact, allowStubModel, request.getRemoteRepositories());
189         }
190     }
191 
192     @Override
193     public List<ProjectBuildingResult> build(List<File> pomFiles, boolean recursive, ProjectBuildingRequest request)
194             throws ProjectBuildingException {
195         try (BuildSession bs = new BuildSession(request)) {
196             return bs.build(pomFiles, recursive);
197         }
198     }
199 
200     private static class StubModelSource implements ModelSource {
201         private final String xml;
202         private final Artifact artifact;
203 
204         StubModelSource(String xml, Artifact artifact) {
205             this.xml = xml;
206             this.artifact = artifact;
207         }
208 
209         @Override
210         @Nullable
211         public ModelSource resolve(@Nonnull ModelLocator modelLocator, @Nonnull String relative) {
212             return null;
213         }
214 
215         @Override
216         @Nullable
217         public Path getPath() {
218             return null;
219         }
220 
221         @Override
222         @Nonnull
223         public InputStream openStream() throws IOException {
224             return new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8));
225         }
226 
227         @Override
228         @Nonnull
229         public String getLocation() {
230             return artifact.getId();
231         }
232 
233         @Override
234         @Nullable
235         public Source resolve(@Nonnull String relative) {
236             return null;
237         }
238 
239         @Override
240         public boolean equals(Object o) {
241             if (this == o) {
242                 return true;
243             }
244             if (o == null || getClass() != o.getClass()) {
245                 return false;
246             }
247             StubModelSource that = (StubModelSource) o;
248             return Objects.equals(xml, that.xml) && Objects.equals(artifact, that.artifact);
249         }
250 
251         @Override
252         public int hashCode() {
253             return Objects.hash(xml, artifact);
254         }
255     }
256 
257     private static class WrapModelSource implements ModelSource {
258         private final org.apache.maven.model.building.ModelSource modelSource;
259 
260         WrapModelSource(org.apache.maven.model.building.ModelSource modelSource) {
261             this.modelSource = modelSource;
262         }
263 
264         @Override
265         @Nullable
266         public ModelSource resolve(@Nonnull ModelLocator modelLocator, @Nonnull String relative) {
267             return null;
268         }
269 
270         @Override
271         @Nullable
272         public Path getPath() {
273             return null;
274         }
275 
276         @Override
277         @Nonnull
278         public InputStream openStream() throws IOException {
279             return modelSource.getInputStream();
280         }
281 
282         @Override
283         @Nonnull
284         public String getLocation() {
285             return modelSource.getLocation();
286         }
287 
288         @Override
289         @Nullable
290         public Source resolve(@Nonnull String relative) {
291             if (modelSource instanceof ModelSource2 ms) {
292                 return toSource(ms.getRelatedSource(relative));
293             } else {
294                 return null;
295             }
296         }
297 
298         @Override
299         public boolean equals(Object o) {
300             if (this == o) {
301                 return true;
302             }
303             if (o == null || getClass() != o.getClass()) {
304                 return false;
305             }
306             WrapModelSource that = (WrapModelSource) o;
307             return Objects.equals(modelSource, that.modelSource);
308         }
309 
310         @Override
311         public int hashCode() {
312             return Objects.hashCode(modelSource);
313         }
314     }
315 
316     class BuildSession implements AutoCloseable {
317         private final ProjectBuildingRequest request;
318         private final InternalSession session;
319         private final ModelBuilder.ModelBuilderSession modelBuilderSession;
320         private final Map<String, MavenProject> projectIndex = new ConcurrentHashMap<>(256);
321 
322         // Store computed repositories per project to avoid leakage between projects
323         private final Map<String, List<ArtifactRepository>> projectRepositories = new ConcurrentHashMap<>();
324 
325         /**
326          * Get the effective repositories for a project. If project-specific repositories
327          * have been computed and stored, use those; otherwise fall back to request repositories.
328          */
329         private List<ArtifactRepository> getEffectiveRepositories(String projectId) {
330             List<ArtifactRepository> stored = projectRepositories.get(projectId);
331             return stored != null ? stored : request.getRemoteRepositories();
332         }
333 
334         BuildSession(ProjectBuildingRequest request) {
335             this.request = request;
336             InternalSession session = InternalSession.from(request.getRepositorySession());
337             Path basedir = request.getLocalRepository() != null
338                     ? request.getLocalRepository().getBasedirPath()
339                     : null;
340             if (basedir != null) {
341                 LocalRepository localRepository = session.createLocalRepository(basedir);
342                 session = InternalSession.from(session.withLocalRepository(localRepository));
343             }
344             this.session = session;
345             this.modelBuilderSession = modelBuilder.newSession();
346             // Save the ModelBuilderSession for later retrieval by the DefaultConsumerPomBuilder.
347             // Use replace(key, null, value) to make sure the *main* session, i.e. the one used
348             // to load the projects, is stored. This is to avoid the session being overwritten
349             // if a plugin uses the ProjectBuilder.
350             this.session
351                     .getData()
352                     .replace(SessionData.key(ModelBuilder.ModelBuilderSession.class), null, modelBuilderSession);
353         }
354 
355         @Override
356         public void close() {}
357 
358         ProjectBuildingResult build(boolean parent, Path pomFile, ModelSource modelSource)
359                 throws ProjectBuildingException {
360             ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
361 
362             try {
363                 MavenProject project = request.getProject();
364 
365                 ProblemCollector<ModelProblem> problemCollector = null;
366                 Throwable error = null;
367 
368                 if (project == null) {
369                     project = new MavenProject();
370                     project.setFile(pomFile != null ? pomFile.toFile() : null);
371 
372                     boolean reactorMember = pomFile != null
373                             && session.getProjects() != null // this is for UTs
374                             && session.getProjects().stream()
375                                     .anyMatch(
376                                             p -> p.getPomPath().toAbsolutePath().equals(pomFile.toAbsolutePath()));
377                     boolean isStandalone = pomFile == null
378                             && modelSource != null
379                             && modelSource.getLocation().startsWith("jar:")
380                             && modelSource.getLocation().endsWith("/org/apache/maven/project/standalone.xml");
381 
382                     ModelBuilderRequest.ModelBuilderRequestBuilder builder = getModelBuildingRequest();
383                     ModelBuilderRequest.RequestType type = reactorMember
384                                     || isStandalone
385                                     || (pomFile != null
386                                             && this.request.isProcessPlugins()
387                                             && this.request.getValidationLevel()
388                                                     == ModelBuildingRequest.VALIDATION_LEVEL_STRICT)
389                             ? ModelBuilderRequest.RequestType.BUILD_EFFECTIVE
390                             : (parent
391                                     ? ModelBuilderRequest.RequestType.CONSUMER_PARENT
392                                     : ModelBuilderRequest.RequestType.CONSUMER_DEPENDENCY);
393                     MavenProject theProject = project;
394                     ModelBuilderRequest request = builder.source(modelSource)
395                             .requestType(type)
396                             .locationTracking(true)
397                             .lifecycleBindingsInjector(
398                                     (m, r, p) -> injectLifecycleBindings(m, r, p, theProject, this.request))
399                             .build();
400 
401                     if (pomFile != null) {
402                         project.setRootDirectory(rootLocator.findRoot(pomFile.getParent()));
403                     }
404 
405                     ModelBuilderResult result;
406                     try {
407                         result = modelBuilderSession.build(request);
408                     } catch (ModelBuilderException e) {
409                         result = e.getResult();
410                         if (result == null || result.getEffectiveModel() == null) {
411                             throw new ProjectBuildingException(
412                                     e.getModelId(), e.getMessage(), pomFile != null ? pomFile.toFile() : null, e);
413                         }
414                         // validation error, continue project building and delay failing to help IDEs
415                         error = e;
416                     }
417 
418                     problemCollector = result.getProblemCollector();
419 
420                     initProject(project, result);
421                 }
422 
423                 DependencyResolutionResult resolutionResult = null;
424 
425                 if (request.isResolveDependencies()) {
426                     projectBuildingHelper.selectProjectRealm(project);
427                     resolutionResult = resolveDependencies(project);
428                 }
429 
430                 ProjectBuildingResult result =
431                         new DefaultProjectBuildingResult(project, convert(problemCollector), resolutionResult);
432 
433                 if (error != null) {
434                     ProjectBuildingException e = new ProjectBuildingException(List.of(result));
435                     e.initCause(error);
436                     throw e;
437                 }
438 
439                 return result;
440             } finally {
441                 Thread.currentThread().setContextClassLoader(oldContextClassLoader);
442             }
443         }
444 
445         ProjectBuildingResult build(
446                 boolean parent, Artifact artifact, boolean allowStubModel, List<ArtifactRepository> repositories)
447                 throws ProjectBuildingException {
448             org.eclipse.aether.artifact.Artifact pomArtifact = RepositoryUtils.toArtifact(artifact);
449             pomArtifact = ArtifactDescriptorUtils.toPomArtifact(pomArtifact);
450 
451             boolean localProject;
452 
453             try {
454                 ArtifactCoordinates coordinates = session.createArtifactCoordinates(session.getArtifact(pomArtifact));
455                 // Use provided repositories if available, otherwise fall back to request repositories
456                 ArtifactResolverRequest req = ArtifactResolverRequest.builder()
457                         .session(session)
458                         .repositories(repositories.stream()
459                                 .map(RepositoryUtils::toRepo)
460                                 .map(session::getRemoteRepository)
461                                 .toList())
462                         .coordinates(List.of(coordinates))
463                         .build();
464                 ArtifactResolverResult res =
465                         session.getService(ArtifactResolver.class).resolve(req);
466                 ArtifactResolverResult.ResultItem resItem = res.getResult(coordinates);
467 
468                 pomArtifact = InternalMavenSession.from(session).toArtifact(resItem.getArtifact());
469                 localProject = resItem.getRepository() instanceof org.apache.maven.api.WorkspaceRepository;
470             } catch (ArtifactResolverException e) {
471                 if (e.getResult().getResults().values().iterator().next().isMissing() && allowStubModel) {
472                     return build(parent, null, createStubModelSource(artifact));
473                 }
474                 throw new ProjectBuildingException(
475                         artifact.getId(), "Error resolving project artifact: " + e.getMessage(), e);
476             }
477 
478             Path pomFile = pomArtifact.getPath();
479 
480             if (!artifact.isResolved() && "pom".equals(artifact.getType())) {
481                 artifact.selectVersion(pomArtifact.getVersion());
482                 artifact.setFile(pomFile.toFile());
483                 artifact.setResolved(true);
484             }
485 
486             if (localProject) {
487                 return build(parent, pomFile, Sources.buildSource(pomFile));
488             } else {
489                 return build(
490                         parent,
491                         null,
492                         Sources.resolvedSource(
493                                 pomFile,
494                                 artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion()));
495             }
496         }
497 
498         List<ProjectBuildingResult> build(List<File> pomFiles, boolean recursive) throws ProjectBuildingException {
499             List<ProjectBuildingResult> results = doBuild(pomFiles, recursive);
500             if (results.stream()
501                     .flatMap(r -> r.getProblems().stream())
502                     .anyMatch(p -> p.getSeverity() != org.apache.maven.model.building.ModelProblem.Severity.WARNING)) {
503                 org.apache.maven.model.building.ModelProblem cycle = results.stream()
504                         .flatMap(r -> r.getProblems().stream())
505                         .filter(p -> p.getException() instanceof CycleDetectedException)
506                         .findAny()
507                         .orElse(null);
508                 if (cycle != null) {
509                     final CycleDetectedException cde = (CycleDetectedException) cycle.getException();
510                     throw new ProjectBuildingException(
511                             null,
512                             "The projects in the reactor contain a cyclic reference: " + cycle.getMessage(),
513                             null,
514                             cde);
515                 }
516 
517                 throw new ProjectBuildingException(results);
518             }
519 
520             return results;
521         }
522 
523         List<ProjectBuildingResult> doBuild(List<File> pomFiles, boolean recursive) {
524             ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
525             try {
526                 return pomFiles.stream()
527                         .map(pomFile -> build(pomFile, recursive))
528                         .flatMap(List::stream)
529                         .collect(Collectors.toList());
530             } finally {
531                 Thread.currentThread().setContextClassLoader(oldContextClassLoader);
532             }
533         }
534 
535         @SuppressWarnings("checkstyle:parameternumber")
536         private List<ProjectBuildingResult> build(File pomFile, boolean recursive) {
537             ModelBuilderResult result;
538             try {
539                 ModelTransformer injector = (m, r, p) -> {
540                     MavenProject project = projectIndex.computeIfAbsent(m.getId(), f -> new MavenProject());
541                     return injectLifecycleBindings(m, r, p, project, request);
542                 };
543                 ModelBuilderRequest modelBuildingRequest = getModelBuildingRequest()
544                         .source(Sources.buildSource(pomFile.toPath()))
545                         .requestType(ModelBuilderRequest.RequestType.BUILD_PROJECT)
546                         .locationTracking(true)
547                         .recursive(recursive)
548                         .lifecycleBindingsInjector(injector)
549                         .build();
550                 result = modelBuilderSession.build(modelBuildingRequest);
551             } catch (ModelBuilderException e) {
552                 result = e.getResult();
553                 if (result == null || result.getEffectiveModel() == null) {
554                     return List.of(new DefaultProjectBuildingResult(
555                             e.getModelId(), pomFile, convert(e.getProblemCollector())));
556                 }
557             }
558 
559             List<ProjectBuildingResult> results = new ArrayList<>();
560             List<ModelBuilderResult> allModels = results(result).toList();
561             for (ModelBuilderResult r : allModels) {
562                 if (r.getEffectiveModel() != null) {
563                     File pom = r.getSource().getPath().toFile();
564                     MavenProject project =
565                             projectIndex.get(r.getEffectiveModel().getId());
566                     Path rootDirectory =
567                             rootLocator.findRoot(pom.getParentFile().toPath());
568                     project.setRootDirectory(rootDirectory);
569                     project.setFile(pom);
570                     project.setExecutionRoot(pom.equals(pomFile));
571                     initProject(project, r);
572                     project.setCollectedProjects(results(r)
573                             .filter(cr -> cr != r && cr.getEffectiveModel() != null)
574                             .map(cr -> projectIndex.get(cr.getEffectiveModel().getId()))
575                             .collect(Collectors.toList()));
576 
577                     DependencyResolutionResult resolutionResult = null;
578                     if (request.isResolveDependencies()) {
579                         resolutionResult = resolveDependencies(project);
580                     }
581                     results.add(new DefaultProjectBuildingResult(
582                             project, convert(r.getProblemCollector()), resolutionResult));
583                 } else {
584                     // Extract project identification even when effective model is null
585                     String projectId = extractProjectId(r);
586                     File sourcePomFile = r.getSource() != null && r.getSource().getPath() != null
587                             ? r.getSource().getPath().toFile()
588                             : null;
589                     results.add(new DefaultProjectBuildingResult(
590                             projectId, sourcePomFile, convert(r.getProblemCollector())));
591                 }
592             }
593             return results;
594         }
595 
596         private Stream<ModelBuilderResult> results(ModelBuilderResult result) {
597             return Stream.concat(result.getChildren().stream().flatMap(this::results), Stream.of(result));
598         }
599 
600         private List<org.apache.maven.model.building.ModelProblem> convert(
601                 ProblemCollector<ModelProblem> problemCollector) {
602             if (problemCollector == null) {
603                 return null;
604             }
605             ArrayList<org.apache.maven.model.building.ModelProblem> problems = new ArrayList<>();
606             problemCollector.problems().map(BuildSession::convert).forEach(problems::add);
607             if (problemCollector.problemsOverflow()) {
608                 problems.add(
609                         0,
610                         new DefaultModelProblem(
611                                 "Too many model problems reported (listed problems are just a subset of reported problems)",
612                                 org.apache.maven.model.building.ModelProblem.Severity.WARNING,
613                                 null,
614                                 null,
615                                 -1,
616                                 -1,
617                                 null,
618                                 null));
619                 return new ArrayList<>(problems) {
620                     @Override
621                     public int size() {
622                         return problemCollector.totalProblemsReported();
623                     }
624                 };
625             } else {
626                 return problems;
627             }
628         }
629 
630         private static org.apache.maven.model.building.ModelProblem convert(ModelProblem p) {
631             return new DefaultModelProblem(
632                     p.getMessage(),
633                     org.apache.maven.model.building.ModelProblem.Severity.valueOf(
634                             p.getSeverity().name()),
635                     org.apache.maven.model.building.ModelProblem.Version.valueOf(
636                             p.getVersion().name()),
637                     p.getSource(),
638                     p.getLineNumber(),
639                     p.getColumnNumber(),
640                     p.getModelId(),
641                     p.getException());
642         }
643 
644         @SuppressWarnings({"checkstyle:methodlength", "deprecation"})
645         private void initProject(MavenProject project, ModelBuilderResult result) {
646             project.setModel(new org.apache.maven.model.Model(result.getEffectiveModel()));
647             project.setOriginalModel(new org.apache.maven.model.Model(result.getFileModel()));
648 
649             initParent(project, result);
650 
651             Artifact projectArtifact = repositorySystem.createArtifact(
652                     project.getGroupId(), project.getArtifactId(), project.getVersion(), null, project.getPackaging());
653             project.setArtifact(projectArtifact);
654 
655             // only set those on 2nd phase, ignore on 1st pass
656             if (project.getFile() != null) {
657                 Build build = project.getBuild().getDelegate();
658                 List<org.apache.maven.api.model.Source> sources = build.getSources();
659                 Path baseDir = project.getBaseDirectory();
660                 Function<ProjectScope, String> outputDirectory = (scope) -> {
661                     if (scope == ProjectScope.MAIN) {
662                         return build.getOutputDirectory();
663                     } else if (scope == ProjectScope.TEST) {
664                         return build.getTestOutputDirectory();
665                     } else {
666                         return build.getDirectory();
667                     }
668                 };
669                 boolean hasScript = false;
670                 boolean hasMain = false;
671                 boolean hasTest = false;
672                 for (var source : sources) {
673                     var src = DefaultSourceRoot.fromModel(session, baseDir, outputDirectory, source);
674                     project.addSourceRoot(src);
675                     Language language = src.language();
676                     if (Language.JAVA_FAMILY.equals(language)) {
677                         ProjectScope scope = src.scope();
678                         if (ProjectScope.MAIN.equals(scope)) {
679                             hasMain = true;
680                         } else {
681                             hasTest |= ProjectScope.TEST.equals(scope);
682                         }
683                     } else {
684                         hasScript |= Language.SCRIPT.equals(language);
685                     }
686                 }
687                 /*
688                  * `sourceDirectory`, `testSourceDirectory` and `scriptSourceDirectory`
689                  * are ignored if the POM file contains at least one <source> element
690                  * for the corresponding scope and language. This rule exists because
691                  * Maven provides default values for those elements which may conflict
692                  * with user's configuration.
693                  */
694                 if (!hasScript) {
695                     project.addScriptSourceRoot(build.getScriptSourceDirectory());
696                 }
697                 if (!hasMain) {
698                     project.addCompileSourceRoot(build.getSourceDirectory());
699                 }
700                 if (!hasTest) {
701                     project.addTestCompileSourceRoot(build.getTestSourceDirectory());
702                 }
703                 for (Resource resource : project.getBuild().getDelegate().getResources()) {
704                     project.addSourceRoot(new DefaultSourceRoot(baseDir, ProjectScope.MAIN, resource));
705                 }
706                 for (Resource resource : project.getBuild().getDelegate().getTestResources()) {
707                     project.addSourceRoot(new DefaultSourceRoot(baseDir, ProjectScope.TEST, resource));
708                 }
709             }
710 
711             project.setActiveProfiles(
712                     Stream.concat(result.getActivePomProfiles().stream(), result.getActiveExternalProfiles().stream())
713                             .map(org.apache.maven.model.Profile::new)
714                             .toList());
715 
716             project.setInjectedProfileIds("external", getProfileIds(result.getActiveExternalProfiles()));
717             project.setInjectedProfileIds(
718                     result.getEffectiveModel().getId(), getProfileIds(result.getActivePomProfiles()));
719 
720             //
721             // All the parts that were taken out of MavenProject for Maven 4.0.0
722             //
723 
724             project.setProjectBuildingRequest(request);
725 
726             // pluginArtifacts
727             Set<Artifact> pluginArtifacts = new HashSet<>();
728             for (Plugin plugin : project.getModel().getDelegate().getBuild().getPlugins()) {
729                 Artifact artifact = repositorySystem.createPluginArtifact(new org.apache.maven.model.Plugin(plugin));
730 
731                 if (artifact != null) {
732                     pluginArtifacts.add(artifact);
733                 }
734             }
735             project.setPluginArtifacts(pluginArtifacts);
736 
737             // reportArtifacts
738             Set<Artifact> reportArtifacts = new HashSet<>();
739             for (ReportPlugin report :
740                     project.getModel().getDelegate().getReporting().getPlugins()) {
741                 Plugin pp = Plugin.newBuilder()
742                         .groupId(report.getGroupId())
743                         .artifactId(report.getArtifactId())
744                         .version(report.getVersion())
745                         .build();
746 
747                 Artifact artifact = repositorySystem.createPluginArtifact(new org.apache.maven.model.Plugin(pp));
748 
749                 if (artifact != null) {
750                     reportArtifacts.add(artifact);
751                 }
752             }
753             project.setReportArtifacts(reportArtifacts);
754 
755             // extensionArtifacts
756             Set<Artifact> extensionArtifacts = new HashSet<>();
757             List<Extension> extensions =
758                     project.getModel().getDelegate().getBuild().getExtensions();
759             if (extensions != null) {
760                 for (Extension ext : extensions) {
761                     String version;
762                     if (ext.getVersion() == null || ext.getVersion().isEmpty()) {
763                         version = "RELEASE";
764                     } else {
765                         version = ext.getVersion();
766                     }
767 
768                     Artifact artifact = repositorySystem.createArtifact(
769                             ext.getGroupId(), ext.getArtifactId(), version, null, "jar");
770 
771                     if (artifact != null) {
772                         extensionArtifacts.add(artifact);
773                     }
774                 }
775             }
776             project.setExtensionArtifacts(extensionArtifacts);
777 
778             // managedVersionMap
779             Map<String, Artifact> map = Collections.emptyMap();
780             final DependencyManagement dependencyManagement =
781                     project.getModel().getDelegate().getDependencyManagement();
782             if (dependencyManagement != null
783                     && dependencyManagement.getDependencies() != null
784                     && !dependencyManagement.getDependencies().isEmpty()) {
785                 map = new LazyMap<>(() -> {
786                     Map<String, Artifact> tmp = new HashMap<>();
787                     for (Dependency d : dependencyManagement.getDependencies()) {
788                         Artifact artifact =
789                                 repositorySystem.createDependencyArtifact(new org.apache.maven.model.Dependency(d));
790                         if (artifact != null) {
791                             tmp.put(d.getManagementKey(), artifact);
792                         }
793                     }
794                     return Collections.unmodifiableMap(tmp);
795                 });
796             }
797             project.setManagedVersionMap(map);
798 
799             // release artifact repository
800             if (project.getDistributionManagement() != null
801                     && project.getDistributionManagement().getRepository() != null) {
802                 try {
803                     DeploymentRepository r = project.getModel()
804                             .getDelegate()
805                             .getDistributionManagement()
806                             .getRepository();
807                     if (r.getId() != null
808                             && !r.getId().isEmpty()
809                             && r.getUrl() != null
810                             && !r.getUrl().isEmpty()) {
811                         ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository(
812                                 new org.apache.maven.model.DeploymentRepository(r));
813                         repositorySystem.injectProxy(request.getRepositorySession(), List.of(repo));
814                         repositorySystem.injectAuthentication(request.getRepositorySession(), List.of(repo));
815                         project.setReleaseArtifactRepository(repo);
816                     }
817                 } catch (InvalidRepositoryException e) {
818                     throw new IllegalStateException(
819                             "Failed to create release distribution repository for " + project.getId(), e);
820                 }
821             }
822 
823             // snapshot artifact repository
824             if (project.getDistributionManagement() != null
825                     && project.getDistributionManagement().getSnapshotRepository() != null) {
826                 try {
827                     DeploymentRepository r = project.getModel()
828                             .getDelegate()
829                             .getDistributionManagement()
830                             .getSnapshotRepository();
831                     if (r.getId() != null
832                             && !r.getId().isEmpty()
833                             && r.getUrl() != null
834                             && !r.getUrl().isEmpty()) {
835                         ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository(
836                                 new org.apache.maven.model.DeploymentRepository(r));
837                         repositorySystem.injectProxy(request.getRepositorySession(), List.of(repo));
838                         repositorySystem.injectAuthentication(request.getRepositorySession(), List.of(repo));
839                         project.setSnapshotArtifactRepository(repo);
840                     }
841                 } catch (InvalidRepositoryException e) {
842                     throw new IllegalStateException(
843                             "Failed to create snapshot distribution repository for " + project.getId(), e);
844                 }
845             }
846 
847             // remote repositories
848             List<ArtifactRepository> remoteRepositories = request.getRemoteRepositories();
849             try {
850                 remoteRepositories = projectBuildingHelper.createArtifactRepositories(
851                         project.getModel().getRepositories(), remoteRepositories, request);
852             } catch (Exception e) {
853                 result.getProblemCollector()
854                         .reportProblem(new org.apache.maven.impl.model.DefaultModelProblem(
855                                 "",
856                                 Severity.ERROR,
857                                 Version.BASE,
858                                 project.getModel().getDelegate(),
859                                 -1,
860                                 -1,
861                                 e));
862             }
863             project.setRemoteArtifactRepositories(remoteRepositories);
864         }
865 
866         private void initParent(MavenProject project, ModelBuilderResult result) {
867             Model parentModel = result.getParentModel();
868 
869             if (parentModel != null) {
870                 final String parentGroupId = getGroupId(parentModel);
871                 final String parentVersion = getVersion(parentModel);
872 
873                 project.setParentArtifact(repositorySystem.createProjectArtifact(
874                         parentGroupId, parentModel.getArtifactId(), parentVersion));
875 
876                 MavenProject parent = projectIndex.get(parentModel.getId());
877                 if (parent == null) {
878                     //
879                     // At this point the DefaultModelBuildingListener has fired, and it populates the
880                     // remote repositories with those found in the pom.xml, along with the existing externally
881                     // defined repositories.
882                     //
883                     // Compute merged repositories for this project and store in session
884                     // instead of mutating the shared request to avoid leakage between projects
885                     List<ArtifactRepository> mergedRepositories;
886                     switch (request.getRepositoryMerging()) {
887                         case POM_DOMINANT -> {
888                             LinkedHashSet<ArtifactRepository> reposes =
889                                     new LinkedHashSet<>(project.getRemoteArtifactRepositories());
890                             reposes.addAll(request.getRemoteRepositories());
891                             mergedRepositories = List.copyOf(reposes);
892                         }
893                         case REQUEST_DOMINANT -> {
894                             LinkedHashSet<ArtifactRepository> reposes =
895                                     new LinkedHashSet<>(request.getRemoteRepositories());
896                             reposes.addAll(project.getRemoteArtifactRepositories());
897                             mergedRepositories = List.copyOf(reposes);
898                         }
899                         default ->
900                             throw new IllegalArgumentException(
901                                     "Unsupported repository merging: " + request.getRepositoryMerging());
902                     }
903 
904                     // Store the computed repositories for this project in BuildSession storage
905                     // to avoid mutating the shared request and causing leakage between projects
906                     projectRepositories.put(project.getId(), mergedRepositories);
907 
908                     Path parentPomFile = parentModel.getPomFile();
909                     if (parentPomFile != null) {
910                         project.setParentFile(parentPomFile.toFile());
911                         try {
912                             parent = build(true, parentPomFile, Sources.buildSource(parentPomFile))
913                                     .getProject();
914                         } catch (ProjectBuildingException e) {
915                             // MNG-4488 where let invalid parents slide on by
916                             if (logger.isDebugEnabled()) {
917                                 // Message below is checked for in the MNG-2199 core IT.
918                                 logger.warn("Failed to build parent project for " + project.getId(), e);
919                             } else {
920                                 // Message below is checked for in the MNG-2199 core IT.
921                                 logger.warn("Failed to build parent project for " + project.getId());
922                             }
923                         }
924                     } else {
925                         Artifact parentArtifact = project.getParentArtifact();
926                         try {
927                             parent = build(true, parentArtifact, false, getEffectiveRepositories(project.getId()))
928                                     .getProject();
929                         } catch (ProjectBuildingException e) {
930                             // MNG-4488 where let invalid parents slide on by
931                             if (logger.isDebugEnabled()) {
932                                 // Message below is checked for in the MNG-2199 core IT.
933                                 logger.warn("Failed to build parent project for " + project.getId(), e);
934                             } else {
935                                 // Message below is checked for in the MNG-2199 core IT.
936                                 logger.warn("Failed to build parent project for " + project.getId());
937                             }
938                         }
939                     }
940                 }
941                 project.setParent(parent);
942                 if (project.getParentFile() == null && parent != null) {
943                     project.setParentFile(parent.getFile());
944                 }
945             }
946         }
947 
948         private ModelBuilderRequest.ModelBuilderRequestBuilder getModelBuildingRequest() {
949             ModelBuilderRequest.ModelBuilderRequestBuilder modelBuildingRequest = ModelBuilderRequest.builder();
950 
951             modelBuildingRequest.session(session);
952             modelBuildingRequest.requestType(ModelBuilderRequest.RequestType.BUILD_PROJECT);
953             modelBuildingRequest.profiles(
954                     request.getProfiles() != null
955                             ? request.getProfiles().stream()
956                                     .map(org.apache.maven.model.Profile::getDelegate)
957                                     .toList()
958                             : null);
959             modelBuildingRequest.activeProfileIds(request.getActiveProfileIds());
960             modelBuildingRequest.inactiveProfileIds(request.getInactiveProfileIds());
961             modelBuildingRequest.systemProperties(toMap(request.getSystemProperties()));
962             modelBuildingRequest.userProperties(toMap(request.getUserProperties()));
963             modelBuildingRequest.repositoryMerging(ModelBuilderRequest.RepositoryMerging.valueOf(
964                     request.getRepositoryMerging().name()));
965             modelBuildingRequest.repositories(request.getRemoteRepositories().stream()
966                     .map(r -> session.getRemoteRepository(RepositoryUtils.toRepo(r)))
967                     .toList());
968             return modelBuildingRequest;
969         }
970 
971         private DependencyResolutionResult resolveDependencies(MavenProject project) {
972             DependencyResolutionResult resolutionResult;
973 
974             RepositorySystemSession session = this.session.getSession();
975             try {
976                 DefaultDependencyResolutionRequest resolution =
977                         new DefaultDependencyResolutionRequest(project, session);
978                 resolutionResult = dependencyResolver.resolve(resolution);
979             } catch (DependencyResolutionException e) {
980                 resolutionResult = e.getResult();
981             }
982 
983             Set<Artifact> artifacts = new LinkedHashSet<>();
984             if (resolutionResult.getDependencyGraph() != null) {
985                 RepositoryUtils.toArtifacts(
986                         artifacts,
987                         resolutionResult.getDependencyGraph().getChildren(),
988                         Collections.singletonList(project.getArtifact().getId()),
989                         null);
990 
991                 // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not
992                 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
993                 for (Artifact artifact : artifacts) {
994                     if (!artifact.isResolved()) {
995                         Path path = lrm.getAbsolutePathForLocalArtifact(RepositoryUtils.toArtifact(artifact));
996                         artifact.setFile(path.toFile());
997                     }
998                 }
999             }
1000             project.setResolvedArtifacts(artifacts);
1001             project.setArtifacts(artifacts);
1002 
1003             return resolutionResult;
1004         }
1005     }
1006 
1007     private List<String> getProfileIds(List<Profile> profiles) {
1008         return profiles.stream().map(Profile::getId).collect(Collectors.toList());
1009     }
1010 
1011     private static ModelSource createStubModelSource(Artifact artifact) {
1012         String xml = "<?xml version='1.0'?>" + "<project>"
1013                 + "<modelVersion>4.0.0</modelVersion>"
1014                 + "<groupId>"
1015                 + artifact.getGroupId() + "</groupId>" + "<artifactId>"
1016                 + artifact.getArtifactId() + "</artifactId>" + "<version>"
1017                 + artifact.getBaseVersion() + "</version>" + "<packaging>"
1018                 + artifact.getType() + "</packaging>" + "</project>";
1019         return new StubModelSource(xml, artifact);
1020     }
1021 
1022     /**
1023      * Extracts project identification from ModelBuilderResult, falling back to rawModel or fileModel
1024      * when effectiveModel is null, similar to ModelBuilderException.getModelId().
1025      */
1026     private static String extractProjectId(ModelBuilderResult result) {
1027         Model model = null;
1028         if (result.getEffectiveModel() != null) {
1029             model = result.getEffectiveModel();
1030         } else if (result.getRawModel() != null) {
1031             model = result.getRawModel();
1032         } else if (result.getFileModel() != null) {
1033             model = result.getFileModel();
1034         }
1035 
1036         if (model != null) {
1037             return model.getId();
1038         }
1039 
1040         return "";
1041     }
1042 
1043     static String getGroupId(Model model) {
1044         String groupId = model.getGroupId();
1045         if (groupId == null && model.getParent() != null) {
1046             groupId = model.getParent().getGroupId();
1047         }
1048         return groupId;
1049     }
1050 
1051     static String getVersion(Model model) {
1052         String version = model.getVersion();
1053         if (version == null && model.getParent() != null) {
1054             version = model.getParent().getVersion();
1055         }
1056         return version;
1057     }
1058 
1059     private static Map<String, String> toMap(Properties properties) {
1060         if (properties != null && !properties.isEmpty()) {
1061             return properties.entrySet().stream()
1062                     .collect(Collectors.toMap(e -> String.valueOf(e.getKey()), e -> String.valueOf(e.getValue())));
1063         } else {
1064             return null;
1065         }
1066     }
1067 
1068     static class LazyMap<K, V> extends AbstractMap<K, V> {
1069         private final Supplier<Map<K, V>> supplier;
1070         private volatile Map<K, V> delegate;
1071 
1072         LazyMap(Supplier<Map<K, V>> supplier) {
1073             this.supplier = supplier;
1074         }
1075 
1076         @Override
1077         public Set<Entry<K, V>> entrySet() {
1078             if (delegate == null) {
1079                 synchronized (this) {
1080                     if (delegate == null) {
1081                         delegate = supplier.get();
1082                     }
1083                 }
1084             }
1085             return delegate.entrySet();
1086         }
1087     }
1088 
1089     private Model injectLifecycleBindings(
1090             Model model,
1091             ModelBuilderRequest request,
1092             ModelProblemCollector problems,
1093             MavenProject project,
1094             ProjectBuildingRequest projectBuildingRequest) {
1095         org.apache.maven.model.Model model3 = new org.apache.maven.model.Model(model);
1096         List<ArtifactRepository> remoteRepositories = projectBuildingRequest.getRemoteRepositories();
1097         List<ArtifactRepository> pluginRepositories = projectBuildingRequest.getPluginArtifactRepositories();
1098         try {
1099             pluginRepositories = projectBuildingHelper.createArtifactRepositories(
1100                     model3.getPluginRepositories(), pluginRepositories, projectBuildingRequest);
1101         } catch (Exception e) {
1102             problems.add(Severity.ERROR, Version.BASE, "Invalid plugin repository: " + e.getMessage(), e);
1103         }
1104         project.setPluginArtifactRepositories(pluginRepositories);
1105 
1106         if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_PROJECT) {
1107             try {
1108                 ProjectRealmCache.CacheRecord record =
1109                         projectBuildingHelper.createProjectRealm(project, model3, projectBuildingRequest);
1110 
1111                 project.setClassRealm(record.getRealm());
1112                 project.setExtensionDependencyFilter(record.getExtensionArtifactFilter());
1113             } catch (PluginResolutionException | PluginManagerException | PluginVersionResolutionException e) {
1114 
1115                 problems.add(Severity.ERROR, Version.BASE, "Unresolvable build extension: " + e.getMessage(), e);
1116             }
1117             projectBuildingHelper.selectProjectRealm(project);
1118         }
1119 
1120         // (re)build the regular repos after extensions are loaded to allow for custom layouts
1121         try {
1122             remoteRepositories = projectBuildingHelper.createArtifactRepositories(
1123                     model3.getRepositories(), remoteRepositories, projectBuildingRequest);
1124         } catch (Exception e) {
1125             problems.add(Severity.ERROR, Version.BASE, "Invalid artifact repository: " + e.getMessage(), e);
1126         }
1127         project.setRemoteArtifactRepositories(remoteRepositories);
1128 
1129         if (projectBuildingRequest.isProcessPlugins()) {
1130             return lifecycleBindingsInjector.injectLifecycleBindings(model3.getDelegate(), request, problems);
1131         } else {
1132             return model3.getDelegate();
1133         }
1134     }
1135 }