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.File;
26  import java.io.IOException;
27  import java.util.*;
28  import java.util.concurrent.*;
29  import java.util.concurrent.atomic.AtomicInteger;
30  import java.util.function.Supplier;
31  import java.util.stream.Collectors;
32  
33  import org.apache.maven.ProjectCycleException;
34  import org.apache.maven.RepositoryUtils;
35  import org.apache.maven.api.feature.Features;
36  import org.apache.maven.artifact.Artifact;
37  import org.apache.maven.artifact.InvalidArtifactRTException;
38  import org.apache.maven.artifact.InvalidRepositoryException;
39  import org.apache.maven.artifact.repository.ArtifactRepository;
40  import org.apache.maven.bridge.MavenRepositorySystem;
41  import org.apache.maven.internal.impl.InternalSession;
42  import org.apache.maven.model.Build;
43  import org.apache.maven.model.Dependency;
44  import org.apache.maven.model.DependencyManagement;
45  import org.apache.maven.model.DeploymentRepository;
46  import org.apache.maven.model.Extension;
47  import org.apache.maven.model.Model;
48  import org.apache.maven.model.Plugin;
49  import org.apache.maven.model.Profile;
50  import org.apache.maven.model.ReportPlugin;
51  import org.apache.maven.model.building.ArtifactModelSource;
52  import org.apache.maven.model.building.DefaultModelBuilder;
53  import org.apache.maven.model.building.DefaultModelBuildingRequest;
54  import org.apache.maven.model.building.DefaultModelProblem;
55  import org.apache.maven.model.building.FileModelSource;
56  import org.apache.maven.model.building.ModelBuilder;
57  import org.apache.maven.model.building.ModelBuildingException;
58  import org.apache.maven.model.building.ModelBuildingRequest;
59  import org.apache.maven.model.building.ModelBuildingResult;
60  import org.apache.maven.model.building.ModelProblem;
61  import org.apache.maven.model.building.ModelProcessor;
62  import org.apache.maven.model.building.ModelSource;
63  import org.apache.maven.model.building.StringModelSource;
64  import org.apache.maven.model.building.TransformerContext;
65  import org.apache.maven.model.building.TransformerContextBuilder;
66  import org.apache.maven.model.resolution.ModelResolver;
67  import org.apache.maven.model.root.RootLocator;
68  import org.apache.maven.repository.internal.ArtifactDescriptorUtils;
69  import org.apache.maven.repository.internal.ModelCacheFactory;
70  import org.apache.maven.utils.Os;
71  import org.eclipse.aether.RepositorySystem;
72  import org.eclipse.aether.RepositorySystemSession;
73  import org.eclipse.aether.RequestTrace;
74  import org.eclipse.aether.impl.RemoteRepositoryManager;
75  import org.eclipse.aether.repository.LocalRepositoryManager;
76  import org.eclipse.aether.repository.RemoteRepository;
77  import org.eclipse.aether.repository.WorkspaceRepository;
78  import org.eclipse.aether.resolution.ArtifactRequest;
79  import org.eclipse.aether.resolution.ArtifactResult;
80  import org.slf4j.Logger;
81  import org.slf4j.LoggerFactory;
82  
83  /**
84   * DefaultProjectBuilder
85   */
86  @Named
87  @Singleton
88  public class DefaultProjectBuilder implements ProjectBuilder {
89      public static final String BUILDER_PARALLELISM = "maven.projectBuilder.parallelism";
90      public static final int DEFAULT_BUILDER_PARALLELISM = Runtime.getRuntime().availableProcessors() / 2 + 1;
91  
92      private final Logger logger = LoggerFactory.getLogger(getClass());
93      private final ModelBuilder modelBuilder;
94      private final ModelProcessor modelProcessor;
95      private final ProjectBuildingHelper projectBuildingHelper;
96      private final MavenRepositorySystem repositorySystem;
97      private final org.eclipse.aether.RepositorySystem repoSystem;
98      private final RemoteRepositoryManager repositoryManager;
99      private final ProjectDependenciesResolver dependencyResolver;
100     private final ModelCacheFactory modelCacheFactory;
101 
102     private final RootLocator rootLocator;
103 
104     @SuppressWarnings("checkstyle:ParameterNumber")
105     @Inject
106     public DefaultProjectBuilder(
107             ModelBuilder modelBuilder,
108             ModelProcessor modelProcessor,
109             ProjectBuildingHelper projectBuildingHelper,
110             MavenRepositorySystem repositorySystem,
111             RepositorySystem repoSystem,
112             RemoteRepositoryManager repositoryManager,
113             ProjectDependenciesResolver dependencyResolver,
114             ModelCacheFactory modelCacheFactory,
115             RootLocator rootLocator) {
116         this.modelBuilder = modelBuilder;
117         this.modelProcessor = modelProcessor;
118         this.projectBuildingHelper = projectBuildingHelper;
119         this.repositorySystem = repositorySystem;
120         this.repoSystem = repoSystem;
121         this.repositoryManager = repositoryManager;
122         this.dependencyResolver = dependencyResolver;
123         this.modelCacheFactory = modelCacheFactory;
124         this.rootLocator = rootLocator;
125     }
126     // ----------------------------------------------------------------------
127     // MavenProjectBuilder Implementation
128     // ----------------------------------------------------------------------
129 
130     @Override
131     public ProjectBuildingResult build(File pomFile, ProjectBuildingRequest request) throws ProjectBuildingException {
132         try (BuildSession bs = new BuildSession(request, false)) {
133             return bs.build(pomFile, new FileModelSource(pomFile));
134         }
135     }
136 
137     @Override
138     public ProjectBuildingResult build(ModelSource modelSource, ProjectBuildingRequest request)
139             throws ProjectBuildingException {
140         try (BuildSession bs = new BuildSession(request, false)) {
141             return bs.build(null, modelSource);
142         }
143     }
144 
145     @Override
146     public ProjectBuildingResult build(Artifact artifact, ProjectBuildingRequest request)
147             throws ProjectBuildingException {
148         return build(artifact, false, request);
149     }
150 
151     @Override
152     public ProjectBuildingResult build(Artifact artifact, boolean allowStubModel, ProjectBuildingRequest request)
153             throws ProjectBuildingException {
154         try (BuildSession bs = new BuildSession(request, false)) {
155             return bs.build(artifact, allowStubModel);
156         }
157     }
158 
159     @Override
160     public List<ProjectBuildingResult> build(List<File> pomFiles, boolean recursive, ProjectBuildingRequest request)
161             throws ProjectBuildingException {
162         try (BuildSession bs = new BuildSession(request, true)) {
163             return bs.build(pomFiles, recursive);
164         }
165     }
166 
167     static class InterimResult {
168 
169         File pomFile;
170 
171         ModelBuildingRequest request;
172 
173         ModelBuildingResult result;
174 
175         MavenProject project;
176 
177         boolean root;
178 
179         List<InterimResult> modules = Collections.emptyList();
180 
181         ProjectBuildingResult projectBuildingResult;
182 
183         InterimResult(
184                 File pomFile,
185                 ModelBuildingRequest request,
186                 ModelBuildingResult result,
187                 MavenProject project,
188                 boolean root) {
189             this.pomFile = pomFile;
190             this.request = request;
191             this.result = result;
192             this.project = project;
193             this.root = root;
194         }
195 
196         InterimResult(ModelBuildingRequest request, ProjectBuildingResult projectBuildingResult) {
197             this.request = request;
198             this.projectBuildingResult = projectBuildingResult;
199             this.pomFile = projectBuildingResult.getPomFile();
200             this.project = projectBuildingResult.getProject();
201         }
202     }
203 
204     class BuildSession implements AutoCloseable {
205         private final ProjectBuildingRequest request;
206         private final RepositorySystemSession session;
207         private final List<RemoteRepository> repositories;
208         private final ReactorModelPool modelPool;
209         private final TransformerContextBuilder transformerContextBuilder;
210         private final ExecutorService executor;
211 
212         BuildSession(ProjectBuildingRequest request, boolean localProjects) {
213             this.request = request;
214             this.session =
215                     RepositoryUtils.overlay(request.getLocalRepository(), request.getRepositorySession(), repoSystem);
216             this.repositories = RepositoryUtils.toRepos(request.getRemoteRepositories());
217             this.executor = createExecutor(getParallelism(request));
218             if (localProjects) {
219                 this.modelPool = new ReactorModelPool();
220                 this.transformerContextBuilder = modelBuilder.newTransformerContextBuilder();
221             } else {
222                 this.modelPool = null;
223                 this.transformerContextBuilder = null;
224             }
225         }
226 
227         ExecutorService createExecutor(int parallelism) {
228             //
229             // We need an executor that will not block.
230             // We can't use work stealing, as we are building a graph
231             // and this could lead to cycles where a thread waits for
232             // a task to finish, then execute another one which waits
233             // for the initial task...
234             // In order to work around that problem, we override the
235             // invokeAll method, so that whenever the method is called,
236             // the pool core size will be incremented before submitting
237             // all the tasks, then the thread will block waiting for all
238             // those subtasks to finish.
239             // This ensures the number of running workers is no more than
240             // the defined parallism, while making sure the pool will not
241             // be exhausted
242             //
243             return new ThreadPoolExecutor(
244                     parallelism, Integer.MAX_VALUE, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()) {
245                 final AtomicInteger parked = new AtomicInteger();
246 
247                 @Override
248                 public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
249                         throws InterruptedException {
250                     setCorePoolSize(parallelism + parked.incrementAndGet());
251                     try {
252                         return super.invokeAll(tasks);
253                     } finally {
254                         setCorePoolSize(parallelism + parked.decrementAndGet());
255                     }
256                 }
257             };
258         }
259 
260         @Override
261         public void close() {
262             this.executor.shutdownNow();
263         }
264 
265         private int getParallelism(ProjectBuildingRequest request) {
266             int parallelism = DEFAULT_BUILDER_PARALLELISM;
267             try {
268                 String str = request.getUserProperties().getProperty(BUILDER_PARALLELISM);
269                 if (str == null) {
270                     str = request.getSystemProperties().getProperty(BUILDER_PARALLELISM);
271                 }
272                 if (str != null) {
273                     parallelism = Integer.parseInt(str);
274                 }
275             } catch (Exception e) {
276                 // ignore
277             }
278             return Math.max(1, Math.min(parallelism, Runtime.getRuntime().availableProcessors()));
279         }
280 
281         ProjectBuildingResult build(File pomFile, ModelSource modelSource) throws ProjectBuildingException {
282             ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
283 
284             try {
285                 MavenProject project = request.getProject();
286 
287                 List<ModelProblem> modelProblems = null;
288                 Throwable error = null;
289 
290                 if (project == null) {
291                     ModelBuildingRequest request = getModelBuildingRequest();
292 
293                     project = new MavenProject();
294                     project.setFile(pomFile);
295 
296                     DefaultModelBuildingListener listener =
297                             new DefaultModelBuildingListener(project, projectBuildingHelper, this.request);
298                     request.setModelBuildingListener(listener);
299 
300                     request.setPomFile(pomFile);
301                     request.setModelSource(modelSource);
302                     request.setLocationTracking(true);
303 
304                     if (pomFile != null) {
305                         project.setRootDirectory(
306                                 rootLocator.findRoot(pomFile.getParentFile().toPath()));
307                     }
308 
309                     ModelBuildingResult result;
310                     try {
311                         result = modelBuilder.build(request);
312                     } catch (ModelBuildingException e) {
313                         result = e.getResult();
314                         if (result == null || result.getEffectiveModel() == null) {
315                             throw new ProjectBuildingException(e.getModelId(), e.getMessage(), pomFile, e);
316                         }
317                         // validation error, continue project building and delay failing to help IDEs
318                         error = e;
319                     }
320 
321                     modelProblems = result.getProblems();
322 
323                     initProject(project, Collections.emptyMap(), result);
324                 } else if (request.isResolveDependencies()) {
325                     projectBuildingHelper.selectProjectRealm(project);
326                 }
327 
328                 DependencyResolutionResult resolutionResult = null;
329 
330                 if (request.isResolveDependencies()) {
331                     resolutionResult = resolveDependencies(project);
332                 }
333 
334                 ProjectBuildingResult result =
335                         new DefaultProjectBuildingResult(project, modelProblems, resolutionResult);
336 
337                 if (error != null) {
338                     ProjectBuildingException e = new ProjectBuildingException(Arrays.asList(result));
339                     e.initCause(error);
340                     throw e;
341                 }
342 
343                 return result;
344             } finally {
345                 Thread.currentThread().setContextClassLoader(oldContextClassLoader);
346             }
347         }
348 
349         ProjectBuildingResult build(Artifact artifact, boolean allowStubModel) throws ProjectBuildingException {
350             org.eclipse.aether.artifact.Artifact pomArtifact = RepositoryUtils.toArtifact(artifact);
351             pomArtifact = ArtifactDescriptorUtils.toPomArtifact(pomArtifact);
352 
353             boolean localProject;
354 
355             try {
356                 ArtifactRequest pomRequest = new ArtifactRequest();
357                 pomRequest.setArtifact(pomArtifact);
358                 pomRequest.setRepositories(repositories);
359                 ArtifactResult pomResult = repoSystem.resolveArtifact(session, pomRequest);
360 
361                 pomArtifact = pomResult.getArtifact();
362                 localProject = pomResult.getRepository() instanceof WorkspaceRepository;
363             } catch (org.eclipse.aether.resolution.ArtifactResolutionException e) {
364                 if (e.getResults().get(0).isMissing() && allowStubModel) {
365                     return build(null, createStubModelSource(artifact));
366                 }
367                 throw new ProjectBuildingException(
368                         artifact.getId(), "Error resolving project artifact: " + e.getMessage(), e);
369             }
370 
371             File pomFile = pomArtifact.getFile();
372 
373             if (!artifact.isResolved() && "pom".equals(artifact.getType())) {
374                 artifact.selectVersion(pomArtifact.getVersion());
375                 artifact.setFile(pomFile);
376                 artifact.setResolved(true);
377             }
378 
379             if (localProject) {
380                 return build(pomFile, new FileModelSource(pomFile));
381             } else {
382                 return build(
383                         null,
384                         new ArtifactModelSource(
385                                 pomFile, artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion()));
386             }
387         }
388 
389         List<ProjectBuildingResult> build(List<File> pomFiles, boolean recursive) throws ProjectBuildingException {
390             List<ProjectBuildingResult> results = doBuild(pomFiles, recursive);
391             if (results.stream()
392                     .flatMap(r -> r.getProblems().stream())
393                     .anyMatch(p -> p.getSeverity() != ModelProblem.Severity.WARNING)) {
394                 ModelProblem cycle = results.stream()
395                         .flatMap(r -> r.getProblems().stream())
396                         .filter(p -> p.getException() instanceof CycleDetectedException)
397                         .findAny()
398                         .orElse(null);
399                 if (cycle != null) {
400                     throw new RuntimeException(new ProjectCycleException(
401                             "The projects in the reactor contain a cyclic reference: " + cycle.getMessage(),
402                             (CycleDetectedException) cycle.getException()));
403                 }
404                 throw new ProjectBuildingException(results);
405             }
406 
407             return results;
408         }
409 
410         List<ProjectBuildingResult> doBuild(List<File> pomFiles, boolean recursive) {
411             Map<File, MavenProject> projectIndex = new ConcurrentHashMap<>(256);
412 
413             // phase 1: get file Models from the reactor.
414             List<InterimResult> interimResults = build(projectIndex, pomFiles, new LinkedHashSet<>(), true, recursive);
415 
416             ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
417 
418             try {
419                 // Phase 2: get effective models from the reactor
420                 List<ProjectBuildingResult> results = build(projectIndex, interimResults);
421 
422                 if (Features.buildConsumer(request.getUserProperties())) {
423                     request.getRepositorySession()
424                             .getData()
425                             .set(TransformerContext.KEY, transformerContextBuilder.build());
426                 }
427 
428                 return results;
429             } finally {
430                 Thread.currentThread().setContextClassLoader(oldContextClassLoader);
431             }
432         }
433 
434         @SuppressWarnings("checkstyle:parameternumber")
435         private List<InterimResult> build(
436                 Map<File, MavenProject> projectIndex,
437                 List<File> pomFiles,
438                 Set<File> aggregatorFiles,
439                 boolean root,
440                 boolean recursive) {
441             List<Callable<InterimResult>> tasks = pomFiles.stream()
442                     .map(pomFile -> ((Callable<InterimResult>)
443                             () -> build(projectIndex, pomFile, concat(aggregatorFiles, pomFile), root, recursive)))
444                     .collect(Collectors.toList());
445             try {
446                 List<Future<InterimResult>> futures = executor.invokeAll(tasks);
447                 List<InterimResult> list = new ArrayList<>();
448                 for (Future<InterimResult> future : futures) {
449                     InterimResult interimResult = future.get();
450                     list.add(interimResult);
451                 }
452                 return list;
453             } catch (Exception e) {
454                 throw new RuntimeException(e);
455             }
456         }
457 
458         private <T> Set<T> concat(Set<T> set, T elem) {
459             Set<T> newSet = new HashSet<>(set);
460             newSet.add(elem);
461             return newSet;
462         }
463 
464         @SuppressWarnings("checkstyle:parameternumber")
465         private InterimResult build(
466                 Map<File, MavenProject> projectIndex,
467                 File pomFile,
468                 Set<File> aggregatorFiles,
469                 boolean isRoot,
470                 boolean recursive) {
471             MavenProject project = new MavenProject();
472             project.setFile(pomFile);
473 
474             project.setRootDirectory(
475                     rootLocator.findRoot(pomFile.getParentFile().toPath()));
476 
477             ModelBuildingRequest modelBuildingRequest = getModelBuildingRequest()
478                     .setPomFile(pomFile)
479                     .setTwoPhaseBuilding(true)
480                     .setLocationTracking(true);
481 
482             DefaultModelBuildingListener listener =
483                     new DefaultModelBuildingListener(project, projectBuildingHelper, request);
484             modelBuildingRequest.setModelBuildingListener(listener);
485 
486             ModelBuildingResult result;
487             try {
488                 result = modelBuilder.build(modelBuildingRequest);
489             } catch (ModelBuildingException e) {
490                 result = e.getResult();
491                 if (result == null || result.getFileModel() == null) {
492                     return new InterimResult(
493                             modelBuildingRequest,
494                             new DefaultProjectBuildingResult(e.getModelId(), pomFile, e.getProblems()));
495                 }
496                 // validation error, continue project building and delay failing to help IDEs
497                 // result.getProblems().addAll(e.getProblems()) ?
498             }
499 
500             Model model = modelBuildingRequest.getFileModel();
501 
502             modelPool.put(model.getPomFile().toPath(), model);
503 
504             InterimResult interimResult = new InterimResult(pomFile, modelBuildingRequest, result, project, isRoot);
505 
506             if (recursive) {
507                 File basedir = pomFile.getParentFile();
508                 List<File> moduleFiles = new ArrayList<>();
509                 for (String module : model.getModules()) {
510                     if (module == null || module.isEmpty()) {
511                         continue;
512                     }
513 
514                     module = module.replace('\\', File.separatorChar).replace('/', File.separatorChar);
515 
516                     File moduleFile = modelProcessor.locateExistingPom(new File(basedir, module));
517 
518                     if (moduleFile == null) {
519                         ModelProblem problem = new DefaultModelProblem(
520                                 "Child module " + moduleFile + " of " + pomFile + " does not exist",
521                                 ModelProblem.Severity.ERROR,
522                                 ModelProblem.Version.BASE,
523                                 model,
524                                 -1,
525                                 -1,
526                                 null);
527                         result.getProblems().add(problem);
528 
529                         continue;
530                     }
531 
532                     if (Os.IS_WINDOWS) {
533                         // we don't canonicalize on unix to avoid interfering with symlinks
534                         try {
535                             moduleFile = moduleFile.getCanonicalFile();
536                         } catch (IOException e) {
537                             moduleFile = moduleFile.getAbsoluteFile();
538                         }
539                     } else {
540                         moduleFile = new File(moduleFile.toURI().normalize());
541                     }
542 
543                     if (aggregatorFiles.contains(moduleFile)) {
544                         StringBuilder buffer = new StringBuilder(256);
545                         for (File aggregatorFile : aggregatorFiles) {
546                             buffer.append(aggregatorFile).append(" -> ");
547                         }
548                         buffer.append(moduleFile);
549 
550                         ModelProblem problem = new DefaultModelProblem(
551                                 "Child module " + moduleFile + " of " + pomFile + " forms aggregation cycle " + buffer,
552                                 ModelProblem.Severity.ERROR,
553                                 ModelProblem.Version.BASE,
554                                 model,
555                                 -1,
556                                 -1,
557                                 null);
558                         result.getProblems().add(problem);
559 
560                         continue;
561                     }
562 
563                     moduleFiles.add(moduleFile);
564                 }
565 
566                 if (!moduleFiles.isEmpty()) {
567                     interimResult.modules = build(projectIndex, moduleFiles, aggregatorFiles, false, recursive);
568                 }
569             }
570 
571             projectIndex.put(pomFile, project);
572 
573             return interimResult;
574         }
575 
576         private List<ProjectBuildingResult> build(
577                 Map<File, MavenProject> projectIndex, List<InterimResult> interimResults) {
578             // The transformation may need to access dependencies raw models,
579             // which may cause some re-entrance in the build() method and can
580             // actually cause deadlocks.  In order to workaround the problem,
581             // we do a first pass by reading all rawModels in order.
582             if (modelBuilder instanceof DefaultModelBuilder) {
583                 List<ProjectBuildingResult> results = new ArrayList<>();
584                 DefaultModelBuilder dmb = (DefaultModelBuilder) modelBuilder;
585                 boolean failure = false;
586                 for (InterimResult r : interimResults) {
587                     DefaultProjectBuildingResult res;
588                     try {
589                         Model model = dmb.buildRawModel(r.request);
590                         res = new DefaultProjectBuildingResult(model.getId(), model.getPomFile(), null);
591                     } catch (ModelBuildingException e) {
592                         failure = true;
593                         res = new DefaultProjectBuildingResult(e.getModelId(), r.request.getPomFile(), e.getProblems());
594                     }
595                     results.add(res);
596                 }
597                 if (failure) {
598                     return results;
599                 }
600             }
601 
602             List<Callable<List<ProjectBuildingResult>>> callables = interimResults.stream()
603                     .map(interimResult ->
604                             (Callable<List<ProjectBuildingResult>>) () -> doBuild(projectIndex, interimResult))
605                     .collect(Collectors.toList());
606 
607             try {
608                 List<Future<List<ProjectBuildingResult>>> futures = executor.invokeAll(callables);
609                 return futures.stream()
610                         .map(listFuture -> {
611                             try {
612                                 return listFuture.get();
613                             } catch (InterruptedException e) {
614                                 uncheckedThrow(e);
615                                 return null;
616                             } catch (ExecutionException e) {
617                                 uncheckedThrow(e.getCause());
618                                 return null;
619                             }
620                         })
621                         .flatMap(List::stream)
622                         .collect(Collectors.toList());
623             } catch (InterruptedException e) {
624                 uncheckedThrow(e);
625                 return null;
626             }
627         }
628 
629         private List<ProjectBuildingResult> doBuild(Map<File, MavenProject> projectIndex, InterimResult interimResult) {
630             if (interimResult.projectBuildingResult != null) {
631                 return Collections.singletonList(interimResult.projectBuildingResult);
632             }
633             MavenProject project = interimResult.project;
634             try {
635                 ModelBuildingResult result = modelBuilder.build(interimResult.request, interimResult.result);
636 
637                 // 2nd pass of initialization: resolve and build parent if necessary
638                 try {
639                     initProject(project, projectIndex, result);
640                 } catch (InvalidArtifactRTException iarte) {
641                     result.getProblems()
642                             .add(new DefaultModelProblem(
643                                     null,
644                                     ModelProblem.Severity.ERROR,
645                                     null,
646                                     result.getEffectiveModel(),
647                                     -1,
648                                     -1,
649                                     iarte));
650                 }
651 
652                 List<ProjectBuildingResult> results = build(projectIndex, interimResult.modules);
653 
654                 project.setExecutionRoot(interimResult.root);
655                 project.setCollectedProjects(
656                         results.stream().map(ProjectBuildingResult::getProject).collect(Collectors.toList()));
657                 DependencyResolutionResult resolutionResult = null;
658                 if (request.isResolveDependencies()) {
659                     resolutionResult = resolveDependencies(project);
660                 }
661 
662                 results.add(new DefaultProjectBuildingResult(project, result.getProblems(), resolutionResult));
663 
664                 return results;
665             } catch (ModelBuildingException e) {
666                 DefaultProjectBuildingResult result;
667                 if (project == null || interimResult.result.getEffectiveModel() == null) {
668                     result = new DefaultProjectBuildingResult(e.getModelId(), interimResult.pomFile, e.getProblems());
669                 } else {
670                     project.setModel(interimResult.result.getEffectiveModel());
671                     result = new DefaultProjectBuildingResult(project, e.getProblems(), null);
672                 }
673                 return Collections.singletonList(result);
674             }
675         }
676 
677         @SuppressWarnings("checkstyle:methodlength")
678         private void initProject(MavenProject project, Map<File, MavenProject> projects, ModelBuildingResult result) {
679             project.setModel(result.getEffectiveModel());
680             project.setOriginalModel(result.getFileModel());
681 
682             initParent(project, projects, result);
683 
684             Artifact projectArtifact = repositorySystem.createArtifact(
685                     project.getGroupId(), project.getArtifactId(), project.getVersion(), null, project.getPackaging());
686             project.setArtifact(projectArtifact);
687 
688             // only set those on 2nd phase, ignore on 1st pass
689             if (project.getFile() != null) {
690                 Build build = project.getBuild();
691                 project.addScriptSourceRoot(build.getScriptSourceDirectory());
692                 project.addCompileSourceRoot(build.getSourceDirectory());
693                 project.addTestCompileSourceRoot(build.getTestSourceDirectory());
694             }
695 
696             List<Profile> activeProfiles = new ArrayList<>();
697             activeProfiles.addAll(
698                     result.getActivePomProfiles(result.getModelIds().get(0)));
699             activeProfiles.addAll(result.getActiveExternalProfiles());
700             project.setActiveProfiles(activeProfiles);
701 
702             project.setInjectedProfileIds("external", getProfileIds(result.getActiveExternalProfiles()));
703             for (String modelId : result.getModelIds()) {
704                 project.setInjectedProfileIds(modelId, getProfileIds(result.getActivePomProfiles(modelId)));
705             }
706 
707             //
708             // All the parts that were taken out of MavenProject for Maven 4.0.0
709             //
710 
711             project.setProjectBuildingRequest(request);
712 
713             // pluginArtifacts
714             Set<Artifact> pluginArtifacts = new HashSet<>();
715             for (Plugin plugin : project.getBuildPlugins()) {
716                 Artifact artifact = repositorySystem.createPluginArtifact(plugin);
717 
718                 if (artifact != null) {
719                     pluginArtifacts.add(artifact);
720                 }
721             }
722             project.setPluginArtifacts(pluginArtifacts);
723 
724             // reportArtifacts
725             Set<Artifact> reportArtifacts = new HashSet<>();
726             for (ReportPlugin report : project.getReportPlugins()) {
727                 Plugin pp = new Plugin();
728                 pp.setGroupId(report.getGroupId());
729                 pp.setArtifactId(report.getArtifactId());
730                 pp.setVersion(report.getVersion());
731 
732                 Artifact artifact = repositorySystem.createPluginArtifact(pp);
733 
734                 if (artifact != null) {
735                     reportArtifacts.add(artifact);
736                 }
737             }
738             project.setReportArtifacts(reportArtifacts);
739 
740             // extensionArtifacts
741             Set<Artifact> extensionArtifacts = new HashSet<>();
742             List<Extension> extensions = project.getBuildExtensions();
743             if (extensions != null) {
744                 for (Extension ext : extensions) {
745                     String version;
746                     if (ext.getVersion() == null || ext.getVersion().isEmpty()) {
747                         version = "RELEASE";
748                     } else {
749                         version = ext.getVersion();
750                     }
751 
752                     Artifact artifact = repositorySystem.createArtifact(
753                             ext.getGroupId(), ext.getArtifactId(), version, null, "jar");
754 
755                     if (artifact != null) {
756                         extensionArtifacts.add(artifact);
757                     }
758                 }
759             }
760             project.setExtensionArtifacts(extensionArtifacts);
761 
762             // managedVersionMap
763             Map<String, Artifact> map = Collections.emptyMap();
764             final DependencyManagement dependencyManagement = project.getDependencyManagement();
765             if (dependencyManagement != null
766                     && dependencyManagement.getDependencies() != null
767                     && !dependencyManagement.getDependencies().isEmpty()) {
768                 map = new LazyMap<>(() -> {
769                     Map<String, Artifact> tmp = new HashMap<>();
770                     for (Dependency d : dependencyManagement.getDependencies()) {
771                         Artifact artifact = repositorySystem.createDependencyArtifact(d);
772                         if (artifact != null) {
773                             tmp.put(d.getManagementKey(), artifact);
774                         }
775                     }
776                     return Collections.unmodifiableMap(tmp);
777                 });
778             }
779             project.setManagedVersionMap(map);
780 
781             // release artifact repository
782             if (project.getDistributionManagement() != null
783                     && project.getDistributionManagement().getRepository() != null) {
784                 try {
785                     DeploymentRepository r = project.getDistributionManagement().getRepository();
786                     if (r.getId() != null
787                             && !r.getId().isEmpty()
788                             && r.getUrl() != null
789                             && !r.getUrl().isEmpty()) {
790                         ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository(r);
791                         repositorySystem.injectProxy(request.getRepositorySession(), Arrays.asList(repo));
792                         repositorySystem.injectAuthentication(request.getRepositorySession(), Arrays.asList(repo));
793                         project.setReleaseArtifactRepository(repo);
794                     }
795                 } catch (InvalidRepositoryException e) {
796                     throw new IllegalStateException(
797                             "Failed to create release distribution repository for " + project.getId(), e);
798                 }
799             }
800 
801             // snapshot artifact repository
802             if (project.getDistributionManagement() != null
803                     && project.getDistributionManagement().getSnapshotRepository() != null) {
804                 try {
805                     DeploymentRepository r = project.getDistributionManagement().getSnapshotRepository();
806                     if (r.getId() != null
807                             && !r.getId().isEmpty()
808                             && r.getUrl() != null
809                             && !r.getUrl().isEmpty()) {
810                         ArtifactRepository repo = MavenRepositorySystem.buildArtifactRepository(r);
811                         repositorySystem.injectProxy(request.getRepositorySession(), Arrays.asList(repo));
812                         repositorySystem.injectAuthentication(request.getRepositorySession(), Arrays.asList(repo));
813                         project.setSnapshotArtifactRepository(repo);
814                     }
815                 } catch (InvalidRepositoryException e) {
816                     throw new IllegalStateException(
817                             "Failed to create snapshot distribution repository for " + project.getId(), e);
818                 }
819             }
820         }
821 
822         private void initParent(MavenProject project, Map<File, MavenProject> projects, ModelBuildingResult result) {
823             Model parentModel = result.getModelIds().size() > 1
824                             && !result.getModelIds().get(1).isEmpty()
825                     ? result.getRawModel(result.getModelIds().get(1))
826                     : null;
827 
828             if (parentModel != null) {
829                 final String parentGroupId = inheritedGroupId(result, 1);
830                 final String parentVersion = inheritedVersion(result, 1);
831 
832                 project.setParentArtifact(repositorySystem.createProjectArtifact(
833                         parentGroupId, parentModel.getArtifactId(), parentVersion));
834 
835                 // org.apache.maven.its.mng4834:parent:0.1
836                 String parentModelId = result.getModelIds().get(1);
837                 File parentPomFile = result.getRawModel(parentModelId).getPomFile();
838                 MavenProject parent = parentPomFile != null ? projects.get(parentPomFile) : null;
839                 if (parent == null) {
840                     //
841                     // At this point the DefaultModelBuildingListener has fired and it populates the
842                     // remote repositories with those found in the pom.xml, along with the existing externally
843                     // defined repositories.
844                     //
845                     request.setRemoteRepositories(project.getRemoteArtifactRepositories());
846                     if (parentPomFile != null) {
847                         project.setParentFile(parentPomFile);
848                         try {
849                             parent = build(parentPomFile, new FileModelSource(parentPomFile))
850                                     .getProject();
851                         } catch (ProjectBuildingException e) {
852                             // MNG-4488 where let invalid parents slide on by
853                             if (logger.isDebugEnabled()) {
854                                 // Message below is checked for in the MNG-2199 core IT.
855                                 logger.warn("Failed to build parent project for " + project.getId(), e);
856                             } else {
857                                 // Message below is checked for in the MNG-2199 core IT.
858                                 logger.warn("Failed to build parent project for " + project.getId());
859                             }
860                         }
861                     } else {
862                         Artifact parentArtifact = project.getParentArtifact();
863                         try {
864                             parent = build(parentArtifact, false).getProject();
865                         } catch (ProjectBuildingException e) {
866                             // MNG-4488 where let invalid parents slide on by
867                             if (logger.isDebugEnabled()) {
868                                 // Message below is checked for in the MNG-2199 core IT.
869                                 logger.warn("Failed to build parent project for " + project.getId(), e);
870                             } else {
871                                 // Message below is checked for in the MNG-2199 core IT.
872                                 logger.warn("Failed to build parent project for " + project.getId());
873                             }
874                         }
875                     }
876                 }
877                 project.setParent(parent);
878                 if (project.getParentFile() == null && parent != null) {
879                     project.setParentFile(parent.getFile());
880                 }
881             }
882         }
883 
884         private ModelBuildingRequest getModelBuildingRequest() {
885             ModelBuildingRequest modelBuildingRequest = new DefaultModelBuildingRequest();
886 
887             RequestTrace trace = RequestTrace.newChild(null, request).newChild(modelBuildingRequest);
888 
889             ModelResolver resolver = new ProjectModelResolver(
890                     session,
891                     trace,
892                     repoSystem,
893                     repositoryManager,
894                     repositories,
895                     request.getRepositoryMerging(),
896                     modelPool);
897 
898             modelBuildingRequest.setValidationLevel(request.getValidationLevel());
899             modelBuildingRequest.setProcessPlugins(request.isProcessPlugins());
900             modelBuildingRequest.setProfiles(request.getProfiles());
901             modelBuildingRequest.setActiveProfileIds(request.getActiveProfileIds());
902             modelBuildingRequest.setInactiveProfileIds(request.getInactiveProfileIds());
903             modelBuildingRequest.setSystemProperties(request.getSystemProperties());
904             modelBuildingRequest.setUserProperties(request.getUserProperties());
905             modelBuildingRequest.setBuildStartTime(request.getBuildStartTime());
906             modelBuildingRequest.setModelResolver(resolver);
907             // this is a hint that we want to build 1 file, so don't cache. See MNG-7063
908             if (modelPool != null) {
909                 modelBuildingRequest.setModelCache(modelCacheFactory.createCache(session));
910             }
911             modelBuildingRequest.setTransformerContextBuilder(transformerContextBuilder);
912             InternalSession session = (InternalSession) this.session.getData().get(InternalSession.class);
913             if (session != null) {
914                 try {
915                     modelBuildingRequest.setRootDirectory(session.getRootDirectory());
916                 } catch (IllegalStateException e) {
917                     // can happen if root directory cannot be found, just ignore
918                 }
919             }
920 
921             return modelBuildingRequest;
922         }
923 
924         private DependencyResolutionResult resolveDependencies(MavenProject project) {
925             DependencyResolutionResult resolutionResult;
926 
927             try {
928                 DefaultDependencyResolutionRequest resolution =
929                         new DefaultDependencyResolutionRequest(project, session);
930                 resolutionResult = dependencyResolver.resolve(resolution);
931             } catch (DependencyResolutionException e) {
932                 resolutionResult = e.getResult();
933             }
934 
935             Set<Artifact> artifacts = new LinkedHashSet<>();
936             if (resolutionResult.getDependencyGraph() != null) {
937                 RepositoryUtils.toArtifacts(
938                         artifacts,
939                         resolutionResult.getDependencyGraph().getChildren(),
940                         Collections.singletonList(project.getArtifact().getId()),
941                         null);
942 
943                 // Maven 2.x quirk: an artifact always points at the local repo, regardless whether resolved or not
944                 LocalRepositoryManager lrm = session.getLocalRepositoryManager();
945                 for (Artifact artifact : artifacts) {
946                     if (!artifact.isResolved()) {
947                         String path = lrm.getPathForLocalArtifact(RepositoryUtils.toArtifact(artifact));
948                         artifact.setFile(new File(lrm.getRepository().getBasedir(), path));
949                     }
950                 }
951             }
952             project.setResolvedArtifacts(artifacts);
953             project.setArtifacts(artifacts);
954 
955             return resolutionResult;
956         }
957     }
958 
959     private List<String> getProfileIds(List<org.apache.maven.model.Profile> profiles) {
960         return profiles.stream().map(org.apache.maven.model.Profile::getId).collect(Collectors.toList());
961     }
962 
963     private static ModelSource createStubModelSource(Artifact artifact) {
964         StringBuilder buffer = new StringBuilder(1024);
965 
966         buffer.append("<?xml version='1.0'?>");
967         buffer.append("<project>");
968         buffer.append("<modelVersion>4.0.0</modelVersion>");
969         buffer.append("<groupId>").append(artifact.getGroupId()).append("</groupId>");
970         buffer.append("<artifactId>").append(artifact.getArtifactId()).append("</artifactId>");
971         buffer.append("<version>").append(artifact.getBaseVersion()).append("</version>");
972         buffer.append("<packaging>").append(artifact.getType()).append("</packaging>");
973         buffer.append("</project>");
974 
975         return new StringModelSource(buffer, artifact.getId());
976     }
977 
978     private static String inheritedGroupId(final ModelBuildingResult result, final int modelIndex) {
979         String groupId = null;
980         final String modelId = result.getModelIds().get(modelIndex);
981 
982         if (!modelId.isEmpty()) {
983             final Model model = result.getRawModel(modelId);
984             groupId = model.getGroupId() != null ? model.getGroupId() : inheritedGroupId(result, modelIndex + 1);
985         }
986 
987         return groupId;
988     }
989 
990     private static String inheritedVersion(final ModelBuildingResult result, final int modelIndex) {
991         String version = null;
992         final String modelId = result.getModelIds().get(modelIndex);
993 
994         if (!modelId.isEmpty()) {
995             version = result.getRawModel(modelId).getVersion();
996             if (version == null) {
997                 version = inheritedVersion(result, modelIndex + 1);
998             }
999         }
1000 
1001         return version;
1002     }
1003 
1004     static <T extends Throwable> void uncheckedThrow(Throwable t) throws T {
1005         throw (T) t; // rely on vacuous cast
1006     }
1007 
1008     static class LazyMap<K, V> extends AbstractMap<K, V> {
1009         private final Supplier<Map<K, V>> supplier;
1010         private volatile Map<K, V> delegate;
1011 
1012         LazyMap(Supplier<Map<K, V>> supplier) {
1013             this.supplier = supplier;
1014         }
1015 
1016         @Override
1017         public Set<Entry<K, V>> entrySet() {
1018             if (delegate == null) {
1019                 synchronized (this) {
1020                     if (delegate == null) {
1021                         delegate = supplier.get();
1022                     }
1023                 }
1024             }
1025             return delegate.entrySet();
1026         }
1027     }
1028 }