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