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