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