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.internal.impl.model;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.lang.reflect.Field;
24  import java.nio.file.Path;
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.LinkedHashSet;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Objects;
33  import java.util.Properties;
34  import java.util.concurrent.Callable;
35  import java.util.concurrent.atomic.AtomicReference;
36  import java.util.function.BiConsumer;
37  import java.util.function.Supplier;
38  import java.util.function.UnaryOperator;
39  import java.util.stream.Collectors;
40  import java.util.stream.Stream;
41  
42  import org.apache.maven.api.Session;
43  import org.apache.maven.api.VersionRange;
44  import org.apache.maven.api.annotations.Nullable;
45  import org.apache.maven.api.di.Inject;
46  import org.apache.maven.api.di.Named;
47  import org.apache.maven.api.di.Singleton;
48  import org.apache.maven.api.feature.Features;
49  import org.apache.maven.api.model.Activation;
50  import org.apache.maven.api.model.ActivationFile;
51  import org.apache.maven.api.model.Build;
52  import org.apache.maven.api.model.Dependency;
53  import org.apache.maven.api.model.DependencyManagement;
54  import org.apache.maven.api.model.Exclusion;
55  import org.apache.maven.api.model.InputLocation;
56  import org.apache.maven.api.model.InputLocationTracker;
57  import org.apache.maven.api.model.InputSource;
58  import org.apache.maven.api.model.Model;
59  import org.apache.maven.api.model.Parent;
60  import org.apache.maven.api.model.Plugin;
61  import org.apache.maven.api.model.PluginManagement;
62  import org.apache.maven.api.model.Profile;
63  import org.apache.maven.api.services.BuilderProblem.Severity;
64  import org.apache.maven.api.services.ModelBuilder;
65  import org.apache.maven.api.services.ModelBuilderException;
66  import org.apache.maven.api.services.ModelBuilderRequest;
67  import org.apache.maven.api.services.ModelBuilderResult;
68  import org.apache.maven.api.services.ModelCache;
69  import org.apache.maven.api.services.ModelProblem;
70  import org.apache.maven.api.services.ModelProblemCollector;
71  import org.apache.maven.api.services.ModelResolver;
72  import org.apache.maven.api.services.ModelResolverException;
73  import org.apache.maven.api.services.ModelSource;
74  import org.apache.maven.api.services.ModelTransformer;
75  import org.apache.maven.api.services.ModelTransformerContext;
76  import org.apache.maven.api.services.ModelTransformerContextBuilder;
77  import org.apache.maven.api.services.ModelTransformerException;
78  import org.apache.maven.api.services.Source;
79  import org.apache.maven.api.services.SuperPomProvider;
80  import org.apache.maven.api.services.VersionParserException;
81  import org.apache.maven.api.services.model.*;
82  import org.apache.maven.api.services.xml.XmlReaderException;
83  import org.apache.maven.api.services.xml.XmlReaderRequest;
84  import org.apache.maven.internal.impl.resolver.DefaultModelCache;
85  import org.apache.maven.internal.impl.resolver.DefaultModelRepositoryHolder;
86  import org.apache.maven.internal.impl.resolver.DefaultModelResolver;
87  import org.apache.maven.model.v4.MavenTransformer;
88  import org.codehaus.plexus.interpolation.InterpolationException;
89  import org.codehaus.plexus.interpolation.Interpolator;
90  import org.codehaus.plexus.interpolation.MapBasedValueSource;
91  import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
92  import org.codehaus.plexus.interpolation.StringSearchInterpolator;
93  import org.slf4j.Logger;
94  import org.slf4j.LoggerFactory;
95  
96  /**
97   */
98  @Named
99  @Singleton
100 public class DefaultModelBuilder implements ModelBuilder {
101 
102     private static final String RAW = "raw";
103     private static final String FILE = "file";
104     private static final String IMPORT = "import";
105 
106     private final Logger logger = LoggerFactory.getLogger(getClass());
107 
108     private final ModelProcessor modelProcessor;
109     private final ModelValidator modelValidator;
110     private final ModelNormalizer modelNormalizer;
111     private final ModelInterpolator modelInterpolator;
112     private final ModelPathTranslator modelPathTranslator;
113     private final ModelUrlNormalizer modelUrlNormalizer;
114     private final SuperPomProvider superPomProvider;
115     private final InheritanceAssembler inheritanceAssembler;
116     private final ProfileSelector profileSelector;
117     private final ProfileInjector profileInjector;
118     private final PluginManagementInjector pluginManagementInjector;
119     private final DependencyManagementInjector dependencyManagementInjector;
120     private final DependencyManagementImporter dependencyManagementImporter;
121     private final LifecycleBindingsInjector lifecycleBindingsInjector;
122     private final PluginConfigurationExpander pluginConfigurationExpander;
123     private final ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator;
124     private final ModelTransformer transformer;
125     private final ModelVersionParser versionParser;
126 
127     @SuppressWarnings("checkstyle:ParameterNumber")
128     @Inject
129     public DefaultModelBuilder(
130             ModelProcessor modelProcessor,
131             ModelValidator modelValidator,
132             ModelNormalizer modelNormalizer,
133             ModelInterpolator modelInterpolator,
134             ModelPathTranslator modelPathTranslator,
135             ModelUrlNormalizer modelUrlNormalizer,
136             SuperPomProvider superPomProvider,
137             InheritanceAssembler inheritanceAssembler,
138             ProfileSelector profileSelector,
139             ProfileInjector profileInjector,
140             PluginManagementInjector pluginManagementInjector,
141             DependencyManagementInjector dependencyManagementInjector,
142             DependencyManagementImporter dependencyManagementImporter,
143             @Nullable LifecycleBindingsInjector lifecycleBindingsInjector,
144             PluginConfigurationExpander pluginConfigurationExpander,
145             ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator,
146             ModelTransformer transformer,
147             ModelVersionParser versionParser) {
148         this.modelProcessor = modelProcessor;
149         this.modelValidator = modelValidator;
150         this.modelNormalizer = modelNormalizer;
151         this.modelInterpolator = modelInterpolator;
152         this.modelPathTranslator = modelPathTranslator;
153         this.modelUrlNormalizer = modelUrlNormalizer;
154         this.superPomProvider = superPomProvider;
155         this.inheritanceAssembler = inheritanceAssembler;
156         this.profileSelector = profileSelector;
157         this.profileInjector = profileInjector;
158         this.pluginManagementInjector = pluginManagementInjector;
159         this.dependencyManagementInjector = dependencyManagementInjector;
160         this.dependencyManagementImporter = dependencyManagementImporter;
161         this.lifecycleBindingsInjector = lifecycleBindingsInjector;
162         this.pluginConfigurationExpander = pluginConfigurationExpander;
163         this.profileActivationFilePathInterpolator = profileActivationFilePathInterpolator;
164         this.transformer = transformer;
165         this.versionParser = versionParser;
166     }
167 
168     @Override
169     public ModelTransformerContextBuilder newTransformerContextBuilder() {
170         return new DefaultModelTransformerContextBuilder(this);
171     }
172 
173     @Override
174     public ModelBuilderResult build(ModelBuilderRequest request) throws ModelBuilderException {
175         request = fillRequestDefaults(request);
176         if (request.getInterimResult() != null) {
177             return build(request, request.getInterimResult(), new LinkedHashSet<>());
178         } else {
179             return build(request, new LinkedHashSet<>());
180         }
181     }
182 
183     private static ModelBuilderRequest fillRequestDefaults(ModelBuilderRequest request) {
184         ModelBuilderRequest.ModelBuilderRequestBuilder builder = ModelBuilderRequest.builder(request);
185         if (request.getModelCache() == null) {
186             builder.modelCache(new DefaultModelCache());
187         }
188         if (request.getModelRepositoryHolder() == null) {
189             builder.modelRepositoryHolder(new DefaultModelRepositoryHolder(
190                     request.getSession(),
191                     DefaultModelRepositoryHolder.RepositoryMerging.POM_DOMINANT,
192                     request.getSession().getRemoteRepositories()));
193         }
194         if (request.getModelResolver() == null) {
195             builder.modelResolver(new DefaultModelResolver());
196         }
197         return builder.build();
198     }
199 
200     protected ModelBuilderResult build(ModelBuilderRequest request, Collection<String> importIds)
201             throws ModelBuilderException {
202         // phase 1
203         DefaultModelBuilderResult result = new DefaultModelBuilderResult();
204 
205         DefaultModelProblemCollector problems = new DefaultModelProblemCollector(result);
206 
207         // read and validate raw model
208         Model fileModel = readFileModel(request, problems);
209         result.setFileModel(fileModel);
210 
211         Model activatedFileModel = activateFileModel(fileModel, request, result, problems);
212         result.setActivatedFileModel(activatedFileModel);
213 
214         if (!request.isTwoPhaseBuilding()) {
215             return build(request, result, importIds);
216         } else if (hasModelErrors(problems)) {
217             throw problems.newModelBuilderException();
218         }
219 
220         return result;
221     }
222 
223     private Model activateFileModel(
224             Model inputModel,
225             ModelBuilderRequest request,
226             DefaultModelBuilderResult result,
227             DefaultModelProblemCollector problems)
228             throws ModelBuilderException {
229         problems.setRootModel(inputModel);
230 
231         // profile activation
232         DefaultProfileActivationContext profileActivationContext = getProfileActivationContext(request, inputModel);
233 
234         problems.setSource("(external profiles)");
235         List<Profile> activeExternalProfiles =
236                 profileSelector.getActiveProfiles(request.getProfiles(), profileActivationContext, problems);
237 
238         result.setActiveExternalProfiles(activeExternalProfiles);
239 
240         if (!activeExternalProfiles.isEmpty()) {
241             Properties profileProps = new Properties();
242             for (Profile profile : activeExternalProfiles) {
243                 profileProps.putAll(profile.getProperties());
244             }
245             profileProps.putAll(profileActivationContext.getUserProperties());
246             profileActivationContext.setUserProperties(profileProps);
247         }
248 
249         profileActivationContext.setProjectProperties(inputModel.getProperties());
250         problems.setSource(inputModel);
251         List<Profile> activePomProfiles =
252                 profileSelector.getActiveProfiles(inputModel.getProfiles(), profileActivationContext, problems);
253 
254         // model normalization
255         problems.setSource(inputModel);
256         inputModel = modelNormalizer.mergeDuplicates(inputModel, request, problems);
257 
258         Map<String, Activation> interpolatedActivations = getProfileActivations(inputModel);
259         inputModel = injectProfileActivations(inputModel, interpolatedActivations);
260 
261         // profile injection
262         inputModel = profileInjector.injectProfiles(inputModel, activePomProfiles, request, problems);
263         inputModel = profileInjector.injectProfiles(inputModel, activeExternalProfiles, request, problems);
264 
265         return inputModel;
266     }
267 
268     @SuppressWarnings("checkstyle:methodlength")
269     private Model readEffectiveModel(
270             final ModelBuilderRequest request,
271             final DefaultModelBuilderResult result,
272             DefaultModelProblemCollector problems)
273             throws ModelBuilderException {
274         Model inputModel = readRawModel(request, problems);
275         if (problems.hasFatalErrors()) {
276             throw problems.newModelBuilderException();
277         }
278 
279         inputModel = activateFileModel(inputModel, request, result, problems);
280 
281         problems.setRootModel(inputModel);
282 
283         ModelData resultData = new ModelData(request.getSource(), inputModel);
284         String superModelVersion = inputModel.getModelVersion() != null ? inputModel.getModelVersion() : "4.0.0";
285         if (!VALID_MODEL_VERSIONS.contains(superModelVersion)) {
286             // Maven 3.x is always using 4.0.0 version to load the supermodel, so
287             // do the same when loading a dependency.  The model validator will also
288             // check that field later.
289             superModelVersion = "4.0.0";
290         }
291         ModelData superData = new ModelData(null, getSuperModel(superModelVersion));
292 
293         // profile activation
294         DefaultProfileActivationContext profileActivationContext = getProfileActivationContext(request, inputModel);
295 
296         List<Profile> activeExternalProfiles = result.getActiveExternalProfiles();
297 
298         if (!activeExternalProfiles.isEmpty()) {
299             Properties profileProps = new Properties();
300             for (Profile profile : activeExternalProfiles) {
301                 profileProps.putAll(profile.getProperties());
302             }
303             profileProps.putAll(profileActivationContext.getUserProperties());
304             profileActivationContext.setUserProperties(profileProps);
305         }
306 
307         Collection<String> parentIds = new LinkedHashSet<>();
308 
309         List<Model> lineage = new ArrayList<>();
310 
311         for (ModelData currentData = resultData; ; ) {
312             String modelId = currentData.id();
313             result.addModelId(modelId);
314 
315             Model model = currentData.model();
316             result.setRawModel(modelId, model);
317             problems.setSource(model);
318 
319             // model normalization
320             model = modelNormalizer.mergeDuplicates(model, request, problems);
321 
322             // profile activation
323             profileActivationContext.setProjectProperties(model.getProperties());
324 
325             List<Profile> interpolatedProfiles =
326                     interpolateActivations(model.getProfiles(), profileActivationContext, problems);
327 
328             // profile injection
329             List<Profile> activePomProfiles =
330                     profileSelector.getActiveProfiles(interpolatedProfiles, profileActivationContext, problems);
331             result.setActivePomProfiles(modelId, activePomProfiles);
332             model = profileInjector.injectProfiles(model, activePomProfiles, request, problems);
333             if (currentData == resultData) {
334                 model = profileInjector.injectProfiles(model, activeExternalProfiles, request, problems);
335             }
336 
337             lineage.add(model);
338 
339             if (currentData == superData) {
340                 break;
341             }
342 
343             // add repositories specified by the current model so that we can resolve the parent
344             if (!model.getRepositories().isEmpty()) {
345                 List<String> oldRepos = request.getModelRepositoryHolder().getRepositories().stream()
346                         .map(Object::toString)
347                         .toList();
348                 request.getModelRepositoryHolder().merge(model.getRepositories(), false);
349                 List<String> newRepos = request.getModelRepositoryHolder().getRepositories().stream()
350                         .map(Object::toString)
351                         .toList();
352                 if (!Objects.equals(oldRepos, newRepos)) {
353                     logger.debug("Merging repositories from " + model.getId() + "\n"
354                             + newRepos.stream().map(s -> "    " + s).collect(Collectors.joining("\n")));
355                 }
356             }
357 
358             // we pass a cloned model, so that resolving the parent version does not affect the returned model
359             ModelData parentData = readParent(model, currentData.source(), request, problems);
360 
361             if (parentData == null) {
362                 currentData = superData;
363             } else if (!parentIds.add(parentData.id())) {
364                 StringBuilder message = new StringBuilder("The parents form a cycle: ");
365                 for (String parentId : parentIds) {
366                     message.append(parentId).append(" -> ");
367                 }
368                 message.append(parentData.id());
369 
370                 problems.add(Severity.FATAL, ModelProblem.Version.BASE, message.toString());
371 
372                 throw problems.newModelBuilderException();
373             } else {
374                 currentData = parentData;
375             }
376         }
377 
378         Model tmpModel = lineage.get(0);
379 
380         // inject interpolated activations
381         List<Profile> interpolated = interpolateActivations(tmpModel.getProfiles(), profileActivationContext, problems);
382         if (interpolated != tmpModel.getProfiles()) {
383             tmpModel = tmpModel.withProfiles(interpolated);
384         }
385 
386         // inject external profile into current model
387         tmpModel = profileInjector.injectProfiles(tmpModel, activeExternalProfiles, request, problems);
388 
389         lineage.set(0, tmpModel);
390 
391         checkPluginVersions(lineage, request, problems);
392 
393         // inheritance assembly
394         Model resultModel = assembleInheritance(lineage, request, problems);
395 
396         // consider caching inherited model
397 
398         problems.setSource(resultModel);
399         problems.setRootModel(resultModel);
400 
401         // model interpolation
402         resultModel = interpolateModel(resultModel, request, problems);
403 
404         // url normalization
405         resultModel = modelUrlNormalizer.normalize(resultModel, request);
406 
407         result.setEffectiveModel(resultModel);
408 
409         // Now the fully interpolated model is available: reconfigure the resolver
410         if (!resultModel.getRepositories().isEmpty()) {
411             List<String> oldRepos = request.getModelRepositoryHolder().getRepositories().stream()
412                     .map(Object::toString)
413                     .toList();
414             request.getModelRepositoryHolder().merge(resultModel.getRepositories(), true);
415             List<String> newRepos = request.getModelRepositoryHolder().getRepositories().stream()
416                     .map(Object::toString)
417                     .toList();
418             if (!Objects.equals(oldRepos, newRepos)) {
419                 logger.debug("Replacing repositories from " + resultModel.getId() + "\n"
420                         + newRepos.stream().map(s -> "    " + s).collect(Collectors.joining("\n")));
421             }
422         }
423 
424         return resultModel;
425     }
426 
427     private List<Profile> interpolateActivations(
428             List<Profile> profiles, DefaultProfileActivationContext context, DefaultModelProblemCollector problems) {
429         if (profiles.stream()
430                 .map(org.apache.maven.api.model.Profile::getActivation)
431                 .noneMatch(Objects::nonNull)) {
432             return profiles;
433         }
434         final Interpolator xform = new RegexBasedInterpolator();
435         xform.setCacheAnswers(true);
436         Stream.of(context.getUserProperties(), context.getSystemProperties())
437                 .map(MapBasedValueSource::new)
438                 .forEach(xform::addValueSource);
439 
440         class ProfileInterpolator extends MavenTransformer implements UnaryOperator<Profile> {
441             ProfileInterpolator() {
442                 super(s -> {
443                     if (isNotEmpty(s)) {
444                         try {
445                             return xform.interpolate(s);
446                         } catch (InterpolationException e) {
447                             problems.add(Severity.ERROR, ModelProblem.Version.BASE, e.getMessage(), e);
448                         }
449                     }
450                     return s;
451                 });
452             }
453 
454             @Override
455             public Profile apply(Profile p) {
456                 return Profile.newBuilder(p)
457                         .activation(transformActivation(p.getActivation()))
458                         .build();
459             }
460 
461             @Override
462             protected ActivationFile.Builder transformActivationFile_Missing(
463                     Supplier<? extends ActivationFile.Builder> creator,
464                     ActivationFile.Builder builder,
465                     ActivationFile target) {
466                 String path = target.getMissing();
467                 String xformed = transformPath(path, target, "missing");
468                 return xformed != path ? (builder != null ? builder : creator.get()).missing(xformed) : builder;
469             }
470 
471             @Override
472             protected ActivationFile.Builder transformActivationFile_Exists(
473                     Supplier<? extends ActivationFile.Builder> creator,
474                     ActivationFile.Builder builder,
475                     ActivationFile target) {
476                 final String path = target.getExists();
477                 final String xformed = transformPath(path, target, "exists");
478                 return xformed != path ? (builder != null ? builder : creator.get()).exists(xformed) : builder;
479             }
480 
481             private String transformPath(String path, ActivationFile target, String locationKey) {
482                 if (isNotEmpty(path)) {
483                     try {
484                         return profileActivationFilePathInterpolator.interpolate(path, context);
485                     } catch (InterpolationException e) {
486                         addInterpolationProblem(problems, target, path, e, locationKey);
487                     }
488                 }
489                 return path;
490             }
491         }
492         return profiles.stream().map(new ProfileInterpolator()).toList();
493     }
494 
495     private static void addInterpolationProblem(
496             DefaultModelProblemCollector problems,
497             InputLocationTracker target,
498             String path,
499             InterpolationException e,
500             String locationKey) {
501         problems.add(
502                 Severity.ERROR,
503                 ModelProblem.Version.BASE,
504                 "Failed to interpolate file location " + path + ": " + e.getMessage(),
505                 target.getLocation(locationKey),
506                 e);
507     }
508 
509     private static boolean isNotEmpty(String string) {
510         return string != null && !string.isEmpty();
511     }
512 
513     public ModelBuilderResult build(final ModelBuilderRequest request, final ModelBuilderResult result)
514             throws ModelBuilderException {
515         return build(request, result, new LinkedHashSet<>());
516     }
517 
518     public Model buildRawModel(ModelBuilderRequest request) throws ModelBuilderException {
519         request = fillRequestDefaults(request);
520         DefaultModelProblemCollector problems = new DefaultModelProblemCollector(new DefaultModelBuilderResult());
521         Model model = readRawModel(request, problems);
522         if (hasModelErrors(problems)) {
523             throw problems.newModelBuilderException();
524         }
525         return model;
526     }
527 
528     private ModelBuilderResult build(
529             ModelBuilderRequest request, final ModelBuilderResult phaseOneResult, Collection<String> importIds)
530             throws ModelBuilderException {
531         DefaultModelBuilderResult result = asDefaultModelBuilderResult(phaseOneResult);
532 
533         DefaultModelProblemCollector problems = new DefaultModelProblemCollector(result);
534 
535         // phase 2
536         Model resultModel = readEffectiveModel(request, result, problems);
537         problems.setSource(resultModel);
538         problems.setRootModel(resultModel);
539 
540         // model path translation
541         resultModel = modelPathTranslator.alignToBaseDirectory(resultModel, resultModel.getProjectDirectory(), request);
542 
543         // plugin management injection
544         resultModel = pluginManagementInjector.injectManagement(resultModel, request, problems);
545 
546         resultModel = fireEvent(resultModel, request, problems, ModelBuildingListener::buildExtensionsAssembled);
547 
548         if (request.isProcessPlugins()) {
549             if (lifecycleBindingsInjector == null) {
550                 throw new IllegalStateException("lifecycle bindings injector is missing");
551             }
552 
553             // lifecycle bindings injection
554             resultModel = lifecycleBindingsInjector.injectLifecycleBindings(resultModel, request, problems);
555         }
556 
557         // dependency management import
558         resultModel = importDependencyManagement(resultModel, request, problems, importIds);
559 
560         // dependency management injection
561         resultModel = dependencyManagementInjector.injectManagement(resultModel, request, problems);
562 
563         resultModel = modelNormalizer.injectDefaultValues(resultModel, request, problems);
564 
565         if (request.isProcessPlugins()) {
566             // plugins configuration
567             resultModel = pluginConfigurationExpander.expandPluginConfiguration(resultModel, request, problems);
568         }
569 
570         result.setEffectiveModel(resultModel);
571 
572         // effective model validation
573         modelValidator.validateEffectiveModel(resultModel, request, problems);
574 
575         if (hasModelErrors(problems)) {
576             throw problems.newModelBuilderException();
577         }
578 
579         return result;
580     }
581 
582     private DefaultModelBuilderResult asDefaultModelBuilderResult(ModelBuilderResult phaseOneResult) {
583         if (phaseOneResult instanceof DefaultModelBuilderResult) {
584             return (DefaultModelBuilderResult) phaseOneResult;
585         } else {
586             return new DefaultModelBuilderResult(phaseOneResult);
587         }
588     }
589 
590     public Result<? extends Model> buildRawModel(Path pomFile, int validationLevel, boolean locationTracking) {
591         return buildRawModel(pomFile, validationLevel, locationTracking, null);
592     }
593 
594     public Result<? extends Model> buildRawModel(
595             Path pomFile, int validationLevel, boolean locationTracking, ModelTransformerContext context) {
596         final ModelBuilderRequest request = ModelBuilderRequest.builder()
597                 .validationLevel(validationLevel)
598                 .locationTracking(locationTracking)
599                 .source(ModelSource.fromPath(pomFile))
600                 .build();
601         DefaultModelProblemCollector problems = new DefaultModelProblemCollector(new DefaultModelBuilderResult());
602         try {
603             Model model = readFileModel(request, problems);
604 
605             try {
606                 if (transformer != null && context != null) {
607                     transformer.transform(context, model, pomFile);
608                 }
609             } catch (ModelBuilderException e) {
610                 problems.add(Severity.FATAL, ModelProblem.Version.V40, null, e);
611             }
612 
613             return Result.newResult(model, problems.getProblems());
614         } catch (ModelBuilderException e) {
615             return Result.error(problems.getProblems());
616         }
617     }
618 
619     Model readFileModel(ModelBuilderRequest request, DefaultModelProblemCollector problems)
620             throws ModelBuilderException {
621         ModelSource modelSource = request.getSource();
622         Model model =
623                 cache(getModelCache(request), modelSource, FILE, () -> doReadFileModel(modelSource, request, problems));
624 
625         if (modelSource.getPath() != null) {
626             if (getTransformerContextBuilder(request) instanceof DefaultModelTransformerContextBuilder contextBuilder) {
627                 contextBuilder.putSource(getGroupId(model), model.getArtifactId(), modelSource);
628             }
629         }
630 
631         return model;
632     }
633 
634     @SuppressWarnings("checkstyle:methodlength")
635     private Model doReadFileModel(
636             ModelSource modelSource, ModelBuilderRequest request, DefaultModelProblemCollector problems)
637             throws ModelBuilderException {
638         Model model;
639         problems.setSource(modelSource.getLocation());
640         try {
641             boolean strict = request.getValidationLevel() >= ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_2_0;
642 
643             try (InputStream is = modelSource.openStream()) {
644                 model = modelProcessor.read(XmlReaderRequest.builder()
645                         .strict(strict)
646                         .location(modelSource.getLocation())
647                         .path(modelSource.getPath())
648                         .inputStream(is)
649                         .build());
650             } catch (XmlReaderException e) {
651                 if (!strict) {
652                     throw e;
653                 }
654                 try (InputStream is = modelSource.openStream()) {
655                     model = modelProcessor.read(XmlReaderRequest.builder()
656                             .strict(false)
657                             .location(modelSource.getLocation())
658                             .path(modelSource.getPath())
659                             .inputStream(is)
660                             .build());
661                 } catch (XmlReaderException ne) {
662                     // still unreadable even in non-strict mode, rethrow original error
663                     throw e;
664                 }
665 
666                 Severity severity = request.isProjectBuild() ? Severity.ERROR : Severity.WARNING;
667                 problems.add(
668                         severity,
669                         ModelProblem.Version.V20,
670                         "Malformed POM " + modelSource.getLocation() + ": " + e.getMessage(),
671                         e);
672             }
673 
674             InputLocation loc = model.getLocation("");
675             InputSource v4src = loc != null ? loc.getSource() : null;
676             if (v4src != null) {
677                 try {
678                     Field field = InputSource.class.getDeclaredField("modelId");
679                     field.setAccessible(true);
680                     field.set(v4src, ModelProblemUtils.toId(model));
681                 } catch (Throwable t) {
682                     // TODO: use a lazy source ?
683                     throw new IllegalStateException("Unable to set modelId on InputSource", t);
684                 }
685             }
686         } catch (XmlReaderException e) {
687             problems.add(
688                     Severity.FATAL,
689                     ModelProblem.Version.BASE,
690                     "Non-parseable POM " + modelSource.getLocation() + ": " + e.getMessage(),
691                     e);
692             throw problems.newModelBuilderException();
693         } catch (IOException e) {
694             String msg = e.getMessage();
695             if (msg == null || msg.isEmpty()) {
696                 // NOTE: There's java.nio.charset.MalformedInputException and sun.io.MalformedInputException
697                 if (e.getClass().getName().endsWith("MalformedInputException")) {
698                     msg = "Some input bytes do not match the file encoding.";
699                 } else {
700                     msg = e.getClass().getSimpleName();
701                 }
702             }
703             problems.add(
704                     Severity.FATAL,
705                     ModelProblem.Version.BASE,
706                     "Non-readable POM " + modelSource.getLocation() + ": " + msg,
707                     e);
708             throw problems.newModelBuilderException();
709         }
710 
711         if (modelSource.getPath() != null) {
712             model = model.withPomFile(modelSource.getPath());
713         }
714 
715         problems.setSource(model);
716         modelValidator.validateFileModel(model, request, problems);
717         if (hasFatalErrors(problems)) {
718             throw problems.newModelBuilderException();
719         }
720 
721         return model;
722     }
723 
724     Model readRawModel(ModelBuilderRequest request, DefaultModelProblemCollector problems)
725             throws ModelBuilderException {
726         ModelSource modelSource = request.getSource();
727 
728         ModelData modelData =
729                 cache(getModelCache(request), modelSource, RAW, () -> doReadRawModel(modelSource, request, problems));
730 
731         return modelData.model();
732     }
733 
734     private ModelData doReadRawModel(
735             ModelSource modelSource, ModelBuilderRequest request, DefaultModelProblemCollector problems)
736             throws ModelBuilderException {
737         Model rawModel = readFileModel(request, problems);
738         if (Features.buildConsumer(request.getUserProperties()) && modelSource.getPath() != null) {
739             Path pomFile = modelSource.getPath();
740 
741             try {
742                 ModelTransformerContextBuilder transformerContextBuilder = getTransformerContextBuilder(request);
743                 if (transformerContextBuilder != null) {
744                     ModelTransformerContext context = transformerContextBuilder.initialize(request, problems);
745                     rawModel = this.transformer.transform(context, rawModel, pomFile);
746                 }
747             } catch (ModelTransformerException e) {
748                 problems.add(Severity.FATAL, ModelProblem.Version.V40, null, e);
749             }
750         }
751 
752         modelValidator.validateRawModel(rawModel, request, problems);
753 
754         if (hasFatalErrors(problems)) {
755             throw problems.newModelBuilderException();
756         }
757 
758         return new ModelData(modelSource, rawModel);
759     }
760 
761     static String getGroupId(Model model) {
762         String groupId = model.getGroupId();
763         if (groupId == null && model.getParent() != null) {
764             groupId = model.getParent().getGroupId();
765         }
766         return groupId;
767     }
768 
769     private String getVersion(Model model) {
770         String version = model.getVersion();
771         if (version == null && model.getParent() != null) {
772             version = model.getParent().getVersion();
773         }
774         return version;
775     }
776 
777     private DefaultProfileActivationContext getProfileActivationContext(ModelBuilderRequest request, Model model) {
778         DefaultProfileActivationContext context = new DefaultProfileActivationContext();
779 
780         context.setActiveProfileIds(request.getActiveProfileIds());
781         context.setInactiveProfileIds(request.getInactiveProfileIds());
782         context.setSystemProperties(request.getSystemProperties());
783         // enrich user properties with project packaging
784         Map<String, String> userProperties = new HashMap<>(request.getUserProperties());
785         if (!userProperties.containsKey(ProfileActivationContext.PROPERTY_NAME_PACKAGING)) {
786             userProperties.put(ProfileActivationContext.PROPERTY_NAME_PACKAGING, model.getPackaging());
787         }
788         context.setUserProperties(userProperties);
789         context.setProjectDirectory(model.getProjectDirectory());
790 
791         return context;
792     }
793 
794     private void checkPluginVersions(List<Model> lineage, ModelBuilderRequest request, ModelProblemCollector problems) {
795         if (request.getValidationLevel() < ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_2_0) {
796             return;
797         }
798 
799         Map<String, Plugin> plugins = new HashMap<>();
800         Map<String, String> versions = new HashMap<>();
801         Map<String, String> managedVersions = new HashMap<>();
802 
803         for (int i = lineage.size() - 1; i >= 0; i--) {
804             Model model = lineage.get(i);
805             Build build = model.getBuild();
806             if (build != null) {
807                 for (Plugin plugin : build.getPlugins()) {
808                     String key = plugin.getKey();
809                     if (versions.get(key) == null) {
810                         versions.put(key, plugin.getVersion());
811                         plugins.put(key, plugin);
812                     }
813                 }
814                 PluginManagement mgmt = build.getPluginManagement();
815                 if (mgmt != null) {
816                     for (Plugin plugin : mgmt.getPlugins()) {
817                         String key = plugin.getKey();
818                         managedVersions.computeIfAbsent(key, k -> plugin.getVersion());
819                     }
820                 }
821             }
822         }
823 
824         for (String key : versions.keySet()) {
825             if (versions.get(key) == null && managedVersions.get(key) == null) {
826                 InputLocation location = plugins.get(key).getLocation("");
827                 problems.add(
828                         Severity.WARNING,
829                         ModelProblem.Version.V20,
830                         "'build.plugins.plugin.version' for " + key + " is missing.",
831                         location);
832             }
833         }
834     }
835 
836     private Model assembleInheritance(
837             List<Model> lineage, ModelBuilderRequest request, ModelProblemCollector problems) {
838         Model parent = lineage.get(lineage.size() - 1);
839         for (int i = lineage.size() - 2; i >= 0; i--) {
840             Model child = lineage.get(i);
841             parent = inheritanceAssembler.assembleModelInheritance(child, parent, request, problems);
842         }
843         return parent;
844     }
845 
846     private Map<String, Activation> getProfileActivations(Model model) {
847         return model.getProfiles().stream()
848                 .filter(p -> p.getActivation() != null)
849                 .collect(Collectors.toMap(Profile::getId, Profile::getActivation));
850     }
851 
852     private Model injectProfileActivations(Model model, Map<String, Activation> activations) {
853         List<Profile> profiles = new ArrayList<>();
854         boolean modified = false;
855         for (Profile profile : model.getProfiles()) {
856             Activation activation = profile.getActivation();
857             if (activation != null) {
858                 // restore activation
859                 profile = profile.withActivation(activations.get(profile.getId()));
860                 modified = true;
861             }
862             profiles.add(profile);
863         }
864         return modified ? model.withProfiles(profiles) : model;
865     }
866 
867     private Model interpolateModel(Model model, ModelBuilderRequest request, ModelProblemCollector problems) {
868         Model interpolatedModel =
869                 modelInterpolator.interpolateModel(model, model.getProjectDirectory(), request, problems);
870         if (interpolatedModel.getParent() != null) {
871             StringSearchInterpolator ssi = new StringSearchInterpolator();
872             ssi.addValueSource(new MapBasedValueSource(request.getSession().getUserProperties()));
873             ssi.addValueSource(new MapBasedValueSource(model.getProperties()));
874             ssi.addValueSource(new MapBasedValueSource(request.getSession().getSystemProperties()));
875             try {
876                 String interpolated =
877                         ssi.interpolate(interpolatedModel.getParent().getVersion());
878                 interpolatedModel = interpolatedModel.withParent(
879                         interpolatedModel.getParent().withVersion(interpolated));
880             } catch (Exception e) {
881                 problems.add(
882                         Severity.ERROR,
883                         ModelProblem.Version.BASE,
884                         "Failed to interpolate field: "
885                                 + interpolatedModel.getParent().getVersion()
886                                 + " on class: ",
887                         e);
888             }
889         }
890         interpolatedModel = interpolatedModel.withPomFile(model.getPomFile());
891         return interpolatedModel;
892     }
893 
894     private ModelData readParent(
895             Model childModel,
896             ModelSource childSource,
897             ModelBuilderRequest request,
898             DefaultModelProblemCollector problems)
899             throws ModelBuilderException {
900         ModelData parentData = null;
901 
902         Parent parent = childModel.getParent();
903         if (parent != null) {
904             parentData = readParentLocally(childModel, childSource, request, problems);
905             if (parentData == null) {
906                 parentData = readParentExternally(childModel, request, problems);
907             }
908 
909             Model parentModel = parentData.model();
910             if (!"pom".equals(parentModel.getPackaging())) {
911                 problems.add(
912                         Severity.ERROR,
913                         ModelProblem.Version.BASE,
914                         "Invalid packaging for parent POM " + ModelProblemUtils.toSourceHint(parentModel)
915                                 + ", must be \"pom\" but is \"" + parentModel.getPackaging() + "\"",
916                         parentModel.getLocation("packaging"));
917             }
918         }
919 
920         return parentData;
921     }
922 
923     private ModelData readParentLocally(
924             Model childModel,
925             ModelSource childSource,
926             ModelBuilderRequest request,
927             DefaultModelProblemCollector problems)
928             throws ModelBuilderException {
929         final Parent parent = childModel.getParent();
930         final ModelSource candidateSource;
931         final Model candidateModel;
932         final WorkspaceModelResolver resolver = getWorkspaceModelResolver(request);
933         if (resolver == null) {
934             candidateSource = getParentPomFile(childModel, childSource);
935 
936             if (candidateSource == null) {
937                 return null;
938             }
939 
940             ModelBuilderRequest candidateBuildRequest = ModelBuilderRequest.build(request, candidateSource);
941 
942             candidateModel = readRawModel(candidateBuildRequest, problems);
943         } else {
944             try {
945                 candidateModel =
946                         resolver.resolveRawModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion());
947             } catch (ModelBuilderException e) {
948                 problems.add(Severity.FATAL, ModelProblem.Version.BASE, e.getMessage(), parent.getLocation(""), e);
949                 throw problems.newModelBuilderException();
950             }
951             if (candidateModel == null) {
952                 return null;
953             }
954             candidateSource = ModelSource.fromPath(candidateModel.getPomFile());
955         }
956 
957         //
958         // TODO jvz Why isn't all this checking the job of the duty of the workspace resolver, we know that we
959         // have a model that is suitable, yet more checks are done here and the one for the version is problematic
960         // before because with parents as ranges it will never work in this scenario.
961         //
962 
963         String groupId = getGroupId(candidateModel);
964         String artifactId = candidateModel.getArtifactId();
965 
966         if (groupId == null
967                 || !groupId.equals(parent.getGroupId())
968                 || artifactId == null
969                 || !artifactId.equals(parent.getArtifactId())) {
970             StringBuilder buffer = new StringBuilder(256);
971             buffer.append("'parent.relativePath'");
972             if (childModel != problems.getRootModel()) {
973                 buffer.append(" of POM ").append(ModelProblemUtils.toSourceHint(childModel));
974             }
975             buffer.append(" points at ").append(groupId).append(':').append(artifactId);
976             buffer.append(" instead of ").append(parent.getGroupId()).append(':');
977             buffer.append(parent.getArtifactId()).append(", please verify your project structure");
978 
979             problems.setSource(childModel);
980             problems.add(Severity.WARNING, ModelProblem.Version.BASE, buffer.toString(), parent.getLocation(""));
981             return null;
982         }
983 
984         String version = getVersion(candidateModel);
985         if (version != null && parent.getVersion() != null && !version.equals(parent.getVersion())) {
986             try {
987                 VersionRange parentRange = versionParser.parseVersionRange(parent.getVersion());
988                 if (!parentRange.contains(versionParser.parseVersion(version))) {
989                     // version skew drop back to resolution from the repository
990                     return null;
991                 }
992 
993                 // Validate versions aren't inherited when using parent ranges the same way as when read externally.
994                 String rawChildModelVersion = childModel.getVersion();
995 
996                 if (rawChildModelVersion == null) {
997                     // Message below is checked for in the MNG-2199 core IT.
998                     problems.add(
999                             Severity.FATAL,
1000                             ModelProblem.Version.V31,
1001                             "Version must be a constant",
1002                             childModel.getLocation(""));
1003 
1004                 } else {
1005                     if (rawChildVersionReferencesParent(rawChildModelVersion)) {
1006                         // Message below is checked for in the MNG-2199 core IT.
1007                         problems.add(
1008                                 Severity.FATAL,
1009                                 ModelProblem.Version.V31,
1010                                 "Version must be a constant",
1011                                 childModel.getLocation("version"));
1012                     }
1013                 }
1014 
1015                 // MNG-2199: What else to check here ?
1016             } catch (VersionParserException e) {
1017                 // invalid version range, so drop back to resolution from the repository
1018                 return null;
1019             }
1020         }
1021 
1022         //
1023         // Here we just need to know that a version is fine to use but this validation we can do in our workspace
1024         // resolver.
1025         //
1026 
1027         /*
1028          * if ( version == null || !version.equals( parent.getVersion() ) ) { return null; }
1029          */
1030 
1031         return new ModelData(candidateSource, candidateModel);
1032     }
1033 
1034     private boolean rawChildVersionReferencesParent(String rawChildModelVersion) {
1035         return rawChildModelVersion.equals("${pom.version}")
1036                 || rawChildModelVersion.equals("${project.version}")
1037                 || rawChildModelVersion.equals("${pom.parent.version}")
1038                 || rawChildModelVersion.equals("${project.parent.version}");
1039     }
1040 
1041     private ModelSource getParentPomFile(Model childModel, ModelSource source) {
1042         String parentPath = childModel.getParent().getRelativePath();
1043         if (parentPath == null || parentPath.isEmpty()) {
1044             return null;
1045         } else {
1046             return source.resolve(modelProcessor::locateExistingPom, parentPath);
1047         }
1048     }
1049 
1050     private ModelData readParentExternally(
1051             Model childModel, ModelBuilderRequest request, DefaultModelProblemCollector problems)
1052             throws ModelBuilderException {
1053         problems.setSource(childModel);
1054 
1055         Parent parent = childModel.getParent();
1056 
1057         String groupId = parent.getGroupId();
1058         String artifactId = parent.getArtifactId();
1059         String version = parent.getVersion();
1060 
1061         ModelResolver modelResolver = getModelResolver(request);
1062         Objects.requireNonNull(
1063                 modelResolver,
1064                 String.format(
1065                         "request.modelResolver cannot be null (parent POM %s and POM %s)",
1066                         ModelProblemUtils.toId(groupId, artifactId, version),
1067                         ModelProblemUtils.toSourceHint(childModel)));
1068 
1069         ModelSource modelSource;
1070         try {
1071             AtomicReference<Parent> modified = new AtomicReference<>();
1072             Session session = request.getSession()
1073                     .withRemoteRepositories(request.getModelRepositoryHolder().getRepositories());
1074             modelSource = modelResolver.resolveModel(session, parent, modified);
1075             if (modified.get() != null) {
1076                 parent = modified.get();
1077             }
1078         } catch (ModelResolverException e) {
1079             // Message below is checked for in the MNG-2199 core IT.
1080             StringBuilder buffer = new StringBuilder(256);
1081             buffer.append("Non-resolvable parent POM");
1082             if (!containsCoordinates(e.getMessage(), groupId, artifactId, version)) {
1083                 buffer.append(' ').append(ModelProblemUtils.toId(groupId, artifactId, version));
1084             }
1085             if (childModel != problems.getRootModel()) {
1086                 buffer.append(" for ").append(ModelProblemUtils.toId(childModel));
1087             }
1088             buffer.append(": ").append(e.getMessage());
1089             if (childModel.getProjectDirectory() != null) {
1090                 if (parent.getRelativePath() == null || parent.getRelativePath().isEmpty()) {
1091                     buffer.append(" and 'parent.relativePath' points at no local POM");
1092                 } else {
1093                     buffer.append(" and 'parent.relativePath' points at wrong local POM");
1094                 }
1095             }
1096 
1097             problems.add(Severity.FATAL, ModelProblem.Version.BASE, buffer.toString(), parent.getLocation(""), e);
1098             throw problems.newModelBuilderException();
1099         }
1100 
1101         int validationLevel = Math.min(request.getValidationLevel(), ModelBuilderRequest.VALIDATION_LEVEL_MAVEN_2_0);
1102         ModelBuilderRequest lenientRequest = ModelBuilderRequest.builder(request)
1103                 .validationLevel(validationLevel)
1104                 .projectBuild(false)
1105                 .source(modelSource)
1106                 .build();
1107 
1108         Model parentModel = readRawModel(lenientRequest, problems);
1109 
1110         if (!parent.getVersion().equals(version)) {
1111             String rawChildModelVersion = childModel.getVersion();
1112 
1113             if (rawChildModelVersion == null) {
1114                 // Message below is checked for in the MNG-2199 core IT.
1115                 problems.add(
1116                         Severity.FATAL,
1117                         ModelProblem.Version.V31,
1118                         "Version must be a constant",
1119                         childModel.getLocation(""));
1120 
1121             } else {
1122                 if (rawChildVersionReferencesParent(rawChildModelVersion)) {
1123                     // Message below is checked for in the MNG-2199 core IT.
1124                     problems.add(
1125                             Severity.FATAL,
1126                             ModelProblem.Version.V31,
1127                             "Version must be a constant",
1128                             childModel.getLocation("version"));
1129                 }
1130             }
1131 
1132             // MNG-2199: What else to check here ?
1133         }
1134 
1135         return new ModelData(modelSource, parentModel);
1136     }
1137 
1138     private Model getSuperModel(String modelVersion) {
1139         return superPomProvider.getSuperPom(modelVersion);
1140     }
1141 
1142     private Model importDependencyManagement(
1143             Model model,
1144             ModelBuilderRequest request,
1145             DefaultModelProblemCollector problems,
1146             Collection<String> importIds) {
1147         DependencyManagement depMgmt = model.getDependencyManagement();
1148 
1149         if (depMgmt == null) {
1150             return model;
1151         }
1152 
1153         String importing = model.getGroupId() + ':' + model.getArtifactId() + ':' + model.getVersion();
1154 
1155         importIds.add(importing);
1156 
1157         List<DependencyManagement> importMgmts = null;
1158 
1159         List<Dependency> deps = new ArrayList<>(depMgmt.getDependencies());
1160         for (Iterator<Dependency> it = deps.iterator(); it.hasNext(); ) {
1161             Dependency dependency = it.next();
1162 
1163             if (!("pom".equals(dependency.getType()) && "import".equals(dependency.getScope()))
1164                     || "bom".equals(dependency.getType())) {
1165                 continue;
1166             }
1167 
1168             it.remove();
1169 
1170             DependencyManagement importMgmt = loadDependencyManagement(model, request, problems, dependency, importIds);
1171 
1172             if (importMgmt != null) {
1173                 if (importMgmts == null) {
1174                     importMgmts = new ArrayList<>();
1175                 }
1176 
1177                 importMgmts.add(importMgmt);
1178             }
1179         }
1180 
1181         importIds.remove(importing);
1182 
1183         model = model.withDependencyManagement(model.getDependencyManagement().withDependencies(deps));
1184 
1185         return dependencyManagementImporter.importManagement(model, importMgmts, request, problems);
1186     }
1187 
1188     private DependencyManagement loadDependencyManagement(
1189             Model model,
1190             ModelBuilderRequest request,
1191             DefaultModelProblemCollector problems,
1192             Dependency dependency,
1193             Collection<String> importIds) {
1194         String groupId = dependency.getGroupId();
1195         String artifactId = dependency.getArtifactId();
1196         String version = dependency.getVersion();
1197 
1198         if (groupId == null || groupId.isEmpty()) {
1199             problems.add(
1200                     Severity.ERROR,
1201                     ModelProblem.Version.BASE,
1202                     "'dependencyManagement.dependencies.dependency.groupId' for " + dependency.getManagementKey()
1203                             + " is missing.",
1204                     dependency.getLocation(""));
1205             return null;
1206         }
1207         if (artifactId == null || artifactId.isEmpty()) {
1208             problems.add(
1209                     Severity.ERROR,
1210                     ModelProblem.Version.BASE,
1211                     "'dependencyManagement.dependencies.dependency.artifactId' for " + dependency.getManagementKey()
1212                             + " is missing.",
1213                     dependency.getLocation(""));
1214             return null;
1215         }
1216         if (version == null || version.isEmpty()) {
1217             problems.add(
1218                     Severity.ERROR,
1219                     ModelProblem.Version.BASE,
1220                     "'dependencyManagement.dependencies.dependency.version' for " + dependency.getManagementKey()
1221                             + " is missing.",
1222                     dependency.getLocation(""));
1223             return null;
1224         }
1225 
1226         String imported = groupId + ':' + artifactId + ':' + version;
1227 
1228         if (importIds.contains(imported)) {
1229             StringBuilder message =
1230                     new StringBuilder("The dependencies of type=pom and with scope=import form a cycle: ");
1231             for (String modelId : importIds) {
1232                 message.append(modelId).append(" -> ");
1233             }
1234             message.append(imported);
1235             problems.add(Severity.ERROR, ModelProblem.Version.BASE, message.toString());
1236 
1237             return null;
1238         }
1239 
1240         Model importModel = cache(
1241                 getModelCache(request),
1242                 groupId,
1243                 artifactId,
1244                 version,
1245                 IMPORT,
1246                 () -> doLoadDependencyManagement(
1247                         model, request, problems, dependency, groupId, artifactId, version, importIds));
1248         DependencyManagement importMgmt = importModel != null ? importModel.getDependencyManagement() : null;
1249         if (importMgmt == null) {
1250             importMgmt = DependencyManagement.newInstance();
1251         }
1252 
1253         // [MNG-5600] Dependency management import should support exclusions.
1254         List<Exclusion> exclusions = dependency.getExclusions();
1255         if (importMgmt != null && !exclusions.isEmpty()) {
1256             // Dependency excluded from import.
1257             List<Dependency> dependencies = importMgmt.getDependencies().stream()
1258                     .filter(candidate -> exclusions.stream().noneMatch(exclusion -> match(exclusion, candidate)))
1259                     .map(candidate -> candidate.withExclusions(exclusions))
1260                     .collect(Collectors.toList());
1261             importMgmt = importMgmt.withDependencies(dependencies);
1262         }
1263 
1264         return importMgmt;
1265     }
1266 
1267     private boolean match(Exclusion exclusion, Dependency candidate) {
1268         return match(exclusion.getGroupId(), candidate.getGroupId())
1269                 && match(exclusion.getArtifactId(), candidate.getArtifactId());
1270     }
1271 
1272     private boolean match(String match, String text) {
1273         return match.equals("*") || match.equals(text);
1274     }
1275 
1276     @SuppressWarnings("checkstyle:parameternumber")
1277     private Model doLoadDependencyManagement(
1278             Model model,
1279             ModelBuilderRequest request,
1280             DefaultModelProblemCollector problems,
1281             Dependency dependency,
1282             String groupId,
1283             String artifactId,
1284             String version,
1285             Collection<String> importIds) {
1286         final WorkspaceModelResolver workspaceResolver = getWorkspaceModelResolver(request);
1287         final ModelResolver modelResolver = getModelResolver(request);
1288         if (workspaceResolver == null && modelResolver == null) {
1289             throw new NullPointerException(String.format(
1290                     "request.workspaceModelResolver and request.modelResolver cannot be null (parent POM %s and POM %s)",
1291                     ModelProblemUtils.toId(groupId, artifactId, version), ModelProblemUtils.toSourceHint(model)));
1292         }
1293 
1294         Model importModel = null;
1295         if (workspaceResolver != null) {
1296             try {
1297                 importModel = workspaceResolver.resolveEffectiveModel(groupId, artifactId, version);
1298             } catch (ModelBuilderException e) {
1299                 problems.add(Severity.FATAL, ModelProblem.Version.BASE, null, e);
1300                 return null;
1301             }
1302         }
1303 
1304         // no workspace resolver or workspace resolver returned null (i.e. model not in workspace)
1305         if (importModel == null) {
1306             final ModelSource importSource;
1307             try {
1308                 Session session = request.getSession()
1309                         .withRemoteRepositories(
1310                                 request.getModelRepositoryHolder().getRepositories());
1311                 importSource = modelResolver.resolveModel(session, dependency, new AtomicReference<>());
1312             } catch (ModelBuilderException e) {
1313                 StringBuilder buffer = new StringBuilder(256);
1314                 buffer.append("Non-resolvable import POM");
1315                 if (!containsCoordinates(e.getMessage(), groupId, artifactId, version)) {
1316                     buffer.append(' ').append(ModelProblemUtils.toId(groupId, artifactId, version));
1317                 }
1318                 buffer.append(": ").append(e.getMessage());
1319 
1320                 problems.add(
1321                         Severity.ERROR, ModelProblem.Version.BASE, buffer.toString(), dependency.getLocation(""), e);
1322                 return null;
1323             }
1324 
1325             Path rootDirectory;
1326             try {
1327                 rootDirectory = request.getSession().getRootDirectory();
1328             } catch (IllegalStateException e) {
1329                 rootDirectory = null;
1330             }
1331             if (importSource.getPath() != null && rootDirectory != null) {
1332                 Path sourcePath = importSource.getPath();
1333                 if (sourcePath.startsWith(rootDirectory)) {
1334                     problems.add(
1335                             Severity.WARNING,
1336                             ModelProblem.Version.BASE,
1337                             "BOM imports from within reactor should be avoided",
1338                             dependency.getLocation(""));
1339                 }
1340             }
1341 
1342             final ModelBuilderResult importResult;
1343             try {
1344                 ModelBuilderRequest importRequest = ModelBuilderRequest.builder()
1345                         .session(request.getSession()
1346                                 .withRemoteRepositories(
1347                                         request.getModelRepositoryHolder().getRepositories()))
1348                         .validationLevel(ModelBuilderRequest.VALIDATION_LEVEL_MINIMAL)
1349                         .systemProperties(request.getSystemProperties())
1350                         .userProperties(request.getUserProperties())
1351                         .source(importSource)
1352                         .modelResolver(modelResolver)
1353                         .modelCache(request.getModelCache())
1354                         .modelRepositoryHolder(
1355                                 request.getModelRepositoryHolder().copy())
1356                         .twoPhaseBuilding(false)
1357                         .build();
1358                 importResult = build(importRequest, importIds);
1359             } catch (ModelBuilderException e) {
1360                 e.getResult().getProblems().forEach(problems::add);
1361                 return null;
1362             }
1363 
1364             importResult.getProblems().forEach(problems::add);
1365 
1366             importModel = importResult.getEffectiveModel();
1367         }
1368 
1369         return importModel;
1370     }
1371 
1372     private static <T> T cache(
1373             ModelCache cache, String groupId, String artifactId, String version, String tag, Callable<T> supplier) {
1374         Supplier<T> s = asSupplier(supplier);
1375         if (cache == null) {
1376             return s.get();
1377         } else {
1378             return cache.computeIfAbsent(groupId, artifactId, version, tag, s);
1379         }
1380     }
1381 
1382     private static <T> T cache(ModelCache cache, Source source, String tag, Callable<T> supplier) {
1383         Supplier<T> s = asSupplier(supplier);
1384         if (cache == null) {
1385             return s.get();
1386         } else {
1387             return cache.computeIfAbsent(source, tag, s);
1388         }
1389     }
1390 
1391     private static <T> Supplier<T> asSupplier(Callable<T> supplier) {
1392         return () -> {
1393             try {
1394                 return supplier.call();
1395             } catch (Exception e) {
1396                 uncheckedThrow(e);
1397                 return null;
1398             }
1399         };
1400     }
1401 
1402     static <T extends Throwable> void uncheckedThrow(Throwable t) throws T {
1403         throw (T) t; // rely on vacuous cast
1404     }
1405 
1406     private Model fireEvent(
1407             Model model,
1408             ModelBuilderRequest request,
1409             ModelProblemCollector problems,
1410             BiConsumer<ModelBuildingListener, ModelBuildingEvent> catapult) {
1411         ModelBuildingListener listener = getModelBuildingListener(request);
1412 
1413         if (listener != null) {
1414             AtomicReference<Model> m = new AtomicReference<>(model);
1415 
1416             ModelBuildingEvent event = new DefaultModelBuildingEvent(model, m::set, request, problems);
1417 
1418             catapult.accept(listener, event);
1419 
1420             return m.get();
1421         }
1422 
1423         return model;
1424     }
1425 
1426     private boolean containsCoordinates(String message, String groupId, String artifactId, String version) {
1427         return message != null
1428                 && (groupId == null || message.contains(groupId))
1429                 && (artifactId == null || message.contains(artifactId))
1430                 && (version == null || message.contains(version));
1431     }
1432 
1433     protected boolean hasModelErrors(ModelProblemCollector problems) {
1434         return problems.hasErrors();
1435     }
1436 
1437     protected boolean hasFatalErrors(ModelProblemCollector problems) {
1438         return problems.hasFatalErrors();
1439     }
1440 
1441     ModelProcessor getModelProcessor() {
1442         return modelProcessor;
1443     }
1444 
1445     private static ModelCache getModelCache(ModelBuilderRequest request) {
1446         return request.getModelCache();
1447     }
1448 
1449     private static ModelBuildingListener getModelBuildingListener(ModelBuilderRequest request) {
1450         return (ModelBuildingListener) request.getListener();
1451     }
1452 
1453     private static WorkspaceModelResolver getWorkspaceModelResolver(ModelBuilderRequest request) {
1454         return null; // request.getWorkspaceModelResolver();
1455     }
1456 
1457     private static ModelResolver getModelResolver(ModelBuilderRequest request) {
1458         return request.getModelResolver();
1459     }
1460 
1461     private static ModelTransformerContextBuilder getTransformerContextBuilder(ModelBuilderRequest request) {
1462         return request.getTransformerContextBuilder();
1463     }
1464 }